I am trying to do a scatter plot, with the x-axis occupying a range of 5 to 15 in steps of 0.25 and the y-axis occupying 41 random data for 20 days.
clc
clear
x = 5:0.25:15;
y = rand(41,20);
How can i achieve a scatter plot on MATLAB, whereby the x-range is applicable to all the 20 columns?
Did you possibly want a scatter plot with connected lines so that you could identify the different datasets? Here I used the same for-loop approach and holding the plot using hold on. In the line plot(t,y(:,n),'.-'); the term '.-' is used to indicate to plot the data with connected lines and dots at the data points. As the comment above indicated for a random dataset doing a best fit polynomial will not reveal very useful information if not any at all.
clf;
Start_Time = 5;
End_Time = 15;
Time_Interval = 0.25;
t = (Start_Time: Time_Interval: End_Time);
y = rand(41,20);
for n = 1:20
plot(t,y(:,n),'.-');
hold on
end
Legend_Labels = "Data 1";
for Dataset_Index = 2: size(y,2)
Legend_Labels = [Legend_Labels "Data "+num2str(Dataset_Index)];
end
Current_Figure = gcf;
Current_Figure.Position = [50 50 1000 400];
title("Plotting Random Data with Respect to Time");
legend(Legend_Labels,'Location','EastOutside','Orientation','vertical');
xlabel("Time (s)"); ylabel("Value");
Ran using MATLAB R2019b
Related
I am attemping to plot a series of points on one single graph:
Klnvalue = [-1.516 -0.609 0.202 0.934 2.486 3.725 4.743 5.590];
temp = [3400 3600 3800 4000 4500 5000 5500 6000];
for i = 1:8
eqn = ((nh^2)/(1-nh))*68.045964 == exp(Klnvalue(i));
y = max(vpa(solve(eqn, nh)))
x = temp(i);
figure
plot(x,y)
hold on
end
But not only does eight graphs jumped out, not a single point is plotted, could you tell me why?
If you only want 1 graph, pass something like figure(1). Also, take figure out of the loop and instead do:
figure(1), clf, hold on
for i = 1:8
eqn = ((nh^2)/(1-nh))*68.045964 == exp(Klnvalue(i));
y = max(vpa(solve(eqn, nh)))
x = temp(i);
plot(x,y)
end
Otherwise you waste time calling the figure each time even when it is still the current figure. Good luck!
I have a matrix of data. I used the polarplot command in MATLAB to plot this matrix with respect to theta.
The data oscillates between 3999.20 and 4001.52 As you can see in the following plot, the order of magnitude of oscillation of data is too small to see.
How can I modify my polar plot to see small oscillations?
My code is as below:
yf=[%750 data point]
theta = 0:4*pi/749:4*pi;
rho = yf
pax = polaraxes;
polarplot(theta,rho)
pax.ThetaDir = 'counterclockwise';
pax.ThetaZeroLocation='right'
pax.ThetaColor='r'
pax.ThetaTick=[0 30 60 90 120 150 180 210 240 270 300 330 ];
pax.RDir='normal';
pax.RTick=[3999.34 3999.67 4000 4000.33 4000.66 4000.99 4001.33 ]
pax.FontSize = 12;
Desired output:
Actual output
2-axis plot
To give an example of setting the r limits as suggested by #beaker
The following code uses the same data with using rlim to set manual limits in the second example. This scales the polar axis so that it only plots values between [3999,4000], exaggerating the scale of the oscillation.
theta = 0:0.01:2*pi;
rho = sin(2*theta).*cos(2*theta) + 3999 %To approximate your data
figure;
subplot(1,2,1)
polarplot(theta,rho)
title('Automatic r-limits')
subplot(1,2,2)
polarplot(theta,rho)
rlim([3999, 4000])
title('rlim([3999, 4000])')
Something like that maybe, where you subtract the mean of the data and scale the amplitude by a factor of 10?
yf=[%750 data point]
amp = yf - mean(yf);
amp = amp*10; % choose whatever scaling factor works for you
theta = 0:4*pi/749:4*pi;
rho = mean(yf) + amp;
Without the actual data, it's difficult to say what this will look like, but the general principle should work.
I have 11 binary datasets all size 297x258 saved in data1 and I would like to produce an image with these data overlapped, each assigned a different color with the background removed (white). An example of the desired output is shown in this image:
I generate the figures of these datasets using:
figure, imshow(data1{1}),axis image, colormap(jet)
Additionally, any help finding the average increase in size (increase in y-axis) between data1{1} and data1{11} for every x value increment is also appreciated. Each set of data data1{1}, data1{2}, ...data1{11} is representative of times 0, 1, ...11 respectively and I would like to plot a graph of the average increase in y-axis against the x-axis (1:297). Your help is very much appreciated. Thanks.
My thoughts so far are:
for x=1:x_dim % where xdim is 297 (along the X-axis)
for y=1:ydim % where ydim is 258 (along the y-axis)
% execute code to determine increase in y-direction between
% binary datasets data1{1}, data1{2},...data1{11}.
% Then compute average for growth in the y-direction between each time
end
% Plot figure of average increase in y-axis against x-axis.
end
So here is the code for the figures you wanted:
all_data = cell2mat(flipud(data1.'));
all_data = diag(repelem(linspace(1,70,11),size(data1{1},1)))*logical(all_data);
% option 1:
figure('Position',[450 100 200 900],'Color',[1 1 1]);
C = colormap('jet');
C = [1 1 1;C];
colormap(C)
imagesc(all_data)
axis image
axis off
% option 2:
all_data(1:size(data1{1},1):size(all_data,1),:) = nan(11,size(all_data,2));
stack = nan(size(all_data));
toprow = zeros(1,size(all_data,2));
for k = 1:size(all_data,2)
tmp = nonzeros(flipud(all_data(:,k)));
stack(1:numel(tmp),k) = tmp;
toprow(k) = numel(tmp);
end
figure('Position',[650 100 200 900],'Color',[1 1 1]);
C = colormap('jet');
C = [1 1 1;C];
colormap(C)
image(stack(1:max(toprow.'),:))
axis xy
axis off
Which gives this:
option 1 no the left, and option 2 on the right.
what is the best way to merge and plot 2 (or more) time stamped data so that the plot includes the gaps between the known data?
For example, I have a cell with time and heart rate values for Monday, and another for Friday. I would like to plot all data from Mon to Friday which includesthe gaps showing nothing was recorded from Tues-Thurs?
So far if I merge the data
data = [mon;fri]
% get the time serial numbers
dateInd = 1;
dateString = data(dateIndex);
dateFormat = 'dd/mm/yyyy HH:MM:SS';
tNum = datenum(dateString{1},dateFormat);
t = linspace(tNum(1),tNum(end),length(tNum));
% get heart rates,
HRIndex = 2;
HR = data(HRIndex);
and plot
plot(t,HR)
datetick('x','ddd');
I obviously get Monday and Fridays data merged into a single plot 2 days long. but I would like to have a plot 5 days long with data only showing on monday and Friday. What is the best way to achieve this?
I hope this makes sense,
Many thanks,
Jon
To achieve such effect I usually fill missing data with NaNs, like here:
x = linspace(0,2*pi,100);
y = sin(x);
y(20:30) = NaN; % there will be a gap from point#20 to point#30
plot(x,y);
The reason is that MatLab does not draw plot points where either x or y data are NaNs.
In your case you may add missing time points to your time data (to have corect gap) and NaNs to corresponding Y-values.
By the way, why don't you plot two separate plots with X-data of the second one properly shifted?
EDIT
Case 1: your x-data is time relative to the start of the day (in 0-24 interval). If you plot them directly they will overlap. You have to add some offset manually, like this:
% generate test data
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25); % 25 points per second day
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
XOffset = 3;
figure;
plot(x1,y1,'*k-', x2+XOffset,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)+XOffset],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Case 2: your x-data have full time information including the date (like MatLab's serial date number, see help on now function, for example). Then just plot them, they will be offset automatically.
% generate test data
XOffset = 3;
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25)+XOffset; % 25 points per second day, with offset
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
figure;
plot(x1,y1,'*k-', x2,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Also instead of
plot(x1,y1,'*k-', x2,y2,'*r-');
you may do like this (number of plots is not limited):
hold on;
plot(x1,y1,'*k-');
plot(x2,y2,'*r-');
hold off;
I have a simple loglog curve as above. Is there some function in Matlab which can fit this curve by segmented lines and show the starting and end points of these line segments ? I have checked the curve fitting toolbox in matlab. They seems to do curve fitting by either one line or some functions. I do not want to curve fitting by one line only.
If there is no direct function, any alternative to achieve the same goal is fine with me. My goal is to fit the curve by segmented lines and get locations of the end points of these segments .
First of all, your problem is not called curve fitting. Curve fitting is when you have data, and you find the best function that describes it, in some sense. You, on the other hand, want to create a piecewise linear approximation of your function.
I suggest the following strategy:
Split manually into sections. The section size should depend on the derivative, large derivative -> small section
Sample the function at the nodes between the sections
Find a linear interpolation that passes through the points mentioned above.
Here is an example of a code that does that. You can see that the red line (interpolation) is very close to the original function, despite the small amount of sections. This happens due to the adaptive section size.
function fitLogLog()
x = 2:1000;
y = log(log(x));
%# Find section sizes, by using an inverse of the approximation of the derivative
numOfSections = 20;
indexes = round(linspace(1,numel(y),numOfSections));
derivativeApprox = diff(y(indexes));
inverseDerivative = 1./derivativeApprox;
weightOfSection = inverseDerivative/sum(inverseDerivative);
totalRange = max(x(:))-min(x(:));
sectionSize = weightOfSection.* totalRange;
%# The relevant nodes
xNodes = x(1) + [ 0 cumsum(sectionSize)];
yNodes = log(log(xNodes));
figure;plot(x,y);
hold on;
plot (xNodes,yNodes,'r');
scatter (xNodes,yNodes,'r');
legend('log(log(x))','adaptive linear interpolation');
end
Andrey's adaptive solution provides a more accurate overall fit. If what you want is segments of a fixed length, however, then here is something that should work, using a method that also returns a complete set of all the fitted values. Could be vectorized if speed is needed.
Nsamp = 1000; %number of data samples on x-axis
x = [1:Nsamp]; %this is your x-axis
Nlines = 5; %number of lines to fit
fx = exp(-10*x/Nsamp); %generate something like your current data, f(x)
gx = NaN(size(fx)); %this will hold your fitted lines, g(x)
joins = round(linspace(1, Nsamp, Nlines+1)); %define equally spaced breaks along the x-axis
dx = diff(x(joins)); %x-change
df = diff(fx(joins)); %f(x)-change
m = df./dx; %gradient for each section
for i = 1:Nlines
x1 = joins(i); %start point
x2 = joins(i+1); %end point
gx(x1:x2) = fx(x1) + m(i)*(0:dx(i)); %compute line segment
end
subplot(2,1,1)
h(1,:) = plot(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Normal Plot')
subplot(2,1,2)
h(2,:) = loglog(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Log Log Plot')
for ip = 1:2
subplot(2,1,ip)
set(h(ip,:), 'LineWidth', 2)
legend('Data', 'Piecewise Linear', 'Location', 'NorthEastOutside')
legend boxoff
end
This is not an exact answer to this question, but since I arrived here based on a search, I'd like to answer the related question of how to create (not fit) a piecewise linear function that is intended to represent the mean (or median, or some other other function) of interval data in a scatter plot.
First, a related but more sophisticated alternative using regression, which apparently has some MATLAB code listed on the wikipedia page, is Multivariate adaptive regression splines.
The solution here is to just calculate the mean on overlapping intervals to get points
function [x, y] = intervalAggregate(Xdata, Ydata, aggFun, intStep, intOverlap)
% intOverlap in [0, 1); 0 for no overlap of intervals, etc.
% intStep this is the size of the interval being aggregated.
minX = min(Xdata);
maxX = max(Xdata);
minY = min(Ydata);
maxY = max(Ydata);
intInc = intOverlap*intStep; %How far we advance each iteraction.
if intOverlap <= 0
intInc = intStep;
end
nInt = ceil((maxX-minX)/intInc); %Number of aggregations
parfor i = 1:nInt
xStart = minX + (i-1)*intInc;
xEnd = xStart + intStep;
intervalIndices = find((Xdata >= xStart) & (Xdata <= xEnd));
x(i) = aggFun(Xdata(intervalIndices));
y(i) = aggFun(Ydata(intervalIndices));
end
For instance, to calculate the mean over some paired X and Y data I had handy with intervals of length 0.1 having roughly 1/3 overlap with each other (see scatter image):
[x,y] = intervalAggregate(Xdat, Ydat, #mean, 0.1, 0.333)
x =
Columns 1 through 8
0.0552 0.0868 0.1170 0.1475 0.1844 0.2173 0.2498 0.2834
Columns 9 through 15
0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822
y =
Columns 1 through 8
0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846
Columns 9 through 15
0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537
We see that as x increases, y tends to decrease slightly. From there, it is easy enough to draw line segments and/or perform some other kind of smoothing.
(Note that I did not attempt to vectorize this solution; a much faster version could be assumed if Xdata is sorted.)