here is the code im using and I've also attached the output. I'd like to plot a two dimensional lognorm function as a 3d surface, the above code is supposed to do this however the output results in the entire plane being skewed rather than just the z values. any help or suggestions would be greatly appreciated.
dx = 90 - (-90)
dy = 90 - (-90)
c = [dx + dx/2.0, dy+dy/2.0]
z = np.zeros((400, 400))
x = np.linspace(-90, 90, 400)
y = x.copy()
for i in range(len(x)):
for j in range(len(y)):
p =[x[i], y[j]]
d = math.sqrt((p[0]-c[0])**2 + (p[1]-c[1])**2)
t = d
z[i][j] = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(x,y, z, cmap = 'viridis')
plt.show()
output of the provided code
ideally I'd like for it to look something like this.
this is the image here
I think you wanted to plot a 3D surface and here is an example:
#!/usr/bin/python3
# 2018/10/25 14:44 (+0800)
# Plot a 3D surface
from scipy.stats import norm, lognorm
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
xy = np.linspace(-5, 5, 400)
xx, yy = np.meshgrid(xy)
t = np.sqrt(xx**2 + yy**2)
zz = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(xx,yy, zz, cmap = 'viridis')
plt.show()
Related
I have a 3d plot made using matplotlib. I now want to fill the vertical space between the drawn line and the x,y axis to highlight the height of the line on the z axis. On a 2d plot this would be done with fill_between but there does not seem to be anything similar for a 3d plot. Can anyone help?
here is my current code
from stravalib import Client
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
... code to get the data ....
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
zi = alt
x = df['x'].tolist()
y = df['y'].tolist()
ax.plot(x, y, zi, label='line')
ax.legend()
plt.show()
and the current plot
just to be clear I want a vertical fill to the x,y axis intersection NOT this...
You're right. It seems that there is no equivalent in 3D plot for the 2D plot function fill_between. The solution I propose is to convert your data in 3D polygons. Here is the corresponding code:
import math as mt
import matplotlib.pyplot as pl
import numpy as np
import random as rd
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
# Parameter (reference height)
h = 0.0
# Code to generate the data
n = 200
alpha = 0.75 * mt.pi
theta = [alpha + 2.0 * mt.pi * (float(k) / float(n)) for k in range(0, n + 1)]
xs = [1.0 * mt.cos(k) for k in theta]
ys = [1.0 * mt.sin(k) for k in theta]
zs = [abs(k - alpha - mt.pi) * rd.random() for k in theta]
# Code to convert data in 3D polygons
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
#list is necessary in python 3/remove for python 2
v.append(list(zip(x, y, z)))
poly3dCollection = Poly3DCollection(v)
# Code to plot the 3D polygons
fig = pl.figure()
ax = Axes3D(fig)
ax.add_collection3d(poly3dCollection)
ax.set_xlim([min(xs), max(xs)])
ax.set_ylim([min(ys), max(ys)])
ax.set_zlim([min(zs), max(zs)])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pl.show()
It produces the following figure:
I hope this will help you.
I am trying to use the colormap feature of a 3d-surface plot in matplotlib to color the surface based on values from another array instead of the z-values.
The surface plot is created and displayed as follows:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def gauss(x, y, w_0):
r = np.sqrt(x**2 + y**2)
return np.exp(-2*r**2 / w_0**2)
x = np.linspace(-100, 100, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x, y)
Z = gauss(X, Y, 50)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap='jet')
Now instead of coloring based on elevation of the 3d-surface, I am looking to supply the color data for the surface in form of another array, here as an example a random one:
color_data = np.random.uniform(0, 1, size=(Z.shape))
However, I did not find a solution to colorize the 3d-surface based on those values. Ideally, it would look like a contourf plot in 3d, just on the 3d surface.
You can use matplotlib.colors.from_levels_and_colors to obtain a colormap and normalization, then apply those to the values to be colormapped.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors
x = np.linspace(-100, 100, 101)
y = np.linspace(-100, 100, 101)
X, Y = np.meshgrid(x, y)
Z = np.exp(-2*np.sqrt(X**2 + Y**2)**2 / 50**2)
c = X+50*np.cos(Y/20) # values to be colormapped
N = 11 # Number of level (edges)
levels = np.linspace(-150,150,N)
colors = plt.cm.get_cmap("RdYlGn", N-1)(np.arange(N-1))
cmap, norm = matplotlib.colors.from_levels_and_colors(levels, colors)
color_vals = cmap(norm(c))
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, facecolors=color_vals, rstride=1, cstride=1)
plt.show()
I am trying to visualize a function of 3 parameters over a cube in R^3 to get an idea of the smoothness of the function. An example of this problem is shown in the sample code below
%pylab
from mpl_toolkits.mplot3d import Axes3D
import itertools
x = np.linspace(0,10,50)
y = np.linspace(0,15,50)
z = np.linspace(0,8,50)
points = []
for element in itertools.product(x, y, z):
points.append(element)
def f(vals):
return np.cos(vals[0]) + np.sin(vals[1]) + vals[2]**0.5
fxyz = map(f, points)
xi, yi, zi = zip(*points)
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xi, yi, zi, c=fxyz, alpha=0.5)
plt.show()
The problem with this approach is that the inside of the cube cannot be visualized. Is there a better way to graph a function over some dense subset of R^3?
As #HYRY and #nicoguaro suggested in the comments above, Mayavi is much better suited for this type of work. There is a good set of examples here that I used for reference. Here is what I came up with
import numpy as np
from mayavi import mlab
x = np.linspace(0,10,50)
y = np.linspace(0,15,50)
z = np.linspace(0,8,50)
X, Y, Z = np.meshgrid(x, y, z)
s = np.cos(X) + np.sin(Y) + Z**0.5
b1 = np.percentile(s, 20)
b2 = np.percentile(s, 80)
mlab.pipeline.volume(mlab.pipeline.scalar_field(s), vmin=b1, vmax=b2)
mlab.axes()
mlab.show()
After which I rotated the figure to desired angles with the GUI and saved desired views
I want to plot a donut and my script is
import numpy as np
import matplotlib.pyplot as plt
pi,sin,cos = np.pi,np.sin,np.cos
r1 = 1
r2 = 2
theta = np.linspace(0,2*pi,36)
x1 = r1*cos(theta)
y1 = r1*sin(theta)
x2 = r2*cos(theta)
y2 = r2*sin(theta)
How to get a donut with red filled area ?
You can traverse the boundaries of the area in closed curve, and use fill method to fill the area inside this closed area:
import numpy as np
import matplotlib.pyplot as plt
n, radii = 50, [.7, .95]
theta = np.linspace(0, 2*np.pi, n, endpoint=True)
xs = np.outer(radii, np.cos(theta))
ys = np.outer(radii, np.sin(theta))
# in order to have a closed area, the circles
# should be traversed in opposite directions
xs[1,:] = xs[1,::-1]
ys[1,:] = ys[1,::-1]
ax = plt.subplot(111, aspect='equal')
ax.fill(np.ravel(xs), np.ravel(ys), edgecolor='#348ABD')
plt.show()
This can easily be applied to any shape, for example, a pentagon inside or outside of a circle:
You can do this by plotting the top and bottom halves separately:
import numpy as np
import matplotlib.pyplot as plt
inner = 5.
outer = 10.
x = np.linspace(-outer, outer, 1000, endpoint=True)
yO = outer*np.sin(np.arccos(x/outer)) # x-axis values -> outer circle
yI = inner*np.sin(np.arccos(x/inner)) # x-axis values -> inner circle (with nan's beyond circle)
yI[np.isnan(yI)] = 0. # yI now looks like a boulder hat, meeting yO at the outer points
ax = plt.subplot(111)
ax.fill_between(x, yI, yO, color="red")
ax.fill_between(x, -yO, -yI, color="red")
plt.show()
Or you can use polar coordinates, though whether this is beneficial depends on the broader context:
import numpy as np
import matplotlib.pyplot as plt
theta = np.linspace(0., 2.*np.pi, 80, endpoint=True)
ax = plt.subplot(111, polar=True)
ax.fill_between(theta, 5., 10., color="red")
plt.show()
It's a bit of a hack but the following works:
import numpy as np
import matplotlib.pyplot as plt
pi,sin,cos = np.pi,np.sin,np.cos
r1 = 1
r2 = 2
theta = np.linspace(0,2*pi,36)
x1 = r1*cos(theta)
y1 = r1*sin(theta)
x2 = r2*cos(theta)
y2 = r2*sin(theta)
fig, ax = plt.subplots()
ax.fill_between(x2, -y2, y2, color='red')
ax.fill_between(x1, y1, -y1, color='white')
plt.show()
It plots the whole area of your donut in red and then plots the central "hole" in white.
The answer given by tom10 is ten ;)
But if you want to define the circle (donut) origin is simple, just add the position x,y in the x, yI, yO and -yO and -yI, like this:
...
pos = [4,2]
ax.fill_between(x+pos[0], yI+pos[1], yO+pos[1], color=color)
ax.fill_between(x+pos[0], -yO+pos[1], -yI+pos[1], color=color)
...
REF Example: https://pastebin.com/8Ew4Vthb
As the title suggests, I'm trying to plot a Basemap map on the z=0 surface of a matplotlib.mplot3d lineplot. I know the Axes3D object is capable of plotting on the z=0 surface (via Axes3D.plot, Axes3D.scatter, etc.), but I can't figure out how to do so with a Basemap object. Hopefully the code below shows what I need clearly enough. Any ideas would be much appreciated!
import matplotlib.pyplot as pp
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
# make sample data for 3D lineplot
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
# make the 3D line plot
FIG = ct.pp.figure()
AX = Axes3D(FIG)
AX.plot(x, y, z, '-b')
# make the 2D basemap
### NEEDS TO SOMEHOW BE AT z=0 IN FIG
M = ct.Basemap(projection='stere', width=3700e3, height=2440e3,
lon_0=-5.0, lat_0=71.0, lat_ts=71.0,
area_thresh=100, resolution='c')
PATCHES = M.fillcontinents(lake_color='#888888', color='#282828')
Just add your map as a 3d collection to the Axes3D instance:
import numpy as np
import matplotlib.pyplot as pp
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-500, 500, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
FIG = pp.figure()
AX = Axes3D(FIG)
AX.plot(x, y, z, '-b')
M = Basemap(projection='stere', width=3700e3, height=2440e3,
lon_0=-5.0, lat_0=71.0, lat_ts=71.0,
area_thresh=100, resolution='c')
AX.add_collection3d(M.drawcoastlines())
AX.grid(True)
pp.draw()
pp.show()
AX.add_collection3d(M.drawcoastlines())
works but
PATCHES = M.fillcontinents(lake_color='#888888', color='#282828')
does not work.
As soon as you add color fill you get an error similar to: "AttributeError: 'Polygon' object has no attribute 'do_3d_projection'"
M.fillcontinents(lake_color='#888888', color='#282828')`
returns an array of Polygons, not one of the inputs required by add_collection(). collect.PatchCollection() does not seem to work either.
So what do you use to add `M.fillcontinents(lake_color='#888888', color='#282828') to a 3D plot?