obtaining a single plot of two contour lines matplotlib - python

I am making a program that interpolates the points of some level curves, but when it comes to graphing, I am obtaining two individual graphs of the two level curves and not a single graph.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
pts1 = np.array([[19.02678991587782, -98.62426964439068] ,[19.02642477902292, -98.62396923697386],[19.02614078313657, -98.62409798300963],[19.025207650377993, -98.62439839042645],
[19.02378765569075, -98.62461296715276],[19.022692222926803, -98.62452713646223],[19.021393922893306, -98.62422672904542],[19.020866485607627, -98.6230680147234],
[19.020978059006985, -98.6220595041113],[19.020795484294528, -98.62195221574815],[19.02058248020984, -98.6220595041113],[19.019923180101493, -98.6228427091539],
[19.019923180101493, -98.62287489566285],[19.019426167537492, -98.6239799658033],[19.01909144395283, -98.62516013779798],[19.018533569789643, -98.62622229253545],
[19.01849299705195, -98.62694112456855],[19.019243591116275, -98.62830368671746],[19.019750747335433, -98.62919418013162],[19.019659459330185, -98.63011686005473],
[19.019618886877918, -98.63087860733337],[19.020136185037668, -98.63175837191123],[19.02097805899266, -98.632090965837],[19.02212421792218, -98.63189784679251],
[19.024102084177514, -98.63043872507744],[19.02554236171496, -98.62930146843671],[19.0258770723203, -98.62851826341256],[19.026232067679466, -98.6269303956773],
[19.02672905989373, -98.62547127397141]])
pts2 = np.array([[19.024832367299116, -98.62688748111249],[19.024548368691026, -98.62624375101424],[19.023899227192743, -98.62615792033446],[19.02260093658879, -98.62590042829517],
[19.0217489278678, -98.62568585159576],[19.02101863120187, -98.6252996135368],[19.020754912182237, -98.62528888442091],[19.020572337215178, -98.62560002091598],
[19.02024775901759, -98.62611500499459],[19.020085469681103, -98.62684456577261],[19.0204100481956, -98.62774578791017],[19.020815770447378, -98.62856117936796],
[19.021262063780405, -98.62911907878645],[19.021262063780405, -98.62976280888472],[19.021434494983918, -98.63030997918734],[19.022022788299633, -98.63035289452722],
[19.022692222987843, -98.62996665646827],[19.023665941356825, -98.62932292637001],[19.024477368972605, -98.62816421219316],[19.024680225257438, -98.6276277704446]])
for lst in pts1, pts2:
######## level curve interpolation #######################
pad = 3
lst = np.pad(lst, [(pad,pad), (0,0)], mode='wrap')
y,x = lst.T
i = np.arange(0, len(lst))
interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad))
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
#grafico de la interpolaciĆ³n
plt.figure(figsize = (8,8))
plt.plot(xi, yi, "k")
plt.title("level curves")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
I would like to get this output:

You need to declare plt.figure() only once, outside of the for loop. Inside the for loop you add elements to the plot. Finally, outside of the loop you set axis labels and show the plot.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
pts1 = np.array([[19.02678991587782, -98.62426964439068] ,[19.02642477902292, -98.62396923697386],[19.02614078313657, -98.62409798300963],[19.025207650377993, -98.62439839042645],
[19.02378765569075, -98.62461296715276],[19.022692222926803, -98.62452713646223],[19.021393922893306, -98.62422672904542],[19.020866485607627, -98.6230680147234],
[19.020978059006985, -98.6220595041113],[19.020795484294528, -98.62195221574815],[19.02058248020984, -98.6220595041113],[19.019923180101493, -98.6228427091539],
[19.019923180101493, -98.62287489566285],[19.019426167537492, -98.6239799658033],[19.01909144395283, -98.62516013779798],[19.018533569789643, -98.62622229253545],
[19.01849299705195, -98.62694112456855],[19.019243591116275, -98.62830368671746],[19.019750747335433, -98.62919418013162],[19.019659459330185, -98.63011686005473],
[19.019618886877918, -98.63087860733337],[19.020136185037668, -98.63175837191123],[19.02097805899266, -98.632090965837],[19.02212421792218, -98.63189784679251],
[19.024102084177514, -98.63043872507744],[19.02554236171496, -98.62930146843671],[19.0258770723203, -98.62851826341256],[19.026232067679466, -98.6269303956773],
[19.02672905989373, -98.62547127397141]])
pts2 = np.array([[19.024832367299116, -98.62688748111249],[19.024548368691026, -98.62624375101424],[19.023899227192743, -98.62615792033446],[19.02260093658879, -98.62590042829517],
[19.0217489278678, -98.62568585159576],[19.02101863120187, -98.6252996135368],[19.020754912182237, -98.62528888442091],[19.020572337215178, -98.62560002091598],
[19.02024775901759, -98.62611500499459],[19.020085469681103, -98.62684456577261],[19.0204100481956, -98.62774578791017],[19.020815770447378, -98.62856117936796],
[19.021262063780405, -98.62911907878645],[19.021262063780405, -98.62976280888472],[19.021434494983918, -98.63030997918734],[19.022022788299633, -98.63035289452722],
[19.022692222987843, -98.62996665646827],[19.023665941356825, -98.62932292637001],[19.024477368972605, -98.62816421219316],[19.024680225257438, -98.6276277704446]])
plt.figure(figsize = (8,8))
for lst in pts1, pts2:
######## level curve interpolation #######################
pad = 3
lst = np.pad(lst, [(pad,pad), (0,0)], mode='wrap')
y,x = lst.T
i = np.arange(0, len(lst))
interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad))
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
#grafico de la interpolaciĆ³n
plt.plot(xi, yi, "k")
plt.title("level curves")
plt.xlabel("x")
plt.ylabel("y")
plt.show()

Related

Get distinct boundaries in matplolib contourf

I am trying to linearly interpolate values using scipy of sets of coordinates, thereafter plotting in matplotlib. How can I achieve the distinct boundaries between each region?
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
np.random.seed(42)
from scipy.interpolate import griddata
x = np.random.random(20)
y = np.random.random(20)
z = np.random.random(20)
meshSize = 50
extensionFact = 10
xi, yi, = np.meshgrid(
np.linspace(np.min(x) - np.average(x) / extensionFact, np.max(x) + np.average(x) / extensionFact, meshSize),
np.linspace(np.min(y) - np.average(y) / extensionFact, np.max(y) + np.average(y) / extensionFact, meshSize))
zi = griddata((x, y), z, (xi, yi), method='nearest')
fig = plt.figure(figsize=(8, 6))
ax1 = fig.add_subplot(111)
bounds1 = np.linspace(np.nanmin(zi), np.nanmax(zi), 11)
colors1 = plt.get_cmap('jet')(np.linspace(0, 1, len(bounds1) + 1))
cmap1 = mcolors.ListedColormap(colors1[1:-1])
norm1 = mcolors.BoundaryNorm(boundaries=bounds1, ncolors=len(bounds1) - 1)
im1 = ax1.contourf(xi, yi, zi, levels=bounds1, cmap=cmap1, alpha=1)
fig.colorbar(im1, orientation='vertical', shrink=1, aspect=30, pad=0.03, ticks=bounds1)
plt.scatter(x, y, marker='x', c='k')
plt.show()
Presently the intersection of two regions are blurred.

How to plot normal vectors in each point of the curve with a given length?

How to plot normal vectors in each point of the curve with a given length?
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
plt.show()
To plot the normals, you need to calculate the slope at each point; from there, you get the tangent vector that you can rotate by pi/2.
here is one approach using python i/o np, which makes it probably easier to understand at first.
Changing the length will adjust the size of the normals to properly scale with your plot.
import matplotlib.pyplot as plt
import numpy as np
import math
def get_normals(length=.1):
for idx in range(len(x)-1):
x0, y0, xa, ya = x[idx], y[idx], x[idx+1], y[idx+1]
dx, dy = xa-x0, ya-y0
norm = math.hypot(dx, dy) * 1/length
dx /= norm
dy /= norm
ax.plot((x0, x0-dy), (y0, y0+dx)) # plot the normals
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
get_normals()
plt.show()
or longer normals, directed downwards: get_normals(length=-.3)
(use ax.set_aspect('equal') to maintain angles)
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
# Calculating the gradient
L=.1 # gradient length
grad = np.ones(shape = (2, x.shape[0]))
grad[0, :] = -2*x
grad /= np.linalg.norm(grad, axis=0) # normalizing to unit vector
nx = np.vstack((x - L/2 * grad[0], x + L/2 * grad[0]))
ny = np.vstack((y - L/2 * grad[1], y + L/2 * grad[1]))
# ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
ax.plot(nx, ny, 'r')
ax.axis('equal')
plt.show()

3D plot of the CONE using matplotlib

I'm looking for help to draw a 3D cone using matplotlib.
My goal is to draw a HSL cone, then base on the vertex coordinats i will select the color.
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
theta1 = np.linspace(0, 2*np.pi, 100)
r1 = np.linspace(-2, 0, 100)
t1, R1 = np.meshgrid(theta1, r1)
X1 = R1*np.cos(t1)
Y1 = R1*np.sin(t1)
Z1 = 5+R1*2.5
theta2 = np.linspace(0, 2*np.pi, 100)
r2 = np.linspace(0, 2, 100)
t2, R2 = np.meshgrid(theta2, r2)
X2 = R2*np.cos(t2)
Y2 = R2*np.sin(t2)
Z2 = -5+R2*2.5
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
# ax.set_xlim(-2.5, 2.5)
# ax.set_ylim(-2.5, 2.5)
# ax.set_zlim(0, 5)
ax.set_aspect('equal')
ax.plot_surface(X1, Y1, Z1, alpha=0.8, color="blue")
ax.plot_surface(X2, Y2, Z2, alpha=0.8, color="blue")
# ax.plot_surface(X, Y, Z, alpha=0.8)
#fig. savefig ("Cone.png", dpi=100, transparent = False)
plt.show()
HSL CONE
My cone
So my question now is how to define color of each element.
i have found a solution, maybe it will be usefull for others.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import colorsys
from matplotlib.tri import Triangulation
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
n_angles = 80
n_radii = 20
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.0, 0.5, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z = 1+-np.sqrt(x**2+y**2)*2
print(x.shape, y.shape, angles.shape, radii.shape, z.shape)
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri = Triangulation(x, y)
triangle_vertices = np.array([np.array([[x[T[0]], y[T[0]], z[T[0]]],
[x[T[1]], y[T[1]], z[T[1]]],
[x[T[2]], y[T[2]], z[T[2]]]]) for T in tri.triangles])
x2 = np.append(0, (radii*np.cos(angles)).flatten())
y2 = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z2 = -1+np.sqrt(x**2+y**2)*2
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri2 = Triangulation(x2, y2)
triangle_vertices2 = np.array([np.array([[x2[T[0]], y2[T[0]], z2[T[0]]],
[x2[T[1]], y2[T[1]], z2[T[1]]],
[x2[T[2]], y2[T[2]], z2[T[2]]]]) for T in tri2.triangles])
triangle_vertices = np.concatenate([triangle_vertices, triangle_vertices2])
midpoints = np.average(triangle_vertices, axis=1)
def find_color_for_point(pt):
c_x, c_y, c_z = pt
angle = np.arctan2(c_x, c_y)*180/np.pi
if (angle < 0):
angle = angle + 360
if c_z < 0:
l = 0.5 - abs(c_z)/2
#l=0
if c_z == 0:
l = 0.5
if c_z > 0:
l = (1 - (1-c_z)/2)
if c_z > 0.97:
l = (1 - (1-c_z)/2)
col = colorsys.hls_to_rgb(angle/360, l, 1)
return col
facecolors = [find_color_for_point(pt) for pt in midpoints] # smooth gradient
# facecolors = [np.random.random(3) for pt in midpoints] # random colors
coll = Poly3DCollection(
triangle_vertices, facecolors=facecolors, edgecolors=None)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.add_collection(coll)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.elev = 50
plt.show()
Inspired from Jake Vanderplas with Python Data Science Handbook, when you are drawing some 3-D plot whose base is a circle, it is likely that you would try:
# Actually not sure about the math here though:
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:20j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
and then think about the z-axis. Since viewing from the z-axis the cone is just a circle, so the relationships between z and x and y is clear, which is simply: z = np.sqrt(x ** 2 + y ** 2). Then you can draw the cone based on the codes below:
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(x, y):
return np.sqrt(x ** 2 + y ** 2)
fig = plt.figure()
ax = plt.axes(projection='3d')
# Can manipulate with 100j and 80j values to make your cone looks different
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:80j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = f(x, y)
ax.plot_surface(x, y, z, cmap=cm.coolwarm)
# Some other effects you may want to try based on your needs:
# ax.plot_surface(x, y, -z, cmap=cm.coolwarm)
# ax.scatter3D(x, y, z, color="b")
# ax.plot_wireframe(x, y, z, color="b")
# ax.plot_wireframe(x, y, -z, color="r")
# Can set your view from different angles.
ax.view_init(azim=15, elev=15)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
And from my side, the cone looks like:
and hope it helps.

matplotlib correct colors/colorbar for plot with multiple surfaces each of a different color

Is it possible to plot multiple surfaces in one pyplot figure? Here is my attempt. The ax.plot_surface command seems to reset the figure, as I only get a single plane in the resulting plot. I am hoping to produce "stacked" planes, each with distinctive colors, and a color bar showing the numeric value of each color. Currently my colors show up wrong.
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import pylab
from scipy.interpolate import griddata
dat = open('ex.csv', 'w')
dat.write('x,y,z,c\n')
for x in range(20):
for y in range(20):
for c in range(0,7):
dat.write(','.join([str(s) for s in [x,y,x+y+c,c/10.0,'\n']]))
dat.close()
fig = matplotlib.pyplot.gcf()
dat = np.genfromtxt('ex.csv', delimiter=',',skip_header=1)
X_dat = dat[:,0]
Y_dat = dat[:,1]
Z_dat = dat[:,2]
C_dat = dat[:,3]
ax1 = fig.add_subplot(111, projection='3d')
for color in np.unique(C_dat):
X, Y, Z, C = np.array([]), np.array([]), np.array([]), np.array([])
for i in range(len(X_dat)):
if C_dat[i]==color:
X = np.append(X,X_dat[i])
Y = np.append(Y,Y_dat[i])
Z = np.append(Z,Z_dat[i])
C = np.append(C,C_dat[i])
xi = np.linspace(X.min(),X.max(),100)
yi = np.linspace(Y.min(),Y.max(),100)
zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='cubic')
ci = griddata((X, Y), C, (xi[None,:], yi[:,None]), method='cubic')
xig, yig = np.meshgrid(xi, yi)
surf = ax1.plot_surface(xig, yig, zi,facecolors=cm.rainbow(ci), alpha = 0.7)
xi = np.linspace(X_dat.min(),X_dat.max(),100)
yi = np.linspace(Y_dat.min(),Y_dat.max(),100)
ci = griddata((X_dat, Y_dat), C_dat, (xi[None,:], yi[:,None]), method='cubic')
m = cm.ScalarMappable(cmap=cm.rainbow)
m.set_array(ci)
col = plt.colorbar(m)
plt.show()
(there should be a red plane)
Move the line
ax1 = fig.add_subplot(111, projection='3d')
outside of the for color in... loop. By recreating the axes each iteration, you hide the previously created surfaces
EDIT (to answer second question about colormaps)
You need to normalise your data. Currently, you have facecolors in the range 0 to 0.6, so when you feed the maximum (0.6) to cm.rainbow, you get green, not red (since it expects a range of 0 to 1).
Here's a modified script, which I think works as it should. We use Normalise from matplotlib.colors with a vmin and vmax determined from your C_dat data. Then, use facecolors=cm.rainbow(norm(ci)) to set the colors of your surfaces.
You also then want to set the array of your ScalarMappable using the values in C_dat, so we don't need to use griddata again here.
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors as colors
from mpl_toolkits.mplot3d import Axes3D
import pylab
from scipy.interpolate import griddata
dat = open('ex.csv', 'w')
dat.write('x,y,z,c\n')
for x in range(20):
for y in range(20):
for c in range(0,7):
dat.write(','.join([str(s) for s in [x,y,x+y+c,c/10.0,'\n']]))
dat.close()
fig = matplotlib.pyplot.gcf()
dat = np.genfromtxt('ex.csv', delimiter=',',skip_header=1)
X_dat = dat[:,0]
Y_dat = dat[:,1]
Z_dat = dat[:,2]
C_dat = dat[:,3]
# Create a Normalize instance.
norm = colors.Normalize(vmin=C_dat.min(),vmax=C_dat.max())
ax1 = fig.add_subplot(111, projection='3d')
for color in np.unique(C_dat):
X, Y, Z, C = np.array([]), np.array([]), np.array([]), np.array([])
for i in range(len(X_dat)):
if C_dat[i]==color:
X = np.append(X,X_dat[i])
Y = np.append(Y,Y_dat[i])
Z = np.append(Z,Z_dat[i])
C = np.append(C,C_dat[i])
xi = np.linspace(X.min(),X.max(),100)
yi = np.linspace(Y.min(),Y.max(),100)
zi = griddata((X, Y), Z, (xi[None,:], yi[:,None]), method='cubic')
ci = griddata((X, Y), C, (xi[None,:], yi[:,None]), method='cubic')
xig, yig = np.meshgrid(xi, yi)
# Note the use of norm in the facecolors option
surf = ax1.plot_surface(xig, yig, zi,facecolors=cm.rainbow(norm(ci)), alpha = 0.7)
m = cm.ScalarMappable(cmap=cm.rainbow)
m.set_array(np.unique(C_dat))
col = plt.colorbar(m)
plt.show()

Fixing jagged edges of 3D plot, selecting an appropriate mask

So I have some 3D data that I am able to plot just fine except the edges look jagged.
The relevant code:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-1, 1, 0.01)
y = np.arange(-1, 1, 0.01)
x, y = np.meshgrid(x, y)
rho = np.sqrt(x**2 + y**2)
# Attempts at masking shown here
# My Mask
row=0
while row<np.shape(x)[0]:
col=0
while col<np.shape(x)[1]:
if rho[row][col] > 1:
rho[row][col] = None
col=col+1
row=row+1
# Calculate & Plot
z = rho**2
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=8, cstride=8, cmap=cm.bone, alpha=0.15, linewidth=0.25)
plt.show()
Produces:
This is so close to what I want except the edges are jagged.
If I disable my mask in the code above & replace it with rho = np.ma.masked_where(rho > 1, rho) it gives:
It isn't jagged but not want I want in the corners.
Any suggestions on different masking or plotting methods to get rid of this jaggedness?
Did you consider using polar coordinates (like in this example) ?
Something like:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# create supporting points in polar coordinates
r = np.linspace(0,1.25,50)
p = np.linspace(0,2*np.pi,50)
R,P = np.meshgrid(r,p)
# transform them to cartesian system
x, y = R * np.cos(P), R * np.sin(P)
rho = np.sqrt(x**2 + y**2)
# Calculate & Plot
z = rho**2
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.bone, alpha=0.15, linewidth=0.25)
plt.show()

Categories