In matlab 2016b use a colormap to color the slices of a pie chart - matlab

This seems like it should be a lot easier than it is...
In matlab 2016b, I want to use a colormap to color the slices of a pie chart.
My data are three element vectors and might contain a zero. I have three colors in my colormap that need to be used in the order of the vector data.
For example:
data = [1 0 1];
my_cols = [1.0000 0.8398 0; 0.8594 0.0781 0.2344; 0.2539 0.4102 0.8789];
labels = {'','',''};
p = pie(data,labels);
p.Patch = my_cols;
I have tried all sorts of ways that have been previously suggested but it seems to not work with version 2016b.
Note that I need the first element of my data to always correspond to the first color in my colormap. I think Matlab colors slices based on size, but I don't want this.

I do not have 16b at hand. The following was done in 17a:
data = [1 0.5 1];
my_cols = [1.0000 0.8398 0; 0.8594 0.0781 0.2344; 0.2539 0.4102 0.8789];
labels = {'','',''};
p = pie(data,labels);
p(1).FaceColor = my_cols(1,:);
p(3).FaceColor = my_cols(2,:);
p(5).FaceColor = my_cols(3,:);
Explanation: pie returns 2 elements for each slice, the patch object and the corresponding string object. You must set the color for the patch objects, i.e. in your case p(1), p(3), and p(5).
Note that I changed your data input. With the zero in the vector you will get a warning and your variable dimensions are off.

Related

How to set arbitrary colors for bars in a 3D bar plot?

Say that I have a matrix Z with some values, and I want to illustrate it by a plotting the values in Z by height. The first solution comes to mind is a surface, but using surf and similar functions with small matrices doesn't look good.
So I thought about using something like a 3D bar plot with bar3. But the problem is that this function always sets the color by the group and not by height, and I can't get it to do so.
Here is an example:
Z = peaks(5);
subplot 121
surf(Z)
title('Surface look bad')
subplot 122
bar3(Z)
title('The color is not by height')
I tried to look for the color properties in the handles returned by bar3 (like CData and FaceColor) but got lost with all the values and how they relate to the bars themselves.
Ultimately, I would like to have a general solution that for 2 matrices Z and C I can create a 3D bar plot with bars in height given by Z and color given by C.
How can I do so?
The function bar3 returns a surface object, one for each group (i.e. one for each color), so all the bars in one group are essentially plotted as one 'broken' surface. This is explained very good in this answer, so I won't repeat it here.
Instead, I'll get to the solution for this specific problem. The relevant property of the surface is CData. When we create the bar plot, each surface's CData is assigned with a matrix in some size (we'll get to this) that is all equal one value. A different value for each surface. This is how the figure as a whole translates its color map to the color of the groups.
As written above (and elaborated in the linked answer), each group represented by a surface, so it takes a whole matrix to define the color at each point of the surface. The first thing we want to do is to get this matrix size:
Z = peaks(5);
bar_h = bar3(Z);
% we take only the first one, but they are all the same size:
cdata_sz = size(bar_h(1).CData)
cdata_sz =
30 4
CData has always 4 columns (see here why), and the number of rows is always 6*number of groups. This is because it takes 5 vertices to create one closed rectangle with an area object (the last vertex is like the first one) and one line is for spacing between the bars with NaNs, so they will look separated.
Next, we need to enlarge our original colormap (which is the same size of Z) to fit CData in the right way. Essentially, we just want to repeat the same value for all vertices that belong to the same bar. Assuming Z is also our color data (i.e. we color by height) we do:
z_color = repelem(Z,6,4)
Now we need to split our z_color to different cells in the number of our groups. Each cell will contain the coloring data for one surface object:
z_color = mat2cell(z_color,cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));
And finally, we apply the new color data to the bar plot:
set(bar_h,{'CData'},z_color.')
As a bonus, if we want to remove all zero values from our bar, it can be done easily by setting them to NaN:
Z(abs(Z)<eps) = nan;
C(isnan(Z)) = nan; % if we use a colormap C different from Z
All the above could be boiled down to this handy function:
function bar_h = Cbar3(Z,C,b,y)
% Z - The data
% C - CData (if other then Z values)
% b - Minimum absolute value to keep colored
% y - y-axis values to order the data by
if nargin<2, C = Z; end
if nargin<3 || isempty(b), b = 0; end
Z(abs(Z)<b) = nan;
C(isnan(Z)) = nan;
if nargin<4
bar_h = bar3(Z);
else
bar_h = bar3(y,Z);
end
cdata_sz = size(bar_h(1).CData);
z_color = repelem(C,6,4);
z_color = mat2cell(z_color,...
cdata_sz(1),ones(1,size(Z,2))*cdata_sz(2));
set(bar_h,{'CData'},z_color.')
end
Example of usage:
subplot 121
Z = peaks(30);
Cbar3(Z,Z,0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using height as color')
subplot 122
Cbar3(Z,rand(size(Z)),0.5);
pbaspect auto
shading flat % just to get a cleaner look
title('Cbar3 using random as color')
Result:
This is a partial answer.
The case of using the bar height as color is covered by the official MATLAB documentation. Essentially the example code boils down to:
function q45423394
hB = bar3(peaks(25)); colorbar;
for indE = 1:numel(hB)
hB(indE).CData = hB(indE).ZData;
end
All you need to do afterwards is make sure that the colormap is the one you want.
While I find EBH's solution aesthetically more pleasing, here there is a simpler solution: interpolation
z = peaks(5);
[x,y]=meshgrid(1:0.1:size(z,1),1:0.1:size(z,2));
zn=interp2(z,x,y,'nearest');
% plot it
surf(zn,'edgecolor','none','facecolor','interp')

Multiple colormaps using bar(z,'stacked')

I have read the article recommended by a post on another colormap thread http://www.mathworks.com/matlabcentral/answers/101346 and I understand the concept. I am having trouble understanding the values of CDATA when using the bar(z,'stacked') function.
I have one figure with a major axis plotted using cmap, and I have created and positioned a new axis for the bar chart and I want it to use cmap2.
For example, my code includes:
maps = colormap([cmap;cmap2]);
bH = bar(z,'stacked');
Where z = 25x10 (annual data for 10 years over 25 sites)
Now when I look at the CDATA
get(bH,'CDATA') A cell array is returned of size 1x10 with each cell containing the string 'scaled'.
Now if I look at the CDATA of each of the children
childH = get(bH,'children');
get(childH{i},'CDATA')
A matrix of size 25x10 is returned with every value equal.
e.g. childH{i}'s CDATA is a matrix of size 25x10 having all values = i
So how can I scale these to map to my colormap since
from the documentation above I need to perform:
m = size(colormap,1); % Number of colors in the current colormap
Data = get(H,'CData') % Where H is a handle to a surface or patch object
cmin = min(CData(:)); % Minimum color value
cmax = max(CData(:)); % Maximum color value
idx = min(m,round((m-1)*(CData-cmin)/(cmax-cmin))+1);
idx becomes min(m,nan) which is always m?
I really need help understanding this.
Am I missing something or is this function a special case?
First make sure that cmap2 has exactly the number of colors that you want to use, then get the barseries object to map into it directly. Something like:
childH = get(bH, 'children');
for a = 1:numel(childH)
C = get(childH{a}, 'FaceVertexCData');
C(:) = a+size(cmap, 1);
set(childH{a}, 'FaceVertexCData', C, 'CDataMapping', 'direct');
end

How to mark points in a plot that are over a specified value in Matlab?

Say I have a data set with x values of longitude and Y values of 1 to 100. How can I plot the whole data set and represent all Y values over 90 with a different symbol?
Thanks for the help!
The easiest way would be to plot the sets separately, and specify a different symbol for each set i.e.
plot(x(Y<=90),Y(Y<=90),'bx',x(Y>90),Y(Y>90),'bo');
You could also do different colors. The scatter function has the ability to specify a different color for each point with the syntax scatter(x,y,s,c). For your example, you could do:
% make data
rng(0,'twister'); theta = linspace(0,2*pi,150);
x = sin(theta) + 0.75*rand(1,150); x = x*100;
y = cos(theta) + 0.75*rand(1,150); y = y*100;
mask = y>90;
% plot with custom colors for each point
c = zeros(numel(x),3); % matrix of RGB colorspecs
c(mask,:) = repmat([1 0 0],nnz(mask),1); % red
c(~mask,:) = repmat([0 0 1],nnz(~mask),1); % blue
scatter(x,y,10,c,'+');
Or instead of and RGB colorspec matrix, you can index into the current colormap. This allows you to get a nice smooth variation with some value:
scatter(x,y,10,y+x,'o') % x+y is mapped to indexes into default colormap, jet(64)
You can combine this color mapping with the approach of separating the data into two sets to also get different markers. Split the data, plot the first set with scatter as above, hold on, and plot the second set with a different marker. For example,
cv = x+y; % or just y, but this is an interesting example
scatter(x(mask),y(mask),10,cv(mask),'+');
hold on
scatter(x(~mask),y(~mask),10,cv(~mask),'o');
The result is different marker styles, where '+' is used where y>90 and '+' elsewhere, and different colors, where color is determined by mapping the values of cv=x+y onto the current colormap. The idea here is to look at 2 different modes of variations, but you could just use cv=y.

Scatter plot grouped by row for a 31 x 3 2d array

I have a 2d array and I want to scatter plot the points grouped by row so that each row has a different symbol. This is my code so far, all the points are the same symbol so I can't tell which points are part of which row.
a = zeros (31,3);
for k = 0:30
y = 5*k
dent = [1 10 10 y]
a(k+1, [1 2 3]) = roots(dent)
end
t = 1:3
gscatter(real(a(:,t)),imag(a(:,t)));
You do not need a loop, you can exploit the gscatter options:
a = zeros (31,3);
for k = 0:30
y = 5*k;
dent = [1 10 10 y];
a(k+1, [1 2 3]) = roots(dent);
end
group = ones(size(a));
group(:,1) = group(:,1).*0;
group(:,3) = group(:,3).*2;
gscatter(real(a(:)),imag(a(:)),group(:),'brg','xo+');
You need an additional vector, group, which contains information on which points in your data-set belong to a specific group. This variable is very versatile, see it's documentation.
In your case, I suggest setting up a matrix that is 0 in the first column, 1 in the second and 2 in the third.
In the gscatter function call, reshape all your matrices into vectors using (:) (because gscatter only works with vectors.
The other two strings passed to gscatter:
'brg'
'xo+'
determine the color and shape of the symbols, respectively. Your plot then looks like this:
EDIT
For those users without access to the gscatter function, this is how it can be done using scatter:
s = 40;
hold on
COLORS='brg';
SYMBOLS='xo+';
for t=1:size(a,2)
scatter(real(a(:,t)),imag(a(:,t)),s,SYMBOLS(t),'MarkerEdgeColor',COLORS(t))
end
hold off
A few things to take note of:
to be used in this way, scatter needs a symbol-size, which was set to s = 40 in this example.
the symbols are stored in a string variable so that the can be called in the loop.
the same is true for the edge colors (face colors could also be specified, check the scatter documentation
when called in a loop, use hold to plot into the same figure (roughly speaking)
This is the output from the standard scatter plot:

Create a tailored colorbar in matlab

I have to create a map to show how far o how close some values are from a range and give them colors in consequence. Meanwhile, values that are within that range should have another different color.
For example: only the results that are within [-2 2] can be considered valid. For the other values, colors must show how far are from these limits (-3 lighter than -5, darker)
I've tried with colorbar but I'm not able to set up a self-defined color scale.
Any idea??
Thanks in advance!
You need to define a colormap for the range of values you have.
The colormap is N*3 matrix, defining the RGB values of each color.
See the example below, for a range -10:10 and valid values v1,v2:
v1=-3;
v2=+3;
a = -10:10;
graylevels=[...
linspace(0,1,abs(-10-v1)+1) , ...
ones(1, v2-v1-1) , ...
linspace(1,0,abs(10-v2)+1)];
c=repmat(graylevels , [3 1])';
figure;
imagesc(a);
colormap(c);
Here is some code that I just put together to demonstrate a simple means of creating your own lookup table and assigning values from it to the image that you're working with. I'm assuming that your results are in a 2D array and I just used randomly assigned values, but the concept is the same.
I mention the potentila use of HSV as a coloring scheme. Just note that, that requires you to have a m by n by 3 matrix. The top layer is the H - hue, 2nd being the S - saturation and the 3rd being the V or value (light/dark). Simply set the H and S to whatever values you want for the color and vary the V in a similar manner as shown below and you can get the varied light and dark color you want.
% This is just assuming a -10:10 range and randomly generating the values.
randmat = randi(20, 100);
randmat = randmat - 10;
% This should just be a black and white image. Black is negative and white is positive.
figure, imshow(randmat)
% Create your lookup table. This will illustrate having a non-uniform
% acceptable range, for funsies.
vMin = -3;
vMax = 2;
% I'm adding 10 here to account for the negative values since matlab
% doesn't like the negative indicies...
map = zeros(1,20); % initialized
map(vMin+10:vMax+10) = 1; % This will give the light color you want.
%linspace just simply returns a linearly spaced vector between the values
%you give it. The third term is just telling it how many values to return.
map(1:vMin+10) = linspace(0,1,length(map(1:vMin+10)));
map(vMax+10:end) = linspace(1,0,length(map(vMax+10:end)));
% The idea here is to incriment through each position in your results and
% assign the appropriate colorvalue from the map for visualization. You
% can certainly save it to a new matrix if you want to preserve the
% results!
for X = 1:size(randmat,1)
for Y = 1:size(randmat,2)
randmat(X,Y) = map(randmat(X,Y)+10);
end
end
figure, imshow(randmat)