This question already has answers here:
surface plots in matplotlib
(9 answers)
How to surface plot/3d plot from dataframe
(2 answers)
Closed 4 years ago.
I'm trying to plot some data, which consists of 4 variables. I'm using 2 approaches one is scatter plot and another one is surface. The problem is that when I'm using surface the data is missing. I think it has to do with the color setup.
For the scatter plot, I use this:
def scatter3d(x,y,z, cs, colorsMap='jet'):
cm = plt.get_cmap(colorsMap)
cNorm = matplotlib.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z,c=scalarMap.to_rgba(cs))
ax.set_xlabel('Thita1')
ax.set_ylabel('Thita2')
ax.set_zlabel('Fairness (%)')
scalarMap.set_array(cs)
fig.colorbar(scalarMap,label='Error Rate (%)')
plt.show()
I want to convert it to a surface plot, using this:
def surfacePlot(x,y,z, cs, colorsMap='jet'):
cm = plt.get_cmap(colorsMap)
cNorm = matplotlib.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, facecolors=scalarMap.to_rgba(cs))
ax.set_xlabel('Thita1')
ax.set_ylabel('Thita2')
ax.set_zlabel('Fairness')
scalarMap.set_array(cs)
fig.colorbar(scalarMap,label='Error Rate (%)')
plt.show()
However, this results in an empty grid:
Although the axes have received the min and max values from the vectors, the points are missing. What am I doing wrong ?
As mentioned, plot_surface requires 2d array data, or mesh--similar to how you would create a heatmap if you are familiar with that. If your data is regularly spaced across x,y axis (which it seems you do), you can simply use the z data formatted into a 2d array as shown in previous examples linked in the comments:
grid_x, grid_y = np.meshgrid(x, y)
# I'm assuming that your data is already mesh-like, which it looks like it is.
# The data would also need to be appropriately sorted for `reshape` to work.
# `dx` here is number of unique x values, and `dy` is number unique y values.
grid_z = z.reshape(dy, dx)
ax.plot_scatter(grid_x, grid_y, grid_z)
However, in the general case where you have unevenly spaced x,y,z points, you can interpolate your data to create your mesh. Scipy has the function griddata that will interpolate onto a defined meshgrid. You can use this to plot your data:
from scipy.interpolate import griddata
xy = np.column_stack([x, y])
grid_x, grid_y = np.mgrid[0:1:100j, 0:1:100j] # grid you create
grid_z = griddata(xy, z, (grid_x, grid_y))
ax.plot_scatter(grid_x, grid_y, grid_z)
Related
I have data regarding x, y and color values which are all NxM arrays. Normally, when using Polygon function from Matplotlib, it needs to have color values per FACE (which would be a 1xM array) instead of VERTEX. However, my color data is per VERTEX. So, my question is that is there a way to convert my vertex data to single face color data? Or even better, is there a way to plot polygons using color values per vertex instead of per face?
Note: What I want is quite similar to MATLAB's patch function which you can use color values per vertex.
Edit:
Below, you can find a data example of one element. A mesh consists of many elements and below is just one of them. X and Y refer to coordinates and C refers to color values of each coordinate. Note that the below arrays are actually stored as columns in the original code as there are many other elements.
import numpy as np
X = np.array([0,0.176611,0.343377,0.366506,0.390424,0.254526,0.117785,0.602218])
Y = np.array([0,0.0248673,0.0436277,0.0834644,0.123043,0.110448,0.0995953,0.0509885])
C = np.array([0.136649,0.114582,0.100576,0.102412,0.102754,0.104971,0.110123,0.120756])
To assign colors to vertices instead of faces, pcolormesh accepts the parameter shading='gouraud'. Here is an example:
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0, 15, 8)
y = np.linspace(0, 10, 5)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
fig, axs = plt.subplots(ncols=2, figsize=(12, 3))
cmap = 'plasma'
axs[0].scatter(X, Y, s=50, c=Z, cmap=cmap, edgecolors='black')
axs[0].set_title('given vertex colors')
axs[1].pcolormesh(x, y, Z, shading='gouraud', cmap=cmap)
axs[1].scatter(X, Y, s=50, c=Z, cmap=cmap, edgecolors='black')
axs[1].set_title('interpolated vertex colors')
axs[1].set_xlim(axs[0].get_xlim())
axs[1].set_ylim(axs[0].get_ylim())
plt.show()
I have a matrix generated by parsing a file the numpy array is the size 101X101X41 and each entry has a value which represents the magnitude at each point.
Now what I want to do is to plot it in a 3d plot where the 4th dimension will be represented by color. so that I will be able to see the shape of the data points (represent molecular orbitals) and deduce its magnitude at that point.
If I plot each slice of data I get the desired outcome, but in a 2d with the 3rd dimension as the color.
Is there a way to plot this model in python using Matplotlib or equivalent library
Thanks
EDIT:
Im trying to get the question clearer to what I desire.
Ive tried the solution suggested but ive received the following plot:
as one can see, due to the fact the the mesh has lots of zeros in it it "hide" the 3d orbitals. in the following plot one can see a slice of the data, where I get the following plot:
So as you can see I have a certain structure I desire to show in the plot.
my question is, is there a way to plot only the structure and ignore the zeroes such that they won't "hide" the structure.
the code I used to generate the plots:
x = np.linspase(1,101,101)
y = np.linspase(1,101,101)
z = np.linspase(1,101,101)
xx,yy,zz = np.meshgrid(x,y,z)
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xx, yy, zz, c=cube.calc_data.flatten())
plt.show()
plt.imshow(cube.calc_data[:,:,11],cmap='jet')
plt.show()
Hope that now the question is much clearer, and that you'd appreciate the question enough now to upvote
Thanks.
you can perform the following:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
epsilon = 2.5e-2 # threshold
height, width, depth = data.shape
global_min = np.inf
global_max = -np.inf
for d in range(depth):
slice = data[:, :, d]
minima = slice.min()
if (minima < global_min): global_min = minima
maxima = slice.max()
if (maxima>global_max): global_max=maxima
norm = colors.Normalize(vmin=minima, vmax=maxima, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap=cm.jet)
points_gt_epsilon = np.where(slice >= epsilon)
ax.scatter(points_gt_epsilon[0], points_gt_epsilon[1], d,
c=mapper.to_rgba(data[points_gt_epsilon[0],points_gt_epsilon[1],d]), alpha=0.015, cmap=cm.jet)
points_lt_epsilon = np.where(slice <= -epsilon)
ax.scatter(points_lt_epsilon[0], points_lt_epsilon[1], d,
c=mapper.to_rgba(data[points_lt_epsilon[0], points_lt_epsilon[1], d]), alpha=0.015, cmap=cm.jet)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.title('Electron Density Prob.')
norm = colors.Normalize(vmin=global_min, vmax=global_max, clip=True)
cax, _ = colorbar.make_axes(ax)
colorbar.ColorbarBase(cax, cmap=cm.jet,norm=norm)
plt.savefig('test.png')
plt.clf()
What this piece of code does is going slice by slice from the data matrix and for each scatter plot only the points desired (depend on epsilon).
in this case you avoid plotting a lot of zeros that 'hide' your model, using your words.
Hope this helps
You can adjust the color and size of the markers for the scatter. So for example you can filter out all markers below a certain threshold by putting their size to 0. You can also make the size of the marker adaptive to the field strength.
As an example:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
f = lambda x,y,z: np.exp(-(x-3)**2-(y-3)**2-(z-1)**2) - \
np.exp(-(x+3)**2-(y+3)**2-(z+1)**2)
t1 = np.linspace(-6,6,101)
t2 = np.linspace(-3,3,41)
# Data of shape 101,101,41
data = f(*np.meshgrid(t1,t1,t2))
print(data.shape)
# Coordinates
x = np.linspace(1,101,101)
y = np.linspace(1,101,101)
z = np.linspace(1,101,41)
xx,yy,zz = np.meshgrid(x,y,z)
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
s = np.abs(data/data.max())**2*25
s[np.abs(data) < 0.05] = 0
ax.scatter(xx, yy, zz, s=s, c=data.flatten(), linewidth=0, cmap="jet", alpha=.5)
plt.show()
I have a set of points (> 1k) in this form:
y,x
173.549,308.176
173.549,313.328
213.26,419.588
Using KDE, i can plot points density with pcolormesh and contourf. This is an example result, plotting points too:
This is the code i used to have the plot:
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
from scipy.stats.kde import gaussian_kde
x, y = np.genfromtxt('terzinoSX.csv', delimiter=',', unpack=True)
y = y[np.logical_not(np.isnan(y))]
x = x[np.logical_not(np.isnan(x))]
k = gaussian_kde(np.vstack([x, y]))
xi, yi = np.mgrid[x.min():x.max():x.size**0.5*1j,y.min():y.max():y.size**0.5*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
fig = plt.figure(figsize=(7,4))
ax2 = fig.add_subplot(111)
#alpha=0.5 will make the plots semitransparent
#ax1.pcolormesh(yi, xi, zi.reshape(xi.shape), alpha=0.5)
ax2.contourf(yi, xi, zi.reshape(xi.shape), alpha=0.5)
plt.axis('off')
ax2.plot(y,x, "o")
ax2.set_xlim(0, 740)
ax2.set_ylim(515, 0)
#overlay soccer field
im = plt.imread('statszone_football_pitch.png')
ax2.imshow(im, extent=[0, 740, 0, 515], aspect='auto')
fig.savefig('test.png', bbox_inches='tight')
I would like to have one point representing coordinates of most populated zone (middle point for example), like a middle point over the "red" zone. Is it possible in some way?
I solved this by adding these lines that calculate the point in the most populated area:
xy = np.vstack([x,y])
kde = stats.gaussian_kde(xy)
density = kde(xy)
pts = xy.T[np.argmax(density)]
You can use np.argmax to get the coordinates of the maximum. For example:
kde = compute_my_kde() # Returns a two-dimensional array
y, x = np.argmax(kde) # x and y are swapped because matplotlib displays images as a matrix (first index is rows, second index is colums)
plt.imshow(kde) # Show the kde
plt.scatter(x, y) # Show the maximum point
I got a problem when I was plotting a 3d figure using matplotlib of python. Using the following python function, I got this figure:
Here X, Y are meshed grids and Z and Z_ are functions of X and Y. C stands for surface color.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
def plot(X, Y, Z, Z_, C):
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(C),
linewidth=0, antialiased=False, shade=False)
surf_ = ax.plot_surface(
X, Y, Z_, rstride=1, cstride=1,
facecolors=cm.jet(C),
linewidth=0, antialiased=False, shade=False)
ax.view_init(elev=7,azim=45)
plt.show()
But now I want to cut this figure horizontally and only the part whose z is between -1 and 2 remain.
What I want, plotted with gnuplot, is this:
I have tried ax.set_zlim3d and ax.set_zlim, but neither of them give me the desired figure. Does anybody know how to do it using python?
Nice conical intersections you have there:)
What you're trying to do should be achieved by setting the Z data you want to ignore to NaN. Using graphene's tight binding band structure as an example:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate dummy data (graphene tight binding band structure)
kvec = np.linspace(-np.pi,np.pi,101)
kx,ky = np.meshgrid(kvec,kvec)
E = np.sqrt(1+4*np.cos(3*kx/2)*np.cos(np.sqrt(3)/2*ky) + 4*np.cos(np.sqrt(3)/2*ky)**2)
# plot full dataset
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(kx,ky,E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1)
ax.plot_surface(kx,ky,-E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1)
# focus on Dirac cones
Elim = 1 #threshold
E[E>Elim] = np.nan
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.plot_surface(kx2,ky2,E2,cmap='viridis',vmin=-Elim,vmax=Elim)
#ax.plot_surface(kx2,ky2,-E2,cmap='viridis',vmin=-Elim,vmax=Elim)
ax.plot_surface(kx,ky,E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim)
ax.plot_surface(kx,ky,-E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim)
plt.show()
The results look like this:
Unfortunately, there are problems with the rendering of the second case: the apparent depth order of the data is messed up in the latter case: cones in the background are rendered in front of the front ones (this is much clearer in an interactive plot). The problem is that there are more holes than actual data, and the data is not connected, which confuses the renderer of plot_surface. Matplotlib has a 2d renderer, so 3d visualization is a bit of a hack. This means that for complex overlapping surfaces you'll more often than not get rendering artifacts (in particular, two simply connected surfaces are either fully behind or fully in front of one another).
We can get around the rendering bug by doing a bit more work: keeping the data in a single surface by not using nans, but instead colouring the the surface to be invisible where it doesn't interest us. Since the surface we're plotting now includes the entire original surface, we have to set the zlim manually in order to focus on our region of interest. For the above example:
from matplotlib.cm import get_cmap
# create a color mapping manually
Elim = 1 #threshold
cmap = get_cmap('viridis')
colors_top = cmap((E + Elim)/2/Elim) # listed colormap that maps E from [-Elim, Elim] to [0.0, 1.0] for color mapping
colors_bott = cmap((-E + Elim)/2/Elim) # same for -E branch
colors_top[E > Elim, -1] = 0 # set outlying faces to be invisible (100% transparent)
colors_bott[-E < -Elim, -1] = 0
# in nature you would instead have something like this:
#zmin,zmax = -1,1 # where to cut the _single_ input surface (x,y,z)
#cmap = get_cmap('viridis')
#colors = cmap((z - zmin)/(zmax - zmin))
#colors[(z < zmin) | (z > zmax), -1] = 0
# then plot_surface(x, y, z, facecolors=colors, ...)
# or for your specific case where you have X, Y, Z and C:
#colors = get_cmap('viridis')(C)
#colors[(z < zmin) | (z > zmax), -1] = 0
# then plot_surface(x, y, z, facecolors=colors, ...)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# pass the mapped colours as the facecolors keyword arg
s1 = ax.plot_surface(kx, ky, E, facecolors=colors_top, rstride=1, cstride=1)
s2 = ax.plot_surface(kx, ky, -E, facecolors=colors_bott, rstride=1, cstride=1)
# but now we need to manually hide the invisible part of the surface:
ax.set_zlim(-Elim, Elim)
plt.show()
Here's the output:
Note that it looks a bit different from the earlier figures because 3 years have passed in between and the current version of matplotlib (3.0.2) has very different (and much prettier) default styles. In particular, edges are now transparent in surface plots. But the main point is that the rendering bug is gone, which is evident if you start rotating the surface around in an interactive plot.
I am trying to make a contour plot in python with complex numbers (i am using matplotlib, pylab).
I am working with sharp bounds on harmonic polynomials, but specifically right now I am trying to plot:
Re(z(bar) - e^(z))= 0
Im(z(bar) - e^z) = 0
and plot them over each other in a contour in order to find their zeros to determine how many solutions there are to the equation z(bar) = e^(z).
Does anyone have experience in contour plotting, specifically with complex numbers?
import numpy as np
from matplotlib import pyplot as plt
x = np.r_[0:10:30j]
y = np.r_[0:10:20j]
X, Y = np.meshgrid(x, y)
Z = X*np.exp(1j*Y) # some arbitrary complex data
def plotit(z, title):
plt.figure()
cs = plt.contour(X,Y,z) # contour() accepts complex values
plt.clabel(cs, inline=1, fontsize=10) # add labels to contours
plt.title(title)
plt.savefig(title+'.png')
plotit(Z, 'real')
plotit(Z.real, 'explicit real')
plotit(Z.imag, 'imaginary')
plt.show()
EDIT: Above is my code, and note that for Z, I need to plot both real and imaginary parts of (x- iy) - e^(x+iy)=0. The current Z that is there is simply arbitrary. It is giving me an error for not having a 2D array when I try to plug mine in.
I don't know how you are plotting since you didn't post any code, but in general I advise moving away from using pylab or the pyplot interface to matplotlib, using the direct object methods is much more robust and just as simple. Here is an example of plotting contours of two sets of data on the same plot.
import numpy as np
import matplotlib.pyplot as plt
# making fake data
x = np.linspace(0, 2)
y = np.linspace(0, 2)
c = x[:,np.newaxis] * y
c2 = np.flipud(c)
# plot
fig, ax = plt.subplots(1, 1)
cont1 = ax.contour(x, y, c, colors='b')
cont2 = ax.contour(x, y, c2, colors='r')
cont1.clabel()
cont2.clabel()
plt.show()
For tom10, here is the plot this code produces. Note that setting colors to a single color makes distinguishing the two plots much easier.