Looping through columns to make subplots - matlab

I have an spectral absorbance matrix in xls format which has column 1 as wavelength, and columns 2:3 as the associated absorbance spectra for particles from depth1 in water column. 2:3 are duplicates so they have to be plotted together. Next i have columns 4:5 as once again duplicate absorbance spectra for particles from depth2 in water column. Then data for depth3, depth4 etc. The matrix is 1001 rows by 13 columns.
I would like have 3 subplots (depth1, depth2 and depth3) in one figure with each subplot holding the 2 duplicate spectra of each depth.
I tried to follow the excellent responses to this question but this gives me one line per subplot but i want to plot two lines (duplicate spectra). So I did the following and it works but I can only get 3 subplots:
[num,txt,raw]=xlsread('ANACONDAS2010-ST1.xls');
legendCell=cellstr(txt);
figure
subplot(3,1,1)
plot(num(:,1), num(:,2:3),'r');grid on; box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(2:3)),legend boxoff
subplot(3,1,2)
plot(num(:,1), num(:,4:5),'b');grid on; box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(4:5)), legend boxoff
subplot(3,1,3)
plot(num(:,1), num(:,6:7),'g');grid on; box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(6:7)), legend boxoff
title('STATION 1','fontweight','bold','fontsize',16);
But as you can see this gives me only 1 figure with 3 subplots and the rest od the depths (d4, d5, d6) remain unplotted as i havent been able to specifiy them,
Because my script is long and cumbersome I would have liked to run this through a loop but I couldn't figure out how to do it in spite of battling with the code provided in the 2nd answer which I kind of understood unlike the first one.

Updated answer
Inserted version V2 of the code
Version V2 of the code allows displaying unlimited pairs of data, also it is even simpler than V1.
% Generation of example data
num=1:33;
number_of_data_colums=14;
num=[num' rand(length(num),number_of_data_colums)];
% Generation of legend string
for i=1:number_of_data_colums
legendCell{i}=['Absor. ' num2str(i)];
end
% Get the size of data to be plotted (columns from 2 to ...)
[r,c]=size(num);
n_data=floor((c-1)/2);
% Define the number of data to be plotted in each subplt
data_x_plot=2;
% Consistency check: if the number of column data is not even generate an
% error message and exit
if(n_data*2 ~= (c-1))
error('Number of data columns is not even')
else
% Define the number of subplot of each figure
n_sub_plot=3;
% Subplot and figure counters
s_plot_cnt=1;
fig_cnt=1;
% Create the first figure
figure
% External loop on figures
for i=2:2:n_data*2
% If three subplot have been added to a figure, open a new figure
if(s_plot_cnt == 4)
% The Title is assigne to the first subplot of each figure
title(ax(fig_cnt,1),['STATION ' num2str(fig_cnt)],'fontweight','bold','fontsize',16);
s_plot_cnt=1;
fig_cnt=fig_cnt+1;
figure
end
ax(fig_cnt,s_plot_cnt)=subplot(n_sub_plot,1,s_plot_cnt);
% The indices of columns to be plotted are computed automatically
plot(num(:,1), num(:,i:i+1));
grid on;
box on;
xlabel('Wavelength')
ylabel('Absorbance')
% add legend
legend(legendCell(i-1:i),-1)
% Increment subplot's counter
s_plot_cnt=s_plot_cnt+1;
% legend boxoff
end
end
% Add the last title
title(ax(fig_cnt,1),['STATION ' num2str(fig_cnt)],'fontweight','bold','fontsize',16);
Previous answer and
Version V1 of the code
I'm not sure I've understood your question, nevertheless, if you have 6 pairs of data and you want 3 subplot, you need 2 figures.
I've modified your original script in order to automatically determine the number of figure you need, generate the subplots and plot 2 set of data in each of them.
Updated code - now with legends
% Generation of example data
num=1:33;
num=[num' rand(length(num),12)];
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% UPDATED CODE STARS HERE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Generation of legend string
legendCell{1}='Wavel';
for i=2:13
legendCell{i}=['Absor. ' num2str(i)];
end
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% UPDATED CODE ENDS HERE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Get the size of data to be plotted
[r,c]=size(num);
% Define the number of data to be plotted in each subplt
data_x_plot=2;
% Define the number of subplot of each figure
n_sub_plot=3;
% Evaluate the number of figures to be created
n_fig=(c-1)/(data_x_plot*n_sub_plot);
% Define the index of data columns
idx=[2:2:c-1];
idx=reshape(idx,n_sub_plot,data_x_plot)';
% External loop on figures
for i=1:n_fig
figure
% Internal loop on subplots
for j=1:n_sub_plot
% The subplot indices are computed automatically
ax(i,j)=subplot(n_sub_plot,1,j);
% The indices of columns to be plotted are computed automatically
plot(num(:,1), num(:,idx(i,j):idx(i,j)+1));
grid on;
box on;
xlabel('Wavelength')
ylabel('Absorbance')
% add legend
legend(legendCell(idx(i,j):idx(i,j)+1),-1)
% legend boxoff
end
% The Title is assigne to the first subplot of each figure
title(ax(i,1),['STATION ' num2str(i)],'fontweight','bold','fontsize',16);
end
Given a set of 12 columns of data, this is the output:
Updated graphs, with legends

Maybe you should use Hold to retain current plot when adding new plots.
So your code should be like this:
[num,txt,raw]=xlsread('ANACONDAS2010-ST1.xls');
legendCell=cellstr(txt);
figure
subplot(3,1,1)
plot(num(:,1), num(:,2),'r');grid on;
hold on;
plot(num(:,1), num(:,3),'m');
box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(2:3)),legend boxoff
subplot(3,1,2)
plot(num(:,1), num(:,4),'b');
hold on;
plot(num(:,1), num(:,5),'c');
grid on; box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(4:5)), legend boxoff
subplot(3,1,3)
plot(num(:,1), num(:,6),'g');
hold on;
plot(num(:,1), num(:,7),'k');
grid on; box on; xlabel('Wavelength'), ylabel('Absorbance'),legend(legendCell(6:7)), legend boxoff
title('STATION 1','fontweight','bold','fontsize',16);

Related

Graphs is not plotting properly in the subplot in MATLAB

I have I have a data file which has 3 columns, 1st column is the field for the data, or you can say different index. 2nd column is the x-axis data, and the 3rd column is the y-axis data. Now I have similar data files for different variables like 8 files. I want to plot all the graph in one figure in MATLAB. For my problem, I am showing only one subplot. This subplot data file should plot 5 "line plots" for 5 indexes (1st column). But when I plot it as subplot it shows only 1 plot. here is my code in the below:
% open Zdiff Odd Mode data file
fid = fopen('Data_test.txt');
% Read data in from csv file
readData = textscan(fid,'%f %f %f','Headerlines',1,'Delimiter',',');
fclose(fid);
% Extract data from readData
index_Data = readData{1,1}(:,1);
% Identify the unique indices
uni_idx=unique(index_Data);
xData = readData{1,2}(:,1);
yData = readData{1,3}(:,1);
% Plot Data
f = figure;
%Top Title for all the subplots
p = uipanel('Parent',f,'BorderType','none');
p.Title = 'Electrical Characteristics';
p.TitlePosition = 'centertop';
p.FontSize = 14;
p.FontWeight = 'bold';
cla; hold on; grid on, box on;
ax1 = subplot(2,4,1,'Parent',p);
% Loop over the indices to plot the corresponding data
for i=1:length(uni_idx)
idx=find(index_Data == uni_idx(i));
plot(xData(idx,1),yData(idx,1))
end
The plot results like below:
When I plot the data as a full figure, the plot is perfect. But as I have lots of data to plot in one figure as subplots, I need to know what is wrong in my subplot code.
Here is my code for the whole figure of the data without the subplot
Before plotting code it is same as before:
% Plot Data
f1 = figure(1);
cla; hold on; grid on;
% Loop over the indices to plot the corresponding data
for i=1:length(uni_idx)
idx=find(index_Data == uni_idx(i));
plot(xData(idx,1),yData(idx,1))
end
The resulting figure is below:
What is wrong with my plotting code in the subplot? Can anyone help me out?
This is your sequence of commands, and what they do:
f = figure;
Creates an empty figure, there are no axes yet defined here.
cla
Clears the current axes, since there is no current axes, it creates one.
hold on
Sets the "hold" property on the current axes
grid on, box on
Sets some other properties of the current axes
ax1 = subplot(2,4,1,'Parent',p);
Creates new axes. Because it overlays the previously created axes, those are deleted.
plot(xData(idx,1),yData(idx,1))
Plots to the current axes (i.e. the one created by subplot). These axes don't have the "hold" property set, so subsequent plot commands will overwrite the data plotted here.
The solution, as suggested by Ander in a comment, is to set the "hold" property of the axes created by subplot. Replace:
cla; hold on; grid on, box on;
ax1 = subplot(2,4,1,'Parent',p);
with:
ax1 = subplot(2,4,1,'Parent',p);
hold on; grid on, box on;
(note that cla is not necessary, since you're drawing to a new, empty figure).

How to set a common y-axis label for different subplots in the same figure [duplicate]

I have used the following loop to get subplots:
for j=1:19;
Aj=B(j,:);
subplot(5,4,j);
plot(Aj,h)
end
For all these subplots, I need to have only one x-label and one y-label. How to do this? Also how to insert legend to all the subplots?
You can use suplabel from the FileExchange to have combined x and y label for all subplots.
Example:
subplot(1,2,1);
plot(randperm(40)); hold on; plot(randperm(40)); %Plotting some random data
legend('show') %To show the legend
subplot(1,2,2);
plot(randperm(40)); hold on; plot(randperm(40)); %Plotting some random data
legend('show') %To show the legend
%Using suplabel from the FileExchange to give a single x and y label for all subplots
suplabel('Combined X label','x');
suplabel('Combined Y label','y');
Output:
Sometimes you have to maximize the figure window to see the xlabel when using suplabel.

Scatterplot matlab

I have some problems with a scatter plot.
I am plotting a matrix containing grades per assignment for students e.g. [assignments x grades], but if more than one student gets the same grade in the same assignment, the points will be on top of each other. I want to add a small random number (between -0.1 and 0.1) to the x- and y-coordinates of each dot.
On the x-axis it should be number of assignments and on the y-axis it should be all the grades.
the grades matrix is defined as a 12x4 matrix
My code looks like this:
n_assignments = size(grades,2); % Total number of assignments.
n_students = size(grades,1); % Total number of student.
hold on; % Retain current plot when adding new plots.
for i = 1:n_assignments % Loop through every assignment.
% Scatter plot of assignment vs grades for that assignment.
% One assignment on every iteration.
scatter(i*ones(1, n_students), grades(i, :), 'jitter', 'on', 'jitterAmount', 0.1);
end
hold off; % Set the hold state to off.
set(gca, 'XTick', 1:n_assignments); % Display only integer values in x-axis.
xlabel('assignment'); % Label for x-axis.
ylabel('grades'); % Label for y-axis.
grid on; % Display grid lines.
But I keep getting the error message:
X and Y must be vectors of the same length.
Please note that the scatter plot jitter is an undocumented
feature. You can also have semi-transparent markers in line and
scatter plots, which could be another alternative to solve your
current problem.
I will cover the scatter 'jitter' feature in this answer.
Note that 'jitter' only affects the x-axis but not the y-axis (more info on Undocumented Matlab).
Have a look at this example I made based on your description:
Suppose you have a class with 20 students and they have completed 5 assignments. The grades for the assignments are stored in a matrix (grades) where the rows are the assignments and the columns are the students.
Then I simply generate a scatter plot of the data in the grades matrix, one row at a time, in a for loop and using hold on to keep all the graphics on the same figure.
n_assignments = 5; % Total number of assignments.
n_students = 20; % Total number of students.
grades = randi(10, n_assignments, n_students); % Random matrix of grades.
hold on; % Retain current plot when adding new plots.
for i = 1:n_assignments % Loop through every assignment.
% Scatter plot of assignment vs grades for that assignment.
% One assignment on every iteration.
scatter(i*ones(1, n_students), grades(i, :), 'jitter', 'on', 'jitterAmount', 0.1);
end
hold off; % Set the hold state to off.
set(gca, 'XTick', 1:n_assignments); % Display only integer values in x-axis.
xlabel('assignment'); % Label for x-axis.
ylabel('grades'); % Label for y-axis.
grid on; % Display grid lines.
This is the result:
If you still want to add jitter in the y-axis, you would have to do that manually by adding random noise to your grades data, which is something I personally wouldn't recommend, because the grades in the scatter plot could get mixed, thus rendering the plot completely unreliable.

advanced plotting (legend manipulation) in Matlab

I want to add a legend to my plot so that it look as in the picture (reduce the length of the line and suppress unnecessary spaces)
x = randn(6,20);
figure(2)
hax = gca;
plot(x(1,:),'--k','linewidth',1.5);
hold on;
plot(x(2,:),'b','linewidth',1.5);
hold on;
plot(x(3,:),'g','linewidth',1.5);
hold on;
plot(x(4,:),'r','linewidth',1.5);
hold on;
plot(x(5,:),'c','linewidth',1.5);
hold on;
plot(x(6,:),':r','linewidth',1.5);
ylabel('states','fontsize',14); xlabel('time(s)','fontsize',14);
legend('True','SCKS(h1)','SCKS(h2)','SCKS(h3)','SCKS(h4)','DEM',14);
legendshrink(0.8,[]);
%Fig_legend = legend('Taylor','Euler','LLscheme','LLscheme1');
%set(Fig_legend,'FontSize',7)
grid(hax,'on')
axis(hax,'tight')
set(hax,'box','on','Layer','top');
set(hax,'tickdir','out')
is there anyone can help me?
As reported in the MatLab online documentation Starting in R2014b, colorbars and legends are no longer axes objects.
This limits the possibilites to work on the legend in order to achieve your target.
A possible solution could be to create a your own axes based legend following these steps:
create the legend calling the legend function with the following syntax [lgd,icons,plots,txt] = legend(___) (notice, this syntax is not recommended nevertheless, we are going to delete the legend in the next steps, so it will not be a problem)
get the position of the legend
identify the number of items in the legend
for each line in the graph there are three items in the legend (the line, the marker and the text)
in the case of six line in a graph the legend handle will contains 18 objects in the following order:
6 x handles to text
for each of the 6 lines
1 handle to the line
1 handle to the marker
get the data of the items in the legend:
line data: XData, YData, Color, 'LineStyle`
text data: string, position
delete the original legend
add an axes to the figure with position and size equal to the original legend
plot on this axes the lines that will represent the legend (you've got the data on the previous steps)
to shrink the lines, just reduce reduce the value of the second element of XData
add the legend text correspinding to the line (with the text function. Since in the previous step you've recuced the lengt of the lines you've to move on the left the text
reduce the size of the axes:
you can use the Extent property of the text items to identify the length of the longer one
then add to it the length of the corresponding line
update the axes position (its width) with the value caomputed in the above step
This is a possible implementation (based on you code):
x = randn(6,20);
figure(2)
hax = gca;
plot(x(1,:),'--k','linewidth',1.5);
hold on;
plot(x(2,:),'b','linewidth',1.5);
% hold on;
plot(x(3,:),'g','linewidth',1.5);
% hold on;
plot(x(4,:),'r','linewidth',1.5);
% hold on;
plot(x(5,:),'c','linewidth',1.5);
% hold on;
plot(x(6,:),':r','linewidth',1.5);
ylabel('states','fontsize',14); xlabel('time(s)','fontsize',10);
%legend('True','SCKS(h1)','SCKS(h2)','SCKS(h3)','SCKS(h4)','DEM',14);
%
% New call "legend"
%
[leg_h,leg_item_h,~,~]=legend('True','SCKS(h1)','SCKS(h2)','SCKS(h3)','SCKS(h4)','DEM',14);
%
% legendshrink(0.8,[]);
%Fig_legend = legend('Taylor','Euler','LLscheme','LLscheme1');
%set(Fig_legend,'FontSize',7)
grid(hax,'on')
% axis(hax,'tight')
set(hax,'box','on','Layer','top');
set(hax,'tickdir','out')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% GENERATION OF THE LEGEND %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Define a scale factor fot the lines
line_scale_factor=1.4;
% Define a scale factor fot the lines
text_scale_factor=1.35;
% Get the "Position" of the legend
orig_leg_pos=get(leg_h,'position')
% Get the number on objects in the legend
n_obj=length(leg_item_h);
% Extract the "Line" object
line_obj=leg_item_h(n_obj/3+1:2:n_obj);
% Get the "LineStyle" of each "Line" in the legend
l_style=get(line_obj,'LineStyle')
% Get the "Color" of each "Line" in the legend
l_col=cell2mat(get(line_obj,'color'))
% Get the "XData" and "YData" of the "Lines" in the legend
leg_x_data=cell2mat(get(line_obj,'xdata'))
leg_y_data=cell2mat(get(line_obj,'ydata'))
% Get the handle of the "Text" of the items in the legend
leg_t=leg_item_h(1:n_obj/3)
% Get the "Text" of the items in the legend
str=get(leg_t,'string')
% Get the "Position" of each "Text" item in the legend
tx=cell2mat(get(leg_t,'position'))
% Delete the original legend
delete(leg_h)
% Create an axes with the same position and size of the original legend
ax=axes('position',orig_leg_pos,'xlim',[0 1],'ylim',[0 1], ...
'xtick',[],'ytick',[],'box','on')
hold on
% Add the legend items to the axes
for i=1:n_obj/3
% Add the lines with the original settings (color, style, ...)
plot([leg_x_data(i,1) leg_x_data(i,2)/line_scale_factor],leg_y_data(i,:),'color',l_col(i,:), ...
'linestyle',l_style{i}, ...
'linewidth',1.4)
% Add the text
th(i,:)=text(leg_x_data(i,2)/text_scale_factor,tx(i,2),0,str{i},'fontsize',9, ...
'unit','normalized')
end
% Get the maximun extent of the lagend "Text"
tx_max_ext=max(reshape([th(:).Extent],4,6)');
% Evaluate the axis scaling factor
tx_r_1=tx_max_ext(3)+leg_x_data(i,2)/line_scale_factor
% Get the axes position
axp=ax.Position
% Resize the axes width
ax.Position=[axp(1) axp(2) axp(3)*tx_r_1 axp(4)]

Matlab - Legend does not match my graph

I am having trouble matching my graph with my axis. The first two plots work and the second two do not. I am trying to plot Temperature versus Pressure for two Argo floats and then Salinity versus Pressure. Here is my code:
% First Plot
subplot(221);
plot(float1winter.T,float1winter.P,'b');
hold on;
plot(float1summer.T,float1summer.P,'r');
hold on;
tempAdiff = abs(float1summer.T-float1winter.T)
plot(tempAdiff,float1summer.P,'--k');
hold on;
set(gca,'ydir','reverse');
title('Argo Float #1901440 Temp Profiles');
legend(['float1winter','float1summer','tempAdiff'],{'11-29-2013','07-01-2013','Temperature Difference'},'location','southwest');
xlabel('Temperature (°C)');
ylabel('Pressure');
shg;
% Second Plot
subplot(222);
plot(float2winter.S,float2winter.P,'m');
hold on;
plot(float2summer.S,float2summer.P,'c');
hold on;
set(gca,'ydir','reverse');
title('Argo Float #1901440 Salinity Profiles');
legend(['float2winter','float2summer'],{'11-29-2013','06-02-2013'},'location','southwest');
xlabel('Salinity (psu)');
ylabel('Presure');
shg;
% Third Plot
subplot(223);
% Matrix demensions did not agree bewteen winter and summer profiles. The summer profile was 71 x 2 and the winter was 70 x 2. I tried "reshape"
% and that didn't work. So I changed the vector of float3summer.T to
% float3bsummer.T with an array of 70 x 2
float3bsummer.T = float3summer.T(1:70,1:2);
float3bsummer.P = float3summer.P(1:70,1:2);
plot(float3winter.T,float3winter.P,'Linewidth',1,'color','blue');
hold on;
plot(float3bsummer.T,float3bsummer.P,'Linewidth',1,'color','red');
hold on;
tempdiff = abs(float3bsummer.T-float3winter.T)
plot(tempdiff,float3bsummer.P,'--k');
hold on;
set(gca,'ydir','reverse'); % this line reverses the y-axis so that depth increases downward
title('Argo Float #1901415 Tempearture Profiles');
hold on;
summerfloat = plot(float3bsummer.T,float3bsummer.P,'r');
legend(['float3winter.T','summerfloat','tempdiff'],{'12-03-2013','07-03-2013','Temp Diff'},'location','southeast');
xlabel('Temperature (°C)');
ylabel('Pressure');
axis ([-1,4,0,2000]);
shg;
% Fourth Plot
subplot(224);
plot(float3winter.S,float3winter.P,'g');
% Changed matrix dimensions for Salinity of Summer
float3bsummer.S = float3summer.S(1:70,1:2);
float3bsummer.P = float3summer.P(1:70,1:2);
plot(float3bsummer.S,float3bsummer.P,'.b');
hold on;
set(gca,'ydir','reverse');
title('Argo Float #1901415 Salinity Profiles');
h4 = plot(float3winter.S,float3winter.P,'g');
hold on;
h5 = plot(float3bsummer.S,float3bsummer.P,'.b');
hold on;
legend(['float3winter','float3bsummer.T'],{'12-03-2013','07-03-2013'},'location','southwest');
xlabel('Salinity (psu)');
ylabel('Pressure');
axis ([33.8,34.8,0,2000]);
shg;
% Save File to Desktop
set(gcf,'color','w');
saveas(gcf,'~/Desktop/hw1_figure1.pdf','pdf');![enter image description here][1]
I guess that you're trying to associate the set of strings for your legend, {'11-29-2013','07-01-2013','Temperature Difference'}, with the plots made from the variables ['float1winter','float1summer','tempAdiff'].
However, this isn't how legend works. MATLAB has no way of associating the plot produced by plot(float1winter.T,float1winter.P,'b'); to the string float1winter. If you want to specify which plots go with which legend entries, you need to pass the object handles of the plots to legend, which is easiest done by returning the handles when you plot originally:
h(1) = plot(float1winter.T,float1winter.P,'b');
hold on;
h(2) = plot(float1summer.T,float1summer.P,'r');
h(3) = plot(tempAdiff,float1summer.P,'--k');
legend(h,{'11-29-2013','07-01-2013','Temperature Difference'});
Side note: you only need to call hold on once per axis - so once for each subplot but not after every plot call.
Alternatively, you can not give handles at all; legend will assign the text to the plots in the order that they were plotted:
legend({'11-29-2013','07-01-2013','Temperature Difference'})
Understanding graphics handles allows you a lot more control over plots, especially if you might want to make small adjustments to them. For example, if I decide that I want the first plot to be green rather than blue, then I can just do:
set(h(1),'Color','g');
This will change the plot color and the legend changes to match automagically. To see a list of all the properties of an object, use get with only the handle. You can set more than one property at a time. For example:
get(h(1))
set(h(1),'DisplayName','Winter','LineWidth',3,'Marker','x')