Custom Scaling on y-axis in matlab - matlab

I come across a strange scaling in y- axis in a IEEE journal paper.
From top to bottom
y_axis = [0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001].
The scaling is not logaritmic. The space between the ticks are not equal. Definitely, it is not linear. You can see the big numbers (0.9... 0.999) and very small numbers(0.0001... 0.1) in detail in only one y-axis. I don't know how to do in MATLAB. I googled it, but i could not find. Can anyone help me? Thanks in advance.
The figure is the following code:
clear all
close all
set(0,'defaulttextinterpreter','latex')
P_tb = 1e-2;
Ntrial = 1e7; % # of Monte Carlo trials
jey=sqrt(-1);
omega_db = -15; % sidelobe gain of main antenna
omega = db2pow(omega_db); % omega in linear scale
F_db = [-5 -2 0 2 5];
JNR_db = -5:20;
beta_db = 2;
F_lin = 10.^(0.1*F_db);
JNR = 10.^(0.1*JNR_db);
beta = 10.^(0.1*beta_db);
temp = cell (length(F_db),1);
P_b = zeros (length(JNR_db), length(F_db));
for ii = 1:length(F_db)
SNR = JNR;
x = sqrt(omega);
F = F_lin(ii);
P_b(:,ii) = 1 - 1./(F+1).*(1-marcumq(x.*sqrt(2.*SNR./(F+1)),sqrt(2.*SNR.*F./(F+1))))-F./(F+1).*marcumq(sqrt(2.*SNR.*F./(F+1)), x.*sqrt(2.*SNR./(F+1)));
temp (ii) = {['$F=$' num2str(F_db(ii)) ' dB']};
end
figure,
h = plot(JNR_db, (P_b));
set(gca,'YTick',fliplr([0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001]))
set(gca,'YTickLabel',num2cell(fliplr([0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001])));
grid on

In the figure posted by the OP, the tickmarks are not at all equally distributed. Also, the scale of the axis is rather the inverse of sigmoidal (see below). To really see what the authors of that paper did, we can skeletonize the figure
im = conv2(im, fspecial('gaussian', [5, 5], 2));
im = ~bwmorph(im < 2.5, 'skel', inf);
and obtain the exact positions y_px as in
y_px = flipud(find(im(:, 285) == 0));
y_px([1, 2, 3, 11, 15, 19, 28]) = [];
I am removing some of the detections which are from the trendlines or from the -10 tick mark label of the x-axis.
If you plot the y-tick labels y_val (flipping your y_axis into ascending order) as a function of those pixel positions y_px, you can confirm that the relationship is exactly sigmoidal as suggested by #chapjc. However, in order to produce similar plots, you may rather want to invert the formula and define a function
px_y = #(y) log(-y ./ (y - 1));
You can use this function then for plotting. The paper you refer to shows data in a range x = -10 : 0.1 : 4. Let's plot in this same range a sine function somewhere between 0.0001 and 0.9999. Note that we use our scaling function px_y, and then replace the y-tick positions and labels
plot(x, px_y(0.7 + sin(x) / 4));
set(gca(), 'ytick', px_y(y_val), 'yticklabel', num2cell(y_val));
resulting in something that should look like this
In summary, you can define a function y_px to transform your y-data, and then set the ytick and yticklabel properties of your axes. Whatever scale you choose for this approach, it is important to transform both the ytick values and all y-data with the same function.

Try this:
set(gca,'YTick',fliplr([0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001]));
set(gca,'YTickLabel',num2cell(fliplr([0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001])));
Matlab wants the numbers to be in increasing order, so instead of reordering your numbers, I just invoked fliplr instead.

To get an idea of this custom y-axis space, you can do plot(linspace(-5,5,numel(y_axis)),fliplr(y_axis),'r'). This looked a lot like a dose curve, so plot an equation I found with hold on; x=-5:0.01:5; plot(x,exp(x)./(1+exp(x))). Here's what it looks like:
The form looks right. There is probably a constant on one of the terms to tweak the shape. If you want to put your data in this space, apply this function (yscFun = #(x) exp(x)./(1+exp(x)) or yscFun = #(x) 1./(1+exp(-x))). Then set the tick labels to y_axis with set(gca,'YTickLabel',num2cell(fliplr(y_axis))); as suggested by nispio.

This is the final solution that I found. Thank you very much. I did not know the sigmoid function. Below are the MATLAB codes for this puporse.
close all
clear all
%your desired y_ticks
y_axis = [0.9999 0.9995 0.999 0.995 0.99 0.98 0.95 0.9 0.8 0.7 0.5 0.3 0.2 0.1 0.05 0.02 0.01 0.005 0.001 0.0005 0.0001];
%order in increasing way
y_val = fliplr (y_axis);
% the Sigmoid function and its inverse
py_x = #(x) 0.5*erf(x*sqrt(pi/8)) + 0.5;
px_y = #(x) sqrt (8/pi)*erfinv (2*x - 1);
beta = 10^(0.1*-15);
F = 10^(0.1*-5);
JNR_db = -5:20;
SNR = 10.^(0.1.*JNR_db);
% my original data
P_b = # (x) 1 - 1./(F+1).*(1-marcumq(x.*sqrt(2.*SNR./(F+1)),sqrt(2.*SNR.*F./(F+1))))-F./(F+1).*marcumq(sqrt(2.*SNR.*F./(F+1)), x.*sqrt(2.*SNR./(F+1)));
P_b1 = P_b(sqrt(beta));
% tranfrom it using inverse function
P_b2 = px_y(P_b1);
figure,
plot(JNR_db, P_b2);
grid on
ylim ([ px_y(0.0001) px_y(0.9999) ]);
% tranform the desired y_tick using inverse function
set(gca(), 'ytick', px_y (y_val));
set (gca (), 'yticklabel', num2cell(y_val));
% % Sigmoid function verification
x_temp = linspace(-5,5,numel(y_axis));
figure
plot(x_temp, fliplr(y_axis), 'r');
hold on
plot (x_temp, py_x (x_temp), 'b' )

Related

Matlab find x of weighted average

How to find the weighted average x? From graphing x(y) it looks to be around x=0.45?
y = [0.1 0.1 0.2 0.5 0.4 0.2]
x = [0.1 0.2 0.3 0.4 0.5 0.6]
One way to perhaps compute it would to find x where the area under the curve y(x) is half of the area under the whole curve. But how to write that in matlab?
Assuming you intend y to be the weights, and you want to compute the weighted average of x, then the weighted average is simply
sum(x.*y) / sum(y)

Matlab colormap & caxis matching nonlinear plots

I am trying to fit my data to match the published map below (the figure on the left), but cannot figure out the right code for the colour bar. The increments are non linear.
Here is the relevant code that I have (it is part of a very large code for spatial mapping so unfortunately not possible to make it reproducible):
caxes.donatt = [0 2.5];
colormaps.donat = mf_colormap_cpt('BlWhRe', 8);
colormaps.donat(1:1,:) = [0.00 0.00 0.50];
colormaps.donat(2:2,:) = [0.00 0.50 1.00];
colormaps.donat(3:3,:) = [0.67 0.90 0.93];
colormaps.donat(4:4,:) = [1.00 1.00 1.00];
colormaps.donat(5:5,:) = [1.00 0.87 0.68];
colormaps.donat(6:6,:) = [0.98 0.67 0.38];
colormaps.donat(7:7,:) = [1.00 0.40 0.10];
colormaps.donat(8:8,:) = [1.00 0.00 0.00];
Here is a picture of the expected result of the colorbar:
expected result
Here is my current result using the code shown above:
current colorbar result
The problem here is that the colormap you're trying to mimic is non-linear, but MATLAB maps the data to a colormap in a linear fashion. You can solve this by first binning your data using histcounts to create a linear mapping, then adjusting the color bar ticks labels to make your linear colormap appear non-linear. Here's an example:
data = 2.5.*rand(200); % Sample random data in the range [0 2.5]
edges = [0 0.5 0.75 0.9 1.1 1.25 1.5 2 2.5]; % Define edges of nonlinear map
imgMap = [0.00 0.00 0.50; ... % Colormap colors
0.00 0.50 1.00; ...
0.67 0.90 0.93; ...
1.00 1.00 1.00; ...
1.00 0.87 0.68; ...
0.98 0.67 0.38; ...
1.00 0.40 0.10; ...
1.00 0.00 0.00];
[~, ~, bin] = histcounts(data, edges); % Bin the data according to the edges
image(bin); % Plot bin index, not the original data
colormap(imgMap); % Add new colormap
hBar = colorbar; % Add color bar
set(hBar, 'TickLabels', ... % Modify color bar tick labels
[{''}; cellstr(num2str(edges(2:(end-1)).', '%g')); {''}]);
And here's the sample plot:
Note that if your data contains NaN values, you will get a bin value of 0, which in the above example will be mapped to the lowest value in the colormap range (which is the first element of imgMap). To reconstitute these NaN values, do the following after binning the data with histcounts:
bin(bin == 0) = nan;

Drawing star-like figure in Matlab

I had to plot this in matlab :
so I wrote the following code:
x=[0.9 1 0.9 -0.9 0.7 0.7 -0.9 0.9];
y=[-1 0 1 -0.5 -0.8 0.8 -0.4 -1];
plot(x,y);
but this gives me:
Is this method insufficient to draw the first figure....Is there some other short method for this...
You can do better positioning your data points
nPoints = 7;
th = linspace(-2*pi, 2*pi, 2*nPoints+1);%// extra point to close the curve
r = 1;
x = r*cos(th);
y = r*sin(th);
scatter( x, y, 'ro' );
hold on;
plot( x(1:2:end), y(1:2:end), 'b-' );
axis equal;
Resulting with:
BTW, this code works for any odd nPoints - just try ;)
It seems to me that you want to construct a star polygon, which has the property that all points are equidistant to its neighboring points, and all points lie on the same circle.
First, we generate the desired angles a (measured from the x-axis to a line that connects the origin with the desired point, see wikipedia). Based on the polygon, we rearrage the angles in such a way that a desired number of points is skipped (in the code below, this is done by using among others repmat - but many alternatives exist).
To convert the angles to actual points in the plane, we take sine and cosine values, and then we can plot the dots and lines. Putting this together results in the following code, which results in the dersired figure
n = 7;
a = linspace(0,2*pi,n+1);
skip = 1;
b = [repmat(a(1:end-1),1,skip+1) a(end)];
a = b(1:skip+1:end);
figure;
clf;
hold on;
plot(cos(a),sin(a),'b-');
plot(cos(a(1:end-1)),sin(a(1:end-1)),'ro');
axis([-1 1 -1 1])
axis equal
A little less involved would be to compute the vector a like this:
a = mod(linspace(0,(skip+1)*2*pi,n+1),(skip+1)*2*pi);
I tried manually:
x = [-0.4 0.6 1 0.6 -0.4 -0.8 -0.8];
y = [-0.9 -0.7 0 0.7 0.9 0.4 -0.4];
x_ = [x,x];
y_ = [y,y];
figure;
hold on;
for ii=1:numel(x),
scatter(x(ii),y(ii),'ro');
plot([x_(ii),x_(ii+1+1)],[y_(ii),y_(ii+1+1)],'b-');
end
axis([-1 1 -1 1]);
And I get his:

interpolating a data along y axis at points defined along x axis

Due to lack of explaining i am going to edit my question a bit.
I have a data set along y axis plotted against x axis with step of 0.01 along x axis. Of course along y axis the step can be any arbitrary value. For example i have 0.02 and 0.03 then 0.05. Which means value 0.04 is missing along y axis. I want to interpolate this and values like this.
Please help me out.
Data: (Its just part of data, Actuall data goes till 1 in both columns)
0 0.154994
0.01 0.161559
0.02 0.16794
0.03 0.168151
0.04 0.172584
0.05 0.177927
0.06 0.187229
0.07 0.194835
0.08 0.195799
0.09 0.200876
0.1 0.207076
0.11 0.213972
0.12 0.220275
0.13 0.227207
0.14 0.234465
0.15 0.238785
0.16 0.250232
0.17 0.257551
Following what #tmpearce said, the simplest use would be something like:
>> x = 0.0:0.01:0.17;
>> pred_y = interp1(Data(:,1), Data(:,2), x);
Edit follows:
If you data you posted is named Data, and your x and y data exist on {0,1} you might want to do this:
>> X = Data(:,1);
>> Y = Data(:,2);
>> pred_x = 0.0:0.01:1.0;
>> pred_y = interp1(X,Y,pred_x);
See the literature on interp1 for all of the options.

Unordered X Y coordinate pairs + Concentration contourplot

I have 2 vectors, X and Y, corresponding to a list of unordered coordinates, and a corresponding concentration vector C for each point.
I'd like to plot this on a structured grid as a 2D contour plot.
scatter3(X,Y,C,[],C);
gives me what I want visually, but I'm looking for 2D contours, i.e. pcolor. Is there an easy solution like griddata or trigriddata?
EDIT: Ok, so `scatter3(X,Y,C,[],C); view([0 90])ยด is the correct visual.
TriScatteredInterp works nicely for a rectangle. But what about an irregular shape like a map? :=)
F = TriScatteredInterp(x,y,C);
ty=0:0.005:0.284;
tx=0:0.005:0.65;
[qx,qy] = meshgrid(tx,ty);
qC = F(qx,qy);
pcolor(qx,qy,qC);
EXAMPLE: (X=width coordinate , Y= height coordinate, C= concentration of pollutant)
X Y C
0.1 0.0 5
0.1 0.1 10
0.1 0.21 5
0.2 0.1 4
0.2 0.3 1
0.2 0.5 2
0.2 0.51 7
0.3 0.15 4
0.3 0.36 6
0.3 0.5 3
0.3 0.52 7
scatter3(X,Y,C,[],C,'filled'); %individual plotting of X,Y pairs and colors=C
view([0 90]) %see only XY and Z becomes flat
Imagine we had 10000 XY pairs so scatter3 produces almost an image but without interpolation.
If I understand your question correctly you can use contour(X,Y,Z)
EDIT: You can use imagesc with a matrix that you make yourself. So if your x and y values are in a reasonable range you can just start with:
I = zeros(max(x), max(y));
for d = 1: length(x),
I(x(d),y(d)) = z(d);
end
imagesc(I);