axis equal in a Matlab loglog plot - matlab

In Matlab the command 'axis equal':
sets the aspect ratio so that equal tick mark
increments on the x-,y- and z-axis are equal in size. This
makes SPHERE(25) look like a sphere, instead of an ellipsoid
However, when using the loglog plotting function, this doesn't work "properly". What I would like to happen is that I get an aspect ratio so that a given factor occupies the same visual distance. What actually happens is that
>> loglog(2.^[1:20]*1e10,(2.^[1:20]).^2)
>> axis equal
results in
rather than
So that the slope 2 (from the squared) could be easily observed, and so that there wouldn't be all that extra white space.
My question is:
Is there a Matlab command that does this for me? Alternatively, has anyone solved this problem before?

One solution is for you to modify the axes limits and 'DataAspectRatio' properties yourself so that a decade on one axis equals a decade on the other. Here's how you can do it for your example:
loglog(2.^[1:20]*1e10,(2.^[1:20]).^2); %# Plot your sample data
xLimits = [1e10 1e16]; %# Limits for the x axis
yLimits = [1 1e12]; %# Limits for the y axis
logScale = diff(yLimits)/diff(xLimits); %# Scale between the x and y ranges
powerScale = diff(log10(yLimits))/... %# Scale between the x and y powers
diff(log10(xLimits));
set(gca,'Xlim',xLimits,'YLim',yLimits,... %# Set the limits and the
'DataAspectRatio',[1 logScale/powerScale 1]); %# data aspect ratio
set(gca,'XTick',[1e10 1e12 1e14 1e16]); %# Change the x axis tick marks
And here's the resulting plot:
Notice that the space between the 100 and 102 tick marks on the y axis spans the same number of pixels as the space between the 1010 and 1012 tick marks on the x axis, thus making a decade on one axis equal to a decade on the other.
If you don't want to change the axes limits, and instead want to use the default limits chosen by MATLAB, you can simply fetch the limits from the axes to perform the computations:
xLimits = get(hAxes,'XLim');
yLimits = get(hAxes,'YLim');
However, in order to disable MATLAB's automatic axes resizing behavior you will still have to either set the axes limits to the same values or set the limit mode properties to 'manual' when you update the 'DataAspectRatio' property:
set(gca,'Xlim',xLimits,'YLim',yLimits,...
'DataAspectRatio',[1 logScale/powerScale 1]);
%# OR...
set(gca,'XLimMode','manual','YLimMode','manual',...
'DataAspectRatio',[1 logScale/powerScale 1]);
If all of this seems like a lot of work, you can simplify things by putting it all into a function. I will actually be submitting a function decades_equal to the MathWorks File Exchange based on the code in this answer. For the time being, here is a trimmed down version (i.e. no error checking or help) that you can use:
function decades_equal(hAxes,xLimits,yLimits)
if (nargin < 2) || isempty(xLimits)
xLimits = get(hAxes,'XLim');
end
if (nargin < 3) || isempty(yLimits)
yLimits = get(hAxes,'YLim');
end
logScale = diff(yLimits)/diff(xLimits);
powerScale = diff(log10(yLimits))/diff(log10(xLimits));
set(hAxes,'Xlim',xLimits,...
'YLim',yLimits,...
'DataAspectRatio',[1 logScale/powerScale 1]);
end
And you can call the function as follows:
loglog(2.^[1:20]*1e10,(2.^[1:20]).^2); %# Plot your sample data
decades_equal(gca); %# Make the decades equal sizes
How it works...
You may be wondering what the logic is behind how I chose the scaling factors above. When trying to make the displayed size of a decade equal for each axes, we have to take into account both the number and sizes of decades within the axes ranges. In the above code, I am basically computing the average decade size for each axis, then using the ratios of the average decade sizes to scale the axes accordingly. For example, diff(yLimits) gives the total size of the y axis, and diff(log10(yLimits)) gives the number of decades (i.e. powers of ten) displayed on the y axis.
This may be easier to see if I reorder the operations in the above code like so:
yDecade = diff(yLimits)/diff(log10(yLimits)); %# Average y decade size
xDecade = diff(xLimits)/diff(log10(xLimits)); %# Average x decade size
set(gca,'XLim',xLimits,'YLim',yLimits,...
'DataAspectRatio',[1 yDecade/xDecade 1]);
And this will give the same scaling results as before.

Related

Matlab : Pan a figure programmatically

I've been plotting some figures and I want to save them multiple times zoomed in certain areas. All my views have pivot-point at y=0 except one, the last view. I've been using 'XLim' to change the view on the X axis. Now I need to pan the view on the Y axis. When using YLim, the aspect ratio of the image changes.
Is it possible to set some pivot point on the Y-axis?
Here is a minimum working code where my problem can be seen. Pauses 2 times.
f1 = figure(10);
hold on
axis equal
x = linspace(1,2*pi);
y = sin(x);
plot(x,y,'*')
xlims=[0 2*pi; 1 2*pi; 0.5 1.5];
for i = 1:size(xlims,1)
set(gca,'XLim',xlims(i,:))
disp('Here I am saving this view! (Paused)')
pause
end
% Now I want to PAN the Y axis and set the view around Y = 1.
% But still keep last set Xlims AND keep the aspect ratio of figure.
% When using YLim, the aspect ratio changes.
set(gca,'YLim',[0.9 1.1]) % Not what I have in mind.
% Just need to pivot Y=1...
There's no "panning" command as such. (there is a pan command but this just toggles the ability to pan using the mouse on or off).
But, there is the ability to set your axis limits dictating which portion of the plot to show, using the axis function, with manually specified limits.
e.g. axis([ xmin, xmax, ymin, ymax ]);
Furthermore, if you apply this after any other axis calls that affect shape / aspect ratio (e.g. axis square or axis equal) then these will be preserved.
Note that in your particular example, the use of axis equal therefore necessarily means that for a smaller range of Y your aspect ratio will be smaller. Therefore you might to opt against that.
Alternatively, you can also control the aspect ratio manually with the daspect function. e.g., in your example:
>> axis equal % your initial axis / aspect ratio situation
>> A = axis;
>> axis([0.5,1.5,0.9,1.1]); % your new values. aspect ratio will change to preserve equality
>> B = axis;
>> Y_factor = (A(4)-A(3))/(B(4)-B(3));
>> daspect([Y_factor, 1, 1]); % back to the old "aspect ratio"
However, this clearly means that the two axes are now not "equal", and the comparison with previous graphs might be misleading. This may or may not be a factor to take into consideration.
Finally, if what you're really after is simply to make sure your axes object does not change size, you can simply set the size of your axes object manually (e.g. set(gca, 'position', [x,y,x_width, y_width]) ) and ensure axis is on normal mode (i.e. adapting to the axes size).
The answer given in a comment by #jodag is perfect. Since comments have a tendency to disappear I thought I would reproduce it here.
To "pan", you need to change both limits without changing their interval. Since you can read the current interval with
get(gca, 'YLim')
you can simply pan the y axis by a distance dy with the command:
set(gca, 'YLim', get(gca, 'YLim')+dy)

Draw evenly-spaced height lines of a function in MATLAB

I would like to draw height lines of a function (represented by matrices, of course), using MATLAB.
I'm familiar with contour, but contour draws lines at even-spaced heights, while I would like to see lines (with height labels), in constant distance from one another when plotted.
This means that if a function grows rapidly in one area, I won't get a plot with dense height lines, but only a few lines, at evenly spaced distances.
I tried to find such an option in the contour help page, but couldn't see anything. Is there a built in function which does it?
There is no built-in function to do this (to my knowledge). You have to realize that in the general case you can't have lines that both represent iso-values and that are spaced with a fixed distance. This is only possible with plots that have special scaling properties, and again, this is not the general case.
This being said, you can imagine to approach your desired plot by using the syntax in which you specify the levels to plots:
...
contour(Z,v) draws a contour plot of matrix Z with contour lines at the data values specified in the monotonically increasing vector v.
...
So all you need is the good vector v of height values. For this we can take the classical Matlab exemple:
[X,Y,Z] = peaks;
contour(X,Y,Z,10);
axis equal
colorbar
and transform it in:
[X,Y,Z] = peaks;
[~, I] = sort(Z(:));
v = Z(I(round(linspace(1, numel(Z),10))));
contour(X,Y,Z,v);
axis equal
colorbar
The result may not be as nice as what you expected, but this is the best I can think of given that what you ask is, again, not possible.
Best,
One thing you could do is, instead of plotting the contours at equally spaces levels (this is what happens when you pass an integer to contour), to plot the contours at fixed percentiles of your data (this requires passing a vector of levels to contour):
Z = peaks(100); % generate some pretty data
nlevel = 30;
subplot(121)
contour(Z, nlevel) % spaced equally between min(Z(:)) and max(Z(:))
title('Contours at fixed height')
subplot(122)
levels = prctile(Z(:), linspace(0, 100, nlevel));
contour(Z, levels); % at given levels
title('Contours at fixed percentiles')
Result:
For the right figure, the lines have somewhat equal spacing for most of the image. Note that the spacing is only approximately equal, and it is impossible to get the equal spacing over the complete image, except in some trivial cases.

Changing perspective of Matlab plots

Say I have a matrix hey 15x15. I want to plot the value of the matrix as a 2D plot for better visualization. But Matlab plots with the convention that origin is in bottom-left corner and positive x is along the left and positive y is along the up direction from origin.
but i want to make my plots such that origin is in top-left corner, +ve x is left and +ve y is down.
So i just used a slight trick.
figure
axis([0 15 -15 0]);
daspect([1,1,1])
hold on
rectangle('Position',[3,-6,2,3],...
'EdgeColor','black',...
'LineWidth',2,...
'FaceColor','cyan')
for i=1:nrows
for j=1:ncolumns
if char(hey(i,j))=='^'
text(j,-i,'^');
elseif char(hey(i,j))=='>'
text(j,-i,'>');
elseif char(hey(i,j))=='v'
text(j,-i,'v');
elseif char(hey(i,j))=='<'
text(j,-i,'<');
end
if obstacle(i,j)==1
text(j,-i,'X');
end
end
end
text(goalY,-goalX,'T');
I made the transformation (x,y)-->(y,-x). But the downside is that the axes are then numbered along y as -1 to -15. However if reader was following above, i only wanted to plot the matrix values and in matrix the y runs +ve downwards from 1 to 15 for my case.
So i want the plot to show +1 thru +15 along y with origin at top-left and x graduated as it is but the values +1 to +15 written at the top of the plot rather than below.
How to do this? In the extreme case, i am alos willing to transfer the matrix hey to another software that can do the nice plot as i want. If any of the two alternatives is possible, please give concrete steps to do it.
EDIT:
After using the helpful methods below, i still have to use a trick like plot (j,i) instead of the innocent plot(i,j). This is because for matrix (i,j) is mapped to graph plot (x,y) as x=j, y=i. Is there a similar workaround? a matrix element is (row #, column #). But in 2D matlab graph, we will denote it's position as (column #, row #). I was just guessing if there was some matlab in-built function to take care of this. like i will give it (row #, column #) but matlab will plot (column #, row #). Is there such a function?
I think axis ij does what you want:
axis ij places the coordinate system origin in the upper left corner. The i-axis is vertical, with values increasing from top to bottom. The j-axis is horizontal with values increasing from left to right.
To locate the x axis on top, change the 'XAxisLocation' of the axes to 'top' (default is 'bottom').
Example:
x = 1:10;
y = x.^2;
plot(x,y)
axis ij
set(gca,'XAxisLocation','top')
Original plot (lines 1-3 of above code):
After axis ij (line 4):
After set(gca,'XAxisLocation','top') (line 5):
If I followed correctly you are looking for the axes XAxisLocation and YDir properties. You can set them to top and reverse respectively to get the output you want. You can also set the XTick property to 1:15 to show every value from 1 to 15.
Example:
clear
clc
%// Create dummy data
[x,y] = meshgrid(1:15,1:15);
u = cos(x).*y;
v = sin(x).*y;
figure
quiver(x,y,u,v)
set(gca,'XAxisLocation','top','XTick',1:15,'YDir','reverse')
hold on
%// I changed the coordinated of the rectangle to fit with the change in
%y-axis.
rectangle('Position',[3,3,2,3],...
'EdgeColor','black',...
'LineWidth',2,...
'FaceColor','cyan')
axis([0 15 0 15])
Which gives the following:

How to plot bar with different height and differenth width in matlab?

I am having a big problem in Matlab, because it seems that I want to do something that is not so usual.
Basically I am trying to implement a way of group distribution together called Vincentizing.In order to do that I am following the instruction of a paper (Ratcliff 1979 - Group Reaction Time Distributions and an Analysis of Distribution Statistics). Everything is fine until I have to plot the actual graph. I have an array that contains the quantiles of my dataset. The tutorial I am following says:
distribution histograms can be constructed by plotting quantiles on the abscissa and then constructing rectangles between adjacent quantiles such that all the rectangles have equal areas, as in Figure 2 (link of the image below)
http://postimg.org/image/btftrd6y7/
Once I calculate the quantiles, I can set the area to some value, let's say 10, and I can therefore calculate the height of each bar. The width of each bar is the distance between two adjacent quantiles, and of course I can calculate that as well. I have all the information I need, but I don't know how to plot a graph. How can, in matlab, plot I graph like the one in figure?
(it seems that I can plot histogram of different width, but with the hist function I cannot actually specify the height. With the bar function, however, I can specify the height but it seems I cannot change the width..)
Every help is appreciated.
The simplest solution is to use rectangle:
% sample data: set the start of each bar, the bottom (here 0), the width and the height
x = [0.5 0.6 0.9 1 1.2]; % start of bar
y = zeros(length(x),1);
dx = diff([x 1.8]); % width of bar
dy = [1 3 2 .5 .1];
figure, hold on
for ii=1:length(x)
rectangle('position',[x(ii) y(ii) dx(ii) dy(ii)])
end
axis([0.5 2 0 4.1])
ylabel('Prob density')
xlabel('Time')

measure valley width 2d, matlab

I have a 2d image, I have locations where local minimas occurs.
I want to measure the width of the valleys "leading" to those minimas.
I need either the radii of the circles or ellipses fitted to these valley.
An example attached here, dark red lines on the peaks contours is what I wish to find.
Thanks.
I am partially extending the answer of #Lucas.
Given a threshold t I would consider the points P_m that are below t and closer to a certain point m of minimum of your f (given a characteristic scale length r).
(You said your data are noisy; to distinguish minima and talk about wells, you need to estimate such r. In your example it can be for instance r=4, i.e. half the distance between the minima).
Then you have to consider a metric for each well region P_m, say for example
metric(P_m) = .5 * mean{ maximum vertical diameter of P_m ,
maximum horizontal diameter of P_m}.
In your picture metric(P_m) = 2 for both wells.
On the whole, in terms of pseudo-code you may consider
M := set of local minima of f
for_each(minimum m in M){
P_m += {p : d(p,m) < r and f(r)<t} % say that += is the push operation in a Stack
}
radius_of_region_around(m) = metric(P_m); %
I would suggest making a list of points that describe the values at the edge of your ellipse, perhaps by finding all the points where it crosses a threshold.
above = data > threshold
apply a simple edge detector
edges = EdgeDetector(above)
find coordinates of edges
[row,col] = find(edges)
Then apply this ellipse fitter http://www.mathworks.com/matlabcentral/fileexchange/3215-fitellipse
I'm assuming here you have access to the x, y and z data and are not processing a given JPG (or so) image. Then, you can use the function contourc to your advantage:
% plot some example function
figure(1), clf, hold on
[x,y,z] = peaks;
surf(x,y,z+10,'edgecolor', 'none')
grid on, view(44,24)
% generate contour matrix. The last entry is a 2-element vector, the last
% element of which is to ensure the right algorithm gets called (so leave
% it untouched), and the first element is your threshold.
C = contourc(x(1,:), y(:,1), z, [-4 max(z(:))+1]);
% plot the selected points
plot(C(1,2:end), C(2,2:end), 'r.')
Then use this superfast ellipse fitting tool to fit an ellipse through those points and find all the parameters of the ellipse you desire.
I suggest you read help contourc and doc contourc to find out why the above works, and what else you can use it for.