How can I plot a graph like this one in Python? - python

I would like to plot a graph like the one in the following picture:
I wrote the following code that plots a wave graph for each time step.
import matplotlib.pyplot as plt
import numpy as np
def u_0(x):
a = 1.0/np.cosh(2.0*(x+8.0))
b = 1.0/np.cosh((x+1.0))
return 8.0*a*a+2.0*b*b
#spatial grid
N = 100
x = np.linspace(-10,10,N)
#time
Nt = 100
tlist = np.linspace(0.0,2.0,Nt)
#velocity
c = 5.0
count = 0
for t in tlist:
u = u_0(x-c*t)
plt.figure()
plt.plot(x,u)
plt.savefig(str(count))
count = count+1
plt.close()
How can I join these pictures together and get a graph like the one in the picture?
Is there a standard way to do this?

Don't close plot and draw all on one image.
Every plot would need some offset for Y values
u += count # offset
Code
import matplotlib.pyplot as plt
import numpy as np
def u_0(x):
a = 1.0/np.cosh(2.0*(x+8.0))
b = 1.0/np.cosh((x+1.0))
return 8.0*a*a + 2.0*b*b
# spatial grid
N = 100
x = np.linspace(-10, 10, N)
# time
Nt = 100
tlist = np.linspace(0.0, 2.0, Nt)
#velocity
c = 5.0
count = 0
for t in tlist:
u = u_0(x-c*t)
u += count # offset
plt.plot(x, u)
count += 1
plt.savefig("result.png")
Image:
EDIT: Something similar in 3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # need for `projection=`
import numpy as np
def u_0(x):
a = 1.0/np.cosh(2.0*(x+8.0))
b = 1.0/np.cosh((x+1.0))
return 8.0*a*a + 2.0*b*b
#velocity
c = 5.0
#spatial grid
N = 30
x = np.linspace(-10, 10, N)
t = np.linspace(0.0, 2.0, N)
X, T = np.meshgrid(x, t)
Y = u_0(X-c*T)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, T, Y)
plt.show()
plt.savefig('result.png')

Related

How to scale legend elements down in a scatterplot matplotlib?

Lets say I have this scatterplot and would like to keep the size of the dots in the plot but in the legend I would like to have the size denoted as 1,2,... instead of 50,100,...
import numpy as np
import matplotlib.pyplot as plt
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
a2 = 300*np.random.rand(N)
sc = plt.scatter(x, y, s=a2, alpha=0.5)
plt.legend(*sc.legend_elements("sizes", num=6))
plt.show()
It depends. If the numbers you want to show are just arbitrary, i.e. unrelated to the actual sizes, you can supply a list of numbers as labels.
import numpy as np
import matplotlib.pyplot as plt
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
a2 = 300*np.random.rand(N)
sc = plt.scatter(x, y, s=a2, alpha=0.5)
plt.legend(sc.legend_elements("sizes", num=6)[0], [1,2,3,4,5])
plt.show()
If, however, there is a relation between the numbers to show and some data,
import numpy as np
import matplotlib.pyplot as plt
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
a3 = np.random.randint(1,6, size=N)
f = lambda a: 12*a**2 # function to calculate size from data
g = lambda s: np.sqrt(s/12) # inverse function to calc. data from size
sc = plt.scatter(x, y, s=f(a3), alpha=0.5)
plt.legend(*sc.legend_elements("sizes", num=5, func=g))
plt.show()

How to 4D plot with contour over cube, using matplotlib?

I would like to 4D plot over the cube (x,y,z) vs. q, using the colormap on the 3 surfaces of the cubes, where the color and contour are determined depending on the q variable. Basically, I am looking for a similar image like this:
Any help is appreciated.
See my example of 3D ABC feild
import pyvista as pv
import numpy as np
from numpy import mgrid
import matplotlib.pyplot as plt
print('initializing domain')
xmin = -800.
xmax = 800.
Lx = xmax-xmin
B0 = 1
k = 1
alpha = 2.0*np.pi*k/Lx
x, y, z = Lx*mgrid[0:1:51j, 0:1:51j, 0:1:51j]
print('initializing 3D B field')
Bx = B0*(np.sin(alpha*z) + np.cos(alpha*y))
By = B0*(np.sin(alpha*x) + np.cos(alpha*z))
Bz = B0*(np.sin(alpha*y) + np.cos(alpha*x))
B = np.column_stack((Bx.ravel(), By.ravel(), Bz.ravel()))
grid = pv.StructuredGrid(x, y, z)
grid["ABC field magnitude"] = np.linalg.norm(B, axis=1)
grid["ABC field vectors"] = B
grid.set_active_vectors("ABC field vectors")
#contours = grid.contour(8, scalars="ABC field magnitude")
#arrows = contours.glyph(orient="ABC field vectors", factor=50.0)
print('plotting')
pv.set_plot_theme('document')
p = pv.Plotter(notebook=0, shape=(1,1))
#p.background_color='white'
#p.window_size
cmap = plt.cm.get_cmap("viridis", 4)
p.add_mesh(grid, cmap=cmap)
p.show_grid()
#p.add_mesh(arrows)
#p.subplot(0,1)
#slices = grid.slice_orthogonal(x=20, y=20, z=30)
#slices = grid.slice_orthogonal()
#p.add_mesh(slices, cmap=cmap)
##p.subplot(1,0)
#p.add_mesh(contours, opacity=1)
#p.subplot(1,1)
#p.add_mesh(arrows)
#single_slice = arrows.slice(normal=[1, 1, 0])
#slices = arrows.slice_orthogonal(x=20, y=20, z=30)
#slices = grid.slice_orthogonal()
#p.add_mesh(single_slice, cmap=cmap)
p.show_grid()
p.link_views()
p.view_isometric()
p.show(screenshot='abc3d_slicing.png')
A simple answer is
import numpy as np
import matplotlib.pyplot as plt
length = 10
data = length*np.mgrid[0:1:51j, 0:1:51j, 0:1:51j].reshape(3,-1).T
contour = np.random.rand(data.shape[0])
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
data_plot = ax.scatter(data[:,0], data[:,1], data[:,2], c=contour)
fig.colorbar(data_plot)
To optimize to only boundary points
length = 10
vol_data = length*np.mgrid[0:1:51j, 0:1:51j, 0:1:51j].reshape(3,-1).T
bound_data = np.array([data_i for data_i in vol_data
if any([coord in [0, length] for coord in data_i])])
contour = np.random.rand(bound_data.shape[0])
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
data_plot = ax.scatter(bound_data[:,0], bound_data[:,1], bound_data[:,2], c=contour)
fig.colorbar(data_plot)

matplotlib : plot with periodic boundary

I have a system composed of N elliptical particles in a box with periodic boundary conditions:
import numpy as np
import matplotlib.pyplot as plt
import numpy.random as rnd
from matplotlib.patches import Ellipse
import time
infile = open ('init.txt')
N_p = 100
x, y = [], []
m = []
for i in xrange(N_p):
data = infile.readline()
raw = data.split()
x.append(float(raw[0]))
y.append(float(raw[1]))
m.append(float(raw[4]))
xnp = np.array(x)
ynp = np.array(y)
mnp = np.array(m)
fig = plt.figure(0)
ax = fig.add_subplot(111, aspect='equal')
for x, y, m in zip(xnp, ynp, mnp):
ell = Ellipse(xy=(x, y), width = 4.0, height = 2.0, angle = m)
ell.set_facecolor('blue')
ax.add_artist(ell)
ell.set_clip_box(ax.bbox)
ax.set_xlim(0, 100)
ax.set_ylim(0, 100)
fig.savefig("init.png")
plt.show()
As it is seen from the image, the ellipses are cut in the boundary, but I want to show the remainder of the ellipse in the other side of the box. How can I do this?

Vertically fill 3d matplotlib plot

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.

Create surface grid from point cloud data in Python

Here is an example creating a point cloud which I then want to fit a grided surface to. The problem comes at the end when I try to pass in meshgrid arrays to a function which interpolated the data:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# Create some point cloud data:
c = 1
a = 3
b = 4
slice = {}
t = np.linspace(0,2*np.pi,50)
for s in np.linspace(1,9,10):
c = 5*s
r = (-s**2+10.0*s)/10.0
X = r*np.cos(t)
Y = r*np.sin(t)
Z = c*(Y**2/b**2 - X**2/a**2) + c
slice[str(int(s))] = np.vstack([X,Y,Z])
# Visualize it:
fig = plt.figure()
ax = fig.gca(projection = '3d')
for k,v in slice.iteritems():
print type(v)
print np.shape(v)
X = v[0,:]
Y = v[1,:]
Z = v[2,:]
ax.scatter(X,Y,Z)
plt.show()
It looks like this:
I now need to create a surface mesh based on these points. There are multiple interpretations of surface in this case because I just have a point cloud rather than a function z = f(x,y) but the correct surface in this case should be the one that creates a hollow "warped cylinder". I thought of attacking the problem like this:
# stack all points from each slice into one vector for each coordinate:
Xs = []
Ys = []
Zs = []
for k,v in slice.iteritems():
#ax.plot_surface(X,Y,Z)
X = v[0,:]
Y = v[1,:]
Z = v[2,:]
Xs = np.hstack((Xs,X))
Ys = np.hstack((Ys,Y))
Zs = np.hstack((Zs,Z))
XX, YY = np.meshgrid(Xs,Ys)
from scipy import interpolate
f = interpolate.interp2d(Xs,Ys,Zs, kind = 'cubic')
ZZ = f(XX,YY)
which I would then be able to plot using
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.plot_surface(XX, YY, ZZ)
plt.show()
However the interpolated function does not seem to accept arrays as inputs so this method might not work. Can anyone come up with a suggestion on how to do this properly?
Edit:
Actually the data is obviously not able to be represented as a function as it would not be one to one.
I stumbled upon the same question and wondered why it has not been solved in the last 7 years. Here's my solution for any future reader based on plot_trisurf (and the corresponding code examples).
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
# Create some point cloud data:
a = 3
b = 4
# def grid of parametric variables
u = np.linspace(0,2*np.pi,50)
v = np.linspace(1,9,50)
U, V = np.meshgrid(u, v)
U, V = U.flatten(), V.flatten()
# Triangulate parameter space to determine the triangles
tri = mtri.Triangulation(U, V)
# get the transformed data as list
X,Y,Z = [],[],[]
for _u in u:
for _v in v:
r = (-_v**2+10.0*_v)/10.0
x = r*np.cos(_u)
y = r*np.sin(_u)
z = 5*_v*(y**2/b**2 - x**2/a**2) + 5*_v
X.append(x)
Y.append(y)
Z.append(z)
# Visualize it:
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.scatter(X,Y,Z, s=1, c='r')
ax.plot_trisurf(X, Y, Z, triangles=tri.triangles, alpha=.5)
plt.show()
This produces the following plot.

Categories