Excluding values from surf plot - matlab

Suppose we have a surf plot like this:
A = round(peaks,0);
surf(A)
Is there any way to modify the colormap so that it excludes all values in A that are equal to 0, as if they were NaN?
Colouring 0's as white without affecting the rest of the colourmap would be another acceptable solution.

You mentioned the solution: set all 0s to nan:
A = round(peaks, 0);
A(A==0)=nan; % in-place logical mask on zero entries
surf(A)
% The same, but with a temporary variable, thus not modifying A
B = A;
B(B==0) = nan;
surf(B)
Results in (R2007a):
If you do not want to modify A or use a temporary variable, you'll need to "cut a hole" in your colour map
A = round(peaks);
unique_vals = unique(A); % Get the unique values
cmap = jet(numel(unique_vals)); % Set-up your colour map
zero_idx = find(unique_vals==0); % find 0 index
cmap(zero_idx,:) = 1; % all ones = white. Nan would make it black
surf(A, 'EdgeColor', 'none')
colormap(cmap) % Use our colour map with hole
NB: the 'EdgeColor', 'none' pair is used to remove the edges of each patch, such that you don't see a "grid" where the values are 0.

Related

Changing the transparency of patches based on their value

I have a matrix containing integers ranging from 0 to 20. The regions with the same integer are formed in a quite random fashion. I want to color code each integer with a different degree of grayscale. So for example, for regions with 0, I want them to be white, then for regions with 1, 5% transparency of black, for regions with 2, 10% transparency of black,..., for regions with 20, totally black.
I've tried to get the coordinates of each region, but that does not seem to be efficient for my matrix.
How can I change the transparency of my regions based on their value?
You can use surface() with its FaceAlpha name-value pair. It only accepts scalars though, so you'll have to plot each of your patches separately, something along the lines of:
% Create a random matrix with values 1 - 20 for plotting
my_matrix = ceil(20* rand(30));
% Build its x and y grids
[x, y] = meshgrid(1:30, 1:30);
% Open and hold the figure
figure;
hold on
for ii = unique(my_matrix)
% Create a mask for the current value
temp_colour = nan(size(my_matrix));
temp_colour(my_matrix == ii) = 1;
surface(x, y, ones(size(my_matrix)), ...
temp_colour, ...
'EdgeColor', 'none', ...
'FaceAlpha', 1 - ii/max(my_matrix, 'all'))
colormap(gray)
end
Results in, on my R2007b,
If you're good with just a gray-scale image, rather than true transparency, you can simply use imagesc(my_matrix); colormap(gray).

Matlab: 1D array to RGB triplets with colormap

I'm trying to draw a set of rectangles, each with a fill color representing some value between 0 and 1. Ideally, I would like to use any standard colormap.
Note that the rectangles are not placed in a nice grid, so using imagesc, surf, or similar seems unpractical. Also, the scatter function does not seem to allow me to assign a custom marker shape. Hence, I'm stuck to plotting a bunch of Rectangles in a for-loop and assigning a FillColor by hand.
What's the most efficient way to compute RGB triplets from the scalar values? I've been unable to find a function along the lines of [r,g,b] = val2rgb(value,colormap). Right now, I've built a function which computes 'jet' values, after inspecting rgbplot(jet). This seems a bit silly. I could, of course, obtain values from an arbitrary colormap by interpolation, but this would be slow for large datasets.
So, what would an efficient [r,g,b] = val2rgb(value,colormap) look like?
You have another way to handle it: Draw your rectangles using patch or fill specifying the color scale value, C, as the third parameter. Then you can add and adjust the colorbar:
x = [1,3,3,1,1];
y = [1,1,2,2,1];
figure
for ii = 1:10
patch(x + 4 * rand(1), y + 2 * rand(1), rand(1), 'EdgeColor', 'none')
end
colorbar
With this output:
I think erfan's patch solution is much more elegant and flexible than my rectangle approach.
Anyway, for those who seek to convert scalars to RGB triplets, I'll add my final thoughts on the issue. My approach to the problem was wrong: colors should be drawn from the closest match in the colormap without interpolation. The solution becomes trivial; I've added some code for those who stumble upon this issue in the future.
% generate some data
x = randn(1,1000);
% pick a range of values that should map to full color scale
c_range = [-1 1];
% pick a colormap
colormap('jet');
% get colormap data
cmap = colormap;
% get the number of rows in the colormap
cmap_size = size(cmap,1);
% translate x values to colormap indices
x_index = ceil( (x - c_range(1)) .* cmap_size ./ (c_range(2) - c_range(1)) );
% limit indices to array bounds
x_index = max(x_index,1);
x_index = min(x_index,cmap_size);
% read rgb values from colormap
x_rgb = cmap(x_index,:);
% plot rgb breakdown of x values; this should fall onto rgbplot(colormap)
hold on;
plot(x,x_rgb(:,1),'ro');
plot(x,x_rgb(:,2),'go');
plot(x,x_rgb(:,3),'bo');
axis([c_range 0 1]);
xlabel('Value');
ylabel('RGB component');
With the following result:

Adding colors to lines of a stairstep plot

I am trying to plot a hypnogram (graph that shows sleep cycles) and am currently using stairstep function to plot it. Below is a sample data since the one I am working with is huge:
X = linspace(0,4*pi,10);
Y = sin(X);
stairs(X,Y)
How do I make the lines of every ticks/score on the y-axis have a unique color? Which looks something like this:
One way to do it would be to segregate your data into as many dataset as your have flat levels, then plot all these data sets with the required properties.
There is however a way to keep the original dataset into one piece. If we consider your initial example data:
X = linspace(0,4*pi,10);
Y = sin(X);
step 1: recreate a "stair" like data set
Then by recombining the elements of X and Y we can obtain the exact same output than with the stairs function:
x = reshape( [X;X], 1,[] ); x(1) = [] ; % duplicate each element, remove the first one
y = reshape( [Y;Y], 1,[] ); y(end) = [] ; % duplicate each element, remove the lastone
hp = plot(x,y) ;
step 2: Use patch to be able to specify level colors
The patch object has many option for colouring faces, vertex and edges. The default patch object will try to close any profile given in coordinate by joining the first and last point. To override this behaviour, you just need to add a NaN element to the end of the coordinate set and patch will produce a simple line (but all the colouring options remain !).
To determine how many levels and how many colors we will need, we use the function unique. This will tell us how many unique levels exist in the data, and also we can associate each level with an index which will point to the color map.
%% Basic level colored line patch
% create profile for patch object
x = reshape([X;X],1,[]); x(1) = [] ; % same as above to get a "stairs" shape
y = reshape([Y;Y],1,[]); y(end) = [] ; % idem
xp = [x,NaN] ; % add NaN in last position so the patch does not close the profile
yp = [y,NaN]; % idem
% prepare colour informations
[uy,~,colidx] = unique(Y) ;
ncolor = length(uy) ; % Number of unique level
colormap(hsv(ncolor)) % assign a colormap with this number of color
% create the color matrix wich will be sent to the patch object
% same method of interleaving than for the X and Y coordinates
cd = reshape([colidx.';colidx.'],1,[]);
hp = patch(xp,yp,cd,'EdgeColor','interp','LineWidth',2) ;
colorbar
Yes! ... now our flat levels have a colour corresponding to them ... but wait, those pesky vertical lines are still there and polluting the graph. Could we colour them in a different way? Unfortunately no. No worries however, there is still a way to make them completely disappear ...
step 3: Use NaN to disable some segments
Those NaN will come to the rescue again. Any segment defined with a NaN will not be plotted by graphic functions (be it plot, patch, surf or any other ...). So what we can do is again interleave some NaN in the original coordinate set so only the horizontal lines will be rendered. Once the patch is created, we can build a second, "opposite", coordinate set where only the vertical lines are visible. For this second set, since we do not need fancy colouring, we can simply render them with plot (but you could also build a specific patch for that too if you wanted to colour them differently).
%% invisible vertical line patch + dashed vertical lines
% prepare profile points, interleaving NaN between each pair
vnan = NaN(size(X)) ;
xp = reshape([X;vnan;X],1,[]); xp([1:2 end]) = [] ;
yp = reshape([Y;Y;vnan],1,[]); yp(end-2:end) = [] ;
% prepare the vertical lines, same method but we interleave the NaN at one
% element offset
xv = reshape([X;X;vnan],1,[]); xv([1:3 end]) = [] ;
yv = reshape([Y;vnan;Y],1,[]); yv([1:2 end-1:end]) = [] ;
% prepare colormap and color matrix (same method than above)
[uy,~,colidx] = unique(Y) ;
ncolor = length(uy) ; % Number of unique level
colormap(hsv(ncolor)) % assign a colormap with this number of color
% create the color matrix wich will be sent to the patch object
% same method of interleaving than for the X and Y coordinates
cd = reshape([colidx.';colidx.';vnan],1,[]); cd(end-2:end) = [] ;
% draw the patch (without vertical lines)
hp = patch(xp,yp,cd,'EdgeColor','flat','LineWidth',2) ;
% add the vertical dotted lines
hold on
hv = plot(xv,yv,':k') ;
% add a label centered colorbar
colorbar('Ticks',((1:ncolor)+.5)*ncolor/(ncolor+1),'TickLabels',sprintf('level %02d\n',1:ncolor))
I have used the hsv colormap in the last example because your example seems to indicate that you do not need gradually progressing colors. You could also define a custom colormap with the exact color you want for each level (but that would be another topic, already covered many time if you search for it on Stack Overflow).
Happy R.E.M. sleeping !
Below code is not that efficient, but works well.
Basically, it draws line by line from left to right.
Firstly, generate sample data
num_stage = 6;
% generate sample point
x = linspace(0,1,1000)';
% generate its stage
y = round((sin(pi*x)+1)*(num_stage-1)/2)/(num_stage-1);
stage = unique(y); % find value of each stage
color_sample = rand(num_stage,3); % set color sample
Then we can draw like this
idx = find([1;diff(y)]); % find stage change
idx(end+1) = length(x)+1; % add last point
% display routine
figure;
% left end stage
k = 1;
% find current stage level
c = find(stage == y(idx(k)));
% plot bold line
plot(x([idx(k),idx(k+1)-1]),y(idx(k))*ones(2,1),'color',color_sample(c,:),'linewidth',5);
hold on;
for k = 2 : length(idx)-1
% find current stage level
c = find(stage == y(idx(k)));
% plot dashed line from left stage to current stage
plot(x([idx(k)-1,idx(k)]),[y(idx(k-1));y(idx(k))],'--','color',[0.7,0.7,0.7]);
% plot bold line for current stage with specified color
plot(x([idx(k),idx(k+1)-1]),y(idx(k))*ones(2,1),'color',color_sample(c,:),'linewidth',5);
end
% set x-axis
set(gca,'xlim',[x(1),x(end)]);
Following is result
Use if statement and divide it blocks. Check the criteria of Y-axis to be in a certain range and if it falls in that range, plot it there using the colors you want. For example if (y>1) plot(x,y,'r') else if (y some range) plot(x,y,'b'). Hope it helps

Using different intensities of a specific color for contour plots

This question is in reference to visualization of EM clustering(or K-means) of 2D gaussian data. Say, I have displayed 3 clusters obtained from EM in a scatter plot with 3 different colors(say r,g,b) for the data samples of 3 clusters. Now I want to plot elliptical contours on top of this. I don't want the color of each of the three countours vary over entire colorspectrum from r to b. For contours of cluster 1, I want varying intensities of red, for cluster 2, I want varying intensities of blue and same of cluster 3. I have set the number of concentric contours to be 5 and tried passing a Color array as follows, but it did not work.
ColorVec = ['r' ; 'g' ; 'b' ; 'm' ; 'c' ; 'y'; 'b'];
String2RBG = #(C)rem(floor((strfind('kbgcrmyw', C) - 1) * [0.25 0.5 1]), 2);
x = -3:0.1:4;
y = -3:0.1:4;
[X,Y] = meshgrid(x,y);
for k=1:numberOfClusters
Z = mvnpdf([X(:) Y(:)],estimatedMu(k,:),estimatedSigma{k});
Z = reshape(Z,length(y),length(x));
ColorVecConcentricCountours = [String2RBG(ColorVec(k));String2RBG(ColorVec(k))/1.2;String2RBG(ColorVec(k))/1.4;String2RBG(ColorVec(k))/1.6;String2RBG(ColorVec(k))/1.8];
contour(X,Y,Z,5,'color',ColorVecConcentricCountours);hold on;
end
Use of ColorVecConcentricCountours throws an error, but if I give ColorVec(k), it gives a single shade of r, g or b for all 5 contours which is not what I want.
Contour plots are groups of patch objects. Patch objects have an 'EdgeColor' property, which is what sets what we'd call the line color of the contours. You can set the edge colors of all contour objects in the figure with the contourcmap function, but since you want separate control over each of the groups of lines that won't work. What you can do, however, is to address the patch objects themselves and change the edge color of each one separately.
To start, I have written functions called green, cyan, and the rest of the RGB + CYM colors that allow me to use those as colormaps in addition to the built-in ones. The green function looks like this:
function G = green(m)
%GREEN Green Colormap
% GREEN(M) is an M-by-3 matrix colormap for increasing red intensity.
% GREEN, by itself, is the same length as the current figure's
% colormap. If no figure exists, MATLAB creates one.
%
% See also RED, JET, HSV, HOT, PINK, FLAG, COLORMAP, RGBPLOT.
if nargin < 1
m = size(get(gcf,'colormap'),1);
end
G = zeros(m,3);
G(:,2) = (0:(1/(m-1)):1);
You can take a look at the built-in functions to see how this one is similar. To run the code below you'll need a cyan function as well (or change that function call to whatever colormap function you'd like).
Armed with a couple of contour plots put into the same axes and their handles (H1 and H2 below) we can pull out the levels the contours are drawn at with the contour group's LevelList property, yielding a vector of each contour level. Each of the contour patch objects (children of the group) has the level that line was drawn at saved in the patch object's UserData property. We can use the group's LevelList property to make a matrix of colors we want to use, using the colormap function call, then the position of the contour line's level in the LevelList vector is the row of the colormap that we want to use to color that line. Change the EdgeColor of the patch object to that color for each line and we're good to go. Two groups are drawn here to show how to get two contour groups with different colors.
figure()
[C1, H1] = contour(linspace(-50, 0, 50), linspace(-50, 0, 50), peaks(50), 50);
hold on
[C2, H2] = contour(linspace(0, 50, 50), linspace(0, 50, 50), peaks(50), 20);
hold off
axis([-50 50 -50 50]);
levels1 = get(H1, 'LevelList');
cmap1 = green(numel(levels1));
child1 = get(H1, 'Children');
for m = 1:numel(child1)
cmapHere = cmap1(levels1 == get(child1(m), 'UserData'), :);
set(child1(m), 'EdgeColor', cmapHere);
end
levels2 = get(H2, 'LevelList');
cmap2 = cyan(numel(levels2));
child2 = get(H2, 'Children');
for m = 1:numel(child2)
cmapHere = cmap2(levels2 == get(child2(m), 'UserData'), :);
set(child2(m), 'EdgeColor', cmapHere);
end

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)