Creating a number of figures dependent on user input - matlab

I'm fairly new to Matlab so any help would be appreciated.
I'm trying to write a function using simple logic operators to create a number of 2D scatter graphs, the problem I've been having is that I cannot work out how to use a input from the user (the number of figures) to actually create that number of figures.
*edit (Just for the sake of clarity I'm plotting multiple sets of data ie columns on each figure but the important bit is that there will be multiple figures as the user specifies how many figures they want, this is the bit I cannot understand. I understand how to use hold on to plot more than one graph on each figure but how do I vary the number of figures depending on the input of the user?)
The user inputs are a matrix with dimensions 4000x30 (this will remain constant for my use) and the number of figures (this will change from 1-30) to plot from this data set. Each column represents a different sensor so the columns represent 1 set of data each.
The simpler the answer the better as I'm not a very experienced coder.
Thanks
GibGib

See if this works for you:
Data = rand(40,30); %// Just a small data set for testing.
%// Ask user how many figures are desired
prompt = {'Enter desired number of figures:'};
dlg_title = 'Input';
num_lines = 1;
def = {'5'};
NumFigures = inputdlg(prompt,dlg_title,num_lines,def);
%// Get # of figures. If the entry is not valid (i.e. remainder of division 30/entry is not 0), ask again.
while rem(size(Data,2),str2double(NumFigures{1})) ~= 0
NumFigures = inputdlg(prompt,dlg_title,num_lines,def);
end
NumFigures = str2double(NumFigures{1}); %// Convert to number
ColPerFig = size(Data,2)/NumFigures; %// Number of columns to plot per figure
ColStart = 1:ColPerFig:size(Data,2) %// Indices of the starting columns to plot
ColStart looks like this:
ColStart =
1 7 13 19 25
So its easier in the loop to index into Data and fetch the appropriate values.
%// Plot
for k = 1:NumFigures;
hFig(k) = figure;
plot(Data(:,ColStart(k):ColStart(k)+ColPerFig-1));
end

Ok, it seems like what you are asking is that you have this data matrix M, where the user defines U, and you to plot U number of plots where each plot is the 2D scatter of U columns that corresponds to M?
in that case, will this do?
figure;
hold on %is optional depending how you want your plot
for i = 1:U
plot(M(:,i))
end
If this is not what you are looking for, please specify your question further.

Related

Looking for a tool that extracts data from a plot figure ( here 2D contours from Covariance matrix or Markov chains) and reproduce the original figure

I am looking for an application or a tool which is able for example to extract data from a 2D contour plot like below :
I have seen https://dash-gallery.plotly.host/Portal/ tool or https://plotly.com/dash/ , https://automeris.io/ , but I have test them and this is difficult to extract data (here actually, the data are covariance matrices with ellipses, but I would like to extend it if possible to Markov chains).
If someone could know if there are more efficient tools, mostly from this kind of 2D plot.
I am also opened to commercial applications. I am on MacOS 11.3.
If I am not on the right forum, please let me know it.
UPDATE 1:
I tried to apply the method in Matlab with the script below from this previous post :
%// Import the data:
imdata = importdata('Omega_L_Omega_m.png');
Gray = rgb2gray(imdata.cdata);
colorLim = [-1 1]; %// this should be set manually
%// Get the area of the data:
f = figure('Position',get(0,'ScreenSize'));
imshow(imdata.cdata,'Parent',axes('Parent',f),'InitialMagnification','fit');
%// Get the area of the data:
title('Click with the cross on the most top left area of the *data*')
da_tp_lft = round(getPosition(impoint));
title('Click with the cross on the most bottom right area of the *data*')
da_btm_rgt = round(getPosition(impoint));
dat_area = double(Gray(da_tp_lft(2):da_btm_rgt(2),da_tp_lft(1):da_btm_rgt(1)));
%// Get the area of the colorbar:
title('Click with the cross within the upper most color of the *colorbar*')
ca_tp_lft = round(getPosition(impoint));
title('Click with the cross within the bottom most color of the *colorbar*')
ca_btm_rgt = round(getPosition(impoint));
cmap_area = double(Gray(ca_tp_lft(2):ca_btm_rgt(2),ca_tp_lft(1):ca_btm_rgt(1)));
close(f)
%// Convert the colormap to data:
data = dat_area./max(cmap_area(:)).*range(colorLim)-abs(min(colorLim));
It seems that I get data in data array but I don't know how to exploit it to reproduce the original figure from these data.
Could anyone see how to plot with Matlab this kind of plot with the data I have normally extracted (not sure the Matlab. script has generated all the data for green, orange and blue contours, with each confidence level, that is to say, 68%, 95%, 99.7%) ?
UPDATE 2: I have had first elements of answer on the following link :
partial answer but not fully completed
I cite elements of the approach :
clc
clear all;
imdata = imread('https://www.mathworks.com/matlabcentral/answers/uploaded_files/642495/image.png');
close all;
Gray = rgb2gray(imdata);
yax=sum(conv2(single(Gray),[-1 -1 -1;0 0 0; 1 1 1],'valid'),2);
xax=sum(conv2(single(Gray),[-1 -1 -1;0 0 0; 1 1 1]','valid'),1);
figure(1),subplot(211),plot(xax),subplot(212),plot(yax)
ROIy = find(abs(yax)>1e5);
ROIyinner = find(diff(ROIy)>5);
ROIybounds = ROIy([ROIyinner ROIyinner+1]);
ROIx = find(abs(xax)>1e5);
ROIxinner = find(diff(ROIx)>5);
ROIxbounds = ROIx([ROIxinner ROIxinner+1]);
PLTregion = Gray(ROIybounds(1):ROIybounds(2),ROIxbounds(1):ROIxbounds(2));
PLTregion(PLTregion==255)=nan;
figure(2),imagesc(PLTregion)
[N X]=hist(single(PLTregion(:)),0:255);
figure(3),plot(X,N),set(gca,'yscale','log')
PLTitems = find(N>2000)% %limit "color" of interest to items with >1000 pixels
PLTitems = 1×10
1 67 90 101 129 132 144 167 180 194
PLTvalues = X(PLTitems);
PLTvalues(1)=[]; %ignore black?
%test out region 1
for ind = 1:numel(PLTvalues)
temp = zeros(size(PLTregion));
temp(PLTregion==PLTvalues(ind) | (PLTregion<=50 & PLTregion>10))=255;
% figure(100), imagesc(temp)
temp = bwareaopen(temp,1000);
temp = imfill(temp,'holes');
figure(100), subplot(3,3,ind),imagesc(temp)
figure(101), subplot(3,3,ind),imagesc(single(PLTregion).*temp,[0 255])
end
If someone could know how to improve these first interesting results, this would be fine to mention it.
Restating the problem - My understanding given the different comments and your updates is the following:
someone other than you is in possession of data, which as it happens is 2D data, i.e. an Nx2 matrix;
using the covariance matrix, they are effectively saying something about the joint distribution of these two dimensions, specifically about the variance;
if they assume a Gaussian distribution, as is implied by your comment regarding 68%, 95% and 99.7% for 1sigma, 2sigma and 3sigma, they can draw ellipses which represent the 2D-normal distribution: these are in fact some of the contour lines associated with the 3D "bell" surface;
you have obtained the contour lines in a graph and are trying to obtain the covariance matrix (not the original data...);
you are concerned about the complexity of having to extract the information from each ellipsis.
Partial answer:
It is impossible to recover the original data, I hope you are already aware of that, but in case you are not let's just note that the covariance matrix is a summary statistic of the data, much like the average, and although it says something about the data many different datasets could happen to have the same summary statistic (the same way many different sets of numbers can give you an average of 10).
It is possible to somewhat recover the covariance matrix, i.e. the 3 numbers a, b and c in the matrix [a,b;b,c], though the error in doing so will likely be large because of how imprecise the pixel representation is. Essentially, you will be looking for the dimensions of the two axes, for the variances, as well as the angle of one of the axes, for the covariance.
Unless I am mistaken, under the Gaussian assumption above, you only need to measure this for one of the three ellipses, and then factor by whatever number of sigmas that contour represents. Here you might want to either use the best-defined ellipse, or attempt to use the largest one, which will provide the maximum precision for your measurements (cf. pixelization).
Also, the problem of finding the axes and angle for the ellipse need not be as complex as what it seems like in your first trials: instead of trying to find the contour of the ellipses, find the bounding rectangle.
In order to further simplify this process, if your images are color-coded the way you show, then a filter on blue pixels might be enough in terms of image processing. Then simply take the minimum and maximum (x,y) coordinates in order to obtain the bounding rectangle.
Once the bounding rectangle is obtained, find the equation to your ellipse (that's a question for a math group, but you could start here for example).
Happy filtering!

Dynamic input data for plot() in MATALAB

I have text files that contain two columns with numbers. Over a for loop, I store the first and second column as X(n) and Y(n) respectively (as floats), n being the iteration number.
Let's say that I don't know how many files I have and that the length/range of the data is variable.
Is there a way to create a sort of dynamic variable so I can use it as an input to graphically represent the data like
plot(dynamic_variable)
instead of writing per hand
plot(X1,Y1,X2,Y2,...,XN,YN)
I know there should be the possibility to interpolate the data (since the files haven't the same length/range) so it is possible to create two matrices, let say XM and YM, and finally write (XM,YM), where
XM = [X1_intrpl X2_intrpl ... XN_intrpl]
YM = [Y1_intrpl Y2_intrpl ... YN_intrpl].
Is there a more direct way to do it?
I am far from being an expert: so I would also appreciate any comment and/or criticism on my idea/approach.
The Matlab plot function does not seem to support what you are looking for. I guess you have already checked the documention on the plot command here:
https://de.mathworks.com/help/matlab/ref/plot.html?requestedDomain=www.mathworks.com
What you can do is write your own plot function that takes both matrices as parameters.
In the function you would loop over the pairs in the matrices plotting them using hold on to display all the data in one plot.
One option would be reading in each set of X(n) and Y(n) into a cell array such that,
X{1} = X1
Y{1} = Y1
...
X{N} = XN
Y{N} = YN
Then to plot, rather than trying to merge everything into a single array, you can simply plot each set of X and Y one at a time onto the same figure.
%Instead of:
%plot(X1,Y1,X2,Y2,...,XN,YN)
%Use:
figure()
hold on
for i=1:N
plot(X{i},Y{i})
end

How to automatically normalize multiple histograms to get to the same maximum level?

I have multiple histograms generated from various samples that need to be combined in the end. What I have found is that I am not getting good results at the combination stage because different plots have different max values, but if I normalize them to somewhat similar values I get a good result.
For example the below three plots:
Now as can be seen one of the plots peak at around 0.067 while the other two at around 0.4. I cannot combine them in this state, but after looking at the plots visually I know that if I multiply the first plot 0.6 I get this:
Now they are at same level and can be displayed together.
I am doing this visually for every result. Would it be possible to automate this? As its not always like this, sometimes the first and second inputs(plot) are low but the third one is peaked and I would have to divide the third plot by a certain value, which I know after visually looking at the plots.
Matlabs function histogram has some normalization types built in. You can either normalize the number of counts, or the sum of the histogram area (see also), ... but you cannot yet normalize for a maximum value which is what you want probably.
I recommend to compute the histograms without plotting using histcounts, then normalizing them to a common maximum like 1 for example and then plotting them all together or separate in bar plots.
Example:
% generate example data
a = randn(100, 1) + 5;
b = randn(100, 1) * 4 + 8;
nbins = 0:20;
% compute histograms
[na, edges] = histcounts(a, nbins);
centers = mean([edges(1:end-1);edges(2:end)]);
nb = histcounts(b, nbins);
% normalize histograms to maximum equals 1
na = na / max(na);
nb = nb / max(nb);
% plot as bar plots with specified colors (or however you want to plot them)
figure;
bar_handle = bar(centers', [na',nb']);
bar_handle(1).FaceColor = 'r';
bar_handle(2).FaceColor = 'g';
title('histogram normalized to max');
and it looks like

matlab, symbol not updating in legend

I am creating a program where the user can select multiple files to plot and compare data. The program can properly graph the data, the problem I have encountered is within the legend.
I tried posting an image, however I do not have a high enough reputation. So I will try to explain the graph in detail. Two sets of points are plotted (two matrices of different sizes). The curves are labeled by the user, and in this example they are: "PS, Cs" and "PS, Po."
The program successfully plots the "PS, Cs" curve with the red squares then plots the "PS, Po" with the blue circles however the legend continues to show the red squares for both sets of points. Below is the loop within the code that does the plotting.
fig = small_group_struct;
mystyles = {'bo','rs','go'};
mat_len = size(small_group_struct,2);
for q = 1:mat_len
plotstyle = mystyles{mod(q,mat_len)+1};
semilogy(1:size(small_group_struct(1).values),small_group_struct(q).values,plotstyle);
hold all;
[~,~,~,current_entries] = legend;
legend([current_entries {small_group_struct(q).name}]);
end
hold off;
%legend(small_group_struct.values,{small_group_struct.name});
Other threads that I have seen suggested putting the plot command into a handle but since each set of points is a nxm matrix of different sizes, the program does not like this.
Also, as mentioned at the beginning the user will select the number of files and while this is typically two, it will not always be the case and why I am trying to plot it within a for loop.
Any suggestions and comments would be greatly appreciated.
EDIT: I now have a high enough reputation to post images, so here is a screenshot of the graph
You can use handles to specify what labels go with what data in the legend.
You say that "each set of points is a nxm matrix of different sizes." Plotting an mxn matrix creates n line objects and returns n handles. You can keep track of all of these handles and assign labels to them when you create the legend.
Here's an example:
% Cell array of data. Each element is a different size.
data = {rand(100, 1), rand(150, 2)};
styles = {'ro', 'gs'};
% Vector to store the handles to the line objects in.
h = [];
figure
hold on
for t = 1:length(data)
% plots the data and stores the handle or handles to the line object.
h = [h; semilogx(data{t}, styles{t})];
end
% There are three strings in the legend because a total of three columns of data are
% plotted. One column of data is from the first element of data, two columns of data
% are from the second element of data.
strings = {'data1' ,'data2', 'data3'};
legend(h,strings)
You might want to do something different with the legend but hopefully this will get you started.

Matlab cdfplot: how to control the spacing of the marker spacing

I have a Matlab figure I want to use in a paper. This figure contains multiple cdfplots.
Now the problem is that I cannot use the markers because the become very dense in the plot.
If i want to make the samples sparse I have to drop some samples from the cdfplot which will result in a different cdfplot line.
How can I add enough markers while maintaining the actual line?
One method is to get XData/YData properties from your curves follow solution (1) from #ephsmith and set it back. Here is an example for one curve.
y = evrnd(0,3,100,1); %# random data
%# original data
subplot(1,2,1)
h = cdfplot(y);
set(h,'Marker','*','MarkerSize',8,'MarkerEdgeColor','r','LineStyle','none')
%# reduced data
subplot(1,2,2)
h = cdfplot(y);
set(h,'Marker','*','MarkerSize',8,'MarkerEdgeColor','r','LineStyle','none')
xdata = get(h,'XData');
ydata = get(h,'YData');
set(h,'XData',xdata(1:5:end));
set(h,'YData',ydata(1:5:end));
Another method is to calculate empirical CDF separately using ECDF function, then reduce the results before plotting with PLOT.
y = evrnd(0,3,100,1); %# random data
[f, x] = ecdf(y);
%# original data
subplot(1,2,1)
plot(x,f,'*')
%# reduced data
subplot(1,2,2)
plot(x(1:5:end),f(1:5:end),'r*')
Result
I know this is potentially unnecessary given MATLAB's built-in functions (in the Statistics Toolbox anyway) but it may be of use to other viewers who do not have access to the toolbox.
The empirical CMF (CDF) is essentially the cumulative sum of the empirical PMF. The latter is attainable in MATLAB via the hist function. In order to get a nice approximation to the empirical PMF, the number of bins must be selected appropriately. In the following example, I assume that 64 bins is good enough for your data.
%# compute a histogram with 64 bins for the data points stored in y
[f,x]=hist(y,64);
%# convert the frequency points in f to proportions
f = f./sum(f);
%# compute the cumulative sum of the empirical PMF
cmf = cumsum(f);
Now you can choose how many points you'd like to plot by using the reduced data example given by yuk.
n=20 ; % number of total data markers in the curve graph
M_n = round(linspace(1,numel(y),n)) ; % indices of markers
% plot the whole line, and markers for selected data points
plot(x,y,'b-',y(M_n),y(M_n),'rs')
verry simple.....
try reducing the marker size.
x = rand(10000,1);
y = x + rand(10000,1);
plot(x,y,'b.','markersize',1);
For publishing purposes I tend to use the plot tools on the figure window. This allow you to tweak all of the plot parameters and immediately see the result.
If the problem is that you have too many data points, you can:
1). Plot using every nth sample of the data. Experiment to find an n that results in the look you want.
2). I typically fit curves to my data and add a few sparsely placed markers to plots of the fits to differentiate the curves.
Honestly, for publishing purposes I have always found that choosing different 'LineStyle' or 'LineWidth' properties for the lines gives much cleaner results than using different markers. This would also be a lot easier than trying to downsample your data, and for plots made with CDFPLOT I find that markers simply occlude the stairstep nature of the lines.