Search code examples
matlabplotmatlab-figure

How to concatenate these subplots on one graph?


Here is the a simpler version of my code.

.....
ch_array = [36, 40, 44, 48, 149, 161];
figure;

for i=1:length(ch_array) 
    ch = ch_array(i);     
    subplot(3, 3, i);
    eval(sprintf('plot(mean_a_%d_f, ''r'')', ch));
    hold on;
    eval(sprintf('plot(mean_b_%d_f, ''b'')', ch));
    xlabel('Subcarrier (f)');
    ylabel('Absolute values');
    eval(sprintf('title(''Channel: %d'')', ch));
end
.....

The mean_a and mean_b depend on the ch_array so that as a result, there are mean_a_36_f, mean_a_40_f,..., mean_a_161_f and the same thing with the mean_b.

This for loop plots graphs according to ch_array, the following figure:

enter image description here

As you can see, for each ch_array element is plotted the corresponding mean_a_ch and mean_b_ch.

Now, the purpose is these subplots to concatenate so that all are on one figure, but concatenated and not so how the hold on does. The concatenation should look like this:

enter image description here

where for the each concatenated plot will be denoted on the X axis, as can be seen on the pic.


Solution

  • You have two problems. I'll start with the one you didn't ask about, since I'm worried you'll stop reading once I answer the other one.

    You should not be using eval unless it's really necessary, and it's never necessary. eval is slow and insecure. If you eval malicious code, it can easily do serious harm to your system. In this case this is unlikely, but still using eval prevents MATLAB's just-in-time compiler to be able to optimize anything in the code inside, so you'll get the worst possible performance.

    Now, you're claiming that you're stuck with eval because the variables are already set up dynamically. Note that this is a perfect example of an XY problem: you shouldn't end up with these data in the first place. Do them differently. If you're not in control of data creation, keep hitting the head of the person who is, so that they stop.

    Anyway, once the damage is done, you can still quickly recover from the eval pit of doom. You need to save and reload your variables, which allows you to push them into a struct. This is nice, because struct fields can be accessed dynamically. Rewriting your original:

    tmpfile = 'tmp.mat';
    save(tmpfile,'mean_*_*_f'); % save relevant variables to tmp mat file
    dat = load(tmpfile); % reload them into a struct named dat
    
    ch_array = [36, 40, 44, 48, 149, 161]; % we could deduce these programmatically
    figure;
    
    for i=1:length(ch_array) 
        ch = ch_array(i);     
        subplot(3, 3, i);
        plot(dat.(sprintf('mean_a_%d_f',ch)), 'r'); % look, Ma, no eval!
        hold on;
        plot(dat.(sprintf('mean_b_%d_f',ch)), 'b');
        xlabel('Subcarrier (f)');
        ylabel('Absolute values');
        title(sprintf('Channel: %d',ch)); % seriously, this DID NOT need eval
    end
    

    Now, for your question. The problem is that plot(y) with this simple syntax plots y as a function of 1:numel(y): essentially plot(1:numel(y),y). What you want to do is manually shift the x points for each data set so they don't overlap:

    figure;
    
    offset = 0;
    midpoints = zeros(size(ch_array));
    for i=1:length(ch_array) 
        ch = ch_array(i);     
    
        % deduce data to plot
        tmpdat_a = dat.(sprintf('mean_a_%d_f',ch));
        tmpdat_b = dat.(sprintf('mean_b_%d_f',ch));
        x_a = offset+1:offset+numel(tmpdat_a);
        x_b = offset+1:offset+numel(tmpdat_b);
    
        % plot
        plot(x_a, tmpdat_a, 'r');
        hold on;
        plot(x_b, tmpdat_b, 'b');
    
        % store xtick position
        midpoints(i) = mean(mean(x_a), mean(x_b));
    
        % increment offset
        offset = offset + numel(max([tmpdat_a, tmpdat_b])) + 10; % leave a border of width 10, arbitrary now
    end
    
    xlabel('Subcarrier (f)');
    ylabel('Absolute values');
    xticks(midpoints);
    xticklabels(arrayfun(@num2str, ch_array, 'uniformoutput', false));
    title('All channels');