MATLAB will generate multiple line objects when plot is used with a vector and a scalar:
a = 1:4;
ph = plot(a, 1, 'o');
numel(ph) % == 4
And usually, it is desired that plot generate a single line object for x and y arguments being vectors of the same length:
b = 1:2:8;
ph = plot(a, b, 'o');
numel(ph) % == 1
However, I'd like to generate a line object for each pair of values from a and b.
Note that the low-level line function also does not create one line per column if there is just one row.
So how can I force MATLAB to generate numel(a) line objects in an elegant way?
The best solution I could come up with uses arrayfun and requires an additional step to turn the cell array into an object array:
hold on;
ph = arrayfun(#(x, y) plot(x, y, 'o'), a, b, 'uni', 0)
ph = cat(2, ph{:});
(elegant means: no loop. Also, scatter won't work for me as it does not allow different marker types)
Edit:
Second best solution might be
ph = plot([a;nan(size(a))], [b;nan(size(a))], 'o')
This will produce four line objects but comes at the cost of having extra NaN data elements. Note the NaN have to be added to both arguments, otherwise there would be only two line series (the second one being invisible containing only NaNs for one coordinate).
If I understand you correctly, you want to plot both (or an arbitrary number) lines in one plot command. If so, you can use cellfun and input cell arrays, see example below:
x1 = 1:100;
x2 = 1:2:200;
y1 = rand(1,100);
y2 = rand(1,100);
X = {x1,x2};
Y = {y1,y2};
figure(1)
hold on
cellfun(#plot, X, Y)
Related
I have a collection of values stored in a 350x130 matrix, r, that I'm using to create a simple plot in Matlab:
figure;
plot(r)
Is there a way to color the resulting lines from r using another 350x1 vector, v? V contains a collection of integers, in this case, that vary from 1 to 4.
I'm imaging the call would look something like this, where, again, r is 350x130 and v is 350x1:
figure;
plot(r,v)
Let's define some example data:
r = rand(10,15);
v = randi(4,1,15);
Method 1 (more fun): using a comma-separated list
This creates a cell array and transforms it into a comma-separated list to call plot as plot(x1, y1, s1, x2, y2, s2, ...). The colors are limited to be defined as plot's linespec strings (such as 'g' or 'c+').
linespecs = {'r' 'g' 'b' 'm'};
c = repmat({1:size(r,1)}, 1, size(r,2));
c(2,:) = num2cell(r, 1);
c(3,:) = linespecs(v);
plot(c{:})
Method 2 (more readable and flexible): using a loop
linespecs = {'r' 'g' 'b' 'm'};
hold on
for k = 1:size(r,2)
plot(r(:,k), linespecs{v(k)})
end
This method allows using a colormap to specify arbitrary colors, not limited to linespec strings:
colors = winter(4); % arbitrary colormap
hold on
for k = 1:size(r,2)
plot(r(:,k), 'color', colors(v(k),:))
end
As seen on the figure below, I have a collection of data from different time sets. However, there is a line connecting the end of each data section to the start of the next.
Is there any way of suppressing this connection without altering the data?
Intro
Currently MATLAB is drawing lines between all consecutive points in your arrays, including when there is a big jump in x value.
An equivalent example would be this plot:
% Create some x with points spaced by 0.1, except at 2 jumps of 5
x = [0:0.1:10, 15:0.1:25, 30:0.1:40];
y = sin(x);
plot(x,y)
Notice the straight line joins between our 3 sections (10-15, 25-30).
Method 1: hold on for multiple plots
We can use the find and diff to get the regions where x jumps, and then plot each region individually.
% Include indices for the first (we're going to +1, so start at 0) and last elements
% You can change this 1 to any tolerance you like, it might be of the order 10^4 for you!
idx = [0, find(diff(x) > 1), numel(x)];
% Hold on for multiple plots
hold on;
% Loop over sections and plot
for ii = 2:numel(idx)
% Plots will loop through colours if you don't specify one, specified blue ('b') here
plot(x(idx(ii-1)+1:idx(ii)), y(idx(ii-1)+1:idx(ii)), 'b')
end
hold off; % good practise so you don't accidentally plot more on this fig!
Method 2: using NaN
You could use also use NaN values to break up your data. NaN values aren't plotted by MATLAB, so if we include one in each break, there will be nothing to connect the line to and we'll see a break:
% Notice the NaNs!
x = [0:0.1:10, NaN, 15:0.1:25, NaN, 30:0.1:40];
y = sin(x);
plot(x,y)
Automation: You may want to use the diff function to find these jumps on the fly for some pre-existing x, like so
% Define some x and y as before, without NaNs, with big gaps in x
x = [0:0.1:10, 15:0.1:25, 30:0.1:40];
y = sin(x);
% Create anonymous function to insert NaN at vector index 'n'
insertNaN = #(v, n) [v(1:n), NaN, v(n+1:end)];
% Get indices where gap is bigger than some value (in this case 1)
idx = find(diff(x) > 1);
% Loop *backwards* through indices (so they don't shift up by 1 as we go)
for ii = numel(idx):-1:1
x = insertNaN(x,idx(ii));
y = insertNaN(y,idx(ii));
end
% Plot appears the same as the second plot above
plot(x,y);
Note: If you want to do further processing on x and y, it might not be best to add random NaN values into the array! You can either:
Remove them afterwards (assuming there weren't pre-existing NaNs)
x = x(~isnan(x));
y = y(~isnan(y));
Use temporary variables for plotting
xplot = x;
% then adding NaNs to xplot for plotting ...
How can I make a simple plot of function y = sin(x) + sin(3x) + ... + sin(100x) without using any loops?
Yes, it's possible by using a call to bsxfun to generate the right points to be applied per sinusoid, then using a sum call to sum all of the sinusoids for each point. You'd then plot this normally.
Something like this comes to mind:
x = -5:0.01:5; %// Define x points here
pts = bsxfun(#times, 1:2:101, x(:)); %// Generate a grid of points
y = sum(sin(pts), 2); %// Compute the y values for each x value
plot(x(:),y); %// Plot the result
The first line of code generates a set of x values that you wish to plot. The next line of code generates a 2D grid of points. Each row applies x, 3*x, 5*x, ..., 101*x for one particular point in x. Each column represents one unique x point. As such, when we use sum (next line), we also apply the sin operator to each of these individual points on the grid, then go ahead and sum over each row to produce the output for each unique point of x. We then plot the results.
Note that I used x(:) to unroll the x vector so that it's a column vector. This is needed for the code to work. This also allows you to make x a row or column vector and the code will still work.
This is the plot I get:
Use cumsum.
octave:1> x = 1;
octave:2> sin(x)
ans = 0.841470984807897
octave:3> sin(x*1)
ans = 0.841470984807897
octave:4> sin(x*1) + sin(x*2)
ans = 1.75076841163358
octave:5> sin(x*1) + sin(x*2) + sin(x*3)
ans = 1.89188841969345
octave:6> cumsum(sin(x * (1:3)))
ans =
0.841470984807897 1.75076841163358 1.89188841969345
Generate a plot showing the graphs of
y=(2*a+1)*exp(-x)-(a+1)*exp(2*x)
in the range x ∈ <-2, 4> for all integer values of a between -3 and 3
I know how to make typical plot for 2 values and set a range on the axes, but how to draw the graph dependent on the parameter a?
To elaborate on Ben Voigt's comment: A more advanced technique would be to replace the for-loop with a call to bsxfun to generate a matrix of evaluations of M(i,j) = f(x(i),a(j)) and call plot with this matrix. Matlab will then use the columns of the matrix and plot each column with individual colors.
%%// Create a function handle of your function
f = #(x,a) (2*a+1)*exp(-x)-(a+1)*exp(2*x);
%%// Plot the data
x = linspace(-2, 4);
as = -3:3;
plot(x, bsxfun(f,x(:),as));
%%// Add a legend
legendTexts = arrayfun(#(a) sprintf('a == %d', a), as, 'uni', 0);
legend(legendTexts, 'Location', 'best');
You could also create the evaluation matrix using ndgrid, which explicitly returns all combinations of the values of x and as. Here you have to pay closer attention on properly vectorizing the code. (We were lucky that the bsxfun approach worked without having to change the original f.)
f = #(x,a) (2*a+1).*exp(-x)-(a+1).*exp(2*x); %// Note the added dots.
[X,As] = ndgrid(x,as);
plot(x, f(X,As))
However for starters, you should get familiar with loops.
You can do it using a simple for loop as follows. You basically loop through each value of a and plot the corresponding y function.
clear
clc
close all
x = -2:4;
%// Define a
a = -3:3;
%// Counter for legend
p = 1;
LegendText = cell(1,numel(a));
figure;
hold on %// Important to keep all the lines on the same plot.
for k = a
CurrColor = rand(1,3);
y= (2*k+1).*exp(-x)-(k+1).*exp(2.*x);
plot(x,y,'Color',CurrColor);
%// Text for legend
LegendText{p} = sprintf('a equals %d',k);
p = p+1;
end
legend(LegendText,'Location','best')
Which gives something like this:
You can customize the graph as you like. Hope that helps get you started!
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.