How to have a log scale plot so that the smallest value on the vertical axis is a power of 10 - matlab

I have the following logarithmic plot shown below:
I want to change this plot so that the "x axis" is such that the vertical value lies at the smallest possible power of 10. What I mean by this is that I would like to make sure that the horizontal axis seen at the bottom of the plot is perhaps y = 10e-2 such that the rightmost group of bars in the above plot can be above the "x axis". I tried 'xaxislocation' but it doesn't work. In hindsight, I suppose the y=10e0 line is not the x axis anyway.
% plot group_err
data_names = cell(1,8);
data_names{1}='1'; data_names{2}='2';
data_names{3}='3'; data_names{4}='4';
data_names{5}='5'; data_names{6}='6';
data_names{7}='7'; data_names{8}='8';
h = bar(group_err);
grid on;
set(gca,'xticklabel',data_names,'YScale','log','FontSize',14);
ylabel('Error rate (%))','FontSize',14);
xlabel('Dataset','FontSize',14);
title('Error rate of sequential algorithms','FontSize',14);
ylim([0.01 100]);
group_err:
79.0407673860911 80.6235000000000 80.3837000000000
28.2600000000000 24.3600000000000 25.0200000000000
2.18055555555556 1.44290190000000 1.92145600000000
34.1692954784437 14.9053400000000 17.9127200000000
0.0551724137931035 0.0298850500000000 0.0459770500000000
33.2005921539600 22.4352400000000 25.6802200000000
0.0979391960824322 0.0685568400000000 0.155070440000000

Now that we've seen your edit, that's very straight forward. Simply find whatever y value is the smallest and you need to round this down so that the resulting value is a power of 10 and is smaller than the smallest y value you're looking at.
To do this, you want to the floor of the following relationship where given your minimum value ymin, it satisfies this relationship:
10^floor(x) = ymin
Re-arranging this equation by taking the log of both sides, we get:
x = log(ymin) / log(10)
... and we now take the floor of x to get what you need. Take special note that you need to take the floor as it rounds down to minus infinity. Don't use fix as this rounds towards 0 so for negative values, this will add 1 to negative values and not what you want. Specifically, this will ensure that you find the smallest power x that respects negative powers when the above relationship is less than 1.
The value of x serves as the smallest power of 10 that satisfies what you need. You will the need to take 10^x to complete the task. This is the smallest power of 10 that will serve as the horizontal axis of your plot. You then use ylim to limit the vertical axis so that you see what the smallest and largest values you have. Because you are using a semi-logarithmic plot, to do what you need you must specify these values as powers of 10. This is the whole reason why we need to determine the smallest power of 10 to serve as the minimum limit or the x axis of your data.
Therefore, assuming you have your plot already open, simply do the following:
x = floor(log(min(y)) / log(10));
ylim([10^x max(y)]);
ylim takes two values: The minimum value and maximum value of the y axis you would like to see. I've made sure that the largest value to visualize is just the largest value in your data itself.

what you want in to change the 'BaseValue' property of your bar plot, in your case would be:
set(h,'BaseValue',0.01)
You will get something like this:

Related

How do I customize dual axis values?

I want to plot 2 variables in my worksheet.X variable vs Y variable. The Y variable is composed of 3 sub-variables y1,y2,y3 which when summed up result to Y. I have expressed y1,y2,y3 as percentages of Y say Y=45(100%) and y1=35%,y2=40%,y3=25%.How do I plot X vs Y with the Y axis value being 45 and not 100%?
I have been to plot the measure values of Y(y1,y2,y3) but now the axis values are automatically generated from 0 to 100%. What I want is the real axis values to be displayed 0 to 45.
I want the axis to be real values of Y(45) but the sub_variables (y1=35%,y2=40%,y3=25%)
There are a couple of things you can do here.
Click on the secondary axis and select "Synchronize axis". This
will ensure that the secondary axis is tied to the scope of the
primary axis.
You can manually edit either/both axis by right clicking on them and setting the axis values manually.
Beyond this, it might be how you are setting up your viz. If you could provide a picture/sample data it would be helpful.

Gnuplot: how to eliminate gap between minimum value and "floor"

I use
splot "merged2.dat" using 9:10:11
to draw my points.
No matter what values i use for 'set zrange', there is always a huge 'gap' between the point with minimal z value and the 'floor' (i.e. the rectangle bounded by the x and y axes), about a third of the z-axis.
Is there a way to reduce or remove this gap?
I think you are looking for the set xyplane relative <frac> command, where:
The form set xyplane relative <frac> places the xy plane below the range in Z, where the distance from the xy plane to Zmin is given as a fraction of the total range in z. The default value is 0.5.
The default value 0.5 explains the "one third" which you are experiencing. Setting it to 0.0 should remove the gap.
The xy-plane can be set to an absolute value withset xyplane at <zlevel>.

How to make a plot of a circle with dashed coloured border in MATLAB?

So i have this code to obtain radial gravity on Earth in function of the latitude:
G=6.6e-11;
M=5.976e24;
N=1000;
r=6371000;
w=2*pi/(24*3600);
for i=1:1:360
Theta=i*pi/180;
x(i)=i;
Vi(i)=-G*M/(r*r);
Phii(i)=r*w*w*sin(Theta)*sin(Theta);
gr(i)=Vi(i)+Phii(i);
end
plot(x,gr)
And it runs well. I want to make a graph of a circle made of a border of points (representing angle (i)) that change colour according to the value of gr(I want to set ranges of values of gr so that if the value obtained falls in a specific category, the point will have a specific colour).
I'm really new to MATLAB. Is there any possible way to make this?
Thanks in advance.
Here is the basic algorithm that I would do:
Determine how many colours you want to represent in your plot.
Create a colour map that has this many points for what you want to compute.
Determine a linearly increasing vector that varies from the minimum value of gr to the maximum value of gr with as many points as you have determined in Step #2
For each point in gr:
a. Determine which point yields the closest distance of this point to the vector in Step #3
b. Use this to index which colour you want.
c. Convert your angle into Cartesian co-ordinates, then plot this point with the colour found in Step 4b.
Let's tackle each point in detail.
Step #1 - Determine how many colours you want
This is pretty simple. Just determine how many colours you want. For now, let's assume that you want 20 colours, so:
num_colours = 20;
Step #2 - Create a colour map
What we can do is create a 20 x 3 matrix where each row determines a RGB tuple that denotes the amount of red, green and blue that each colour will occupy. MATLAB has built-in colour maps that will help you facilitate this. Here are all of the available colour maps that MATLAB has:
Each colour map has a special variable where you can provide it an integer number, and it'll return this 2D matrix of as many rows as the number you have provided. Each row gives you an RGB triplet which denotes the proportion of red, green and blue respectively. This matrix varies from the beginning of the colour map (top row) to the end (bottom row). All you have to do is use any name seen in the figure I've shown you above to create a colour map of that type. For example, if you wanted to get a bones colour map of 15 points, simply do:
colour_map = bones(15);
If you wanted to get a jet colour map of 25 points, simply do:
colour_map = jet(25);
.... you get the idea right? I like hsv so let's use the HSV colour map. You can use any colour map you want, but let's just stick with HSV for the sake of this example.
As such:
colour_map = hsv(num_colours);
Step #3 - Get that linearly increasing vector
You want certain colours to map into certain ranges, which is why this step is important. Given a value in gr, we want to figure out which colour we want to choose, and all you have to do is determine which value in gr is the closest to a value in this vector in Step #3. Therefore, you can use linspace to do this for you:
bin_vector = linspace(min(gr), max(gr), num_colours);
This will create a num_colours 1D array where the beginning of this array starts at the minimum value of gr and varies up to the maximum value of gr and each value is equally spaced such that we generate a num_colours array.
Step #4 - Bring it all home
Now, for each point in gr, we need to figure out which point is the closest to that vector in Step #3, we then use this to figure out the colour we want, then we need to convert our angle into Cartesian co-ordinates, then plot this point.
For illustration purposes, I'm going to assume your radius is 1. You can figure out how to get the x and y co-ordinates by simply doing cos(theta) and sin(theta), where theta is the angle you are examining. Since your gr array has 360 slots, I'm going to assume a resolution of 1 degree per slot. Therefore, you can easily do this in a for loop. Make sure you use hold on because we are going to call plot multiple times, and we don't want to overwrite the plot each time we call plot. You want all of the points to stay in the plot.
Without further ado:
figure; %// Create blank figure
hold on; %// Remember all points
%// For each point in our array...
for idx = 1 : 360
%// Find the closest slot between gr and our vector in Step #3
[~,min_idx] = min(abs(gr(idx) - bin_vector));
%// Grab this colour
clr = colour_map(min_idx,:);
%// Plot the point with this colour
plot(cosd(idx), sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Take notice that cosd and sind take in degrees as the input argument while cos and sin take in radians. Also, take note that I also changed the size of the point so that it's bigger. With the above logic, and your array in gr, this is what I get:
If you want the radius to get larger, all you have to do is multiply each cosd and sind term with your radius. Therefore, you can do something like this:
radius = 2;
for idx = 1 : 360
... %// Insert colour code here
...
...
%// Now plot
plot(radius*cosd(idx), radius*sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Just leave the code the same, but for the plot command, just multiply each x and y value by the radius.
Minor note in efficiency
The way you're calculating your gr array is using an inefficient for loop. There are some situations (like mine above) where you need to use a for loop, but for simple computations there is no need. It's better if you vectorize its creation. Therefore, you can get rid of the for loop to calculate your gr array like so:
x = 1 : 360;
Theta = x*pi/180;
Phii = r*w*w*sin(Theta).*sin(Theta);
Vi = -G*M/(r*r);
gr = Vi + Phii;
x is simply a vector going from 1 to 360, and that's done in the first line. Also, Vi is just an array which contains a single value and if you know how operations work between a scalar and an array, you can just do an addition with this single value and it'll add every value in your array by this much. As such, there's no need to create an array for Vi. Also, take a look at how I calculated Phii. I'm using element-by-element operations as Theta is now an array. You want to create an array Phii that takes corresponding values of Theta, and applies that formula to each value in Theta to produce Phii.
Hope this helps. Good luck!

Contouring a mesh and assigning magnitude arrows in Matlab

I want to assign vector to a contourf graph, in order to show the direction and magnitude of wind.
For this I am using contourf(A) and quiver(x,y), where as A is a matrix 151x401 and x,y are matrices with the same sizes (151x401) with magnitude and direction respectively.
When I am using large maps i get the position of the arrows but they are to densily placed and that makes the graph look bad.
The final graph has the arrows as desired, but they are to many of them and too close, I would like them to be more scarce and distributed with more gap between them, so as to be able to increase their length and at the same time have the components of the contour map visible.
Can anyone help , any pointers would be helpful
i know its been a long time since the question was asked, but i think i found a way to make it work.
I attach the code in case someone encounters the same issues
[nx,ny]= size(A) % A is the matrix used as base
xx=1:1:ny; % set the x-axis to be equal to the y
yy=1:1:nx; % set the y-axis to be equal to the x
contourf(xx,yy,A)
hold on, delta = 8; %delta is the distance between arrows)
quiver(xx(1:delta:end),yy(1:delta:end),B(1:delta:end,1:delta:end),C(1:delta:end,1:delta:end),1) % the 1 at the end is the size of the arrows
set(gca,'fontsize',12);, hold off
A,B,C are the corresponding matrices ones want to use

axis equal in a Matlab loglog plot

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.