I'm attempting to use Python and Matplotlib to render a 3D surface of a polyhedron, given by
However my code (shown below) does not seem to draw it correctly. How should this be done instead?
Failed Attempt:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
delta = 0.1
def x_func(x):
return abs(x)
def y_func(y):
return abs(y)
def z_func(z):
return abs(z)
x = np.arange(-1, 1, delta)
x1 = x_func(x)
y = np.arange(-1, 1, delta)
y1 = y_func(y)
X, Y = meshgrid(x1, y1)
z = np.arange(-1, 1, delta)
Z = z_func(z)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.RdBu, linewidth=0.1)
Here's one solution:
import mpl_toolkits.mplot3d as a3
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import scipy as sp
# Vertex data
verts= [
(-1, -1, -1), (-1, -1, 1), (-1, 1, 1), (-1, 1, -1),
(1, -1, -1), (1, -1, 1), (1, 1, 1), (1, 1, -1)
]
# Face data
faces = np.array([
[0, 1, 2, 3], [4, 5, 6, 7], [0, 3, 7, 4], [1, 2, 6, 5],
[0, 1, 5, 4], [2, 3, 7, 6]
])
ax = a3.Axes3D(plt.figure())
ax.dist=30
ax.azim=-140
ax.elev=20
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
for i in np.arange(len(faces)):
square=[ verts[faces[i,0]], verts[faces[i,1]], verts[faces[i, 2]], verts[faces[i, 3]]]
face = a3.art3d.Poly3DCollection([square])
face.set_color(colors.rgb2hex(sp.rand(3)))
face.set_edgecolor('k')
face.set_alpha(0.5)
ax.add_collection3d(face)
plt.show()
The figure output is this:
The surface of a cube
Related
I have 3 curves and I want to fill the area between them. How should I do this?
This is what I have so far:
import numpy as np
import matplotlib.pyplot as plt
y = lambda z: -(z ** 2)
y1 = lambda x: x ** (1 / 3)
x = np.linspace(0, 2, 100)
z = np.linspace(0, 2, 100)
plt.plot(z, y(z), color='blue', label="y=-(x^2)")
plt.ylim(-2, 2)
plt.xlim(0, 2)
plt.plot(x, y1(x), color='red', label='y=x^(1/3)')
plt.plot([1, 1, 1], [0, -2, 2], color='black', label='x=1')
plt.grid(True, zorder=5)
plt.legend()
k = np.arange(0,2)
f = [0,-0.2]
p = [0,0.2]
plt.fill_between(k,f,p,interpolate=True)
plt.show()
You can use where in fill_between to take care of x = 1 line. See below:
import numpy as np
import matplotlib.pyplot as plt
y = lambda z: -(z ** 2)
y1 = lambda x: x ** (1 / 3)
x = np.linspace(0, 2, 100)
z = np.linspace(0, 2, 100)
plt.ylim(-2, 2)
plt.xlim(0, 2)
#plt.grid(True, zorder=5)
plt.plot(z, y(z), color='blue', label="y=-(x^2)")
plt.plot(x, y1(x), color='red', label='y=x^(1/3)')
plt.plot([1, 1, 1], [0, -2, 2], color='black', label='x=1')
plt.fill_between(x, y(z), y1(x), where=x<=1)
plt.legend()
plt.show()
i'm looking for the best way to create a contour plot using a numpy meshgrid.
I have excel data in columns simplyfied looking like this:
x data values: -3, -2, -1, 0, 1, 2 ,3, -3, -2, -1, 0, 1, 2, 3
y data values: 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2
z data values: 7 , 5, 6, 5, 1, 0, 9, 5, 3, 8, 3, 1, 0, 4
The x and y values define a 2d plane with the length (x-Axis) of 7 values and depth (y-Axis) of 2 values. The z values define the colour at the corresponing points (more or less a z-Axis).
I've tried:
import matplotlib.pyplot as plt
import numpy as np
x = [-3,-2,-1,0,1,2,3]
y = [1,2]
z = [7,5,6,5,1,0,9,5,3,8,3,1,0,4]
x, y = np.meshgrid(x, y)
A = np.array(z)
B = np.reshape(A, (-1, 2))
fig = plt.figure()
ax1 = plt.contourf(x, y, B)
plt.show()
I'm pretty sure i'm not getting how the meshgrid works. Do i have to use the whole List of x and y values for it to work?
How do i create a rectangular 2d plot with the length (x) of 7 and the depth (y) of 2 and the z values defining the shading/colour at the x and y values?
Thanks in advance guys!
Try
x_, y_ = np.meshgrid(x, y)
z_grid = np.array(z).reshape(2,7)
fig = plt.figure()
ax1 = plt.contourf(x_,y_,z_grid)
plt.show()
Edit: If you would like to smooth, as per your comment, you can try something like scipy.ndimage.zoom() as described here, i.e., in your case
from scipy import ndimage
z_grid = np.array(z).reshape(2,7)
z_grid_interp = ndimage.zoom(z_grid, 100)
x_, y_ = np.meshgrid(np.linspace(-3,3,z_grid_interp.shape[1]),np.linspace(1,2,z_grid_interp.shape[0]))
and then plot as before:
fig = plt.figure()
ax1 = plt.contourf(x_,y_,z_grid_interp)
plt.show()
This is one way where you use the shape of the meshgrid (X or Y) to reshape your z array. You can, moreover, add a color bar using plt.colorbar()
import matplotlib.pyplot as plt
import numpy as np
x = [-3,-2,-1,0,1,2,3]
y = [1,2]
z = np.array([7,5,6,5,1,0,9,5,3,8,3,1,0,4])
X, Y = np.meshgrid(x, y)
print (X.shape, Y.shape)
# (2, 7) (2, 7) Both have same shape
Z = z.reshape(X.shape) # Use either X or Y to define shape
fig = plt.figure()
ax1 = plt.contourf(X, Y, Z)
plt.colorbar(ax1)
plt.show()
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2, 3 )
y = np.linspace(0, 3, 4)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
plt.contour(X, Y, Z, cmap='RdGy');
In my previous question, (How to Animate multiple columns as dots with matplotlib from pandas dataframe with NaN in python), I managed to animate multiple dots from a dataframe as an animation.
However, I wanted to set a background for the animation as a network graph, so that it seems that the dots are moving on the lines of the network.
Using the code from How to Animate multiple columns as dots with matplotlib from pandas dataframe with NaN in python
I've created a new MCV example,
the code is listed below:
import random
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import math
import pandas as pd
from matplotlib import animation
#from JSAnimation import IPython_display
%matplotlib inline
# initialise graph object
G = nx.Graph()
color_map =[]
G.add_node(1, pos=(1, 0)); color_map.append('r')
G.add_node(2, pos=(2, 0)); color_map.append('r')
G.add_node(3, pos=(3, -1)); color_map.append('r')
G.add_node(4, pos=(3, 1)); color_map.append('r')
G.add_node(5, pos=(4, -1)) ;color_map.append('r')
G.add_node(6, pos=(4, 1)); color_map.append('r')
G.add_node(7, pos=(5, 0)); color_map.append('r')
G.add_node(8, pos=(6, 0)); color_map.append('r')
e = [(1, 2, 1),
(2, 3, 1),
(2, 4, 2),
(3, 5, 5),
(4, 6, 2),
(5, 7, 1),
(6, 7, 2),
(7, 8, 1)]
G.add_weighted_edges_from(e)
labels = nx.get_edge_attributes(G,'weight')
nx.draw(G,nx.get_node_attributes(G, 'pos'))
nx.draw_networkx_edge_labels(G,nx.get_node_attributes(G, 'pos'),edge_labels=labels)
nx.draw_networkx_labels(G,nx.get_node_attributes(G, 'pos'))
df_x = pd.DataFrame(data=
np.array(
[[np.NaN, np.NaN, np.NaN, np.NaN],
[1, np.nan, np.NaN,np.NaN],
[1.5, 4, np.NaN,np.NaN],
[2, 5, 3,4]]
), index= [1, 2, 3, 4], columns=[1, 2, 3, 4])
print(df_x)
df_y = pd.DataFrame(data=np.array(
[[np.NaN, np.NaN, np.NaN, np.NaN],
[0, np.nan, np.NaN,np.NaN],
[0, -1, np.NaN,np.NaN],
[0, 0, 1,1]]
), index= [1, 2, 3, 4], columns=[1, 2, 3, 4])
%matplotlib notebook
from matplotlib import animation
#from JSAnimation import IPython_display
#from IPython.display import HTML
fig = plt.figure(figsize=(10,10))
ax = plt.axes()
nx.draw(G,nx.get_node_attributes(G, 'pos'),node_size = 10)
n_steps = df_x.index
graph, = plt.plot([],[],'o')
def get_data_x(i):
return df_x.loc[i]
def get_data_y(i):
return df_y.loc[i]
def animate(i):
x = get_data_x(i)
y= get_data_y(i)
graph.set_data(x,y)
return graph,
animation.FuncAnimation(fig, animate, frames=n_steps, repeat=True, blit = True)
This creates a workable animation, which works. But however, when I use a very large dataset ( pandas dataframe index is ~8000 rows * 800 columns instead of the example pandas dataset I posted), the animation takes very long(hour or so) to render and most of the times my browser( google chrome) crashes.
So I thought is maybe due to it needs to redraw the networks graph each frame? How can I set the background as the networkx graph? From there on it is just plotting points right? My actual graph is a bit larger (~5000 nodes, ~6000 edges).
Hopes anyone can help me speed the rendering of the animation up!
After some digging around, I found no 'easy' solution to this problem when trying to animate large datasets into an animation with matplotlib in a jupyter notebook. I just decided to write everything to an mp4 file, which works just as good for animations.
My code for this including the MVC example:
import random
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import math
import pandas as pd
from matplotlib import animation
#from JSAnimation import IPython_display
%matplotlib inline
# initialise graph object
G = nx.Graph()
color_map =[]
G.add_node(1, pos=(1, 0)); color_map.append('r')
G.add_node(2, pos=(2, 0)); color_map.append('r')
G.add_node(3, pos=(3, -1)); color_map.append('r')
G.add_node(4, pos=(3, 1)); color_map.append('r')
G.add_node(5, pos=(4, -1)) ;color_map.append('r')
G.add_node(6, pos=(4, 1)); color_map.append('r')
G.add_node(7, pos=(5, 0)); color_map.append('r')
G.add_node(8, pos=(6, 0)); color_map.append('r')
e = [(1, 2, 1),
(2, 3, 1),
(2, 4, 2),
(3, 5, 5),
(4, 6, 2),
(5, 7, 1),
(6, 7, 2),
(7, 8, 1)]
G.add_weighted_edges_from(e)
labels = nx.get_edge_attributes(G,'weight')
nx.draw(G,nx.get_node_attributes(G, 'pos'))
nx.draw_networkx_edge_labels(G,nx.get_node_attributes(G, 'pos'),edge_labels=labels)
nx.draw_networkx_labels(G,nx.get_node_attributes(G, 'pos'))
df_x = pd.DataFrame(data=
np.array(
[[np.NaN, np.NaN, np.NaN, np.NaN],
[1, np.nan, np.NaN,np.NaN],
[1.5, 4, np.NaN,np.NaN],
[2, 5, 3,4]]
), index= [1, 2, 3, 4], columns=[1, 2, 3, 4])
print(df_x)
df_y = pd.DataFrame(data=np.array(
[[np.NaN, np.NaN, np.NaN, np.NaN],
[0, np.nan, np.NaN,np.NaN],
[0, -1, np.NaN,np.NaN],
[0, 0, 1,1]]
), index= [1, 2, 3, 4], columns=[1, 2, 3, 4])
def get_data_x(i):
return df_x.loc[i]
def get_data_y(i):
return sdf_y.loc[i]
def animate(i):
x = get_data_x(i)
y= get_data_y(i)
graph.set_data(x,y)
return graph,
# Set up formatting for the movie files
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, metadata=dict(artist='Me'), bitrate=1800)
fig = plt.figure(figsize=(20,20))
ax = plt.axes()
nx.draw(G,nx.get_node_attributes(G, 'pos'),node_size = 1)
n_steps = df_x.index
graph, = plt.plot([],[],'o')
ani = animation.FuncAnimation(fig, animate, frames= n_steps, interval=1, repeat=True, blit = True)
ani.save('path/file.mp4', writer=writer)
I have array points
nodes = [(1, 2), (6, 15), (10, 6), (10, 3), (3, 7)]
And now, I need draw Spline passing through the points. You can see image result
But I don't know how to draw with matplotlib.pyplot. Help me
So , the right piece of code is:
from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
nodes = np.array( [ [1, 2], [6, 15], [10, 6], [10, 3], [3, 7] ] )
x = nodes[:,0]
y = nodes[:,1]
tck,u = interpolate.splprep( [x,y] ,s = 0 )
xnew,ynew = interpolate.splev( np.linspace( 0, 1, 100 ), tck,der = 0)
plt.plot( x,y,'o' , xnew ,ynew )
plt.legend( [ 'data' , 'spline'] )
plt.axis( [ x.min() - 1 , x.max() + 1 , y.min() - 1 , y.max() + 2 ] )
plt.show()
In my case, X is a range(0, 100), Y is a range(0, 10), Z is a list of list. Z has the same length as X, which is 100, and each element list inside of Z has the same dimension of Y.
Z = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]].
I have the following code, but it does not work, it complains two or more arrays have incompatible dimensions on axis 1.
fig = plt.figure(figsize=(200, 6))
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.set_xticklabels(x_ax)
ax.set_yticklabels(y_ax)
ax.set_title("my title of chart")
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
ax.set_zlim(0, 100)
fig.colorbar(surf, shrink = 0.5, aspect = 5)
plt.show()
I guess the error is due to the data structure of Z, how do I make a compatible structure with X, and Y? Thanks
Here is a basic 3D surface plotting procedure. It seems that your X and Y are just 1D arrays. However, X, Y, and Z have to be 2D arrays of the same shape. numpy.meshgrid function is useful for creating 2D mesh from two 1D arrays.
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.array(np.linspace(-2,2,100))
y = np.array(np.linspace(-2,2,10))
X,Y = np.meshgrid(x,y)
Z = X * np.exp(-X**2 - Y**2);
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink = 0.5, aspect = 5)
plt.show()