I have to plot a surface plot which has axes x,y,z and a colormap set by a function of x,y,z [B(x,y,z)].
I have the coordinate arrays:
x=np.arange(-100,100,1)
y=np.arange(-100,100,1)
z=np.arange(-100,100,1)
Moreover, my to-be-colormap function B(x,y,z) is a 3D array, whose B(x,y,z)[i] elements are the (x,y) coordinates at z.
I have tried something like:
Z,X,Y=np.meshgrid(z,x,y) # Z is the first one since B(x,y,z)[i] are the (x,y) coordinates at z.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
img = ax.scatter(Z, X, Y, c=B(x,y,z), cmap=plt.hot())
fig.colorbar(img)
plt.show()
However, it unsurprisingly plots dots, which is not what I want. Rather, I need a surface plot.
The figure I have obtained:
The kind of figure I want:
where the colors are determined by B(x,y,z) for my case.
You have to:
use plot_surface to create a surface plot.
your function B(x, y, z) will be used to compute the color parameter, a number assigned to each face of the surface.
the color parameter must be normalized between 0, 1. We use matplotlib's Normalize to achieve that.
then, you create the colors by applying the colormap to the normalized color parameter.
finally, you create the plot.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import Normalize
t = np.linspace(0, 2*np.pi)
p = np.linspace(0, 2*np.pi)
t, p = np.meshgrid(t, p)
r1, r2 = 1, 3
x = (r2 + r1 * np.cos(t)) * np.cos(p)
y = (r2 + r1 * np.cos(t)) * np.sin(p)
z = r1 * np.sin(t)
color_param = np.sin(x / 2) * np.cos(y) + z
cmap = cm.jet
norm = Normalize(vmin=color_param.min(), vmax=color_param.max())
norm_color_param = norm(color_param)
colors = cmap(norm_color_param)
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.plot_surface(x, y, z, facecolors=colors)
ax.set_zlim(-4, 4)
plt.show()
Related
I have a issue to interpolate my values "c" on cylindrical surface.
The problem is that possibly I dont understand how to indicate surface for gridding with gridddata function..
>import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata as gd
#Creating data in polar coordinates
phi,d = np.linspace(0, 2* np.pi, 20), np.linspace(0,20,20)
PHI,D = np.meshgrid(phi,d)
R = 2
#Transforming in X Y Z coordinates
X = R * np.cos(PHI)
Y = R * np.sin(PHI)
Z = R * D
T=np.linspace(0,10,400)
c=np.sin(T)*np.cos(T/2) #Value c I would like to interpolate
fig1 = plt.figure()
ax = fig1.add_subplot(1,1,1, projection='3d')
xi=np.array(np.meshgrid(X,Y,Z))
img = ax.scatter(X, Y, Z,c=c, cmap=plt.hot()) #To plot data scatter before interpolation
fig1.colorbar(img)
plt.show()
X1,Y1,Z1 =np.meshgrid(X ,Y ,Z) #To define sufrace for interpolation
int = gd((X,Y,Z), c, (X1,Y1,Z1), method='linear')
fig2 = plt.figure() #trying to plot the answer
ax1 = fig2.add_subplot(1,1,1, projection='3d')
ax1.scatter(int)
img = ax1.scatter(X, Y, Z, c=c, cmap=plt.hot())
`
Its gives error: different number of values and points
I dont know how to indicate (X1,Y1,Z1) surface in griddata function
Thanks a lot for any tips ...
I want to plot some data x and y in which I need the marker size to depend on a third array z. I could plot them separately (i.e., scatter x and y with size = z, and errorbar without marker, fmc = 'none') and this solves it. The problem is that I need the legend to show the errorbar AND the dot, together:
and not
Code is here with some made-up data:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(1,10,100)
y = 2*x
yerr = np.random(0.5,1.0,100)
z = np.random(1,10,100)
fig, ax = plt.subplots()
plt.scatter(x, y, s=z, facecolors='', edgecolors='red', label='Scatter')
ax.errorbar(x, y, yerr=yerr, xerr=0, fmt='none', mfc='o', color='red', capthick=1, label='Error bar')
plt.legend()
plt.show()
which produces the legend I want to avoid:
In errorbar the argumentmarkersizedoes not accept arrays asscatter` does.
The idea is usually to use a proxy to put into the legend. So while the errorbar in the plot may have no marker, the one in the legend has a marker set.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(1,10,11)
y = 2*x
yerr = np.random.rand(11)*5
z = np.random.rand(11)*2+5
fig, ax = plt.subplots()
sc = ax.scatter(x, y, s=z**2, facecolors='', edgecolors='red')
errb = ax.errorbar(x, y, yerr=yerr, xerr=0, fmt='none',
color='red', capthick=1, label="errorbar")
proxy = ax.errorbar([], [], yerr=[], xerr=[], marker='o', mfc="none", mec="red",
color='red', capthick=1, label="errorbar")
ax.legend(handles=[proxy], labels=["errorbar"])
plt.show()
I have a matplotlib contourf plot of longitudes and pressure levels in the vertical. I am trying to plot streamlines on this using the plt.streamplot function in matplotlib and using U and V wind data.
If I plot only the streamplot, it works fine. But I cannot get the streamlines to overlay on the contour plot.
Here is my code:-
fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True)
if (nrows==1 and ncols==1):
axes=[axes]
else:
axes=axes.flat
for i, ax in enumerate(axes):
X,Y = np.meshgrid(x[i],y[i])
levels=np.arange(vmin,vmax,step)
h = ax.contourf(X,Y,z[i],cmap=cmap,levels=levels,extend='both')
w = ax.streamplot(X, Y, W[i], Z[i], linewidth=0.2, color='gray')
And this is the plot I get:
The following is the streamline plot, not sure why the y axis is from 0-120 instead of 0 to 1000:
You use curvilinear coordinate system for contour plot (lat-p).
You have to convert u,v to coordinate system of contour something like here (this is example for lat-lon you have to modify it to use pressure levels):
def myStreamPlot(lon,lat,u,v,color='k',density=2.5):
from scipy.interpolate import griddata
n,m = u.shape[1],u.shape[0]
x = np.linspace(np.nanmin(lon), np.nanmax(lon), n)
y = np.linspace(np.nanmin(lat), np.nanmax(lat), m)
xi, yi = np.meshgrid(x,y)
lon = lon.ravel()
lat = lat.ravel()
u = u.ravel()
v = v.ravel()
gu = griddata(zip(lon,lat), u, (xi,yi))
gv = griddata(zip(lon,lat), v, (xi,yi))
gspd = np.sqrt(gu**2 + gv**2)
SL = plt.streamplot(x,y,gu,gv,linewidth=1.,color=color,density=density)
This code use griddata function of scipy.interpolate: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.griddata.html
I would like to group my data and to plot the boxplot for all the groups. There are many questions and answer about that, my problem is that I want to group by a continuos variable, so I want to histogramming my data.
Here what I have done. My data:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
x = np.random.chisquare(5, size=100000)
y = np.random.normal(size=100000) / (0.05 * x + 0.1) + 2 * x
f, ax = plt.subplots()
ax.plot(x, y, '.', alpha=0.05)
plt.show()
I want to study the behaviour of y (location, width, ...) as a function of x. I am not interested in the distribution of x so I will normalized it.
f, ax = plt.subplots()
xbins = np.linspace(0, 25, 50)
ybins = np.linspace(-20, 50, 50)
H, xedges, yedges = np.histogram2d(y, x, bins=(ybins, xbins))
norm = np.sum(H, axis = 0)
H /= norm
ax.pcolor(xbins, ybins, np.nan_to_num(H), vmax=.4)
plt.show()
I can plot histogram, but I want boxplot
binning = np.concatenate(([0], np.sort(np.random.random(20) * 25), [25]))
idx = np.digitize(x, binning)
data_to_plot = [y[idx == i] for i in xrange(len(binning))]
f, ax = plt.subplots()
midpoints = 0.5 * (binning[1:] + binning[:-1])
widths = 0.9 * (binning[1:] - binning[:-1])
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
majorLocator = MultipleLocator(2)
ax.boxplot(data_to_plot, positions = midpoints, widths=widths)
ax.set_xlim(0, 25)
ax.xaxis.set_major_locator(majorLocator)
ax.set_xlabel('x')
ax.set_ylabel('median(y)')
plt.show()
Is there an automatic way to do that, like ax.magic(x, y, binning)? Is there a better way to do that? (Have a look to https://root.cern.ch/root/html/TProfile.html for example, which plot the mean and the error of the mean as error bars)
In addition, I want to minize the memory footprint (my real data are much more than 100000), I am worried about data_to_plot, is it a copy?
I've asked a similar question before (How to set a fixed/static size of circle marker on a scatter plot?), but now I wanna do it in 3D. How can I do that?
thanks
As in the 2D case, you need to draw the spheres yourself. If you want nicely shaped spheres this means to draw many patches and thus gets slow quite quickly.
Here's a basic way of doing it:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
def plot_shere(ax, x, y, z, r, resolution=100, **kwargs):
""" simple function to plot a sphere to a 3d axes """
u = np.linspace(0, 2 * np.pi, resolution)
v = np.linspace(0, np.pi, resolution)
xx = r * np.outer(np.cos(u), np.sin(v)) + x
yy = r * np.outer(np.sin(u), np.sin(v)) + y
zz = r * np.outer(np.ones(np.size(u)), np.cos(v)) + z
ax.plot_surface(xx, yy, zz, rstride=4, cstride=4, **kwargs)
# create some random data (set seed to make it reproducable)
np.random.seed(0)
(x,y,z) = np.random.randint(0,10,(3,5))
r = np.random.randint(2,4,(5,))
# set up the figure
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# loop through the data and plot the spheres
for p in zip(x,y,z,r):
plot_shere(ax, *p, edgecolor='none', color=np.random.rand(3))
# set the axes limits and show the plot
ax.set_ylim([-4,14])
ax.set_xlim([-4,14])
ax.set_zlim([-4,14])
plt.show()
Result: