plz help!
i have a dataset with the format of
x y z p
-0.574142 -0.818671 0.011756 0.000440
-0.364919 0.184603 0.912555 0.000324
-0.990822 -0.022168 0.133345 0.000419
-0.983317 -0.089955 0.158099 0.000417
-0.493497 0.474422 -0.728961 0.000501
-0.789287 -0.566719 0.236336 0.000413
0.293932 0.520691 -0.801551 0.000510
and they are random points on a 3d sphere, with the p value (the fourth column) representing the "heat" of that location, the actual datafile is with 50 such points.
i want to plot the surface of the sphere into a heatplot, not each point being colored.
i've searched for lots of post, all of them would require some kind of isoline, but in my case there isn't any. is it possible still using gnuplot to do heatmap? i'm also opened to any other way.
btw im running linux with newest gnuplot, the data are generated by c
ps: i understand theres a way of making data into isolines and then use gnuplot with pm3d, but i cant do that because all my x data is randomized, hence there is no isoline.
Note: This answer is only relevant to the newest gnuplot, version 5.4
Gnuplot version 5.4 can work in a 3D grid space of voxels, The voxel values can be used to hold a 4th dimension of data. I.e voxel(x,y,z) = value
This is a very new feature and could be improved, so your question serves as a nice example to show what it can and cannot do right now.
$data << EOD
x y z p
-0.574142 -0.818671 0.011756 0.000440
-0.364919 0.184603 0.912555 0.000324
-0.990822 -0.022168 0.133345 0.000419
-0.983317 -0.089955 0.158099 0.000417
-0.493497 0.474422 -0.728961 0.000501
-0.789287 -0.566719 0.236336 0.000413
0.293932 0.520691 -0.801551 0.000510
EOD
#
# Generate a heatmap on the surface of a sphere
#
set view equal xyz
set view 78, 246
set xyplane at 0
unset key
set xtics ("x" 0); set ytics ("y" 0); set ztics ("z" 1)
set hidden3d # only relevant to axis tics and labels
#
# Use spherical mapping to generate a set of points describing the surface.
# Then return to Cartesian coordinates to handle voxels.
#
set mapping spherical
set angle degrees
set samples 51
set isosamples 101
set urange [-90:90]
set vrange [0:360]
set table $surface
splot '++' using 1:2:(1.0) with points
unset table
set mapping cartesian
# define 100 x 100 x 100 voxel grid
rlow = -1.1; rhigh = 1.1
set xrange [rlow:rhigh]; set yrange [rlow:rhigh]; set zrange [rlow:rhigh]
set vgrid $vdensity size 100
vclear $vdensity
# mark voxels in a spherical region with radius <near> around each point in $data
# Note that values are summed rather than averaged
near = 0.1
vfill $data skip 1 using 1:2:3:(near):($4)
show vgrid
# color range should be found automatically but it isn't
set cbtics
set colorbox user origin screen 0.8, screen 0.2
set cbrange [ 0 : 0.001 ]
set palette cubehelix negative # colors from cbmin white -> cbmax dark
set pm3d depthorder
set pm3d lighting primary 0.4 specular 0.1
set title "Sphere surface is colored by the value of the nearest point[s] in $data"
splot $surface using 1:2:3:(voxel($1,$2,$3)) with pm3d
Related
I am working with a biological model of the distribution of microbial biomass (b1) on a 2D grid. From the biomass a protein (p1) is produced. The biomass diffuses over the grid, while the protein does not. Only if a certain amount of protein is produced (p > p_lim), the biomass is supposed to diffuse.
I try to implement this by using a dummy cell variable z multiplied with the diffusion coefficient and setting it from 0 to 1 only in cells where p > p_lim.
The condition works fine and when the critical amount of p is reached in a cell, z is set to 1, and diffusion happens. However, the diffusion still does not work with the rate I would like, because to calculate diffusion, the face variable, not the value of the cell itself is used. The faces of z are always a mean of the cell with z=1 and its neighboring cells with z=0. I I, however, would like the diffusion to work at its original rate even if the neighbouring cell is still at p < p_lim.
So, my question is: Can i somehow access a faceVariable and change it? For example, set a face to 1 if any neigboring cell has reached p1 > p_lim? I guess this is not a proper mathematical thing to do, but I couldn't think of another way to simulate this problem.
I will show a very reduced form of my model below. In any case, I thank you very much for your time!
##### produce mesh
nx= 5.
ny= nx
dx = 1.
dy = dx
L = nx*dx
mesh = Grid2D(nx=nx,ny=ny,dx=dx,dy=dy)
#parameters
h1 = 0.5 # production rate of p
Db = 10. # diffusion coeff of b
p_lim=0.1
# cell variables
z = CellVariable(name="z",mesh=mesh,value=0.)
b1 = CellVariable(name="b1",mesh=mesh,hasOld=True,value=0.)
p1= CellVariable(name="p1",mesh=mesh,hasOld=True,value=0.)
# equations
eqb1 = (TransientTerm(var=b1)== DiffusionTerm(var=b1,coeff=Db*z.arithmeticFaceValue)-ImplicitSourceTerm(var=b1,coeff=h1))
eqp1 = (TransientTerm(var=p1)==ImplicitSourceTerm(var=b1,coeff=h1))
# set b1 to 10. in the center of the grid
b1.setValue(10.,where=((x>2.)&(x<3.)&(y>2.)&(y<3.)))
vi=Viewer(vars=(b1,p1),FIPY_VIEWER="matplotlib")
eq = eqb1 & eqp1
from builtins import range
for t in range(10):
b1.updateOld()
p1.updateOld()
z.setValue(z + 0.1,where=((p1>=p_lim) & (z < 1.)))
eq.solve(dt=0.1)
vi.plot()
In addition to .arithmeticFaceValue, FiPy provides other interpolators between cell and face values, such as .harmonicFaceValue and .minmodFaceValue.
These properties are implemented using subclasses of _CellToFaceVariable, specifically _ArithmeticCellToFaceVariable, _HarmonicCellToFaceVariable, and _MinmodCellToFaceVariable.
You can also make a custom interpolator by subclassing _CellToFaceVariable. Two such examples are _LevelSetDiffusionVariable and ScharfetterGummelFaceVariable (neither is well documented, I'm afraid).
You need to override the _calc_() method to provide your custom calculation. This method takes three arguments:
alpha: an array of the ratio (0-1) of the distance from the face to the cell on one side, relative to the distance from distance from the cell on the other side to the cell on the first side
id1: an array of indices of the cells on one side of the face
id2: an array of indices of the cells on the other side of the face
Note: You can ignore any clause if inline.doInline: and look at the _calc_() method defined under the else: clause.
In Matlab:
How do I modify plot(x,y,'o'), where x=1:10 and y=ones(1,10), such that each point in the plot will have a random shape?
And how can I give it colors chosen from a scheme where the value at x=1 is the darkest blue, and x=10 is red (namely some sort of heat map)?
Can this be done without using loops? Perhaps I should replace "plot" with a different function for this purpose (like "scatter"? I don't know...)? The reason is that I am plotting this inside another loop, which is already very long, so I am interested in keeping the running-time short.
Thanks!
First, the plain code:
x = 1:20;
nx = numel(x);
y = ones(1, nx);
% Color map
cm = [linspace(0, 1, nx).' zeros(nx, 1) linspace(1, 0, nx).'];
% Possible markers
m = 'o+*.xsd^vph<>';
nm = numel(m);
figure(1);
hold on;
for k = 1:nx
plot(x(k), y(k), ...
'MarkerSize', 12, ...
'Marker', m(ceil(nm * (rand()))), ...
'MarkerFaceColor', cm(k, :), ...
'MarkerEdgeColor', cm(k, :) ...
);
end
hold off;
And, the output:
Most of this can be found in the MATLAB help for the plot command, at the Specify Line Width, Marker Size, and Marker Color section. Colormaps are simply n x 3 matrices with RGB values ranging from 0 to 1. So, I interpreted the darkest blue as [0 0 1], whereas plain red is [1 0 0]. Now, you just need a linear "interpolation" between those two for n values. Shuffling the marker type is done by simple rand. (One could generate some rand vector with size n beforehand, of course.) I'm not totally sure, if one can put all of these in one single plot command, but I'm highly sceptical. Thus, using a loop was the easiest way right now.
I have a cell array called output. Output contains matrices of size 1024 x 1024, type = double, grayscale. I would like to plot each matrix and its corresponding histogram on a single plot. Here is what I have so far:
for i = 1:size(output,2)
figure
subplot(2,1,1)
imagesc(output{1,i});
colormap('gray')
colorbar;
title(num2str(dinfo(i).name))
subplot(2,1,2)
[pixelCount, grayLevels] = imhist(output{1,i});
bar(pixelCount);
title('Histogram of original image');
xlim([0 grayLevels(end)]); % Scale x axis manually.
grid on;
end
The plot I get, however, seems to be faulty... I was expecting a distribution of bars.
I am somewhat lost at how to proceed, any help or suggestions would be appreciated!
Thanks :)
Based on the colorbar on your image plot the values of your image pixels range from [0, 5*10^6].
For many image processing functions, MATLAB assumes one of two color models, double values ranging from [0, 1] or integer values ranging from [0 255]. While the supported ranges are not explicitly mentioned in the imhist documentation, in the "Tips" section of the imhist documentation, there is a table of scale factors for different numeric types that hints at these assumptions.
I think the discrepancy between your image range and these models is the root of the problem.
For example, I load a grayscale image and scale the pixels by 1000 to approximate your data.
% Toy data to approximate your image
I = im2double(imread('cameraman.tif'));
output = {I, I .* 1000};
for i = 1:size(output,2)
figure
subplot(2,1,1)
imagesc(output{1,i});
colormap('gray')
colorbar;
subplot(2,1,2)
[pixelCount, grayLevels] = imhist(output{1,i});
bar(pixelCount);
title('Histogram of original image');
grid on;
end
The first image is using a matrix with the standard [0,1] double value range. The imhist calculates a histogram as expected. The second image is using a matrix with the scaled [0, 1000] double value range. imhist assigns all the pixels to the 255 bin since that is the maximum bin. Therefore, we need a method that allows us to scale the bins.
Solution : Use histogram
histogram is designed for any numeric type and range. You may need to fiddle with the bin edges to show the structures that you are interested in as it doesn't initialize bins the same way imhist does.
figure
subplot(2,1,1)
imagesc(output{1,2});
colormap('gray')
colorbar;
subplot(2,1,2)
histogram(output{1,2});
title('Histogram of original image');
grid on;
So I'm no expert in coding: I just have a vague understanding of a few bits and bobs.
I understand that images of pixel dimensions X*Y are stored in array of size 3*X*Y where each vector pulled from a given (x,y) value has 3 elements for the 3 RGB values.
I also understand that one can also store an image in a 4*X*Y array, where each pulled vector now has 4 values, RGBA with A being Alpha, used to represent the opacity of a particular pixel.
Now I'm into animation, and have pencil drawings of white clouds on a black background that I want to import into Flash. I would like the blacker parts of the drawing to be more transparent and the lighter parts to be more opaque. I have the scans of the drawings saved in .png format.
If I had any idea how to manipulate an image at the 'array level', I could have a stab at this myself but I'm at a loss.
I need a program that, given a .png image and a reference RGB value {a b c}, obtains the RGB array of the image and converts it into an RBGA array such that:
a pixel of RGB value {p q r}
...... Becomes ......
a pixel of RGBA value {p q r 1-M[(|p-a|^2 + |q-b|^2 + |r-c|^2)^1/2]}.
Where M is a normalisation factor which makes the largest alpha value = 1.
i.e. M = 1/[(255^2 + 255^2 + 255^2)^1/2]) = 0.0026411...
i.e. the alpha value of the replacement pixel is the 'distance' between the colour of the pixel and some reference colour which can be input.
This then needs to export the new RGBA Array as a png image.
Any ideas or any fellow animators know if this can be done directly with actionscript?
Example: Reference = {250 251 245}
RGB array =
|{250 251 245} {250 250 250}|
|{30 255 22} {234 250 0 }|
...... Becomes ......
RGBA array =
|{250 251 245 1} {250 251 245 0.987}|
|{30 255 22 0.173} {234 250 0 0.352}|
You can do this quite simply, just at the command-line, with ImageMagick which is installed on most Linux distros and is available for free on OSX and Windows.
The "secret sauce" is the -fx operator - described here.
So, let's generate a 300x200 black image and then use -fx to calculate the red channel so that the red varies across the image according to what fraction of the width (w) we are from the left side (i):
convert -size 300x200 xc:black -channel R -fx 'i/w' result.png
Note that I am generating an image "on-the-fly" with -size 300x200 xc:black, whereas if you have a PNG file with your animation frame in it, you can put that in, in its place.
Now let's say we want to vary the opacity/alpha too - according to the distance down the image from the top:
convert -size 300x200 xc:black -alpha on \
-channel R -fx 'i/w' \
-channel A -fx 'j/h' result.png
Ok, we are getting there... your function is a bit more complicated, so, rather than typing it on the command-line every time, we can put it in a script file called RGB2Opacity.fx like this:
convert -size 300x200 xc:black -alpha on -channel A -fx #RGB2Opacity.fx result.png
where RGB2Opacity.fx is simple and looks like this for the moment:
0.2
Now we need to put your "reference" pixel on the command line with your animation frame so that ImageMagick can work out the difference. That means your actual command-line will look more or less exactly like the following:
convert -size 300x200 xc:"rgb(250,251,245)" YourImage.png -alpha on -channel A -fx #RGB2Opacity.fx result.png
And then we need to implement your formula in the -fx script file. Your variable names must be at least 2 letters long with no digits in them, and you should return a single value for the opacity. Variables are all scaled between [0,1.0] so your 255 scaling is a little different. I have no sample image and I am not sure how the answer is supposed to look, but it will be pretty close to this:
MM=1/pow(3,0.5);
pmasq=pow((u.r-v.r),2.0);
qmbsq=pow((u.g-v.g),2.0);
rmcsq=pow((u.b-v.b),2.0);
1-MM*pow((pmasq+qmbsq+rmcsq),0.5)
I don't know if/how to put comments in -fx scripts, so I will explain the variable names below:
pmasq is the square of p minus a.
qmbsq is the square of q minus b.
rmcsq is the square of r minus c.
u.r refers to the red channel of the first image in ImageMagick's list, i.e. the red channel of your reference pixel.
v.g refers to the green channel of the second image in ImageMagick's list, i.e. the green channel of your animation frame.
Let's create your animation frame now:
convert xc:"rgb(250,251,245)" xc:"rgb(250,250,250)" xc:"rgb(30,255,22)" xc:"rgb(234,250,0)" +append frame.png
And check it looks correct:
convert frame.png txt:
Output
# ImageMagick pixel enumeration: 4,1,65535,srgb
0,0: (64250,64507,62965) #FAFBF5 srgb(250,251,245)
1,0: (64250,64250,64250) #FAFAFA grey98
2,0: (7710,65535,5654) #1EFF16 srgb(30,255,22)
3,0: (60138,64250,0) #EAFA00 srgb(234,250,0)
If we apply that to your image and check the results, you can see I have got it slightly wrong somewhere, butI'll leave you (or some other bright spark) to work that out...
convert -size 4x1 xc:"rgb(250,251,245)" frame.png -alpha on -channel A -fx #RGB2Opacity.fx result.png
convert result.png txt:
# ImageMagick pixel enumeration: 4,1,65535,srgba
0,0: (64250,64507,62965,65535) #FAFBF5FF srgba(250,251,245,1)
1,0: (64250,64507,62965,64764) #FAFBF5FC srgba(250,251,245,0.988235)
2,0: (64250,64507,62965,19018) #FAFBF54A srgba(250,251,245,0.290196)
3,0: (64250,64507,62965,29041) #FAFBF571 srgba(250,251,245,0.443137)
I am trying to generate a graph that should look similar to:
My arrays are:
Array4:[Nan;Nan;.......;20;21;22;23;24;..........60]
Array3:[[Nan;Nan;.......;20;21;22;23;24;..........60]
Array2:[0;1;2;3;4;5;6;Nan;Nan;Nan;Nan;17;18;.....60]
Array1:[0;1;2;3;4;5;6;Nan;Nan;Nan;Nan;17;18;.....60]
I cannot find the right way to group my arrays in order to plot them in the way shown on the above graph.
I tried using the following function explained in: http://uk.mathworks.com/help/matlab/ref/barh.html
barh(1:numel(x),y,'hist')
where y=[Array1,Array2;Array3,Array4] and x={'1m';'2m';'3m';......'60m'}
but it does not work.
Why Your Current Approach Isn't Working
Your intuition makes sense to me, but the barh function you are using doesn't work the way you think it does. Specifically, you are interpreting the meaning of the x and y inputs to that function incorrectly. Those are inputs are constant values, not entire axes. The first y input refers to the end-point of the bar that stretches horizontally from x = 0 and the first x input refers to location on the y-axis of the horizontal bar. To illustrate what I mean, I've provided the below horizontal bar graph:
You can find this same picture in the official documentation of the MATLAB barh function. The code used to generate this bar graph is also given in the documentation, shown below:
x = 1900:10:2000;
y = [57,91,105,123,131,150,...
170,203,226.5,249,281.4];
figure;
barh(x, y);
The individual elements of the x array, rather confusingly, show up on the y-axis as the starting locations of each bar. The corresponding elements of the y array are the lengths of each bar. This is the reason that the arrays must be the same length, and this illustrates that they are not specifications of the x and y axes as one might intuitively believe.
An Approach To Solve Your Problem
First things first, the easiest approach is to do this manually with the plot function and a set of lines that represent floating bars. Consult the official documentation for the plot function if you'd like to plot the lines with some sort of color coordination in mind - the code I present (modified version of this answer on StackOverflow) just switches the color of the floating bars between red and blue. I tried to comment the code so that the purpose of each variable is clear. The code I present below matches the floating bar graph that you want to be plotted, if you are alright with replacing thick floating bars with 2D lines floating on a plot.
I used the data that you gave in your question to specify the floating horizontal bars that this script would output - a screenshot is shown below the code. Array1 & Array2:[0;1;2;3;4;5;6;Nan;Nan;Nan;Nan;17;18;.....60], these arrays go from 0 to 6 (length = 6) and 17 to 60 (length = 60 - 17 = 43). Because there is a "discontinuity" of sorts from 7 to 16, I have to define two floating bars for each array. Hence, the first four values in my length array are [6, 6, 43, 43]. Where the first 6 and the first 43 correspond to Array1 and the second 6 and the second 43 correspond to Array2. Recognizing this "discontinuity", the starting point of the first floating bar for Array1 and Array2 is x = 0 and the starting point of the second floating bar for Array1 and Array2 is x = 7. Putting that all together, you arrive at the x-coordinates for the first four points in the floating_bars array, [0 0; 0 1.5; 17 0; 17 1.5]. The y-coordinates in this array only serve to distinguish Array1, Array2, and so on from each other.
Code:
floating_bars=[0 0; 0 1.5; 17 0; 17 1.5; 20 6; 20 7.5]; % Each row is the [x,y] coordinate pair of the starting point for the floating bar
L=[6, 6, 43, 43, 40, 40]; % Length of each consecutive bar
thickness = 0.75;
figure;
for i=1:size(floating_bars,1)
curr_thickness = 0;
% It is aesthetically pleasing to have thicker bars, this makes the plot look for like the grouped horizontal bar graph that you want
while (curr_thickness < thickness)
% Each bar group has two bars; set the first to be red, the second to be blue (i.e., even index means red bar, odd index means blue bar)
if mod(i, 2)
plot([floating_bars(i,1), floating_bars(i,1)+L(i)], [floating_bars(i,2) + curr_thickness, floating_bars(i,2) + curr_thickness], 'r')
else
plot([floating_bars(i,1), floating_bars(i,1)+L(i)], [floating_bars(i,2) + curr_thickness, floating_bars(i,2) + curr_thickness], 'b')
end
curr_thickness = curr_thickness + 0.05;
hold on % Make sure that plotting the current floating bar does not overwrite previous float bars that have already been plotted
end
end
ylim([ -10 30]) % Set the y-axis limits so that you can see more clearly the floating bars that would have rested right on the x-axis (y = 0)
Output:
How Do I Do This With the barh Function?
The short answer is that you'd have to modify the function manually. Someone has already done this with one of the bar graph plotting functions provided by MATLAB, bar3. The logic implemented in this modified bar3 function can be re-applied for your purposes if you read their barNew.m function and tweak it a bit. If you'd like a pointer as to where to start, I'd suggest looking at how they specify z-axis minimum and maximums for their floating bars on the plot, and apply that same logic to specify x-axis minimum and maximums for your floating bars in your 2D case.
I hope this helps, happy coding! :)
I explain here my approach to generate these type of graphs. Not sure if it is the best but it works and there is no need to do anything manually. I came up with this solution based on the following Vladislav Martin's explained fact: "The y-coordinates in this array only serve to distinguish Array1, Array2, and so on from each other".
My original arrays are:
Array4=[Nan....;20;21;22;23;24;..........60]
Array3=[Nan....;20;21;22;23;24;..........60]
Array2=[0;1;2;3;4;5;6;Nan;Nan;Nan;Nan;17;18;.....60]
Array1=[0;1;2;3;4;5;6;Nan;Nan;Nan;Nan;17;18;.....60]
x={'0m';'1m';'2m';'3m';'4m';....'60m'}
The values contained in these arrays make reference to the x-axis on the graph. In order to make the things more simple and to avoid having to code a function to determine the length for each discontinuity in the arrays, I replace these values for y-axis position values. Basically I give to Array1 y-axis position values of 0 and to Array2 0+0.02=0.02. To Array3 I give y-axis position values of 0.5 and to Array4 0.5+0.02=0.52. In this way, Array2 will be plotted on the graph closer to Array1 which will form the first group and Array4 closer to Array3 which will form the second group.
Datatable=table(Array1,Array2,Array3,Array4);
cont1=0;
cont2=0.02;
for col=1:2:size(Datatable,2)
col2=col+1;
for row=1:size(Datatable,1)
if isnan(Datatable{row,col})==0 % For first array in the group: If the value is not nan, I replace it for the corresponnding cont1 value
Datatable{row,col}=cont1;
end
if isnan(Datatable{row,col2})==0 % For second array in the group: If the value is not nan, I replace it for the corresponnding cont2 value
Datatable{row,col2}=cont2;
end
end
cont1=cont1+0.5;
cont2=cont2+0.5;
end
The result of the above code will be a table like the following:
And now I plot the Arrays using 2D floating lines:
figure
for array=1:2:size(Datatable,2)
FirstPair=cell2mat(table2cell(Datatable(:,array)));
SecondPair=cell2mat(table2cell(Datatable(:,array+1)));
hold on
plot(1:numel(x),FirstPair,'r','Linewidth',6)
plot(1:numel(x),SecondPair,'b','Linewidth',6)
hold off
end
set(gca,'xticklabel',x)
And this will generate the following graph: