Obtain the surfaces curves in a figure with python - python
I have a set of data for that I need identify patterns, so I tried to use plt.contours and plt.contourf for that task and it works well, now I can plot contours and show graphically the overdensity among the data. In this step, I tried to get the information of the surface contours (I mean, save the values that define the contour in a variable to use it later.) without success: Is there a way to do this?
Also, I have doubts of what does the values in the color bar means, I know that is the level of overdensity of the data, but if someone could tell me more details, would be great.
I attach the code that I'm using so far (I generate the data in this case), and a plot of the code.
import scipy.interpolate
import numpy as np
import scipy.stats as st
import matplotlib.pyplot as plt
np.random.seed(20)
data = np.random.rand(400,2)
x = data[:,0]
y = data[:,1]
plt.figure(figsize=(12,7))
# Set up a regular grid of points
xi, yi = np.linspace(x.min(), x.max(), 100), np.linspace(y.min(), y.max(), 100)
xi, yi = np.meshgrid(xi, yi)
#contours:
n_contours = 6
positions = np.vstack([xi.ravel(), yi.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
f = np.reshape(kernel(positions).T, xi.shape)
cfset = plt.contourf(xi, yi, f,n_contours, cmap='Greens')
cset = plt.contour(xi, yi, f,n_contours, colors='k')
#For the points data:
positions = np.vstack([x.ravel(), y.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
z = np.reshape(kernel(positions).T, x.shape)
#plot:
plt.scatter(x, y, c=z)
plt.colorbar(cfset)
plt.show()
thanks!
EDIT:
I founded a way to do this, using the get_paths() feature, so basically, you need to choose the contour, and then the number of the segment that you need to get the values (x,y), for example:
#contour 3, section 0
p = cset.collections[3].get_paths()[0]
v = p.vertices
x0 = v[:,0]
y0 = v[:,1]
#contour 3, section 1
p = cset.collections[3].get_paths()[1]
v = p.vertices
x1 = v[:,0]
y1 = v[:,1]
#contour 3, section 2
p = cset.collections[3].get_paths()[2]
v = p.vertices
x2 = v[:,0]
y2 = v[:,1]
plt.plot(x0,y0,'-',x1,y1,'-',x2,y2,'-')
With this, you get:
Related
3D plot using delauney triangulation using four 1dimensional arrays. First three determine the coordinates while fourth determines the color
I am trying to achieve a plot like the one shown bellow: The code I am using is the following: import numpy as np import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D import matplotlib.tri as tri from matplotlib.colors import Normalize # Example usage x = np.linspace(0, 10, 500) f1 = lambda x: x**2 f2 = lambda x: 3*x**1.4 f3 = lambda x: 2*x**1.2 f4 = lambda x: 2*x**1 X = f1(x) Y = f2(x) Z = f3(x) Z1 = f4(x) # Find the indices of the non-nan values valid_indices = np.logical_not(np.logical_or(np.isnan(X), np.logical_or(np.isnan(Y), np.isnan(Z)))) # Use the non-nan indices to index into the arrays x = X[valid_indices] y = Y[valid_indices] z = Z[valid_indices] z1 = Z1[valid_indices] # Create grid values first. ngridx = 300 ngridy = 300 xi = np.linspace(x.min(), x.max(), ngridx) yi = np.linspace(y.min(), y.max(), ngridy) # Perform linear interpolation of the data (x,y) # on a grid defined by (xi,yi) triang = tri.Triangulation(x, y) interpolator_z = tri.LinearTriInterpolator(triang, z) interpolator_z1 = tri.LinearTriInterpolator(triang, z1) Xi, Yi = np.meshgrid(xi, yi) zi = interpolator_z(Xi, Yi) z1i = interpolator_z1(Xi, Yi) X, Y, Z, Z1 = xi, yi, zi, z1i fig = plt.gcf() ax1 = fig.add_subplot(111, projection='3d') minn, maxx = z1.min(), z1.max() norm = Normalize() surf = ax1.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=cm.jet(norm(Z1)), vmin=minn, vmax=maxx, shade=False) #surf =ax.plot_trisurf(X,Y,Z, triangles=tri.triangles, cmap=plt.cm.Spectral) m = cm.ScalarMappable(cmap=cm.jet) m.set_array(Z1) The result I am getting is close but not quite what I want: I am looking to get something that looks closer to this: Any ideas on how I could improve my result?
3D graphing the complex values of a function in Python
This is the real function I am looking to represent in 3D: y = f(x) = x^2 + 1 The complex function would be as follows: w = f(z) = z^2 + 1 Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing. We get: f(x + iy) = x^2 + 2xyi - y^2 + 1 So: u = x^2 - y^2 + 1 and v = 2xy This u is what is being used in the code below. import numpy as np import matplotlib.pyplot as plt x = np.linspace(-100, 101, 150) y = np.linspace(-100, 101, 150) X, Y = np.meshgrid(x,y) U = (X**2) - (Y**2) + 1 fig = plt.figure(dpi = 300) ax = plt.axes(projection='3d') ax.plot_surface(X, Y, Z) plt.show() The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike. Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well. I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes. The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with. I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students. Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code: import numpy as np import matplotlib.pyplot as plt from matplotlib import cm def f(z): return z**2 + 1 def freal(x, y): return x**2 - y**2 + 1 x = np.linspace(-100, 101, 150) y = np.linspace(-100, 101, 150) yproj = 0 # value of y for which to project xu axes xproj = 0 # value of x to project onto yu axes X, Y = np.meshgrid(x,y) Z = X + 1j * Y W = f(Z) U = W.real fig = plt.figure() ax = plt.axes(projection='3d') ## surface ax.plot_surface(X, Y, U, alpha=0.7) # xu projection xuproj = freal(x, yproj) ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5) ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5) # yu projection yuproj = freal(xproj, y) ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5) ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5) # partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s x = np.linspace(-3, 3, 150) y = np.linspace(0, 3, 150) X, Y = np.meshgrid(x,y) U = f(X + 1j*Y).real fig = plt.figure() ax = plt.axes(projection='3d') ## surface ax.plot_surface(X, Y, U, cmap=cm.jet) ax.set_box_aspect( (np.diff(ax.get_xlim())[0], np.diff(ax.get_ylim())[0], np.diff(ax.get_zlim())[0])) #ax.set_aspect('equal') plt.show() gives this result: and The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this. There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.
Python: Appending 2D arrays from meshgrid
I'm plotting two surface plots in python obtained from np.meshgrid, which I want to append to create only one surface plot. For instance: fig = plt.figure(figsize=(8,4)) ax = fig.add_subplot(projection='3d') # First surface: x1 = np.linspace(0,1,100) y1 = np.linspace(0,1,100) X1,Y1 = np.meshgrid(x1,y1) Z1 = 2*Y1 solid = ax.plot_surface(X1,Y1,Z1,cmap=cm.coolwarm,linewidth=0, antialiased=True) # Second surface: x2 = np.linspace(0,1,100) y2 = np.linspace(1,2,100) X2,Y2 = np.meshgrid(x2,y2) Z2 = 2*Y2 solid = ax.plot_surface(X2,Y2,Z2,cmap=cm.coolwarm,linewidth=0, antialiased=True) My first idea was to append them with np.append, such that: X = np.append(X1,X2) Y = np.append(Y1,Y2) Z = np.append(Z1,Z2) solid = ax.plot_surface(X,Y,Z,cmap=cm.coolwarm,linewidth=0, antialiased=True) But as expected, I got an error since Z was flattened and, moreover, I'm not sure if X and Y are right. In the example above it's trivial how could I create only one surface since both of them have the same angle, but I want to solve for the general case where they could have different inclinations. How can I overcome this problem? Thank you in advance!
I would simply fill only one Z array, for example: fig = plt.figure(figsize=(8,4)) ax = fig.add_subplot(projection='3d') x = np.linspace(0,1,100) y = np.linspace(0,2,100) X, Y = np.meshgrid(x, y) Z = np.empty_like(Y) Z[Y < 1] = 2 * Y[Y < 1] Z[Y >= 1] = 10 * Y[Y >= 1] - 8 # different slope solid = ax.plot_surface(X , Y, Z, cmap=plt.cm.coolwarm, linewidth=0, antialiased=True)
How do you create a 3D surface plot with missing values matplotlib?
I am trying to create a 3D surface energy diagram where an x,y position on a grid contains an associated z level. The issue is that the grid is not uniform (ie, there is not a z component for every x,y position). Is there a way to refrain from plotting those values by calling them NaN in the corresponding position in the array? Here is what I have tried so far: import numpy as np from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3D import pylab from matplotlib import cm #Z levels energ = np.array([0,3.5,1,-0.3,-1.5,-2,-3.4,-4.8]) #function for getting x,y associated z values? def fun(x,y,array): return array[x] #arrays for grid x = np.arange(0,7,0.5) y = np.arange(0,7,0.5) #create grid X, Y = np.meshgrid(x,y) zs = np.array([fun(x,y,energ) for x in zip(np.ravel(X))]) Z = zs.reshape(X.shape) plt3d = plt.figure().gca(projection='3d') #gradients now with respect to x and y, but ideally with respect to z only Gx, Gz = np.gradient(X * Y) G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude N = G / G.max() # normalize 0..1 plt3d.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=cm.jet(N), edgecolor='k', linewidth=0, antialiased=False, shade=False) plt.show() I cannot post image here of this plot but if you run the code you will see it But I would like to not plot certain x,y pairs, so the figure should triangle downward to the minimum. Can this be accomplished by using nan values? Also would like spacing between each level, to be connected by lines. n = np.NAN #energ represents the z levels, so the overall figure should look like a triangle. energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]]) plt3d = plt.figure().gca(projection='3d') Gx, Gz = np.gradient(X * energ) # gradients with respect to x and z G = (Gx ** 2 + Gz ** 2) ** .5 # gradient magnitude N = G / G.max() # normalize 0..1 x = np.arange(0,13,1) y = np.arange(0,13,1) X, Y = np.meshgrid(x,y) #but the shapes don't seem to match up plt3d.plot_surface(X, Y, energ, rstride=1, cstride=1, facecolors=cm.jet(N), edgecolor='k', linewidth=0, antialiased=False, shade=False ) Using masked arrays generates the following error: local Python[7155] : void CGPathCloseSubpath(CGMutablePathRef): no current point. n = np.NAN energ = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,2.6,n,2.97,n,2.6,n,2.97,n,2.6,n,3.58,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,1.09,n,1.23,n,1.09,n,1.23,n,1.7,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,-0.65,n,-0.28,n,-0.65,n,0.33,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,-2.16,n,-2.02,n,-1.55,n,n,n,n],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,-3.9,n,-2.92,n,n,n,n,n,],[n,n,n,n,n,n,n,n,n,n,n,n,n],[n,n,n,n,n,n,-4.8,n,n,n,n,n,n,]]) x = np.arange(0,13,1) y = np.arange(0,13,1) X, Y = np.meshgrid(x,y) #create masked arrays mX = ma.masked_array(X, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]]) mY = ma.masked_array(Y, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]]) m_energ = ma.masked_array(energ, mask=[[0,0,0,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,1,0,1,0,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,0,1,0,1,0,1,0,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,0,1,0,1,0,1,0,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,0,1,0,1,0,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,0,1,0,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,1]]) plt3d = plt.figure().gca(projection='3d') plt3d.plot_surface(mX, mY, m_energ, rstride=1, cstride=1, edgecolor='k', linewidth=0, antialiased=False, shade=False) plt.show()
I was playing around with the code from this forum post, and I was able to make the graph have missing values. You can try the code yourself! I got it to work using float("nan") for the missing values. import plotly.graph_objects as go import numpy as np x = np.arange(0.1,1.1,0.1) y = np.linspace(-np.pi,np.pi,10) #print(x) #print(y) X,Y = np.meshgrid(x,y) #print(X) #print(Y) result = [] for i,j in zip(X,Y): result.append(np.log(i)+np.sin(j)) result[0][0] = float("nan") upper_bound = np.array(result)+1 lower_bound = np.array(result)-1 fig = go.Figure(data=[ go.Surface(z=result), go.Surface(z=upper_bound, showscale=False, opacity=0.3,colorscale='purp'), go.Surface(z=lower_bound, showscale=False, opacity=0.3,colorscale='purp')]) fig.show()
Plotting function of 3 dimensions over given domain with matplotlib
I am trying to visualize a function of 3 parameters over a cube in R^3 to get an idea of the smoothness of the function. An example of this problem is shown in the sample code below %pylab from mpl_toolkits.mplot3d import Axes3D import itertools x = np.linspace(0,10,50) y = np.linspace(0,15,50) z = np.linspace(0,8,50) points = [] for element in itertools.product(x, y, z): points.append(element) def f(vals): return np.cos(vals[0]) + np.sin(vals[1]) + vals[2]**0.5 fxyz = map(f, points) xi, yi, zi = zip(*points) fig = plt.figure(figsize=(12, 8)) ax = fig.add_subplot(111, projection='3d') ax.scatter(xi, yi, zi, c=fxyz, alpha=0.5) plt.show() The problem with this approach is that the inside of the cube cannot be visualized. Is there a better way to graph a function over some dense subset of R^3?
As #HYRY and #nicoguaro suggested in the comments above, Mayavi is much better suited for this type of work. There is a good set of examples here that I used for reference. Here is what I came up with import numpy as np from mayavi import mlab x = np.linspace(0,10,50) y = np.linspace(0,15,50) z = np.linspace(0,8,50) X, Y, Z = np.meshgrid(x, y, z) s = np.cos(X) + np.sin(Y) + Z**0.5 b1 = np.percentile(s, 20) b2 = np.percentile(s, 80) mlab.pipeline.volume(mlab.pipeline.scalar_field(s), vmin=b1, vmax=b2) mlab.axes() mlab.show() After which I rotated the figure to desired angles with the GUI and saved desired views