Generate a heatmap in a set X, Y, Z with Z being the intensity of the color - matlab

I have a numerical set X, Y, Z and I would like to reproduce a heatmap with these values. The size of the bin is 20 x 20 and the range of the X and Y axes are from -150 to 150 with Z being the color. Within that bin it should contain the average of the Z values in that range.
In Origin contains this tool to make a heatmap with the average of the values, but I would like to do it in MATLAB. The graph I made in Origin and that I would like to do in MATLAB can be seen in figure 1.
I've tried something like
load xyz.dat
x = xyz(:,1);
y = xyz(:,2);
z = xyz(:,3);
tbl = table(x,y,z);
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
But it printed this warning
Warning: Error updating HeatmapChart. Values in the source table variable 'x' are not grouped into discrete categories. Use the discretize function to group your values.

heatmap expects discrete x and y values. You goal is to bin your x and y's into 20 discrete bins and average the z values for each bin, but x and y themselves are continuous.
To achieve your goal take the advice of the warning message and use discretize to bin your x and y values.
% I used the following stand ins for your data:
% x = rand(540, 1);
% y = rand(540, 1);
n_bins = 20; % Number of grid cells
% If you want the width of the grid cell to be 20, use
% n_bins = (max(x) - min(x)) / 20;
x_discrete = discretize(x, n_bins);
y_discrete = discretize(y, n_bins);
tbl = table(X_discrete,y_discrete,z, 'VariableNames', {'x', 'y', 'z'});
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
Note: Why was this not a problem with the sample data?
Using your sample data,
x = [49.8, 14.5, -60.7, -21.6, -10.6];
y = [45.3, 7.9, 23.9, -58.5, -55.4];
z = [0.2 , -0.06, -0.35, -0.15, -0.08];
tbl = table(x',y',z', 'VariableNames', {'x', 'y', 'z'});
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
Does not throw an error because it treats each x and y value as a separate category. heatmap uses a call to categorical to transform the continuous values into categorical values.
Aside from not really providing the output you want (each point would be its own box instead of averaging grid cells), categorical seems to have a limit in how many categorical values it will create. I wasn't able to find any documentation about exactly what that limit is, but by experimentation, it tops out in the mid 200's. Since your vectors are 540 elements long, you get the warning about using discretize instead.

Related

How can I find a specific point in a figure in MATLAB?

I want a specific value in the figure in MATLAB. I put the black circle and arrow manually through the figure insert option. But How can I set the value now?
I want the x-axes values that are exactly 90% of each CDF curve.
here I am attaching my MatLab figure in jpg mode.
I would use interp1 to find the value. I'll assume that your x variable is called x and your cdf value is called c. You can then use code like this to get the x value where c = 0.9. This will work even if you don't have a cdf value at exactly 0.9
x_at_0p9 = interp1(c, x, 0.9);
You plotted those figures by using:
plot(X,Y)
So, your problem is to find x_0 value that makes Y = 0.9.
You can do this:
ii = (Y==0.9) % finding index
x_0 = X(ii) % using index to get x_0 value
Of course this will only work if your Y vector has exactly the 0.9 value.
As this is not always the case you may want to get the x_0 value that first makes Y to be greater or equal than 0.9.
Then you can do this:
ii = find(Y>=0.9, 1) % finding index
x_0 = X(ii) % using index to get x_0 value
Assuming that your values are x and Y (where x is a vector and the same for all curves) and Y is a matrix with the same number of rows and as many columns as there are curves; you just need to find the first point where Y exceeds 0.9:
x = (0:0.01:pi/2)'; % time vector
Y = sin(x*rand(1,3))*10; % value matrix
% where does the values exceed 90%?
lg = Y>= 0.9;
% allocate memory
XY = NaN(2,size(Y,2));
for i = 1:size(Y,2)
% find first entry of a column, which is 1 | this is an index
idx = find(lg(:,i),1);
XY(:,i) = [x(idx);Y(idx,i)];
end
plot(x,Y, XY(1,:),XY(2,:), 'o')

How can I plot only a specific range of a curve, while having axis with higher limits?

I need to plot a section of curve, using MATLAB. But I need my axes to be larger than the part I am showing.
For example, I have the following data:
x = 0:50
y = 0.5*x
I would like to plot this data from x=0 to x=20, with xlim([0 50]).
Just to clarify, I do not want to change the range of values of x, I just want to change what is shown on the graph.
Say you have some data
x = 0:50;
y = 0.5*x;
and you would like to plot only a part of it, say everything where x<=20. You can do as follows:
index = x <= 20;
plot(x(index), y(index))
xlim(x([1,end])) % set the x-axis limit to the range of all your `x` values
ylim([min(y),max(y)]) % set the y-axis limit to the range of all your `y` values
Do this:
x = 0:20
y = 0.5*x
plot(x,y)
xlim([0 50]) % This will set x-axis to the desired range
ylim([min(y) max(y)])

Interpolate/fill in missing cells in truth-value grid in MATLAB

Consider the 37x101 matrix below:
Each black cell has value 1, the rest of the cells value 0. I would like to fill in the "gaps" by means of cubic spline interpolation, as well as scaling the y axis from 37 to 181. The latter can be done by using the interp1 function, as in:
interp1(37,matrix,181,'pchip')
However, the outcome is now interpolated along the y-axis, but the gaps remain:
I don't want to interpolate along the x-axis, because I want the final matrix to have dimension 181 x 101. I just want to fill in the gaps using the existing cells (of the 181 x 101 matrix).
How can the original matrix (top), be scaled from 37 x 101 to 181 x 101 (without the "smoothing" in the second image), as well as filling in the gaps using some kind of spline interpolation as if this was a proper function?
It appears that your truth value grid has a single one where the true value is in each row. If the true/1 values do in fact create a line through the image, I would recommend parametrize the line with respect to t so that y = fy(t) and x = fx(t). If you're not familiar with this you can find some parametrization info on youtube tutorials or google. The main idea is that if you have , say a truth table that looks like this:
Then you could plot the the location of each pixel with respect to another variable, t and then use interp1(...) on each of these individually. In my case I defined the x and y values as follows:
n = 32;
rand('seed', 1982);
y_orig = 1:n;
x_orig = ceil(n*sin(y_orig/n*pi));
So I can plot as:
t1 = linspace(0,1, n);
plot(t1,x_orig, 'r', 'linewidth', 3);
hold all
plot(t1,y_orig, 'b', 'linewidth', 3);
legend('X', 'Y')
Note that I can get any truth value I want just by using interp1 like this (if you wanted to find the value half way between the 5th and 6th row):
desiredY = 5.5;
t= 1:n;
truthValue= interp1(t, x_orig, desiredY, 'cubic')
But we are looking to make a new image so I chose a more convenient parametrization of t between zero and one. Unfortunately, you may not have x and y off hand, so we need to pull them out of the image. Assuming you have a single true/1 value in each row we can yank out the values with max(...):
[maxVals, x1] = max(data,[],2);
x1(maxVals == 0) = [];
y1 = find(maxVals ~= 0);
Some form of find on each row would also work. If you have a truth value in each row then y1 should equal 1:n. The max function returns the index of the max in dimension 2 in the second return value. I use the next two lines to remove any entries where there truth table was empty (max is zero) and then y1 = 1:n minus those entries that were empty.
A quick and dirty way to get lots of points along this line is:
t2 = linspace(0,1,1024);
x2 = interp1(t1, x1, t2, 'cubic');
y2 = interp1(t1, y1, t2, 'cubic');
I can then plot the original points/image and this newly discovered finer line together like this:
imagesc(data);
hold all;
plot(x2,y2, 'linewidth', 2);
axis image
colormap(flipud(colormap(gray)));
To get this:
Finally, you can quickly turn this into a new image by scaling the parametrization up. My method is not particularly efficient for clarity:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1)*scaleValue + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
Which results in:
Note that this table has connected all the points and you now have multiple true/1's in each row. This is because I chose a very small step size for t2. You could fix this by either choosing t2 smarter, skipping multiple values in each row, or average the location of each indices in each row. Or ignoring this issue.
To fix t2 with the scaling value, you could use
t2 = linspace(0,1,n*scaleValue);
to get only one true/1 per row in the above code.
Also, if you want to only scale one dimension, you could do it like this:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1) + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue,n);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
I see this as a bitmap, so why not a clamped blur?
I=yourmatrixhere
%gaussian blur
% it looks like bump-to-bump distance is 3 empties
% therefore hsize should be about 7
% it looks like bump vertical size is about 4
% therefore simga should be about 10
hsize=[3 3];
sigma = 10;
h=fspecial('gaussian',hsize,sigma)
I2=imfilter(I,h,'replicate');
At this point you have spread information to adjacent columns, but you need to "tidy up" from continuous to binary.
%threshold
th = 0.25;
I3=zeros(size(I));
ind=find(I>=th);
I3(ind)=1;
At this point, I3 is your matrix of interest to do the "erode" or interpolation.

Matlab surface plot not giving desired results

I am charting the following data:
a=[...
0.1, 0.7, 0.00284643369242828;...
0.1, 0.71, 0.00284643369242828;...]
such that column 1 never surpasses approximately 10
also such that column 2 goes from .7 to 1.
Column 3 seems ok
When i chart my surface using surf(a) it looks like this:
it appears not to be properly considering what should be x and y.
anything seem weird there?
I think you need to try one of two things: either break out your height column into its own rectangular matrix Z and use surf(Z) to plot each point relative to its location in the matrix (so your x- and y-axes will not be scaled the way you want), or you can put your desired x- and y-coordinates in their own vectors, and plot the matrix Z (defined at every point (xi, yj) for all i in N and j in M where x is N elements long and y is M elements long) with surf(x,y,Z).
x = 0.1:0.1:10; % or whatever increment you need
y = 0.7:0.01:1; % or whatever increment you need
Z = zeros(length(x),length(y); % initialized to the correct size, fill with data
I think you are going to have to regenerate your Z-data so that it is in a rectangular matrix that is (elements in x) by (elements in y) in dimension.
EDIT: You do not need to recreate your data. If you know that you have n unique elements in x and m unique elements in y, then you can use:
X = reshape(data(:,1),m,n);
Y = reshape(data(:,2),m,n);
Z = reshape(data(:,3),m,n);
surf(X,Y,Z);
And that should give you what you are looking for.

MATLAB - Pixelize a plot and make it into a heatmap

I have a matrix with x and y coordinates as well as the temperature values for each of my data points. When I plot this in a scatter plot, some of the data points will obscure others and therefore, the plot will not give a true representation of how the temperature varies in my data set.
To fix this, I would like to decrease the resolution of my graph and create pixels which represent the average temperature for all data points within the area of the pixel. Another way to think about the problem that I need to put a grid over the current plot and average the values within each segment of the grid.
I have found this thread - Generate a heatmap in MatPlotLib using a scatter data set - which shows how to use python to achieve the end result that I want. However, my current code is in MATLAB and even though I have tried different suggestions such as heatmap, contourf and imagesc, I can't get the result I want.
You can "reduce the resolution" of your data using accumarray, where you specify which output "bin" each point should go in and specify that you wish to take a mean over all points in that bin.
Some example data:
% make points that overlap a lot
n = 10000
% NOTE: your points do not need to be sorted.
% I only sorted so we can visually see if the code worked,
% see the below plot
Xs = sort(rand(n, 1));
Ys = rand(n, 1);
temps = sort(rand(n, 1));
% plot
colormap("hot")
scatter(Xs, Ys, 8, temps)
(I only sorted by Xs and temps in order to get the stripy pattern above so that we can visually verify if the "reduced resolution" worked)
Now, suppose I want to decrease the resolution of my data by getting just one point per 0.05 units in the X and Y direction, being the average of all points in that square (so since my X and Y go from 0 to 1, I'll get 20*20 points total).
% group into bins of 0.05
binsize = 0.05;
% create the bins
xbins = 0:binsize:1;
ybins = 0:binsize:1;
I use histc to work out which bin each X and Y is in (note - in this case since the bins are regular I could also do idxx = floor((Xs - xbins(1))/binsize) + 1)
% work out which bin each X and Y is in (idxx, idxy)
[nx, idxx] = histc(Xs, xbins);
[ny, idxy] = histc(Ys, ybins);
Then I use accumarray to do a mean of temps within each bin:
% calculate mean in each direction
out = accumarray([idxy idxx], temps', [], #mean);
(Note - this means that the point in temps(i) belongs to the "pixel" (of our output matrix) at row idxy(1) column idxx(1). I did [idxy idxx] as opposed to [idxx idxy] so that the resulting matrix has Y == rows and X == columns))
You can plot like this:
% PLOT
imagesc(xbins, ybins, out)
set(gca, 'YDir', 'normal') % flip Y axis back to normal
Or as a scatter plot like this (I plot each point in the midpoint of the 'pixel', and drew the original data points on too for comparison):
xx = xbins(1:(end - 1)) + binsize/2;
yy = ybins(1:(end - 1)) + binsize/2;
[xx, yy] = meshgrid(xx, yy);
scatter(Xs, Ys, 2, temps);
hold on;
scatter(xx(:), yy(:), 20, out(:));