Log scale (x axis) histogram - matlab

what I need is a histogram with X axis displayed on a log scale. However, I still want every bar in a histogram to be of the same width.
Whatever, I came up with (or upon) display bars with higher values as narrower ones (they are of the same width on ordinary scale and not on log scale).
Here is what I am doing now:
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
bar(edges, h); set(gca, 'Xscale', 'log');

Since your bars should be evenly spaced, you can plot them with x-values 1,2,.. and then change the axis labels.
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
centers = sqrt(edges(1:end-1).*edges(2:end));
bar(h)
%# fix the x-labels, x-axis extents
xlim([0.5,length(centers)+0.5])
set(gca,'xticklabel',num2str(centers(:),'%5.2f'))

Use patch instead of bar:
figure, hold on
edges = 10.^(c_min:0.1:c_max);
h = histc(data, edges);
for ii = 1:numel(edges)-1
patch(...
[edges(ii) edges(ii) edges(ii+1) edges(ii+1)], ...
[0 h(ii) h(ii) 0],...
'b');
end
Although this particular code does not do what you want, that is a problem with the definition of edges rather than the methodology, so you'd have to fiddle a bit with the edges (perhaps re-define them for the patch)

Related

How do you rescale the height of a histogram?

I am having trouble plotting a histogram of the x-values of my data points together with a line showing the relationship between x and y, mainly because the scale in the y direction of the histogram is not of the same magnitude as the scale in the line plot. For example:
% generate data
rng(1, 'twister')
x = randn(10000,1);
y = x.^2
% plot line, histogram, then histogram and line.
subplot(3,1,1)
scatter(x, y, 1, 'filled')
ax = gca;
maxlim = max(ax.XLim); % store maximum y-value to rescale histogram to this value
subplot(3,1,2)
h = histogram(x, 'FaceAlpha', 0.2)
subplot(3,1,3)
scatter(x, y, 1, 'filled')
hold on
h = histogram(x, 'FaceAlpha', 0.2)
Produces the following:
where the line chart is completely obscured by the histogram.
Now, one might naively try to rescale the histogram using:
h.Values = h.Values/max(h.Values) * maxlim;
which gives
You cannot set the read-only property 'Values' of Histogram.
Alternatively one can get the bin counts using histcounts, but as far as I can tell, the bar function does not allow one to set the face alpha or have other configurability as per the call to histogram.
As discussed in the comments there are several solutions that depend on the version of Matlab you are using. To restate the problem, the histogram function allows you to control many graphics properties like transparency, but only gives you a limited number of options to change the height of the bars. With histcounts you can get the bar heights and rescale them however you want, but you must plot the bars yourself.
First option: use histogram
As you cannot rescale the histogram heights, you must plot them on separate axis.
From release 2016a and onwards, you can use yyaxis left for the scatter plot and yyaxis right for the histogram, see Matlab documentation:
Prior to this one must manually create and set separate y-axis. Although I have not found a good simple example of this, this is perhaps the most relevant answer here: plot two histograms (using the same y-axis) and a line plot (using a different y-axis) on the same figure
Using histcounts and manually creating a bar chart
Using my example, we can get counts as follows:
[Values, Edges] = histcounts(x);
And rescaling:
Values = Values / max(Values) * maxlim;
and finding centres of bars:
bar_centres = 0.5*(Edges(1:end-1) + Edges(2:end));
Up to release 2014a, bar charts had a 'children' property for the patches that allows transparency to be controlled, e.g.:
% plot histogram
b1 = bar(bar_centres,Values);
% change transparency
set(get(b1,'Children'),'FaceAlpha',0.3)
After 2014a bar charts no longer have this property, and to get around it I plot the patches myself using the code from this mathworks q&a, replicated here:
function ptchs = createPatches(x,y,offset,c,FaceAlpha)
%createPatches.m
% This file will create a bar plot with the option for changing the
% FaceAlpha property. It is meant to be able to recreate the functionality
% of bar plots in versions prior to 2014b. It will create the rectangular
% patches with a base centered at the locations in x with a bar width of
% 2*offset and a height of y.
% Ensure x and y are numeric vectors
validateattributes(x,{'numeric'},{'vector'});
validateattributes(y,{'numeric'},{'vector'});
validateattributes(c,{'char'},{'scalar'});
%#TODO Allow use of vector c
% Check size(x) is same as size(y)
assert(all(size(x) == size(y)),'x and y must be same size');
% Default FaceAlpha = 1
if nargin < 5
FaceAlpha = 1;
end
if FaceAlpha > 1 || FaceAlpha <= 0
warning('FaceAlpha has been set to 1, valid range is (0,1]');
FaceAlpha = 1;
end
ptchs = cell(size(x)); % For storing the patch objects
for k = 1:length(x)
leftX = x(k) - offset; % Left Boundary of x
rightX = x(k) + offset; % Right Boundary of x
ptchs{k} = patch([leftX rightX rightX leftX],...
[0 0 y(k) y(k)],c,'FaceAlpha',FaceAlpha, ...
'EdgeColor', 'none');
end
end
I made one change: that is, imposed the no edge condition. Then, it is perfectly fine to use:
createPatches(bin_centres, Values, 1,'k', 0.2)
to create the bars.

Matlab Aspect Ratio Issue

Recently, I've been using matlab to run a particle aggregation simulation, and I use its plotting itnerface to represent each particle. Thus, I've put together some code which adjusts the size of a marker such that it is equivalent to the actual particle's diameter - in relation to the window's aspect ratio and such. Well, this works very well if I'm dealing with a square domain; see below.
As you can see, the markers are sized such that particles will settle out on topof each other perfectly. Here's the plotting code for that image:
%%Initial Plot at time t = 0 Along with Scaling in the Y-Direction
figure; h=scatter(Pos(1,:),Pos(2,:),6,jet(length(Pos(1,:))),'filled','MarkerEdgeColor','k','linewidth',1);
hold on
axis equal
axis ([0 dp 0 dp]*L*Scale)
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = dp/diff(ylim)*axpos(4); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
Well, I added in periodic boundary conditions to my simulation, and this means that I can have 3X the window size shown above without much additional computation cost. So, I wanted to plot up a 3x1 domain (x is basically 3X longer than the y dimension of the domain). However, I'm having troubling sizing the particles appropriately such that they will be scaled properly in the y-direction yet also touch along the x-direction. I think I could do this using some kind of aspect ratio code, but I can't get it to work. Any ideas? Here's the code/result I came up with:
%Plot all of the data
figure;
h=scatter(Pos(1,:),Pos(2,:),6,jet(length(Pos(1,:))),'filled','MarkerEdgeColor','k','linewidth',1);
hold on
axis equal
axis ([0 3*dp 0 3*dp]*L*Scale)
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = dp/diff(ylim)*axpos(4); % Calculate Marker width in points
set(h, 'SizeData', (markerWidth)^2)
axis ([0 3*dp 0 dp]*L*Scale)
It produces the following result,which is obviously incorrect :(...
You could use something like
%cirlce def
circx = sin(linspace(0,2*pi,100));
circy = cos(linspace(0,2*pi,100));
%example data
[mgx mgy] = meshgrid(5:5:100, 5:5:100);
x = reshape(mgx,[],1); %circles centers x
y = reshape(mgy,[],1); %circles centers x
r = 2.5*rand(400,1); %radii
c = 250*rand(400,1); %colors
% draw
figure
hold on
for i=1:numel(x)
fill(r(i)*circx+x(i), r(i)*circy+y(i), c(i))
end
so that all all geometric data is fixed into coordinate axes space.
Example output is:

Plot vector (or arc) onto a rose plot. MATLAB

I have two datasets. One detailing a list of angles (which I am plotting onto a rose plot):
angles
-0.8481065519
0.0367932161
2.6273740453
...
n
The other, detailing directional statistics from this group of angles:
angle,error
-0.848106563,0.8452778824
Where angle essentially defines the directional mean, and error the circular variance, essentially an error bar either side of the angle
I have thus far plotted a rose histogram using the set of angles, as such:
h = rose(angles,36)
I would like to create a plot of the directional statistic angle (it does not need a length/magnitude - just to the edge of the circle plot) with the error around it.
As an example:
I added the lines by hand in Matlab. If possible it would be good to perhaps have shading within the arc too. Alternatively, (and possibly preferred) would be to have just a sliver above the rose plot bins (so it doesn't cover the data) with a centre line (showing the angle and shading surrounding for the error.
Thanks in advance.
How about this?
%// Data
angles = 2*pi*.8*randn(1,1e4);
angle = -0.848106563;
error = 0.8452778824;
%// Plot rose
rose(angles, 36);
axis image %// make axis square
hold on
%// Plot mean
a = axis;
a = a(2); %// size of axis
plot([0 cos(angle)*a], [0 sin(angle)*a], 'r')
%// Plot error as many shaded triangles that compose a circular wedge
t = linspace(-error/2+angle,error/2+angle,100); %// increase "100" if needed
for k = 1:numel(t)-1
h = patch([0 cos(t(k))*a cos(t(k+1))*a 0], ...
[0 sin(t(k))*a sin(t(k+1))*a 0], [.5 0 0], 'edgecolor', 'none');
%// change color [.5 0 0] to something else if desired. Note also alpha
set(h,'Facealpha',.3) %// make transparent
end
%// Place rose on top by rearranging order of axis children
ch = get(gca,'children');
set(gca,'children',[ch(2:end); ch(1)]);
For this to work, you need to use a figure renderer capable of transparency. So you may need to adjust the figure's renderer property.

Marker size unit proportionnal to the axis values

I have an X Y Z dataset.
X and Y are the 2D coordinates and Z is the intensity.
I plot the data using the scatter function:
markerSize=100;
scatter(x,y,markerSize,z,'s','fill');
I use the options 's' and 'fill' to get filled squares.
My problem is the markerSize value corresponds to the area of the marker, and its unit is points (1/72 of one inch).
The marker size is constant, even if I resize the figure plot. So that the gap between the data points increases when I increase the figure size.
What I would like is a constant marker size which is a constant of the axis unit. For instance, the marker size should be 5x5 (5 in X axis and 5 in Y axis).
Thanks for your help.
You want to make the size of markers proportional to the figure size.
The size of markers is controlled by SizeData parameter of the scattergroup object. The size of figure is stored in Position parameter of the figure object. The difficult part is to interactively resize the marker when the figure size is changed. So you need to use ResizeFcn callback and call setmarkersize function that you define.
function [ ] = setmarkersize( src, evnt )
% # get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% # get the scattergroup object
h = get(get(src,'children'),'children');
% # resize the marker
relativesize = 0.5;
set(h,'SizeData', pos(3)*relativesize);
end
================================================
% # attach the callback to figure
f = figure('ResizeFcn', #setmarkersize);
h = scatter(x,y,markerSize,z,'s','fill');
You will have to set the marker size manually according to the actual figure size on screen. Using axes property Position you can convert data units to relative figure units. In the next step this size can be converted to the absolute size in points on screen. With that information you can set the marker size accordingly. In the following code snippet I've set the x/y axis limits and width/height of the axis to identical values, because the square marker area can only be calculated reasonably if the marker width is equal to the marker height.
Set marker size relative to data units
% test data
x = [25*rand(1,10) 2.5];
y = [25*rand(1,10) 2.5];
z = [rand(1,10) 0.5];
% relative marker size in squared normalized figure units
marker_rel = 5;
%% Set relative marker size (approximately)
scatter(x,y,100,z,'s','fill');
% Set identical x/y limits and set axes height=widht, so that markers
% really represent squares in data units
xlim([0 25]);
ylim([0 25]);
set(gca, 'Units', 'Points');
axpos_pt = get(gca, 'Position');
axpos_pt = [axpos_pt(1) axpos_pt(2) min(axpos(3:4)) min(axpos(3:4))];
set(gca, 'Position', axpos_pt);
grid on;
grid minor;
% Set marker size relative to data units
markerSize = (marker_rel * axpos_pt(3) / diff(xlim))^2;
set(get(gca, 'children'), 'sizedata', markerSize);
As it turns out, the displayed marker size is slightly smaller than expected. Obviously, there's some bounding box of unknown (at least to me) size, see here.
An alternative approach is to plot rectangles "manually" as shown in the following code snippet (same test data is used). When the figure is resized, the rectangles are resized as well without any special callback function being needed.
Draw rectangles manually
%% Use rectangles (exact)
figure;
axes;
cmp = colormap;
z_range = max(z) - min(z);
line_style = 'none'; % set to '-' to make the edges visible
for k = 1:length(x)
x_pos = x(k) - marker_rel/2;
y_pos = y(k) - marker_rel/2;
w = marker_rel;
h = marker_rel;
color = cmp( round(((z(k) - min(z))/z_range)*(length(cmp) - 1)) + 1, : );
rectangle('Position', [x_pos y_pos w h], 'FaceColor', color,...
'LineStyle', line_style);
end
grid on;
grid minor;
The above code produces the desired marker size:
In general, these are no squares. They can (and will) only be squares if xlim = ylim and absolute height of axis = absolute width of axis. I have shown in my first code snippet how to achieve this.
I found an answer on the Matlab central forum which does not use the useful dsxy2figxy function. Here is the link to it (link)
The code is the following:
x = rand(1,50);
y = rand(1,50);
s = 5; %Marker width in units of X
h = scatter(x,y); % Create a scatter plot and return a handle to the 'hggroup' object
%Obtain the axes size (in axpos) in Points
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = s/diff(xlim)*axpos(3); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
The answer by ysakamoto doesn't work in Matlab 2014, where Mathworks changed figure handles from double to object. The following modification makes it work again:
function [ ] = setmarkersize( src, ~ )
% get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% marker size
relativesize = 0.01;
% axes is not necessarily the only child of figure (e.g. colorbar may be first)
for i = 1:numel(src.Children)
if strcmpi(src.Children(i).Type, 'axes')
% make marker size depend on figure width
src.Children(i).Children.SizeData = pos(3) * relativesize;
break
end
end
end
Also, when creating the figure it will not set the marker size to the correct value until resized. So call setmarkersize explicitly:
f = figure('ResizeFcn', #setmarkersize);
...
setmarkersize(f)
Consider abandoning 'markers' and drawing directly on the axis using rectangles or circles. The 'rectangle' command with 'curvature' set to [1 1] is essentially an open circle. Units are in plot units, so each rectangle can be scaled:
rectangle('position', [rectCentX rectCentY widthInPlotUnits, heightInPlotUnits],'curvature',[1 1])

Matlab Ploting with different color for iso-surface

I was trying use the code shown below to plot in such a way that each iso-surface will be different in color and there will be a color bar at the right. I made a ss(k) color matrix for different colors. Number of iso-surfaces is 10 but I have only 8 colors. That's why I wrote ss(9)='r' and ss(10)='r'.
I need a solution to plot the iso-surface with different color and bar at the right side.
ss=['y','m','c','r','g','b','w','k','r','r']
k=1;
for i=.1:.1:1
p=patch(isosurface(x,y,z,v,i));
isonormals(x,y,z,v,p)
hold on;
set(p,'FaceColor',ss(k),'EdgeColor','none');
daspect([1,1,1])
view(3); axis tight
camlight
lighting gouraud
k=k+1;
end
Another possibility is to draw the patches with direct color-mapping (by setting the property 'CDataMapping'='direct'), while assigning the 'CData' of each patch to an index in the colormap of your choice. This is in fact recommended for maximum graphics performance.
Consider the following example:
%# volumetric data, and iso-levels we want to visualize
[x,y,z,v] = flow(25);
isovalues = linspace(-2.5,1.5,6);
num = numel(isovalues);
%# plot isosurfaces at each level, using direct color mapping
figure('Renderer','opengl')
p = zeros(num,1);
for i=1:num
p(i) = patch( isosurface(x,y,z,v,isovalues(i)) );
isonormals(x,y,z,v,p(i))
set(p(i), 'CData',i);
end
set(p, 'CDataMapping','direct', 'FaceColor','flat', 'EdgeColor','none')
%# define the colormap
clr = hsv(num);
colormap(clr)
%# legend of the isolevels
%#legend(p, num2str(isovalues(:)), ...
%# 'Location','North', 'Orientation','horizontal')
%# fix the colorbar to show iso-levels and their corresponding color
caxis([0 num])
colorbar('YTick',(1:num)-0.5, 'YTickLabel',num2str(isovalues(:)))
%# tweak the plot and view
box on; grid on; axis tight; daspect([1 1 1])
view(3); camproj perspective
camlight; lighting gouraud; alpha(0.75);
rotate3d on
I also included (commented) code to display the legend, but I found it to be redundant, and a colorbar looks nicer.
Matlab usually plots different iso-surfaces in different colors automatically, so you don't need to care about that. What kind of bar do you need? A colorbar or a legend? Either way, it is just to use the colorbar or legend function..
%Create some nice data
[x y z] = meshgrid(1:5,1:5,1:5);
v = ones(5,5,5);
for i=1:5
v(:,:,i)=i;
end
v(1:5,3:5,2)=1
v(1:5,4:5,3)=2
%Plot data
for i=1:5
isosurface(x,y,z,v,i)
end
%Add legend and/or colorbar
legend('one','Two','Three','Four')
colorbar
Since the color bar encodes value->color, it is impossible to do what you ask for, unless there is no intersection in z-values between all pairs of surfaces. So the solution below assumes this is the case. If this is not the case, you can still achieve it by adding a constant value to each surface, so to separate the surfaces along the z axis, and eliminate any intersection.
The solution is based on constructing a colormap matrix of piecewise constant values, distributed similarly to the z values of your surfaces. So for example, if you have 3 surfaces, the first has z values between 1 and 10, the 2nd between 11 and 30, and the 3rd between 31 and 60, you should do something like this (I plot in 2D for simplicity)
r = [1 0 0];
g = [0 1 0];
b = [0 0 1];
cmap = [r(ones(10,1),:); g(ones(20,1),:); b(ones(30,1),:)];
z1 = 1:10;
z2 = 11:30;
z3 = 31:60;
figure; hold on
plot(z1,'color',r)
plot(z2,'color',g)
plot(z3,'color',b)
colorbar
colormap(cmap)
More complex colormaps (i.e, more colors) can be constructed with different mixtures of red, green, and blue (http://www.mathworks.com/help/techdoc/ref/colorspec.html)