I'm trying to create an animation with two subplots--one 3D and another 2D. I can't seem to figure out if there is a way to get better font rendering from the 2D axes however. I tried playing around with various settings with font_manager, and even changing the frame_format to raw, but I've had no success. Does anyone have any ideas how to fix this? I get the same results with mpeg4.
The strange thing is that the 3D figure seems to render the font properly.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
w, h = matplotlib.figure.figaspect(.5)
fig = plt.figure(figsize=(w,h))
ax3d = fig.add_subplot(121, projection='3d')
ax2d = fig.add_subplot(122)
ax3d.set_xlim(-3, 3)
ax3d.set_ylim(-3, 3)
ax3d.azim = -90
ax3d.elev = 0
ax3d.set_title('Car on Parking Ramp')
ax2d.set_xlim(-20,20)
ax2d.set_ylim(-20,20)
ax2d.set_ylabel('y')
ax2d.set_xlabel('x')
ax2d.set_title('Intersection with z=0')
''' Helix '''
K = 3 ## Angular velocity
H = 2*np.pi ## Height
t = np.linspace(0, H, 100, endpoint=True)
x = np.cos(K*t)
y = np.sin(K*t)
z = H - t
ax3d.plot(x, y, z, color='k')
''' z = 0 Plane '''
xx, yy = np.meshgrid([-20,20], [-20,20])
ax3d.plot_surface(xx, yy, 0, alpha=0.3, facecolor='b', rstride=1, cstride=1, shade=True)
ax3d.set_axis_off()
''' Tangent Line Data '''
xdata = np.array([ np.cos(K*t), np.cos(K*t) - K*(H - t)*np.sin(K*t) ])
ydata = np.array([ np.sin(K*t), np.sin(K*t) + K*(H - t)*np.cos(K*t) ])
''' Graph Lines '''
proj, = ax2d.plot([],[])
tangent, = ax3d.plot([], [], [], color='b')
def update_graph(n, tangent, proj, xdata, ydata):
tangent.set_data(xdata[:,n],
ydata[:,n])
tangent.set_3d_properties([H - t[n], 0])
proj.set_xdata(xdata[1,:n])
proj.set_ydata(ydata[1,:n])
ani = animation.FuncAnimation(fig, update_graph, len(t),
fargs=(tangent, proj, xdata, ydata), interval=75, blit=True)
ani.save('im.gif', writer='imagemagick', fps=10)
#ani.save('im.mp4', extra_args=['-vcodec', 'libx264'])
For people who face the same issue, it's indeed related to matplotlib backend.
Using different backend might help. In my case, the
%matplotlib nbagg
solved it (thanks to the linked question: Pixelated fonts when plot is saved as jpeg) .
Related
Note:
This is not a conversion question. It is meant to see if Python has the capability to produce 3D plot like Matlab.
I have created a Matlab plot as follows:
I tried to plot it using Python but I could not get it as good as Matlab. Is there any packages that can plot the above as good as the original one? If it is please convert my code to a Python version. Here is my Matlab code.
set(groot,'defaultAxesTickLabelInterpreter','latex');
set(groot,'defaulttextinterpreter','latex');
set(groot,'defaultLegendInterpreter','latex');
x0=0;
y0=0;
width=3000;
height=2000;
set(gcf,'position',[x0,y0,width,height])
[X,Y] = meshgrid(-1:.01:1);
a = 3;
b = 2;
Z = a*X.^2 + b*Y.^2;
subplot(1,3,1)
s = surf(X,Y,Z,'FaceColor','r', 'FaceAlpha',0.5, 'EdgeColor','none');
s.EdgeColor = 'none';
xlabel('$x_1$','Interpreter','latex','FontSize', 15)
ylabel('$x_2$','Interpreter','latex','FontSize', 15)
zlabel('$f(\mathbf{x};\mathbf{\theta})$','Interpreter','latex','FontSize', 15)
legend({'$f([x_1, x_2]^\top; [\theta_1=3,\theta_2=2]^\top)=3x_1^2+2x_2^2$'},'interpreter','latex','FontSize', 10)
subplot(1,3,2)
Z2 = a*X.^2 ;
s2 = surf(X,Y,Z2,'FaceColor','b', 'FaceAlpha',0.5, 'EdgeColor','none');
s2.EdgeColor = 'none';
xlabel('$x_1$','Interpreter','latex','FontSize', 15)
ylabel('$x_2$','Interpreter','latex','FontSize', 15)
zlabel('$f(\mathbf{x};\mathbf{\theta})$','Interpreter','latex','FontSize', 15)
legend({'$f([x_1, x_2]^\top; [\theta_1=3,\theta_2=0]^\top)=3x_1^2$'},'interpreter','latex','FontSize', 10)
subplot(1,3,3)
s3 = surf(X,Y,Z,'FaceColor','r', 'FaceAlpha',0.5, 'EdgeColor','none');
s3.EdgeColor = 'none';
hold
s4 = surf(X,Y,Z2,'FaceColor','b', 'FaceAlpha',0.5, 'EdgeColor','none');
s4.EdgeColor = 'none';
xlabel('$x_1$','Interpreter','latex','FontSize', 15)
ylabel('$x_2$','Interpreter','latex','FontSize', 15)
zlabel('$f(\mathbf{x};\mathbf{\theta})$','Interpreter','latex','FontSize', 15)
legend({'$f(\mathbf{x};\mathbf{\theta})=3x_1^2+2x_2^2$', '$f(\mathbf{x};\mathbf{\theta})=3x_1^2$'},'interpreter','latex','FontSize', 10)
Yes.
numpy + plotly is an effective Matlab replacement - you may recognize some of the code :). As a benefit, the plots render as html, which means they are highly portable, save as a single file, and can be embedded in a webpage. There may be small details that are different (I don't know the current status of latex axis labels), but, provided you have python, numpy and plotly installed, the following is a good replacement of your first plot:
import plotly.graph_objects as go
import numpy as np
x = np.arange(-1,1,.01)
y = np.arange(-1,1,.01)
X,Y = np.meshgrid(x,y)
a = 3
b = 2
Z = a*X**2 + b*Y**2
fig = go.Figure(
data=[go.Surface(z=Z, x=x, y=y, colorscale="Reds", opacity=0.5)])
fig.update_layout(
title='My title',
autosize=False,
width=500,
height=500,
margin=dict(l=65, r=50, b=65, t=90),
scene_aspectmode='cube'
)
fig.show()
Notice that the go-to plotting package in python is Matplotlib. IMO, it inherited all the worst parts of Matlab's plotting and none of the good (performant rendering). Plotly superior from a performance (esp 3D rendering), interactivity, and API standpoint.
For 3D charting in Python I've had the best results with matplotlib.pyplot.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D, get_test_data
from matplotlib import cm
import numpy as np
import random
X_k_list = range(1, 100, 10)
Y_p_list = [ float(x)/100.0 for x in range(1, 100, 10) ]
# set up a figure twice as wide as it is tall
fig = plt.figure(figsize=plt.figaspect(0.5))
# set up the axes for the first plot
ax = fig.add_subplot(1, 1, 1, projection='3d')
# plot a 3D surface like in the example mplot3d/surface3d_demo
X, Y = np.meshgrid(X_k_list, Y_p_list)
def critical_function(b, c):
num = random.uniform(0, 1) * 10.0
return num + (b * c)
Z_accuracy = X.copy()
Z_accuracy = Z_accuracy.astype(np.float32)
for i in range(len(X_k_list)):
for j in range(len(Y_p_list)):
Z_accuracy[j][i] = critical_function(Y_p_list[j], X_k_list[i])
surf = ax.plot_surface(X, Y, Z_accuracy,
rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.colorbar(surf, shrink=0.5, aspect=10)
plt.show()
https://www.python-graph-gallery.com/371-surface-plot
You can increase the smoothness of the chart by adding more datapoints, rotate the graph along the x,y,z axis, with the mouse and you can add a title, legend and other eye candy.
matplotlib.mplot3d looks like it does euclidian continuous surfaces
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
ax = plt.figure().add_subplot(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
cset = ax.contour(X, Y, Z, extend3d=True, cmap=cm.coolwarm)
ax.clabel(cset, fontsize=9, inline=True)
plt.show()
https://matplotlib.org/stable/gallery/mplot3d/contour3d_2.html#sphx-glr-gallery-mplot3d-contour3d-2-py
You're using matlab's meshgrid(...) tool to generate x,y,z data. Python can achieve the same results with numpy.meshgrid fed into matplotlib.pyplot thustly.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
https://jakevdp.github.io/PythonDataScienceHandbook/04.12-three-dimensional-plotting.html
Matplotlib offers various options for the drawstyle. steps-mid does the following:
The steps variants connect the points with step-like lines, i.e. horizontal lines with vertical steps. [...]
'steps-mid': The step is halfway between the points.
This works fine when the x-scale is linear however when using a log-scale it still seems to compute the step points by averaging in data-space rather than log-space. This leads to data points not being centered between the steps.
import matplotlib.pyplot as plt
import numpy as np
x = np.logspace(0, 10, num=10)
y = np.arange(x.size) % 2
fig, ax = plt.subplots()
ax.set_xscale('log')
ax.plot(x, y, drawstyle='steps-mid', marker='s')
Is there a way to use step-like plotting together with x-log-scale such that the steps are centered between data points in log-space?
I don't know of a way other than building the steps correctly in log space yourself:
import matplotlib.pyplot as plt
import numpy as np
x = np.logspace(0, 10, num=10)
y = np.arange(x.size) % 2
def log_steps_mid(x, y, **kwargs):
x_log = np.log10(x)
x_log_mid = x_log[:-1] + np.diff(x_log)/2
x_mid = 10 ** x_log_mid
x_mid = np.hstack([x[0],
np.repeat(x_mid, 2),
x[-1]])
y_mid = np.repeat(y, 2)
ax.plot(x_mid, y_mid, **kwargs)
fig, ax = plt.subplots()
ax.set_xscale('log')
ax.plot(x, y, ls='', marker='s', color='b')
log_steps_mid(x, y, color='b')
I draw the discretized surface of a disk using Axes3d.plot_wireframe, and on the same graph I superimpose a point corresponding to the center of the disk using Axes3d.scatter (attached figures). My problem is that the point is only visible when the disc is seen in profile. I would like it to be visible regardless of the view. I am also joining part of my script (sorry if it doesn't appear as it should).
I tried changing the order of the plots in my script, I tried adding a 'zorder' argument in both plots, and I also tried changing the marker size in the scatter plot but it did not work.
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import pylab as plt
import numpy as np
# data to build the disc
r = np.linspace(0, 5e-3, 30)
psi = np.linspace(0, 2*np.pi, 30)
n = np.array([0.70710678, 0, 0.70710678])
e2 = np.array([0.70710678, 0, -0.70710678])
e3 = np.array([0, 1, 0])
Oc = np.array([0, 0, 0])
R, PSI = np.meshgrid(r, psi)
OpP = np.zeros((len(r),len(psi),3), dtype=float)
OpP[:,:,0] = np.zeros((len(r),len(psi)), dtype=float)
OpP[:,:,1] = R*np.cos(PSI)
OpP[:,:,2] = R*np.sin(PSI)
Mrot = np.zeros((3,3), dtype=float)
Mrot[0,:] = n
Mrot[1,:] = e2
Mrot[2,:] = e3
Ocx = Oc[0]; Ocy = Oc[1]; Ocz = Oc[2]
xp = Ocx + np.dot(OpP, Mrot)[:,:,0]
yp = Ocy + np.dot(OpP, Mrot)[:,:,1]
zp = Ocz + np.dot(OpP, Mrot)[:,:,2]
# Plotting
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(zp,yp,xp, label='piston')
ax.scatter(Ocz, Ocy, Ocx, zdir='z', s=40, color='red', label='Oc')
plt.gca().invert_yaxis()
plt.legend()
plt.show()
Profile view of the disc:
Oriented disc:
Some values of plot's parameters can be set to get better plot. Try replacing relevant lines of code with these:
ax.plot_wireframe(zp,yp,xp, label='piston', \
linewidth=0.5, \
alpha=0.7)
ax.scatter(Ocz, Ocy, Ocx, zdir='z', \
s=80, \
color='red', label='Oc')
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()
I got a .dat file which contains the coordinates of a segment in 3d space.
The file has several lines, each single line characterizes the position at a particular time.
I tried this code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
dati = np.loadtxt('dati.dat')
t=0
p1=[dati[t,1],dati[t,2],dati[t,3]]
p2=[dati[t,4],dati[t,5],dati[t,6]]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
seg,=ax.plot(p1,p2)
def updateFigure(t,dati,seg):
p1=[dati[t,1],dati[t,2],dati[t,3]]
p2=[dati[t,4],dati[t,5],dati[t,6]]
seg.set_data(p1,p2)
return seg,
ani=animation.FuncAnimation(fig, updateFigure,iMax, fargs=(dati,seg), interval=100, blit=True)
plt.show()
The program doesn't report errors but the figure doesn't move.
The same code, a bit modified, in the 2d space works..
Instead of calling set_data, you could set seg._verts3d directly, though note that manipulating the private variable _verts3d is relying on an implementation detail, not part of the Line3D public interface:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
iMax = N = 500
theta = np.linspace(0, 6*np.pi, N)
x = np.cos(theta)
y = np.sin(theta)
z = np.linspace(0, 1, N)
step = 10
dati = np.column_stack(
[theta, x, np.roll(x, -step), np.roll(x, -2*step)
, y, np.roll(y, -step), np.roll(y, -2*step)
, z, np.roll(z, -step), np.roll(z, -2*step)])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
seg, = plt.plot([], [])
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(0, 1)
def init():
return seg,
def updateFigure(t):
p1 = dati[t, 1:4]
p2 = dati[t, 4:7]
p3 = dati[t, 7:10]
seg._verts3d = (p1, p2, p3)
return seg,
ani = animation.FuncAnimation(
fig, updateFigure
, init_func=init
, frames=iMax
, interval=5, blit=True)
plt.show()