How to make multiple graphs with varying x-axis? - matlab

I need to plot multiple graphs in the same y-axis, but the x-axis is a bit tricky for me:
Assuming it goes from 0 to a, increasing by one, it needs to have an overall of a-1 different intervals.
Each one of them should finish at a, but it also has to have a different initialisation point. Only the first starts at 0, while each next one, starts by the previous plus one, as in the shape below. The two dashed lines, I used to visualise my thoughts as clear as I could, are not necessary.
I would appreciate any help!

Replicate your original interval 0 ... a a times (from my understanding, you'll have a intervals, not a-1), such that you get a matrix X of size [a x a+1]. Set the lower left triangle of X to NaN, so that the rows now represent your (shortening) intervals. Do your calculations on X. Pay attention, these have to support/neglect NaN values properly.
After that, you need to adjust the values in X properly, so that the intervals are plotted subsequently. Basically, we add some fixed value to each row.
Last, we need the proper xticks and xticklabels. Therefore, we extract all values from X and the modified X and get rid of the NaN values.
Here's a complete code snippet:
% Parameter
a = 7;
% Initialize intervals
X = repmat(0:a, a, 1);
X = X .* (ones(size(X)) + tril(nan(size(X)), -1));
% Calculation on these intervals; attention: there are NaN in X
Y = sin(X / a * 2 * pi);
% Modify X for plotting
X_plot = X;
X_plot(2:end, :) = X_plot(2:end, :) + cumsum(a:-1:2).';
% Get xticks
xt = X_plot.';
xt = xt(:);
xt(isnan(xt)) = [];
% Get xticklabels
xtl = X.';
xtl = xtl(:);
xtl(isnan(xtl)) = [];
% Plot
plot(X_plot.', Y.');
xticks(xt);
xticklabels(xtl);
The output (Octave 5.1.0, also tested with MATLAB Online) looks like this:
If you only want for example the start and end of each interval, you must further pre-process xt and xtl.
Hope that helps!

Related

Computing a moving average

I need to compute a moving average over a data series, within a for loop. I have to get the moving average over N=9 days. The array I'm computing in is 4 series of 365 values (M), which itself are mean values of another set of data. I want to plot the mean values of my data with the moving average in one plot.
I googled a bit about moving averages and the "conv" command and found something which i tried implementing in my code.:
hold on
for ii=1:4;
M=mean(C{ii},2)
wts = [1/24;repmat(1/12,11,1);1/24];
Ms=conv(M,wts,'valid')
plot(M)
plot(Ms,'r')
end
hold off
So basically, I compute my mean and plot it with a (wrong) moving average. I picked the "wts" value right off the mathworks site, so that is incorrect. (source: http://www.mathworks.nl/help/econ/moving-average-trend-estimation.html) My problem though, is that I do not understand what this "wts" is. Could anyone explain? If it has something to do with the weights of the values: that is invalid in this case. All values are weighted the same.
And if I am doing this entirely wrong, could I get some help with it?
My sincerest thanks.
There are two more alternatives:
1) filter
From the doc:
You can use filter to find a running average without using a for loop.
This example finds the running average of a 16-element vector, using a
window size of 5.
data = [1:0.2:4]'; %'
windowSize = 5;
filter(ones(1,windowSize)/windowSize,1,data)
2) smooth as part of the Curve Fitting Toolbox (which is available in most cases)
From the doc:
yy = smooth(y) smooths the data in the column vector y using a moving
average filter. Results are returned in the column vector yy. The
default span for the moving average is 5.
%// Create noisy data with outliers:
x = 15*rand(150,1);
y = sin(x) + 0.5*(rand(size(x))-0.5);
y(ceil(length(x)*rand(2,1))) = 3;
%// Smooth the data using the loess and rloess methods with a span of 10%:
yy1 = smooth(x,y,0.1,'loess');
yy2 = smooth(x,y,0.1,'rloess');
In 2016 MATLAB added the movmean function that calculates a moving average:
N = 9;
M_moving_average = movmean(M,N)
Using conv is an excellent way to implement a moving average. In the code you are using, wts is how much you are weighing each value (as you guessed). the sum of that vector should always be equal to one. If you wish to weight each value evenly and do a size N moving filter then you would want to do
N = 7;
wts = ones(N,1)/N;
sum(wts) % result = 1
Using the 'valid' argument in conv will result in having fewer values in Ms than you have in M. Use 'same' if you don't mind the effects of zero padding. If you have the signal processing toolbox you can use cconv if you want to try a circular moving average. Something like
N = 7;
wts = ones(N,1)/N;
cconv(x,wts,N);
should work.
You should read the conv and cconv documentation for more information if you haven't already.
I would use this:
% does moving average on signal x, window size is w
function y = movingAverage(x, w)
k = ones(1, w) / w
y = conv(x, k, 'same');
end
ripped straight from here.
To comment on your current implementation. wts is the weighting vector, which from the Mathworks, is a 13 point average, with special attention on the first and last point of weightings half of the rest.

Sketch f(x,y)=(21/4)x^2y over the region x^2 <= y <= 1

Can someone share a technique using MATLAB to plot the surface f(x,y)=(21/4)x^2y over the region x^2 <= y <= 1?
Also, if anyone is aware of some tutorials or links that would help with this type of problem, could you please share them?
Thanks.
Here is another approach:
%%
close all
x=linspace(-1,1,40);
g1=x.^2;
g2=ones(1,40);
y=[];
n=20;
for k=0:n
y=[y;g1+(g2-g1)*k/n];
end
x=x(ones(1,n+1),:);
z=21/4*x.^2.*y;
meshz(x,y,z)
axis tight
xlabel('x-axis')
ylabel('y-axis')
view(136,42)
And the result:
And finally, you can map the region (-1,1)x(0,1) in the uv-plane into the region bounded by $y=x^2 and y=1 in the xy-plane with the parametrization:
f(u,v) = (u\sqrt{v},v)
Capture from: https://math.stackexchange.com/questions/823168/transform-rectangular-region-to-region-bounded-by-y-1-and-y-x2
This code produces the same image shown above:
close all
[u,v]=meshgrid(linspace(-1,1,40),linspace(0,1,20));
x=u.*sqrt(v);
y=v;
z=21/4*x.^2.*y;
meshz(x,y,z)
axis tight
xlabel('x-axis')
ylabel('y-axis')
view(136,42)
First off, let's look at your valid region of values. This is telling us that y >= x^2 and also y <= 1. This means that your y values need to be on the positive plane bounded by the parabola x^2 and they also must be less than or equal to 1. In other words, your y values must be bound within the area dictated from y = x^2 to y = 1. Pictorially, your y values are bounded within this shape:
As such, your x values must also be bound between -1 and 1. Therefore, your actual boundaries are: -1 <= x <= 1 and 0 <= y <= 1. However, this only locates our boundaries for x and y but it doesn't handle where the plot has valid values. We'll tackle that later.
Now that we have that established, you can use ezsurf to plot surface plots in MATLAB that are dictated by a 2D equation.
You call ezsurf like so:
ezsurf(FUN, [XMIN,XMAX,YMIN,YMAX]);
FUN is a function or a string that contains the equation you want, and XMIN,XMAX,YMIN,YMAX contain the lowest and highest x and y values you want to plot. Plotting without these values assumes a span from -2*pi to 2*pi in both dimensions. As such, let's create a new function that will handle when we have valid values, and when we don't. Use this code, and save it to a new file called myfun.m. Make sure you save this to your current Working Directory.
function z = myfun(x,y)
z = (21/4)*x.^2.*y;
z(~(x.^2 <= y & y <= 1)) = nan;
end
This will allow you to take a series of x and y values and output values that are dictated by the 2D equation that you have given us. Any values that don't satisfy the condition of x^2 <= y <= 1, you set them to NaN. ezsurf will not plot NaN values.
Now, call ezsurf like so:
ezsurf(#myfun, [-1,1,0,1]);
You thus get:
This will spawn a new figure for you, and there are some tools at the top that will allow you interact with your 3D plot. For instance, you can use the rotation tool that's at the top bar beside the hand to rotate your figure around and see what this looks like. Click on this tool, then left click your mouse and hold the left mouse button anywhere within the surface plot. You can drag around, changing the azimuth and the latitude to get the perspective that you want.
Edit: June 4th, 2014
Noting your comments, we can decrease the jagged edges by increasing the number of points in the plot. As such, you can append a final parameter to ezsurf which is N, the number of points to add in each dimension. Increasing the number of points will decrease the width in between each point and so the plot will look smoother. The default value of N is 60 in both dimensions. Let's try increasing the amount of points in each dimension to 100.
ezsurf(#myfun, [-1,1,0,1], 100);
Your plot will look like:
Hope this helps!
Try the following to make the required function, compute the values, and plot only the region that is desired:
% Make the function. You could put this in a file by itself, if you wanted.
f = #(x,y) (21/4)*x.^2.*y;
[X Y] = meshgrid(linspace(0,1));
Z = f(X,Y);
% compute the values we want to plot:
valsToPlot = (X.^2 <= Y) & (Y <= 1);
% remove the values that we don't want to plot:
X(~valsToPlot) = nan;
Y(~valsToPlot) = nan;
Z(~valsToPlot) = nan;
% And... plot.
figure(59382);
clf;
surf(X,Y,Z);

Interpolate/fill in missing cells in truth-value grid in MATLAB

Consider the 37x101 matrix below:
Each black cell has value 1, the rest of the cells value 0. I would like to fill in the "gaps" by means of cubic spline interpolation, as well as scaling the y axis from 37 to 181. The latter can be done by using the interp1 function, as in:
interp1(37,matrix,181,'pchip')
However, the outcome is now interpolated along the y-axis, but the gaps remain:
I don't want to interpolate along the x-axis, because I want the final matrix to have dimension 181 x 101. I just want to fill in the gaps using the existing cells (of the 181 x 101 matrix).
How can the original matrix (top), be scaled from 37 x 101 to 181 x 101 (without the "smoothing" in the second image), as well as filling in the gaps using some kind of spline interpolation as if this was a proper function?
It appears that your truth value grid has a single one where the true value is in each row. If the true/1 values do in fact create a line through the image, I would recommend parametrize the line with respect to t so that y = fy(t) and x = fx(t). If you're not familiar with this you can find some parametrization info on youtube tutorials or google. The main idea is that if you have , say a truth table that looks like this:
Then you could plot the the location of each pixel with respect to another variable, t and then use interp1(...) on each of these individually. In my case I defined the x and y values as follows:
n = 32;
rand('seed', 1982);
y_orig = 1:n;
x_orig = ceil(n*sin(y_orig/n*pi));
So I can plot as:
t1 = linspace(0,1, n);
plot(t1,x_orig, 'r', 'linewidth', 3);
hold all
plot(t1,y_orig, 'b', 'linewidth', 3);
legend('X', 'Y')
Note that I can get any truth value I want just by using interp1 like this (if you wanted to find the value half way between the 5th and 6th row):
desiredY = 5.5;
t= 1:n;
truthValue= interp1(t, x_orig, desiredY, 'cubic')
But we are looking to make a new image so I chose a more convenient parametrization of t between zero and one. Unfortunately, you may not have x and y off hand, so we need to pull them out of the image. Assuming you have a single true/1 value in each row we can yank out the values with max(...):
[maxVals, x1] = max(data,[],2);
x1(maxVals == 0) = [];
y1 = find(maxVals ~= 0);
Some form of find on each row would also work. If you have a truth value in each row then y1 should equal 1:n. The max function returns the index of the max in dimension 2 in the second return value. I use the next two lines to remove any entries where there truth table was empty (max is zero) and then y1 = 1:n minus those entries that were empty.
A quick and dirty way to get lots of points along this line is:
t2 = linspace(0,1,1024);
x2 = interp1(t1, x1, t2, 'cubic');
y2 = interp1(t1, y1, t2, 'cubic');
I can then plot the original points/image and this newly discovered finer line together like this:
imagesc(data);
hold all;
plot(x2,y2, 'linewidth', 2);
axis image
colormap(flipud(colormap(gray)));
To get this:
Finally, you can quickly turn this into a new image by scaling the parametrization up. My method is not particularly efficient for clarity:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1)*scaleValue + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
Which results in:
Note that this table has connected all the points and you now have multiple true/1's in each row. This is because I chose a very small step size for t2. You could fix this by either choosing t2 smarter, skipping multiple values in each row, or average the location of each indices in each row. Or ignoring this issue.
To fix t2 with the scaling value, you could use
t2 = linspace(0,1,n*scaleValue);
to get only one true/1 per row in the above code.
Also, if you want to only scale one dimension, you could do it like this:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1) + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue,n);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
I see this as a bitmap, so why not a clamped blur?
I=yourmatrixhere
%gaussian blur
% it looks like bump-to-bump distance is 3 empties
% therefore hsize should be about 7
% it looks like bump vertical size is about 4
% therefore simga should be about 10
hsize=[3 3];
sigma = 10;
h=fspecial('gaussian',hsize,sigma)
I2=imfilter(I,h,'replicate');
At this point you have spread information to adjacent columns, but you need to "tidy up" from continuous to binary.
%threshold
th = 0.25;
I3=zeros(size(I));
ind=find(I>=th);
I3(ind)=1;
At this point, I3 is your matrix of interest to do the "erode" or interpolation.

Positive & Negitive Log10 Scale Y axis in Matlab

Hi i'm having a problem where I have a dataset which ranges between -10^3 to 10^3
I need to be able to plot this as with a log scale but semilogy cannot plot negative values
Say for example my data is:
x = [-3,-2,-1,0,1,2,3];
y = [-1000,-100,-10,1,10,100,1000];
(or in general y=sign(x).*10.^abs(x);)
How can I plot this in MATLAB with a log scale? If possible It would be great if the log scale ticks could be on the Y-axis too
Use your actual data as labels, but scale the plotted data with log10.
% data
x = -3:0.1:3;
y = sign(x).*10.^abs(x);
% scaling function
scale = #(x) sign(x).*log10(abs(x));
N = 7; % number of ticks desired
% picking of adequate values for the labels
TickMask = linspace(1,numel(y),N);
YTickLabels = y(TickMask);
% scale labels and plotdata, remove NaN ->inconsistency, do you really want that?
YTick = scale( YTickLabels );
Y = scale(y);
YTick(isnan(YTick)) = 0;
Y(isnan(Y)) = 0;
% plot
plot(x,Y)
set(gca,'YTick',YTick,'YTickLabels',YTickLabels)
grid on
For N = 7:
For N = 11
How to find a valid value for N?
The following function (thanks to gnovice) will return all possible values you could choose for N:
n = numel(x);
N = find(rem(n./(1:n), 1) == 0) + 1;
about the semilogy-style labels: by adding the following line before the plot:
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you could at least achieve something like this:
not beautiful and not generic, but a good point to start for you.
The reason you can't make a logarithmic axis that crosses zero, is that it doesn't make sense!
Since a logarithmic scale is generally displayed as eg. 100 - 10 - 1 - 1/10 - 1/100 - ..., you would need an infinite amount of space to make the axis cross zero.
How about this:
x=logspace(-3,3);
y=sign(x).*10.^abs(x);
loglog(x,y)
#thewaywewalk has already given a beautiful solution to it. The one I'm suggesting is an epsilon improvement on it. If you make two changes
(a) Define a new MATLAB function signia that basically extracts the sign before a number.
function value = signia(x)
if(x>=0)
value = '';
else
value = '-';
end
and (b) make this little change that instead of
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you use
YTickLabels = cellfun(#(x) [signia(x) '10^{' num2str(x) '}'], num2cell(YTick),'UniformOutput',false);
(notice the presence of curly braces), you'll get an improvement in the Y ticks display. I got the following.
enter image description here

MATLAB - Pixelize a plot and make it into a heatmap

I have a matrix with x and y coordinates as well as the temperature values for each of my data points. When I plot this in a scatter plot, some of the data points will obscure others and therefore, the plot will not give a true representation of how the temperature varies in my data set.
To fix this, I would like to decrease the resolution of my graph and create pixels which represent the average temperature for all data points within the area of the pixel. Another way to think about the problem that I need to put a grid over the current plot and average the values within each segment of the grid.
I have found this thread - Generate a heatmap in MatPlotLib using a scatter data set - which shows how to use python to achieve the end result that I want. However, my current code is in MATLAB and even though I have tried different suggestions such as heatmap, contourf and imagesc, I can't get the result I want.
You can "reduce the resolution" of your data using accumarray, where you specify which output "bin" each point should go in and specify that you wish to take a mean over all points in that bin.
Some example data:
% make points that overlap a lot
n = 10000
% NOTE: your points do not need to be sorted.
% I only sorted so we can visually see if the code worked,
% see the below plot
Xs = sort(rand(n, 1));
Ys = rand(n, 1);
temps = sort(rand(n, 1));
% plot
colormap("hot")
scatter(Xs, Ys, 8, temps)
(I only sorted by Xs and temps in order to get the stripy pattern above so that we can visually verify if the "reduced resolution" worked)
Now, suppose I want to decrease the resolution of my data by getting just one point per 0.05 units in the X and Y direction, being the average of all points in that square (so since my X and Y go from 0 to 1, I'll get 20*20 points total).
% group into bins of 0.05
binsize = 0.05;
% create the bins
xbins = 0:binsize:1;
ybins = 0:binsize:1;
I use histc to work out which bin each X and Y is in (note - in this case since the bins are regular I could also do idxx = floor((Xs - xbins(1))/binsize) + 1)
% work out which bin each X and Y is in (idxx, idxy)
[nx, idxx] = histc(Xs, xbins);
[ny, idxy] = histc(Ys, ybins);
Then I use accumarray to do a mean of temps within each bin:
% calculate mean in each direction
out = accumarray([idxy idxx], temps', [], #mean);
(Note - this means that the point in temps(i) belongs to the "pixel" (of our output matrix) at row idxy(1) column idxx(1). I did [idxy idxx] as opposed to [idxx idxy] so that the resulting matrix has Y == rows and X == columns))
You can plot like this:
% PLOT
imagesc(xbins, ybins, out)
set(gca, 'YDir', 'normal') % flip Y axis back to normal
Or as a scatter plot like this (I plot each point in the midpoint of the 'pixel', and drew the original data points on too for comparison):
xx = xbins(1:(end - 1)) + binsize/2;
yy = ybins(1:(end - 1)) + binsize/2;
[xx, yy] = meshgrid(xx, yy);
scatter(Xs, Ys, 2, temps);
hold on;
scatter(xx(:), yy(:), 20, out(:));