How to display statistics as text in a graph? - matlab

I am using cdfplot in matlab to plot the empirical CDF of certain quantities.
[h,stats]=cdfplot(quantity)
Now stats returns me a structure having min, max, mean etc. I want these values to be displayed as text in the graph.
I have lot of similar graphs to plot and do not want to do it manually.

To put a text on a plot you use the text function. Here is a quick example on one plot:
y = evrnd(0,3,100,1);
[h, stats] = cdfplot(y);
hold on
x = -20:0.1:10;
f = evcdf(x,0,3);
plot(x,f,'m')
legend('Empirical','Theoretical','Location','NW')
stat_type = {'min: ';'max: ';'mean: ';'median: ';'std: '}; % make titles
stat_val = num2str(struct2array(stats).'); % convert stats to string
text(-15,0.7,stat_type) % <-- here
text(-11,0.7,stat_val) % <-- and here
hold off
This will give:
and you can use it inside a loop to do it for all the graphs.
The tricky thing is to define where to put the text on the graph. Here I choose (-15,0.7) and (-11,0.7) as a fixed points where I know there is no data. You should look at your plots, and find the correct place for that.

Related

Logarithmic x axis in a stackplot MatLab

I'm trying to make a stack plot from a table, to present several variables with the same x-axis. however, I want the x-axis to be logarithmic. I couldn't find any way in stackplot documentation. Does anyone have any suggestions on how I can solve this?
I have tried using subplots instead, however, that way my graphs would not fit all on one page and I would have a lot of white space between the subplots. Therefore, I chose stackplot to make it more nice and less space-consuming.
tbl = readtable('usage.csv');
newYlabels = {'Heating (kWh/year)','Cooling (kWh/year)','Electricity (kWh/year)'};
stackedplot(tbl,[{2:16},{17:27},{28:35}],'XVariable',[1],'DisplayLabels',newYlabels);
Here is the output of the code:
Here is an image of what I'm trying to make, but the x-axis needs to be the real variable (\beta) in logarithmic scale
stackedplot has unfortunately no logarithmic axes option, and since it creates a StackedAxes instead of a normal Axes object, there is no way to changes this.
If the only reason you want to use stackedplot is to have less white-space, you might want to check out tight_subplot on the Matlab FEX. This would allow you to just do: set(ax, 'XScale', 'log').
You can however take the log of your x-data, and add that to the table:
tbl = readtable('outages.csv'); % sample data
tbl = sortrows(tbl, 'OutageTime'); % sort by date
% make x vector; for example just row numbers
x = (1:size(tbl,1)).';
xlog = log10(x);
% add x to table
tbl.Xlog = xlog;
tbl.X = x;
% plot normal x
f = figure(1); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'X');
xlabel('rows');
% plot log(x)
f = figure(2); clf;
s = stackedplot(tbl, {'Loss'}, 'XVariable', 'Xlog');
xlabel('log(rows)')
Normal:
Log:

MATLAB: combining and normalizing histograms with different sample sizes

I have four sets of data, the distribution of which I would like to represent in MATLAB in one figure. Current code is:
[n1,x1]=hist([dataset1{:}]);
[n2,x2]=hist([dataset2{:}]);
[n3,x3]=hist([dataset3{:}]);
[n4,x4]=hist([dataset4{:}]);
bar(x1,n1,'hist');
hold on; h1=bar(x1,n1,'hist'); set(h1,'facecolor','g')
hold on; h2=bar(x2,n2,'hist'); set(h2,'facecolor','g')
hold on; h3=bar(x3,n3,'hist'); set(h3,'facecolor','g')
hold on; h4=bar(x4,n4,'hist'); set(h4,'facecolor','g')
hold off
My issue is that I have different sampling sizes for each group, dataset1 has an n of 69, dataset2 has an n of 23, dataset3 and dataset4 have n's of 10. So how do I normalize the distributions when representing these three groups together?
Is there some way to..for example..divide the instances in each bin by the sampling for that group?
You can normalize your histograms by dividing by the total number of elements:
[n1,x1] = histcounts(randn(69,1));
[n2,x2] = histcounts(randn(23,1));
[n3,x3] = histcounts(randn(10,1));
[n4,x4] = histcounts(randn(10,1));
hold on
bar(x4(1:end-1),n4./sum(n4),'histc');
bar(x3(1:end-1),n3./sum(n3),'histc');
bar(x2(1:end-1),n2./sum(n2),'histc');
bar(x1(1:end-1),n1./sum(n1),'histc');
hold off
ax = gca;
set(ax.Children,{'FaceColor'},mat2cell(lines(4),ones(4,1),3))
set(ax.Children,{'FaceAlpha'},repmat({0.7},4,1))
However, as you can see above, you can do some more things to make your code more simple and short:
You only need to hold on once.
Instead of collecting all the bar handles, use the axes handle.
Plot the bar in ascending order of the number of elements in the dataset, so all histograms will be clearly visible.
With the axes handle set all properties at one command.
and as a side note - it's better to use histcounts.
Here is the result:
EDIT:
If you want to also plot the pdf line from histfit, then you can save it first, and then plot it normalized:
dataset = {randn(69,1),randn(23,1),randn(10,1),randn(10,1)};
fits = zeros(100,2,numel(dataset));
hold on
for k = numel(dataset):-1:1
total = numel(dataset{k}); % for normalizing
f = histfit(dataset{k}); % draw the histogram and fit
% collect the curve data and normalize it:
fits(:,:,k) = [f(2).XData; f(2).YData./total].';
x = f(1).XData; % collect the bar positions
n = f(1).YData; % collect the bar counts
f.delete % delete the histogram and the fit
bar(x,n./total,'histc'); % plot the bar
end
ax = gca; % get the axis handle
% set all color and transparency for the bars:
set(ax.Children,{'FaceColor'},mat2cell(lines(4),ones(4,1),3))
set(ax.Children,{'FaceAlpha'},repmat({0.7},4,1))
% plot all the curves:
plot(squeeze(fits(:,1,:)),squeeze(fits(:,2,:)),'LineWidth',3)
hold off
Again, there are some other improvements you can introduce to your code:
Put everything in a loop to make thigs more easily changed later.
Collect all the curves data to one variable so you can plot them all together very easily.
The new result is:

Figure legend is being cut off MATLAB

I'm trying to plot 81 variables into one plot in MATLAB and I need a legend with 81 respective labels. I've managed to separate the legend into several lines such that it would fit the figure better but still, being this large, the legend apears cut off:
is there any way to solve this problem, so that all the legend appears in the figure?
Thank you in advance
In my experience, the legend is not manageable once the number of plots becomes so large.
You could try and hack this out by hand by trying to make a compact legend my manually moving the legend lines and text boxes around individually--but I fear this way will ultimately make you unhappy. If you want, you could start playing with the legend pieces by writing something like:
hl = legend(uniqueHandles, myNames);
hlc = get(hl, 'children');
This may not be possible and MATLAB will resist you. Instead, consider that a legend with 81 different colors becomes very hard to manage and you can't really tell the colors apart anyway. Instead of trying to get them all to fit I recommend grouping them into different colors and styles and then only showing 3-7 different sets like this:
This could be further improved by customizing the data tip so that when you click on a line it gives you more detail.
I put together some basic code to show how you might do this. It has some shortcomings. Regardless, in this example I plot everything at once then go back and group the colors together. I then pick out a representative line from each grouping and use it as an example in the legend. You could also plot everything, color it correctly, and then plot a new line in the existing axis with a value nan and use THOSE in the legend.
(edit) I also noted that you have grouped the colors already for some of them, so this should flow naturally from the work you've already done. If nothing else, you could grab the handles from every third entry and then only have 27 items in your legend... but best if you can reduce it further.
The example to create the above image is as follows:
%% Setup the problem:
nd = 81;
nx = 256;
data = zeros(nx,nd);
t = linspace(0, 2*pi, nx);
%% We divide the data into 3 groups and set a color for each. This calculation then notes what group each line belongs to:
myCategory = ceil((1:nd)/27);
myColor = myColors(myCategory)';
myColors = ['r', 'b', 'g'];
myNames = {'1 to 27', '28 to 54', '55 to 81'};
%% Make a simple set of data:
randn('seed', 1982);
dt = 0;
for ind = 1:nd
dt = dt+randn/5;
data(:, ind) = sin(t+dt) + dt*cos(t);
end
%% Plot all at once:
figure(33);
clf;
h = plot(data);
%% find the unique entries and choose one representative line for the legend.
%% We then use the handles of these lines to create the legend:
lastColor = -1;
uniqueHandles = [];
for ind = 1:nd
set(h(ind), 'color', myColor(ind));
if lastColor ~= myColor(ind)
lastColor = myColor(ind);
uniqueHandles(end+1) = h(ind);
end
end
% make the legend:
legend(uniqueHandles, myNames);

Legend in Matlab Graph

I need to produce a X vs Y graph and make a distinction between positive class and negative class (indicated in original data infile). How do I produce a legend in such a graph? This is my code for the graph right now :
infile = fopen('ClassData1.txt','r');
data = textscan(infile,'%f %f %f');
parameters = [data{1} data{2}];
label = [data{3}];
h = ones(100,9);
g = ones(100,9);
score1= ones(1,9);
sc = 0;
figure
for i = 1:100
if label(i)>0
plot(parameters(i,1),parameters(i,2),'r*')
hold on
else
plot(parameters(i,1),parameters(i,2),'b*')
end
end
To show each line type only once, you have to keep the handles. Storing only one handle per class is sufficient.
h=nan(2,1)
for i = 1:100
if label(i)>0
h(1)=plot(parameters(i,1),parameters(i,2),'r*')
hold on
else
h(2)=plot(parameters(i,1),parameters(i,2),'b*')
end
end
legend(h)
There are (more than) two ways to do this. Firstly, you can just use a single plot command and do the legend normally, by combining the plot definitions using logical indexing, or you can use the DisplayName property for each plot to give information about the legend.
% Some sample data
parameters=rand(100,2);
label=parameters(:,1)-0.5;
% Use logical indexing to replace the for loop and use a single plot command
figure(1)
plot(parameters(label>0,1),parameters(label>0,2),'r*',parameters(label<=0,1),parameters(label<=0,2),'b*')
legend('red','blue')
% Use DisplayName to set the legend entry for each plot, then show the legend using the names given
figure(2)
plot(parameters(label>0,1),parameters(label>0,2),'r*','DisplayName','Red')
hold on
plot(parameters(label<=0,1),parameters(label<=0,2),'b*','DisplayName','Blue')
hold off
legend('show')

How To Put String Labels on Contours for Contour Plots in MATLAB

I am wondering if it is possible to label the contours of a MATLAB contour plot with a set of user-defined strings?
I am currently using the following code snipper to produce a labelled contour plot:
%Create Data
X = 0.01:0.01:0.10
Y = 0.01:0.01:0.10
Z = repmat(X.^2,length(X),1) + repmat(Y.^2,length(Y),1)';
%Create Plot
hold on
[C,h] = contourf(X,Y,Z);
%Add + Format Labels to Plot
hcl = clabel(C,h,'FontSize',10,'Color','k','Rotation',0);
set(hcl,'BackgroundColor',[1 1 1],'EdgeColor',[0 0 0],'LineStyle','-',)
hold off
The issue with this code is that the labels are automatically generated by MATLAB. Even as I can easily change the contours that are labels, I cannot change the labels that they get.
Ideally, I would like to label them with a set of strings that I define myself. However if that is not possible, then I am wondering if it is possible to change the numeric format of the labels. The reason for this is that the code above actually produce a contour plot for an error rate, which I would like to display as a % value (i.e. use 1% in the contour label, instead of 0.01 etc.).
In this case, hcl is actually an array which stores handles to every contour label on your plot. When you set properties using the array (as in your code),
set(hcl, 'name', 'value')
You will set the property of every label to the same value.
You can change the properties of individual labels by iterating over the array. For example, this is how you would add a percentage sign:
for i = 1:length(hcl)
oldLabelText = get(hcl(i), 'String');
percentage = str2double(oldLabelText)*100;
newLabelText = [num2str(percentage) ' %'];
set(hcl(i), 'String', newLabelText);
end