I am trying to make a heatmap of z-values for a given (x,y) value. I have the 3 arrays: x, y and z, each of which is a 1xn array. What I do in the script:
import matplotlib.pyplot as plt
import numpy as np
import yoda #external library
def plotMap(histname, filename):
histos = yoda.core.read(filename+'.aida')
x = []
y = []
z = []
h = histos[histname]
for b in h:
x.append(b.xMid())
y.append(b.yMid())
z.append(b.mean())
plt.rcParams["figure.figsize"] = [14.00, 14.00]
plt.rcParams["figure.autolayout"] = True
combined = np.vstack([x, y, z]).T
plt.imshow(combined, cmap='plasma',interpolation="nearest")
plt.colorbar(orientation="vertical")
plotMap("/stProfile/myArray", "plotArrayFile")
plt.xlabel("$x$ (fm)")
plt.ylabel("$y$ (fm)")
plt.zlabel("Intensity")
plt.title("Intensity for (x,y) values")
plt.savefig("heatmap.pdf")
The z values are decimal values that are always >= 0 maximum of 3. x and y values vary from -2.0 to 2.0. What I need is a y-x plot showing the range from -2 to 2 and z plotted as a heatmap which the colour scale on the right of the plot. What I get looks like:
Can anyone help me by pointing out why the ranges of x,y and z are mixed up and how it can be fixed?
Related
Let's assume I have 3 arrays defined as:
v1=np.linspace(1,100)
v2=np.linspace(1,100)
v3=np.linspace(1,100)
Then I have a function that takes those 3 values and gives me the desired output, let's assume it is like:
f = (v1 + v2*10)/v3
I want to plot that function on a 3D plot with axis v1,v2,v3 and color it's surface depending on its value.
More than the best way to plot it, I was also interested in how to scroll all the values in the in vectors and build the function point by point.
I have been trying with for loops inside other for loops but I am always getting one error.
MANY THANKS
I tried this but i'm always getting a line instead of a surface
import mpl_toolkits.mplot3d.axes3d as axes3d
import sympy
from sympy import symbols, Function
# Parameters I use in the function
L = 132
alpha = 45*math.pi/180
beta = 0
s,t = symbols('s,t')
z = Function('z')(s,t)
figure = plt.figure(figsize=(8,8))
ax = figure.add_subplot(1, 1, 1, projection='3d')
# experiment with various range of data in x and y
x1 = np.linspace(-40,-40,100)
y1 = np.linspace(-40,40,100)
x,y = np.meshgrid(x1,y1)
# My function Z
c1=math.cos(beta)**2
c2=math.cos(alpha)**2
s1=math.sin(alpha)**2
den = math.sqrt((c1*c2)+s1)
z=L*((math.cos(beta)/den)-1)+(s*(math.sin(alpha)))+(t*(1-math.cos(alpha)))
ax.plot_surface(x,y,z,cmap='rainbow')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
In this example I'm going to show you how to achieve your goal. Specifically, I use Numpy because it supports vectorized operations, hence I avoid for loops.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import matplotlib.cm as cm
# Parameters I use in the function
L = 132
alpha = 45*np.pi/180
beta = 0
figure = plt.figure()
ax = figure.add_subplot(1, 1, 1, projection='3d')
# experiment with various range of data in x and y
x1 = np.linspace(-40,40,100)
y1 = np.linspace(-40,40,100)
x,y = np.meshgrid(x1,y1)
# My function Z
c1=np.cos(beta)**2
c2=np.cos(alpha)**2
s1=np.sin(alpha)**2
den = np.sqrt((c1*c2)+s1)
z=L*((np.cos(beta)/den)-1)+(x*(np.sin(alpha)))+(y*(1-np.cos(alpha)))
# compute the color values according to some other function
color_values = np.sqrt(x**2 + y**2 + z**2)
# normalize color values between 0 and 1
norm = Normalize(vmin=color_values.min(), vmax=color_values.max())
norm_color_values = norm(color_values)
# chose a colormap and create colors starting from the normalized values
cmap = cm.rainbow
colors = cmap(norm_color_values)
surf = ax.plot_surface(x,y,z,facecolors=colors)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
# add a colorbar
figure.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), label="radius")
plt.show()
How can I extract the x/y data from the resulting PolyCollection from a fill_between plot?
polyCollection = ax.fill_between(x,ylo,yhi)
Now how do I get the data back from polyCollection?
For other Collection objects, I use x, y = artist.get_offsets().T, but here that returns just zeros for some reason.
For "Line" type objects, I use x, y = artist.get_xdata(), artist.get_ydata().
(I use this information in a callback to locally auto-zoom the y-axis to fit the data within a certain x-range.)
polyCollection.get_paths() gives a list of paths. In this case a list with one element. From there you can get the vertices as an Nx2 numpy array, and there the x and y:
from matplotlib import pyplot as plt
import numpy as np
N = 20
polyCollection = plt.fill_between(np.arange(0, N),
5 + np.random.normal(size=N).cumsum(),
10 + np.random.normal(size=N).cumsum(), color='lightblue', alpha=0.3)
points = polyCollection.get_paths()[0].vertices
xs = points[:, 0]
ys = points[:, 1]
plt.scatter(xs, ys, marker='o', color='crimson')
import numpy as np
from matplotlib import pyplot as plt
data = np.random.normal(0,1,[100,3])
x = data[:,0]
y = data[:,1]
z = data[:,2]
plt.contour([x,y],z)
When I run this code with dummy data I get:
ValueError: Contour levels must be increasing
Do you have any idea what would this mean and how I could fix it?
plt.contour is a bit particular about its input, the z values must be on values on a rectangular 2D grid, see for example:
import matplotlib.pyplot as plt
import numpy as np
x = np.expand_dims(np.arange(1,11,1), axis=1)
y = np.expand_dims(np.arange(2,21,2), axis=0)
z = y * x
print(x.shape)
print(y.shape)
print(z.shape)
plt.figure()
plt.contour(z)
plt.show()
You can also provide x and y values for plt.contour by using np.meshgrid :
XX,YY = np.meshgrid(x,y)
plt.figure()
plt.contour(XX, YY, z)
plt.show()
If you have z-values with irregular values for x and y, you might use plt.tricontour, see the following example:
from matplotlib.tri import Triangulation
data = np.random.normal(0,1,[100,3])
x = data[:,0]
y = data[:,1]
#z = data[:,2]
z = x * y
tri = Triangulation(x,y)
plt.figure()
plt.tricontour(tri, z, )
plt.scatter(x,y, c=z)
plt.show()
Edit: from JohanC's comment i learned that this can be simplified without importing matplotlib.tri by:
plt.figure()
plt.tricontour(x,y,z)
plt.scatter(x,y, c=z)
plt.show()
I'm trying to run the following:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.linspace(-15, 15, 10)
y = np.linspace(-15, 15, 10)
X, Y = np.meshgrid(x, y)
Z = Y;
# Z = X;
cmap = mpl.colors.ListedColormap(['r', 'b'])
bounds = [-300, 0, 300]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
plt.figure();
plt.xlabel('x');
plt.ylabel('y');
im = plt.imshow(Z,cmap= cmap, norm = norm)
plt.show();
If I try to do Z = X, it works fine. But if I do Z = Y, the y-axis is inverted, i.e. red (negative) at the top, and blue (positive) at the bottom. Why is this happening?
Inverted compared to what?
The plot you have is working fine in both cases, as expected. The value of Z[0,0], which is -15 in this case, is plotted in red at the coordinate 0,0.
If you want the y axis to start at the bottom instead of the top, use the origin="lower" keyword argument to imshow.
I'm plotting some functions that have several discontinuities. Each function is given as a list. I want to connect points with lines only where the function is continuous.
Here is a simplified example of what plot is doing.
x=linspace(0,1,100)
y=zeros(100)
y[x<0.5] = x[x<0.5]
y[x>=0.5] = 1 + x[x>=0.5]
plot(x, y, '-o')
There is a discontinuity at x=0.5, but plot connects all points with lines regardless.
My functions are different of course. They typically have several discontinuities in different places. The criterion for the discontinuity is simple. Say, if the function jumps by more than 0.5, I assume it is discontinuous at that point.
Is there an option in plot to tell it to drop the connecting lines between the points where the function is discontinuous? I recall being able to do that easily with gnuplot.
use nan to break the line into multiple segments:
import numpy as np
from pylab import *
x=linspace(0,1,100)
y=zeros(100)
y[x<0.5] = x[x<0.5]
y[x>=0.5] = 1 + x[x>=0.5]
pos = np.where(np.abs(np.diff(y)) >= 0.5)[0]
x[pos] = np.nan
y[pos] = np.nan
plot(x, y, '-o')
Edit:
to insert nan at discontinuities:
pos = np.where(np.abs(np.diff(y)) >= 0.5)[0]+1
x = np.insert(x, pos, np.nan)
y = np.insert(y, pos, np.nan)
Here is my suggestion for plotting tan(x):
import matplotlib.pyplot as plt
from math import *
x_lim = 3*pi/2
y_lim = 5
n = 1000
X = []
Y = []
Z = []
for i in range(0,2*n):
x = -x_lim + i*x_lim/n
y = tan(x)
if y<y_lim and y>-y_lim:
X.append(x)
Y.append(y)
else:
if len(X)>0 and len(Y)>0:
Z.append([X,Y])
del X,Y
X = []
Y = []
for i in range(0, len(Z)):
plt.plot(Z[i][0],Z[i][1])
plt.grid(True)
plt.show()