Is there a way to pass in N datasets and plot them in a line graph using for loop?
I did it by passing fixed number of parameter ( eg M1, M2, M3, M4), and repeat plotting manually like below. But I wonder if there are way to code the function dynamically? Let say I can pass in 4 datasets, or 40 datasets, and plot them in one graph via looping.
function plot_four_cdf(M1,M2,M3,M4)
[ycdf1,xcdf1] = cdfcalc(M1);
ycdf1 = ycdf1(2:length(ycdf1));
plot(xcdf1, ycdf1, '-+k', 'LineWidth', 1);
hold on;
[ycdf2,xcdf2] = cdfcalc(M2);
ycdf2 = ycdf2(2:length(ycdf2));
plot(xcdf2, ycdf2, '-ok', 'LineWidth', 1);
hold off;
hold on;
[ycdf3,xcdf3] = cdfcalc(M3);
ycdf3 = ycdf3(2:length(ycdf3));
plot(xcdf3, ycdf3, '-*k', 'LineWidth', 1);
hold off;
hold on;
[ycdf4,xcdf4] = cdfcalc(M4);
ycdf4 = ycdf4(2:length(ycdf4));
plot(xcdf4, ycdf4, '-sk', 'LineWidth', 1);
legend('M100','M80','M50','M20',...
'Location','SE')
xlabel('Relative Error');
ylabel('CDF');
end
You can group all your data in a cell-array and pass that to the function:
function plot_cdfs(M)
figure, hold on
linestyles = {...
'-+k', '-ok', '-*k', '-sk', ...
'-+r', '-or', '-*r', '-sr');
legendentries = cell(size(M));
for ii = 1:numel(M)
[ycdf, xcdf] = cdfcalc(M{ii});
plot(xcdf, ycdf(2:end), linestyles{ii}, 'LineWidth', 1);
legendentries{ii} = ['M' num2str(ii)];
end
legend(legendentries{:}, 'Location','SE')
xlabel('Relative Error');
ylabel('CDF');
end
Note that M is constructed something like
M = {M1, M2, M3, ...}
possibly also in a loop of its own. Note also that the legendentries are now kind of hard to define. You can pass them as a separate argument to the function (better option), or stuff them in the same cell array M next to the data they describe (not very portable).
Note also that you'd have to do some error checking (now, only 8 different plots can be made. An error will result if you do more).
Related
I am trying to apply a regularized optimization other than Lasso.
How can I plot the figure similar to the one produced by lassoPlot.m included in MATLAB as shown below if all data needed can be provide?
How to plot the I-shaped lines?
I read the lassoPlot.m but cannot find out how it is done.
If I had to create a plot like this manually, I'd do something like this:
function q53809665
DATASET = [
0.601240818 459.5714648 6.549320679
0.38951982 407.6789162 6.915203670
0.250128593 366.9277664 8.668936114
0.162048287 339.5657219 9.739510946
0.104984588 307.3415556 8.790018144
0.067415433 285.0615823 8.484338823
0.043675756 269.5982984 11.06798324
0.028295771 260.4386699 15.11267808
0.018170016 257.2895579 18.61737927
0.011771625 259.6377656 21.91891116
0.007626364 263.2320447 26.44502524
0.004897239 271.3708739 29.95587021
0.003172725 281.8307622 33.79278025
0.002055481 297.9101884 37.48077341
0.001319919 313.8919378 41.61931914
0.000855123 329.4338429 45.13891826
0.000554000 343.8029749 48.41955847
0.000355749 355.8266151 51.22206310
0.000230475 364.9182681 53.60367903
0.000149316 371.7376732 55.08113765
9.58825E-05 376.5047798 56.84605825
6.21184E-05 379.8791639 57.35674048
4.02440E-05 382.3739682 57.86464961];
figure();
hEB = errorbar(DATASET(:,1), DATASET(:,2), DATASET(:,3),'.', 'MarkerEdgeColor','r',...
'MarkerSize',10, 'Color', 0.7*[1 1 1]);
hEB.Parent.XDir = 'reverse';
hEB.Parent.XScale = 'log';
hold on;
plot(DATASET(9,1), DATASET(9,2), 'og', 'MarkerSize', 10);
plot(DATASET(7,1), DATASET(7,2), 'ob', 'MarkerSize', 10);
xline(DATASET(9,1), ':g');
xline(DATASET(7,1), ':b');
xlabel('Lambda');
title('Cross-Validated Devians of Lasso Fit');
legend('Deviance with Error Bars', 'LambdaMinDeviance','Lambda1SE');
Result:
I've got a lab where I need to first derrive a transfer function for a fourth order mass spring damper system, then plot this for a 200N step response.
The dashpot damping coefficient is a variable and we are to plot a range of values graphically and choose the optimium value i.e (the one closest to a critically damped response).
I have all of the above tasks working fully as expected, and maybe I'm just being dumb here but I can't get the graph legend to behave as desired.
I'd ideally like it to say "B="Current Value of B for that itteration of the loop.
%Initialisations
close all;format short g;format compact;clc;clear;
k1=500000;
k2=20000;
m1=20;
m2=400;
opt=stepDataOptions('StepAmplitude',200);
for (b = [1000:1000:15000])
Hs = tf([b k2],[(m1*m2) (b*(m1+m2)) (k2*(m1+m2)+k1*m2) (b*k1) (k1*k2)]);
hold on;
stepplot(Hs,opt)
title("Shock Absorber Performance to Step Input");
ylabel("Force (N)");
legend;
hold off;
end
Thanks in advance :)
Although not such a clean solution, this will work:
close all;format short g;format compact;clc;clear;
k1=500000;
k2=20000;
m1=20;
m2=400;
opt=stepDataOptions('StepAmplitude',200);
% initialize a cell array that will hold the labels
labels = {};
for (b = [1000:1000:15000])
Hs = tf([b k2],[(m1*m2) (b*(m1+m2)) (k2*(m1+m2)+k1*m2) (b*k1) (k1*k2)]);
hold on;
stepplot(Hs,opt)
title("Shock Absorber Performance to Step Input");
ylabel("Force (N)");
hold off;
% set the label name, with the value for b
labels{end+1} = ['B = ' num2str(b)];
end
% create the legend with the labels
legend(labels)
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:
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:
where for the each concatenated plot will be denoted on the X axis, as can be seen on the pic.
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');
I want to make a matrix of scatterplots in Matlab (version R2014b), and have only two ticks (minimum and maximum) for each x and y axis because the real world example has more plots and that will be easier to read. Here is an example, and as you can see, the second for loop isn't working as intended to edit the ticks. Also, is there a way to set this without a for loop?
% Example data
example = [ -5.2844 5.0328 307.5500
-12.0310 5.1611 237.9000
-15.9510 7.5500 290.7600
-11.5500 13.6850 285.8400
-1.9356 9.4700 273.2600
-9.2622 4.7456 232.6000
-6.5639 5.2272 265.3800
-4.4795 14.8320 281.1300
-2.1474 13.0690 288.7300
-3.7342 11.7450 265.2200
-11.9040 10.9660 286.5700
-2.1789 6.7442 258.9700
-2.8316 11.8210 281.7500
-2.6170 7.4740 244.8600
-6.8770 1.6623 116.9800
-10.2210 5.7575 300.2200
-3.9430 5.9715 253.6000
-3.3690 5.6530 230.9700
2.6090 3.2750 236.8700
-5.1430 8.4060 273.9600
-7.9360 2.7855 254.8200
-2.8665 2.0241 176.0600
-0.0960 3.3165 228.6800
-8.8465 10.3240 289.2100
-8.1930 16.4070 289.7000
-6.5840 13.4010 283.2600
2.2405 8.4625 270.1000
-2.2505 7.5555 285.9100
-4.6955 6.2985 279.2000
-0.7610 12.5210 283.2000
-0.7510 9.9470 279.9100
1.7120 8.5990 285.5700
-0.6245 7.6905 251.9100
-19.2320 6.8545 306.2700
-4.1255 9.8265 298.2000
2.9486 3.8881 250.2100
1.7333 5.5000 240.7300
-6.5881 3.9152 234.3800
-7.9543 8.0771 276.8000
-6.9641 8.8573 284.2800
-10.3280 7.4768 291.8700
-8.0818 5.1250 250.6600
-10.1490 3.9427 271.0000
-2.7786 3.7673 213.6500
-14.5410 11.1500 288.9100
-6.8118 11.0210 280.2000
-2.6459 6.5127 213.2600
1.4036 4.2023 253.9400
-5.0014 9.3900 267.0600
-9.7382 12.0990 290.8800]
% Labels for data
data_labels = {'Variable1','Variable2',...
'Variable3'};
% Make scatterplot matrix with histogram on diagonal
figure()
[H,AX]= plotmatrix(example(:,:));
% label the axes of the plots (rotated) works fine
for i = 1:length(AX)
ylabel(AX(i,1),data_labels{i},'Rotation',0,'HorizontalAlignment','right',...
'FontSize',12);
xlabel(AX(end,i),data_labels{i},'Rotation',60,'HorizontalAlignment','right',...
'FontSize',12);
end
% Set ticks (not working yet)
NumTicks = 2;
for i = 1:length(AX)
L = get(AX(i,i),'XLim');
set(AX(i,i),'XTick',linspace(L(1),L(2),NumTicks));
% set y ticks here too
end
You are incorrectly using i or both subscripts when indexing into AX.
AX(i) % Rather than AX(i,i)
Also, it's better to use numel than length and use a variable rather than i as your loop variable (since that's a MATLAB built-in for 0 + 1i). Also, you can use a single linear index into AX rather than specifying row and column subscripts.
for k = 1:numel(AX)
L = get(AX(k),'XLim');
set(AX(k),'XTick',linspace(L(1),L(2),NumTicks));
end
If you want to do this witnout a loop, you could technically do it using cellfun and the fact that you can set the same property in multiple plot objects using the following syntax
set(array_of_plot_handles, {'Property'}, array_of_values_to_set)
So applied to your problem, it would be something like
% Get all XLims as a cell array
oldlims = get(AX, 'XLim');
% Compute the new xlims
newlims = cellfun(#(x)linspace(x(1), x(2), NumTicks), oldlims, 'UniformOutput', false);
% Now set the values
set(AX, {'XTick'}, newlims);
Or if you really want it to be one line
set(AX, {'XTick'}, cellfun(#(x)linspace(x(1),x(2),NumTicks), get(AX, 'Xlim'), 'UniformOutput', false))
I have this data of which I want to make a principal component analysis.
In particular for each data point I want to associate a color.
This is my code:
for ii=1:size(SBF_ens,1)
SBF(ii) = SBF_ens(ii, max(find(SBF_ens(ii,:)~=0)) ); %value at the moment of the measurement
end
%matrix of data
toPCA =[
wind_trend_72h_ens-nanmean(wind_trend_72h_ens);
wind_trend_24h_ens-nanmean(wind_trend_24h_ens);
wind_trend_12to18h_ens-nanmean(wind_trend_12to18h_ens);
wind_trend_0to12h_ens-nanmean(wind_trend_0to12h_ens);
wind_trend_last6h_ens-nanmean(wind_trend_last6h_ens);
Mwind12h_ens-nanmean(Mwind12h_ens);
Mwind24h_ens-nanmean(Mwind24h_ens);
SBF-nanmean(SBF)]';
variables = { 'wt72h','wt24h','wt12to18h','wt0to12h','wtLast6h','Mw12h', 'Mw24h', 'SBF'}; %labels
%PCA algorithm
C = corr(toPCA,toPCA);
w = 1./var(toPCA);
[wcoeff,score,latent,tsquared,explained] = pca(toPCA,'VariableWeights',w);
coefforth = diag(sqrt(w))*wcoeff;
metric=decstd_ens; %metric for colorbar
hbi=biplot(coefforth(:,1:2),'scores',score(:,1:2),'varlabels',...
variables,'ObsLabels', num2str([1:length(toPCA)]'),...
'markersize', 15);
%plotting
cm = autumn;
colormap(cm);
for ii = length(hbi)-length(toPCA):length(hbi)
userdata = get(hbi(ii), 'UserData');
if ~isempty(userdata)
indCol = ceil( size(cm,1) * abs(metric(userdata))/max(abs(metric)) );%maps decstd between 0 and 1 and find the colormap index
if indCol==0 %just avoid 0
indCol=1;
end
col = cm(indCol,:); %color corresponding to the index
set(hbi(ii), 'Color', col); %modify the dot's color
end
end
for ii = 1:length(hbi)-length(toPCA)-1 %dots corresponding to the original dimensions are in black
set(hbi(ii), 'Color', 'k');
end
c=colorbar;
ylabel(c,'decstd') %is this true?
xlabel(['1^{st} PCA component ', num2str(explained(1)), ' of variance explained'])
ylabel(['2^{nd} PCA component ', num2str(explained(2)), ' of variance explained'])
The resulting figure is the following:
Everything is fine except for the colorbar range. In fact decstd has values between 0 and 2. Actually I do not understand at all what the values on the colorbar are.
Does anyone understand it?
Is it possible to rethrive the data in the colorbar? So to understand what they are?
size(autumn)
shows you that the default length of the autumn colourmap (actually of all the colourmaps) is 64. When you call colorbar, by default it will use tick labels from 1 to n where n is the length of your colourmap, in this case 64.
If you want the mapping of the colourbar ticklabels to match the mapping that you used to get your data to fit between 1 and 64 (i.e. this line of yours indCol = ceil( size(cm,1) * abs(metric(userdata))/max(abs(metric)) );), then you will need to set that yourself like this
numTicks = 6;
cAxisTicks = linspace(min(metric), max(metric), numTicks); % or whatever the correct limits are for your data
caxis([min(metric), max(metric)]);
set(c,'YTick', cAxisTicks );