draw a line of best fit through data with shaded region for error - matlab

I have the following data:
dat = [9.3,0.6,0.4,0.7;...
3.2,1.2,0.7,1.9;...
3.9,1.8,0.7,1.9;...
1.0,7.4,5.6,10.7;...
4.7,1.0,0.5,1.3;...
2.2,2.6,1.2,2.7;...
7.2,1.0,0.5,1.1;...
1.0,4.8,7.5,10.3;...
2.7,1.8,1.7,4.0;...
8.2,0.8,0.4,0.9;...
1.0,4.9,5.7,8.2;...
12.9,1.3,0.6,1.6;...
7.7,0.8,0.5,1.3;...
5.8,0.9,0.6,1.9;...
1.1,4.5,6.2,12.1;...
1.1,4.5,2.8,4.8;...
16.4,0.3,0.3,0.5;...
10.4,0.6,0.3,0.7;...
2.2,3.1,2.2,4.6];
where the first column shows the observed values the second column shows the modeled values and the third and fourth columns show the min and max respectively.
I can plot the relationship between observed and modeled by
scatter(d(:,1),d(:,2))
Next, I would like to draw a smooth line through these points to show the relationship. How can this be done? Obviously a simple straight line would not be much use here.
Secondly, I would like to use the min and max (3rd and 4th columns respectively) to draw a shaded region around the modeled values in order to show the associated error.
I would eventually like to have something that looks like
http://www.giss.nasa.gov/research/briefs/rosenzweig_03/figure2.gif

Something like this?
%// Rename and sort your data
[x,I] = sort(dat(:,1));
y = dat(I,2);
mins = dat(I,3);
maxs = dat(I,4);
%// Assume a model of the form y = A + B/x. Solve for A and B
%// using least squares
P = [ones(size(x)) 1./x] \ y;
%// Initialize figure
figure(1), clf, hold on
set(1, 'Renderer', 'OpenGl');
%// Plot "shaded" areas
fill([x; flipud(x)], [mins; flipud(maxs)], 'y',...
'FaceAlpha', 0.2,...
'EdgeColor', 'r');
%// Plot data and fit
legendEntry(1) = plot(x, P(1) + P(2)./x, 'b',...
'LineWidth', 2);
legendEntry(2) = plot(dat(:,1), dat(:,2), 'r.',...
'Markersize', 15);

Related

plotting from cell array of different lengths

this might come as an eco in this forum but I couldn't find a solution that I could apply to my problem. I have a cell of res_A1 of size (1x500) and in each cell there is a vector (1xlength) where length varies. I would like to plot in the same graph every vector or maybe a handful of them. All lines can be in the same color. I have tried following but the graph make no sense
data=res_A1(1,:,end);
plot(cell2mat(data)');
Also I would like to plot the average of the 500 vectors, preferably this should be in the same graph in another color. Is there a nice way of doing this?
You can use cat to combine the vectors along the first dimension. Then you can pass the transpose of this matrix to plot and each column will be plotted as it's own plot.
plot(cat(1, data{:}).');
If we create some example data, this will yield.
data = arrayfun(#(x)rand(1,10), ones(1, 5), 'uni', 0);
plot(cat(1, data{:}).');
If you want specific ones (i.e. [1 3 5]), you can replace : above with the indices of the ones you want.
plot(cat(1, data{[1 3 5]}).');
If you want to plot the average, simply use mean on the result of the call to cat.
avg = mean(cat(1, data{:}), 1);
plot(avg);
And if you wanted it in the same plot:
alldata = cat(1, data{:});
avg = mean(alldata, 1);
% Plot all of the curves
plot(alldata.');
hold on
% Plot the average curve
plot(avg, 'LineWidth', 3, 'Color', [0.5 0.5 0.5], 'LineStyle', '--')
Update
If your data is all different lengths, You have two options, you could plot everything with a loop.
hax = axes;
hold(hax, 'on');
for k = 1:numel(data)
plot(data{k}, 'Parent', hax);
end
Or you could still try to combine everything into one matrix, padding with NaN values.
% Find the longest vector length
maxlength = max(cellfun(#(x)numel(x), data));
alldata = nan(maxlength, numel(data));
for k = 1:numel(data)
alldata(1:numel(data{k}),k) = data{k};
end
Then you can plot this and take the mean using nanmean.
plot(alldata);
avg = nanmean(alldata, 2);

Cracked-glass–like plot in Matlab

I would like make a plot that looks like this:
Namely a scatter plot with series of replicates connected via lines to the centroid.
I am not sure whether I saw it somewhere or whether I am simply getting glyphplot confused. I do not know what it is called, so Google has failed me —"cracked glass plot" is what I would personally call it.
In Matlab there is no native one line way to show a scatterplot with both x and y errorbars —yet Excel can. This makes me think there is a better way, such as this cracked glass plot.
Does it exist or do I need to write it myself?
I don't know of any native function to do this, but you can quite easily make use of line to plot something to fit your purposes, e.g.
function scatterPlotToCentroid(scatterPoints)
numScatters = size(scatterPoints,2);
scatterX = scatterPoints(1,:);
scatterY = scatterPoints(2,:);
centroid = mean(scatterPoints,2);
c1 = centroid(1);
c2 = centroid(2);
X = [repmat(c1,1,numScatters); scatterX];
Y = [repmat(c2,1,numScatters); scatterY];
hold on
line(X,Y,'Color','k');
scatter(scatterX, scatterY, 'r*');
end
Example call
% example: scatter sample (two groups)
numPoints = 10;
scatterDiff = 5;
scatterPointsA = 4+scatterDiff*(rand(2,numPoints)-0.5);
scatterPointsB = 8+scatterDiff*(rand(2,numPoints)-0.5);
% for each scatter sample group, plot scatter points
% and lines to centroid
hold on, box on
scatterPlotToCentroid(scatterPointsA)
scatterPlotToCentroid(scatterPointsB)
axis([0 12 0 12])
Example plot
Below follows the initial version of this answer, that left out the "centroid" part of the question (missed...), and instead generated random scatters around a given center-point; drawing lines from the latter to these scatter points.
function scatterPlotAt(centerPoint, numScatters, maxScatterSideLength)
c1 = centerPoint(1);
c2 = centerPoint(2);
scatterX = c1-maxScatterSideLength + ...
randi(2*maxScatterSideLength,1,numScatters);
scatterY = c2-maxScatterSideLength + ...
randi(2*maxScatterSideLength,1,numScatters);
X = [repmat(c1,1,numScatters); scatterX];
Y = [repmat(c2,1,numScatters); scatterY];
hold on
line(X,Y,'Color','k');
scatter(scatterX, scatterY, 'r*');
end
Example call
hold on, box on
scatterPlotAt([4; 4], 6, 3)
scatterPlotAt([8; 8], 6, 3)
axis([0 12 0 12])
Example result

Add lines of different lengths to plot in Matlab

In this trivial example I extract a portion of x to make y & y1. To plot y1 in the correct position I used NaN padding in the front. I'm wondering whether NaN padding is considered the best practice or is there some way to directly specify the X coordinate where I want the y1 line segment plotted?
figure(500);
hold off;
x = rand(1,100);
plot(x);
y = x(20:60)+3;
hold on;
plot(y);
y1 = horzcat(repmat(nan,1,19), (y+3));
plot(y1);
If your main goal is just to display the sub-set of data within the proper range, seems like you could just specify the x-range directly, like:
x_range = 20:60;
y = x(x_range)+3;
hold on;
plot(y);
plot(x_range, y+3);
grid on
Which gives the identical result you got using Nans:

2-d graph with two different axis showing different min and max MATLAB

I have the graph below I need to create two different x-axis. The unique part of this problem is where the min and max values need to be located. The range for is 0-100 for both, however the 100% value on the second x-axis needs to be where the 50% value is on the first. See the picture for clarification. The red is what I need to add using MATLAB.
I did a lot of looking and while it's very simple to put two different axis on one graph, I couldn't find a solution for this particular problem. I'd like this to be done in the code and not plot tools.
How about this
% dummy data
y = 1:80;
x1 = 100*sin( 4*pi*y/80 ).^2 ;
x2 = 100*cos( 5*pi*y/80).^2;
Plot the first line
figure;
line( x1, y, 'Color', 'b', 'LineWidth', 2 );
Get position and size of first plot
haxes1 = gca;
haxes1_pos = get(haxes1,'Position');
set the 100% of second plot to 50% of first ("tweaking" the width of the axis)
haxes1_pos(3) = haxes1_pos(3)/2;
haxes2 = axes('Position',haxes1_pos,'XAxisLocation','top','Color','none','XColor','r');
Plot the second line
line( x2, y, 'Color', 'k', 'LineWidth',2,'Parent',haxes2);
And this is what you get

MATLAB fill area between lines

I'm trying to do something similar to what's outlined in this post:
MATLAB, Filling in the area between two sets of data, lines in one figure
but running into a roadblock. I'm trying to shade the area of a graph that represents the mean +/- standard deviation. The variable definitions are a bit complicated but it boils down to this code, and when plotted without shading, I get the screenshot below:
x = linspace(0, 100, 101)';
mean = torqueRnormMean(:,1);
meanPlusSTD = torqueRnormMean(:,1) + torqueRnormStd(:,1);
meanMinusSTD = torqueRnormMean(:,1) - torqueRnormStd(:,1);
plot(x, mean, 'k', 'LineWidth', 2)
plot(x, meanPlusSTD, 'k--')
plot(x, meanMinusSTD, 'k--')
But when I try to implement shading just on the lower half of the graph (between mean and meanMinusSTD) by adding the code below, I get a plot that looks like this:
fill( [x fliplr(x)], [mean fliplr(meanMinusSTD)], 'y', 'LineStyle','--');
It's obviously not shading the correct area of the graph, and new near-horizontal lines are being created close to 0 that are messing with the shading.
Any thoughts? I'm stumped.
You may be getting a problem with using mean as a variable, since it's also a reserved MATLAB command. Try clearing the variable space and then using a unique variable name.
As for the second problem, you want
fill( [x fliplr(x)], [meanUniqueName fliplr(meanMinusSTD)], 'y', 'LineStyle','--');
You also don't need to do this in two steps, but can do it all at once. A code snippet from a script I'm currently working on does the exact same thing and contains the lines:
avar = allan(DATA, tau);
xFill = [avar.tau1 fliplr(avar.tau1)];
yFill = [avar.sig2+avar.sig2err fliplr(avar.sig2-avar.sig2err)];
figure(2);
fill(xFill,yFill,'y','LineStyle','--')
line(avar.tau1,avar.sig2);
So I fill the area between the two error lines, and then draw the data line on top.
It turned out to be a column vs row vector issue. For some reason using the fill method above with flipud with the original column vectors doesn't work, but transposing the original variables then using fliplr does. Go figure. Here's the code in case it helps someone else:
x = linspace(0,100, 101);
mean = torqueRnormMean(:,DOF)';
meanPlusSTD = torqueRnormMean(:,DOF)' + torqueRnormStd(:,DOF)';
meanMinusSTD = torqueRnormMean(:,DOF)' - torqueRnormStd(:,DOF)';
fill( [x fliplr(x)], [meanPlusSTD fliplr(meanMinusSTD)], 'k');
alpha(.25);
plot(x, mean, 'k', 'LineWidth', 2)
plot(x, meanPlusSTD, 'k')
plot(x, meanMinusSTD, 'k')
Note that I removed the dotted line and just used thin vs. thick lines to denote standard deviation and mean. I did this because the line style was inconsistent. This code is in a loop where DOF runs from 1:9, and in some of the subplots both std curves would be dashed and in some just the top or bottom. It didn't matter enough to me to have them dashed so I kept it simple. Now this is an example of the graph I get:
One thing that you appear to be doing wrong is that you're applying fliplr to column vectors. That will have no effect. The example you cited uses row vectors. You also concatenate them into a matrix instead of into a single vector like the example. I think that the equivalent with column vectors would be:
fill( [x;flipud(x)], [mean;flipud(meanMinusSTD)], 'y');
Another possibility:
x = 1:1000; % example x values
y_upper = 5+sin(2*pi/200*x); % example upper curve
y_lower = 2+sin(2*pi/230*x); % example lower curve
bar(x, y_upper, 1, 'b', 'edgecolor', 'b');
hold on
bar(x, y_lower, 1, 'w', 'edgecolor', 'w');
axis([0 1000 0 7])
It uses bar (with unit width and same-color edges) to fill the upper curve, and then a second bar to "remove" (plot in white) the lower part.