1

I am trying to replicate the plot below done in mathematica using matplotlib in python. As you can see, it is very clean and you immediately know what you're looking at.

enter image description here

This is the python code to generate the plot:

import numpy as np import matplotlib.pyplot as plt from scipy.signal import butter from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm # Butterworth filter order order = 5 # Create a meshgrid for sigma and omega sigma = np.linspace(-1.5, 0.5, 400) omega = np.linspace(-1.5, 1.5, 400) sigma, omega = np.meshgrid(sigma, omega) # Complex frequency (s = sigma + j*omega) s = sigma + 1j * omega # Butterworth filter (analog, so we use s-domain) b, a = butter(order, 1, analog=True) w, h = s, np.polyval(b, s) / np.polyval(a, s) # Calculate the magnitude of the frequency response response = np.abs(h) response[response > 40] = 40 # 3D Plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(sigma, omega, response, cmap=cm.viridis) ax.set_zlim(0, 40) ax.set_xlabel('Sigma') ax.set_ylabel('Omega') ax.set_zlabel('Magnitude') plt.show() 

It looks way less nicer:

enter image description here

3
  • 1
    What do you like to change in your plot exactly? The angle of view, the transparency of the panes, height or mesh? Commented Dec 9, 2023 at 12:42
  • The shading so that it is easy to see where exactly the hills start to rise. Commented Dec 9, 2023 at 12:49
  • 1
    I am pretty sure you should use a solid color instead of cmap, such as color='orange' and then the shadings will be applied automatically. In fact, shade=True is the default setting. Commented Dec 9, 2023 at 13:43

2 Answers 2

3

Here I generated some data to show how it works:

import numpy as np import matplotlib.pyplot as plt X = np.linspace(0, 9.42, 500) Y = np.linspace(0, 9.42, 500) X, Y = np.meshgrid(X, Y) Z = -np.sqrt(np.sin(X)**2 + np.cos(Y + X)**2) + X**2/30 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, antialiased=False, rstride=5, cstride=5, color='#00c0e7') ax.view_init(45, 80) ax.set_xlabel('X') ax.set_ylabel('Y') plt.show() 

enter image description here

Here is your code:

import numpy as np import matplotlib.pyplot as plt from scipy.signal import butter from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm # Butterworth filter order order = 5 # Create a meshgrid for sigma and omega sigma = np.linspace(-1.5, 0.5, 400) omega = np.linspace(-1.5, 1.5, 400) sigma, omega = np.meshgrid(sigma, omega) # Complex frequency (s = sigma + j*omega) s = sigma + 1j * omega # Butterworth filter (analog, so we use s-domain) b, a = butter(order, 1, analog=True) w, h = s, np.polyval(b, s) / np.polyval(a, s) # Calculate the magnitude of the frequency response response = np.abs(h) response[response > 40] = 40 # 3D Plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(sigma, omega, response, color='#ff7300', antialiased=False, rstride=1, cstride=1) ax.set_zlim(0, 40) ax.set_xlabel('Sigma') ax.set_ylabel('Omega') ax.set_zlabel('Magnitude') 

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. But this doesn't look as good as the mathematica one.
I suggest changing other factors.
1

Here's one that takes on some of the desired aesthetic.

![enter image description here

from matplotlib.colors import LightSource # 3D Plot fig = plt.figure(figsize=(8, 8)) ax = fig.add_subplot(111, projection='3d') ax.view_init(azim=-50, elev=30, roll=0) downsample = 1 lightsource = LightSource(azdeg=50, altdeg=10) #Overlay same plot to help mitigate antialiasing artifacts for _ in range(3): surf = ax.plot_surface( sigma, omega, response, antialiased=True, color='tab:orange', lightsource=lightsource, linewidth=0, alpha=1, rstride=downsample, cstride=downsample, ) # #Alternative using trisurf # for _ in range(3): # surf = ax.plot_trisurf( # sigma.ravel()[::downsample], # omega.ravel()[::downsample], # response.ravel()[::downsample], # color='tab:orange', antialiased=True, # lightsource=lightsource, # linewidth=0, # ) for axis in [ax.xaxis, ax.yaxis, ax.zaxis]: axis.pane.fill = False axis.pane.set_edgecolor('black') ax.grid(False) ax.set_xlabel('σ', fontsize=12) ax.set_ylabel('ω', fontsize=12) ax.set_zlabel('|f(σ, ω)|', fontsize=12) ax.set_title('Butterworth frequency response') ax.set_box_aspect(aspect=None, zoom=0.9) plt.show() 

2 Comments

Thanks, but this one looks wrong.
I've now updated my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.