Plotting log function with two variables - python

I'm looking to plot some function but the logarithm function keeps giving me this error:
TypeError: only size-1 arrays can be converted to Python scalars
This is the code I'm using:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import math
%matplotlib notebook
def f10(x,y):
return x*math.log(y,2) + y*math.log(x,2)
x = np.linspace(-100, 100, 100)
y = np.linspace(-100, 100, 100)
#x = np.arange(-3.2, 3.2, 0.2)
#y = np.arange(-3.2, 3.2, 0.2)
X, Y = np.meshgrid(x, y)
Z = f10(X,Y)
fig = plt.figure(figsize = (10,10))
ax = plt.axes(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,cmap='jet', edgecolor = 'none')
ax.set_title("Surface", fontsize = 13)
ax.set_xlabel('x', fontsize = 11)
ax.set_ylabel('y', fontsize = 11)
ax.set_zlabel('Z', fontsize = 11)
plt.xlabel('x')
plt.ylabel('y')
func = np.vectorize(f10)
fig, ax = plt.subplots(1)
p=ax.contour(X, Y, func(X,Y))
fig.colorbar(surf)
plt.show()

Related

Surface plot in matplotlib

I am having trouble performing surface plot operation in Python, using the matplotlib library. Below is my code snippet
However, I get an error stating that 'Figure' object has no property 'projection'. Without using the projection property, I get an error that ax1 has no function called plot_surface
How can I resolve this error ?
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)
data = 2*(np.sin(X) + np.sin(3*Y))
fig, (ax, ax1) = plt.subplots(2, 1, projection='3d')
fig.set_figheight(height/100)
fig.set_figwidth(width/100)
fig.set_dpi(100)
im = ax.pcolormesh(X, Y, data, cmap='viridis')
cbar = plt.colorbar(im, ax=ax)
cbar.set_label('ColorbarLabel', size=15)
surf = ax1.plot_surface(X, Y, data, cmap='virdis')
cbar1 = plt.colorbar(surf, ax=ax1)
cbar1.set_label('Colorbar2', size=15)
ax.set_xlabel('x_label')
ax.set_ylabel('y_label')
ax.set_title('Title')
Seems like I can solve the problem by adding subplots seperately to the figure object. Is this the most efficient and correct way to achieve my goals ?
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
import pyautogui
from scipy import stats
x = pyautogui.size()
width = x.width
height = x.height
x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)
data = 2*(np.sin(X) + np.sin(3*Y))
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1)
ax1 = fig.add_subplot(2, 1, 2, projection='3d')
fig.set_figheight(height/100)
fig.set_figwidth(width/100)
fig.set_dpi(100)
im = ax.pcolormesh(X, Y, data, cmap='viridis')
cbar = plt.colorbar(im, ax=ax)
cbar.set_ticks([0.2, 0.4, 0.6, 0.8])
cbar.set_ticklabels(["A", "B", "C", "D"])
cbar.set_label('ColorbarLabel', size=15)
surf = ax1.plot_surface(X, Y, data, cmap='jet')
cbar1 = plt.colorbar(surf, ax=ax1)
cbar.set_label('Colorbar2', size=15)
ax.set_xlabel('x_label')
ax.set_ylabel('y_label')
ax.set_title('Title')

Python - Plotting two 3D graphs with a contour map

I am trying to plot a figure in Python with two 3D graphs (same function, different angles) and a 2D contour map of the same function and I'm not sure why but the two first figures are okay and the contour map is weird, it appears at the bottom of the two first figures and the sizing is all weird (see the picture attached). Is there a way to place the map at the right of the 2 other figures and to resize it to make it more like a square?
Thank you for your help.
Here's my code :
import numpy as np
import matplotlib.pylab as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-5, 5, 0.01)
y = np.arange(-5, 5, 0.01)
X, Y = np.meshgrid(x, y)
Z = 5 + (10 * X**2 + 20 * Y**2) * np.exp((-X**2)-(Y**2)) + 3 *np.sin(X) - np.sin(Y)
fig = plt.figure(figsize=(15,5))
ax1 = plt.subplot(131, projection='3d')
surf1 = ax1.plot_surface(X, Y, Z, cmap=cm.coolwarm)
ax2 = plt.subplot(132, projection='3d')
surf2 = ax2.plot_surface(X, Y, Z, cmap=cm.coolwarm)
for angle in range(0,360):
ax2.view_init(20, angle)
plt.pause(.001)
ax3 = plt.subplot(133)
surf3 = ax3.contour(X, Y, Z, colors='black', linestyles='dashed')
plt.clabel(surf3, fmt = '%.0f', inline=True, fontsize=8)
ax1.set_xlabel('X')
ax2.set_xlabel('X')
ax3.set_xlabel('X')
ax1.set_ylabel('Y')
ax2.set_ylabel('Y')
ax3.set_ylabel('Y')
ax1.set_zlabel('Z')
ax2.set_zlabel('Z')
plt.show()
Got it:
import numpy as np
import matplotlib.pylab as plt
import matplotlib.cm as cm
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-5, 5, 0.01)
y = np.arange(-5, 5, 0.01)
X, Y = np.meshgrid(x, y)
Z = 5 + (10 * X**2 + 20 * Y**2) * np.exp((-X**2)-(Y**2)) + 3 *np.sin(X) - np.sin(Y)
fig = plt.figure(figsize=(15,5))
ax1 = fig.add_subplot(1, 3, 1, projection='3d')
surf1 = ax1.plot_surface(X, Y, Z, cmap=cm.coolwarm)
ax3 = fig.add_subplot(1, 3, 2)
surf3 = ax3.contour(X, Y, Z, colors='black', linestyles='dashed')
plt.clabel(surf3, fmt = '%.0f', inline=True, fontsize=8)
ax2 = fig.add_subplot(1, 3, 3, projection='3d')
surf2 = ax2.plot_surface(X, Y, Z, cmap=cm.coolwarm)
for angle in range(0,360):
ax2.view_init(20, angle)
plt.pause(.001)
ax1.set_xlabel('X')
ax2.set_xlabel('X')
ax3.set_xlabel('X')
ax1.set_ylabel('Y')
ax2.set_ylabel('Y')
ax3.set_ylabel('Y')
ax1.set_zlabel('Z')
ax2.set_zlabel('Z')
plt.show()

Python plt.axis('Equal') xlim

I have a simple 3D surface plot in which I want the axes to be equal in all directions.
I have the following piece of code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
X = np.array([-100, 0, 100])
Y = np.array([ 0, 10, 20])
X_grid, Y_grid = np.meshgrid(X,Y)
Z_grid = np.matrix('0 10 4;'
'1 11 3;'
'0 10 5')
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X_grid, Y_grid, Z_grid, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=1, antialiased=True)
plt.axis('Equal')
which yields this plot:
I then have to manually zoom out to get proper axis limits.
I have tried plt.xlim(-100,100), but it doesn't seem to respond?
Also, the plt.axis('Equal') doesn't seem to apply to the z-axis?
The plot should look like this:
You can easily adapt the strategies from the link in the comment so the operations just affect the X-Y plane:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
X = np.array([-100, 0, 100])
Y = np.array([ 0, 10, 20])
X_grid, Y_grid = np.meshgrid(X,Y)
Z_grid = np.matrix('0 10 4;'
'1 11 3;'
'0 10 5')
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X_grid, Y_grid, Z_grid, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=1, antialiased=True)
max_range = np.array([X_grid.max()-X_grid.min(), Y_grid.max()-Y_grid.min()]).max() / 2.0
mid_x = (X_grid.max()+X_grid.min()) * 0.5
mid_y = (Y_grid.max()+Y_grid.min()) * 0.5
ax.set_xlim(mid_x - max_range, mid_x + max_range)
ax.set_ylim(mid_y - max_range, mid_y + max_range)
plt.show()
Output:

Surface_plot: Add legend to facecolors

Let's say I'm plotting something with two different surface colors, as follows in colors red and blue. I want to add two items to the legend, with a custom string and the color for each color in my set.
How would I do that?
# generate the plot
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
from numpy import random
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
col1, col2 = cm.jet(np.array([0.1, 0.9]))
my_choice = random.choice([0, 1], size=X.shape)
my_color = my_choice[..., None] * col1[None, None, :] + (1 - my_choice)[..., None] * col2[None, None, :]
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors = my_color,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
# customstrings for legend:
myLegendLabels = {0: 'very red', 1: 'very blue'}
You can do that with "proxy artists":
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
from numpy import random
# generate the plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
col1, col2 = cm.jet(np.array([0.1, 0.9]))
my_choice = random.choice([0, 1], size=X.shape)
my_color = my_choice[..., None] * col1[None, None, :] + (1 - my_choice)[..., None] * col2[None, None, :]
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors = my_color,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
# Add legend with proxy artists
col1_patch = mpatches.Patch(color=col1, label='very blue')
col2_patch = mpatches.Patch(color=col2, label='very red')
plt.legend(handles=[col1_patch, col2_patch])
Result:

matplotlib surface plot extends past axis limits

How do I make a nice paraboloid in Matplotlib that looks like
All I can get is this,
where the top is not "cut off". I've tried just dropping all values of the Z array outside of the radius of the parabola at the top, but that gives very jagged edges. Can someone help me?
Here is my code:
from matplotlib import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
import math
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
Z = (X**2 + Y**2)
ax.set_zlim(-10, 20)
ax.plot_surface(X, Y, Z, alpha=0.9, rstride=4, cstride=4, linewidth=0.5, cmap=cm.summer)
plt.show()
For future reference, I had a thought to parametrize the surface in cylindrical coordinates, and it looks exactly how I want it:
from matplotlib import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
import math
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = T = np.arange(0, 2*pi, 0.01)
r, T = np.meshgrid(r, T)
#Parametrise it
X = r*np.cos(T)
Y = r*np.sin(T)
Z = r**2
ax.plot_surface(X, Y, Z, alpha=0.9, rstride=10, cstride=10, linewidth=0.5, cmap=cm.summer)
plt.show()
I guess it makes sense: when working with a cylindrical object, use cylindrical coordinates!
Manual data clipping
One approach I've seen that works is to manually clip the data; e.g. your example would be updated to
from matplotlib import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
import math
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
Z = (X**2 + Y**2)
ax.set_zlim(-10, 20)
for i in range(len(X)):
for j in range(len(Y)):
if (Z[j,i] < -10) or (Z[j,i] > 20):
Z[j,i] = NaN
ax.plot_surface(X, Y, Z, alpha=0.9, rstride=4, cstride=4, linewidth=0.5, cmap=cm.summer)
plt.show()
Note
This can be done concisely for this case using
Z[Z>20] = NaN
Resulting in

Categories

Resources