splitting the y axis into a linear and logarithmic scale matlab - matlab

I would like to split the y axis into a linear and logarithmic scale section while plotting the figure. For example, from 1-30 a linear scale and from 30 - 100 a logarithmic scale. Is there any way of doing that? Thank you

I don't know if there is a direct method for this. However, you can join both regions (linear and log) manually. See attached code:
clc; clear;
x = 1:100; % Values to plot
xorg = 0:10:100; % Ticks on the Y-axis
xticks = xorg; % Final tick location
x1 = log10(30); % start of logarithmic scale
x2 = log10(100); % end of logarithmic scale
dx = (x2-x1)*60; % 60 here is an arbitrary scaling factor
scale = #(x)(log10(x)-x1)/(x2-x1)*dx+30; % Scaling from lin to log
x(x>30) = scale(x(x>30)); % Apply scaling to plotting values
xticks(xticks>30) = scale(xticks(xticks>30)); % Apply scaling to Y ticks
plot(x);
ylim([0 max(xticks)]);
set(gca,'YTick',xticks,'YTickLabel',xorg); % Update the ticks
This produces a plot as below.

Related

Generate a mesh with unequal steps based on a density function, using Matlab

I'm trying to generate a 1D mesh with unequal step length, and with a fixed number of elements [same as the initial mesh].
The length should be proportional to a node density. In the example, this density is inversely proportional to the gradient of a function.
[imagine for example that you have a distribution of the temperature in a 1D mesh, and you want to make the mesh denser in the parts of the mesh where the temperature gradient is higher]
This is what I coded so far:
% % % Initial fixed-step 1D mesh
X=(0:.01:2)';
% % % Values of a function at each mesh node [in this example, f(x)=5*sin(2*pi*x)*x ]
Y=5*sin(2*pi*X).*X;
% % % Calculate density of mesh points based on the Y function gradient
rho=[1e-9; abs(diff(Y))];
% % % Calculate x-steps from the original mesh
h = diff(X);
% % % Rescale the steps based on the inverse of the density
F = cumsum([0; h]./rho);
% % % Make sure F is between 0 and 1
F = F/F(end);
% % % Calculate the new mesh with scaled steps
X2 = X(1) + F * (X(end)-X(1));
% % % Interpolate the function Y at the new positions
Y2 = interp1(X,Y,X2);
% % % Plot
figure
subplot(2,1,1)
hold on
plot(X,Y,'ko-')
plot(X2,Y2,'r.-')
xlabel('x')
ylabel('y')
subplot(2,1,2)
plot(X,rho,'ko-')
xlabel('x')
ylabel('rho')
There are a few problems with this approach:
1. as you see from this example, there are big jumps when the density is very low (gradient almost zero). How could I implement a minimum/maximum time step size?
2. the node density is calculated correctly, but after "integrating" the unequal steps it can happen that the imposed large time step when the gradient is small causes to skip a high-gradient region that should have finer time-steps. [for example, please take a look at the region 1.8-1.9 in the example below, which should have small time step because it has high node density, but the large step size at ~1.75 causes to skip a large section of X]
Any suggestion to improve my code will be appreciated!
Calculate the cumulative sum (CDF) of rho. Take equally spaced samples from the CDF. Map from CDF to X to get new X3. Map from X3 to Y to get Y3:
CDF = cumsum(rho);
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF)+1).';
eq_smpl = eq_smpl(1:end-1) + diff(eq_smpl)/2; %use center points
X3 = interp1(CDF, X, eq_smpl);
Y3 = interp1(X, Y, X3);
plot(X3,Y3,'ro-')
hold on
plot(X,Y,'k.')
The third subplot shows the the result.
rahnema1's answer gave me a huge help, but there were still two remaining issues:
1- the first element of the new mesh is not identical to the first element of the original mesh
2- in case the gradient is zero at some point, the interp1 function will give error ["The grid vectors are not strictly monotonic increasing."]
For the 1st point, I replaced the two lines defining eq_smpl with the following line:
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF))';
[taking as many elements as CDF, and not centering the points]
For the 2nd point, I added a line after the calculation of rho to replace eventual 0 with small non-zero values:
rho(rho==0)=1e-12;
The final code that does what I want is the following:
% % % Initial fixed-step 1D mesh
X=(0:.01:2)';
% % % Values of a function at each mesh node [in this example, f(x)=5*sin(2*pi*x)*x ]
Y=5*sin(2*pi*X).*X;
% % % Calculate density of mesh points based on the Y function gradient
rho=[0; abs(diff(Y)./abs(diff(X)))];
% % % Replace eventual 0 with small non-zero values
rho(rho==0)=1e-12;
CDF = cumsum(rho);
eq_smpl = linspace(CDF(1), CDF(end), numel(CDF))';
% % % Calculate new mesh
X3 = interp1(CDF, X, eq_smpl);
% % % Interpolate the function Y at the new positions
Y3 = interp1(X, Y, X3);
% % % Plot
figure
subplot(2,1,1)
hold on
plot(X,Y,'ko-')
plot(X3,Y3,'r.-')
xlabel('x')
ylabel('y')
subplot(2,1,2)
plot(X,rho,'ko-')
xlabel('x')
ylabel('rho')
Thank you again to rahnema1 for providing 90% of the answer [probably I didn't explain very well the original request]!

Specifying quiver vector color by density

I have some vectors with defined position and orientation. I could show them in space by using the below code:
theta = [pi/2,-pi/2,pi/2,pi/2,pi/2,pi/2,pi/2];
r = 0.25; % magnitude (length) of arrow to plot
x = [4,3.5,3.75,4.5,8,10,12]; y = [8.5,8.2,8.3,8,9,10,8];
u = r * cos(theta); % convert polar (theta,r) to cartesian
v = r * sin(theta);
h = quiver(x,y,u,v,'linewidth',2);
set(gca, 'XLim', [2 15], 'YLim', [4 15]);
As is clear from the image, in some regions the number of arrows is more than in other places. I want to show the arrows by color, where each color represents the density of the arrows.
Could anyone help me to do that? It would also be a good solution if there is a continuous background color which shows local densities.
Edit: Below are some options for colouring the background of the plot depending on the density of your points. I'm editing this into the top of my answer because it actually answers your question - individually colouring quiver arrows based on density!
x = rand(200,1)*10; y = rand(200,1)*10; % Set up random points
r = 1; u = r * cos(x); v = r * sin(y); % Quiver directions
colormap winter; c = colormap; % Set colourmap and assign to matrix
% Get density of points broken into a 10x10 grid
[n,~,~,binX,binY] = histcounts2(x,y,[10,10]);
% Get colour based on histogram density and chosen colormap colours
col = c(ceil(n(sub2ind(size(n), binX, binY))/max(n(:))*size(c,1)),:);
figure; hold on;
% Each quiver point must be plotted individually (slow!) because colours can
% only be applied to individual quivers. This could be sped up by plotting
% all of the same colour at once.
for ii = 1:size(x,1);
quiver(x(ii),y(ii),u(ii),v(ii),0,'color',col(ii,:));
end
Output:
Note: unlike the below example, you cannot use hist3 because you need it to return the bin index too. You could try this File Exchange function to achieve the same result (untested).
Here is an option using hist3 to get the density (in this example I use a 10x10 grid, as specified when calling hist3). Then using pcolor to display the density, and shading interp to smooth the colours.
Note: hist3 requires the Stats & ML toolbox, if you have Matlab 2015b or newer you can instead use the standard function histcounts2(x,y).
% Generate points and quiver directions
x = rand(200,1)*10; y = rand(200,1)*10;
u = r * cos(x); v = r * sin(y);
% Get density of points, format for input to pcolor
n = hist3([x,y],[10,10]); % Get density of points broken into a 10x10 grid
colx = linspace(min(x),max(x),size(n,1)+1);
coly = linspace(min(y),max(y),size(n,1)+1);
n = n'; n(size(n,2)+1,size(n,1)+1) = 0;
% Plot
figure
pcolor(colx,coly,n) % Density plot
hold on; colorbar; % Hold on for next plot and show colour bar key
quiver(x,y,u,v,'r') % Quiver plot
shading interp % Smooth plot colours
Output:
Edit: making the colours more muted
You can control the colours using colormap. This could be one of the defaults, or you can create a custom map of RGB triplets and have whatever colours you want! Here is an example, simply calling colormap bone; at the end of the above code:
In a custom colour map, you could make the colours even more muted / less contrasting.
Additionally, you can use caxis to scale the colour axis of a plot! Simply call
caxis([0,2*max(n(:))]);
at the end of the above code to double the maximum colour map value. You can tweak the 2 to get desired results:
this looks way less fancy but specifies the arrow color as function of the number of arrows in a certain number of bins of the x-axis
close all;
cm=colormap;
theta = [pi/2,-pi/2,pi/2,pi/2,pi/2,pi/2,pi/2];
r = 0.25; % magnitude (length) of arrow to plot
x = [4,3.5,3.75,4.5,8,10,12]; y = [8.5,8.2,8.3,8,9,10,8];
[n,c]=hist(x,5); %count arroes in bins
u = r * cos(theta); % convert polar (theta,r) to cartesian
v = r * sin(theta);
figure;hold on
for ii=1:numel(n) %quiver bin by bin
if n(ii)>0
if ii==1
wx=find(x<(c(ii)+(c(ii+1) - c(ii))/2)); %Which X to plot
elseif ii==numel(n)
wx=find(x>c(numel(n)-1));
else
wx=find((x>(c(ii)-(c(ii)-c(ii-1))/2)).*(x<(c(ii+1)-(c(ii+1)-c(ii))/2)));
end
indCol=ceil( (size(cm,1)*n(ii)-0) / max(n));%color propto density of arrows %in this bin
col = cm(indCol,:);%color for this bin
h = quiver(x(wx),y(wx),u(wx),v(wx),0,'linewidth',2,'color',col);
end
end
colorbar
caxis([0 max(n)])

Exponential fitting for matlab

I have my data in arrays which are exponential like e^(ax+c)+d. I'm trying to draw a fit to them.
a = data1 (:,1);
b = data1 (:,2);
log(b);
p = polyfit (a,log(b),1);
But I don't know what to do now. I found an equation by polyfit and I was hoping to take the exponential of the equation I got from polyfit with
exp (0.5632x+2.435)
But I figured out that it doesn't work like that. Does anyone have any suggestions?
try with nonlinear fitting:
%% PARAMETERS (you need this part)
clear all;
clc, clf;
N = 128; % number of datapoints
Nint = N*10; % number of datapoints for curve interpolation
fun = #(prms,x) prms(4).^(prms(1)*x+prms(2))+prms(3); % write your function
iniPrm = rand(4,1); % find some initial values for the parameters (choose meaningful values for better results)
%% SIMULATE DATA (this is only for testing purposes)
SNR = .01; % signal to noise ratio for simulated data
noise = (rand(1,N)-.5)*SNR; % create some random noise
x = linspace(0,10,N); % create the x axis
y = fun(iniPrm,x) + noise; % simulate a dataset that follows the given function
x = x(:); % reshape as a vector
y = y(:); % reshape as a vector
X = linspace(x(1),x(end),Nint); % interpolate the output to plot it smoothly
plot(x,y,'.r','markersize',10); hold on; % plot the dataset
%% FIT AND INTERPOLATE YOUR MODEL
[out.BETA,out.RESID,out.J,out.COVB,out.MSE] = nlinfit(x,y,fun,iniPrm,[]); % model your data
[out.YPRED,out.DELTA] = nlpredci(fun,X,out.BETA,out.RESID,'Covar',out.COVB); % interpolate your model
out.YPREDLOWCI = out.YPRED - out.DELTA; % find lower confidence intervals of your fitting
out.YPREDUPCI = out.YPRED + out.DELTA; % find upper confidence intervals of your fitting
out.X = X; % store the interpolated X
%% PLOT FITTING
plotCI = #(IO,spec) patch([IO.X(:);flipud(IO.X(:))],[IO.YPREDLOWCI(:);flipud(IO.YPREDUPCI(:))],spec{:}); % create patches: IE: patch(0:10,10:-1:0,ones(10,1)-1,1,{'r','facealpha',0.2})
plot(X,out.YPRED,'-b','linewidth',3);
plotCI(out,{'r','facealpha',.3,'edgealpha',0})

Why are these two plots different?

Here I am plotting a spectrogram twice, once using imagesc and once using the spectrogram automatic plotting. I don't know why I get different scaling results, probably some filtering by the automatic plotting function but I would like to know what exactly and how to transform it so that it matches.
fs = 44100;
% Frequency sweep signal
sw = logspace(log10(500),log10(5000),fs*5);
x = 0.95 * sin(cumsum((2*pi*sw)/fs));
N = 128;
win_size = N;
noverlap = N/2;
win = window(#blackman,win_size);
[s,f,t] = spectrogram(x,win,noverlap,N,fs,'yaxis');
%% IMAGESC --- FIGURE 1
figure(1)
imagesc(t,f/1000,20*log(abs(s)));
title('Spectrogram');
set(gca,'Ydir','Normal');
xlabel('Time (secs)');
ylabel('Frequency (kHz)');
hcb=colorbar;
title(hcb,'Spectral Magnitude (dB)');
%% test with automatic spectrogram plot ---FIGURE2
figure(2)
spectrogram(x,win,noverlap,N,fs,'yaxis');
end
Help is appreciated :)
Change your code "imagesc(t,f/1000,20*log(abs(s)))" as imagesc(t,f/1000,20*log10(abs(s))).
log() is natural logarithm.
When you calculate dB scale, you should use log10(), not log().

Plotting a 3D graph of normalized prices in MatLab

I'm doing Gaussian processes and I calculated a regression per year from a given matrix where each row represents a year , so the code is:
M1 = MainMatrix; %This is the given Matrix
ker =#(x,y) exp(-1013*(x-y)'*(x-y));
[ns, ms] = size(M1);
for N = 1:ns
x = M1(N,:);
C = zeros(ms,ms);
for i = 1:ms
for j = 1:ms
C(i,j)= ker(x(i),x(j));
end
end
u = randn(ms,1);
[A,S, B] = svd(C);
z = A*sqrt(S)*u; % z = A S^.5 u
And I wanna plotting each regression in a Graph 3D as the below:
I know that plot is a ribbon, but I have not idea how can I do that
The desired plot can be generated without the use of ribbon. Just use a surf-plot for all the prices and a fill3-plot for the plane at z=0. The boundaries of the plane are calculated from the actual limits of the figure. Therefore we need to set the limits before plotting the plane. Then just some adjustments are needed to generate almost the same appearance.
Here is the code:
% generate some data
days = (1:100)';
price = days*[0.18,-0.08,0.07,-0.10,0.12,-0.08,0.05];
price = price + 0.5*randn(size(price));
years = 2002+(1:size(price,2));
% prepare plot
width = 0.6;
X = ones(size(price,1),1)*0.5;
X = [-X,X]*width;
figure; hold on;
% plot all 'ribbons'
for i = 1:size(price,2)
h = surf([days,days],X+years(i),[price(:,i),price(:,i)]);
set(h,'MeshStyle','column');
end
% set axis limits
set(gca,'ZLim',[-20,20]);
% plot plane at z=0
limx = get(gca,'XLim');
limy = get(gca,'YLim');
fill3(reshape([limx;limx],1,[]),[flip(limy),limy],zeros(1,4),'g','FaceAlpha',0.2)
% set labels
xlabel('Day of trading')
ylabel('Year')
zlabel('Normalized Price')
% tweak appearance
set(gca,'YTick',years);
set(gca,'YDir','reverse');
view([-38,50])
colormap jet;
grid on;
%box on;
This is the result:
That's a ribbon plot with an additional surface at y=0 which can be drawn with fill3