Using "Finding intersection of two contour plots in Python"
as a guide I get the following error message (code follows):
<ipython-input-98-993ccd512742> in <module>
9
10
---> 11 intersection_example = findIntersection(c1,c2)
12
<ipython-input-97-995cbb9fd0d0> in findIntersection(contour1, contour2)
2
3 def findIntersection(contour1,contour2):
----> 4 p1 = contour1.collections[0].get_paths()[0]
5 v1 = p1.vertices
6
IndexError: list index out of range
The first code sample below gives me a 3D contour plot without error:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
fig.set_size_inches(18.5, 10.5) #, forward=True)
ax = plt.axes(projection='3d')
x = np.linspace(0, 21, 20)
y = np.linspace(0, 21, 20)
X, Y = np.meshgrid(x, y)
ax.contour3D(X, Y, ((X - 5) * (Y - 5) - 25), 29, cmap='winter')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
#fig.add_subplot(ax.contour3D(X, Y, Z2, 70, cmap='winter')) #binary'))
ax.contour3D(X, Y, (X**2 + Y**2 - 400), 29, cmap='autumn')
ax.set_title('Contour3D: Using meshgrid X Y ')
The above produces:
This next sample is the problem piece of code that uses contour (rather than contour3D) and results in the error:
IndexError: list index out of range
This is also the error generated when findIntersection is called with undefined parameters.
from shapely import geometry
def findIntersection(contour1,contour2):
p1 = contour1.collections[0].get_paths()[0]
v1 = p1.vertices
p2 = contour2.collections[0].get_paths()[0]
v2 = p2.vertices
poly1 = geometry.LineString(v1)
poly2 = geometry.LineString(v2)
intersection = poly1.intersection(poly2)
return intersection
figtst2 = plt.figure()
figtst2.set_size_inches(18.5, 10.5) #, forward=True)
ax2 = plt.axes(projection='3d')
c1 = ax2.contour(X,Y,((X - 5) * (Y - 5) - 25),1,colors='green', linewidths=3)
c2 = ax2.contour(X,Y,(X**2 + Y**2 - 400),1,colors='orange', linewidths=3)
ax2.set_title('Contour Using meshgrid X Y & looking for intersections')
# Error is generated on the next line
intersection_example = findIntersection(c1,c2)
# where coordinates can be accessed by
intersection_example.x ##get x points
intersection_example.y ##get y points
list(intersection_example.coords) ## get in [x,y] formatting
Plotting ax2 produces:
Note: If I use the linear space x and y rather than the mesh grid X and Y I get:
TypeError: Input z must be a 2D array.
I developed an answer that is a bit of a fudge , so please let me know if there is a way of doing it all in 3D.
The fudge is to do a 2D plot to get my intersection coordinates, then I do a 3D plot and use what I found in the 2D plot (see Jupyter notebook code )
from shapely import geometry
fig2 = plt.figure()
fig2.set_size_inches(18.5, 10.5) #, forward=True)
#plt.axes(projection='3d') # 3D gives no vertices "list index out of range"
tau = np.arange(0,23,1)
x,y= np.meshgrid(tau,tau)
cs = plt.contour(x, y, np.array((x - 5) * (y - 5) - 25), [1],colors='k')
cs2 = plt.contour(x, y, np.array((x**2 + y**2 - 400)), [1],colors='k') #x**2 + y**2 - 400
from shapely.geometry import LineString
v1 = cs.collections[0].get_paths()[0].vertices
v2 = cs2.collections[0].get_paths()[0].vertices
ls1 = LineString(v1)
ls2 = LineString(v2)
points = ls1.intersection(ls2)
print('points', np.array(points))
This gives following image
The 3D code is;
def fhyp(x, y):
return ((x - 5) * (y - 5) - 25)
x = np.linspace(0, 21, 20)
y = np.linspace(0, 21, 20)
X, Y = np.meshgrid(x, y)
Z = fhyp(X, Y)
def fcirc(x, y):
return (x**2 + y**2 - 400)
x = np.linspace(0, 21, 20)
y = np.linspace(0, 21, 20)
X, Y = np.meshgrid(x, y)
Z2 = fcirc(X, Y)
fig = plt.figure()
fig.set_size_inches(18.5, 10.5) #, forward=True)
ax = plt.axes(projection='3d')
x = np.linspace(0, 21, 20)
y = np.linspace(0, 21, 20)
X, Y = np.meshgrid(x, y)
ax.contour3D(X, Y, Z, 1, cmap='winter') #4th parm was 29 to give 29 contour lines
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
#
ax.contour3D(X, Y, Z2, 1, cmap='autumn')
ax.set_title('Contour3D: Using meshgrid X Y ')
from scipy import optimize
def f(p):
x, y = p
e1 = ((x - 5) * (y - 5) - 25)
e2 = (x**2 + y**2 - 400)
return e1, e2
x2, y2 = optimize.fsolve(f, (5, 5))
zval = ((x2 - 5) * (y2 - 5) - 25)
print(x2,y2, zval)
vals = [x2,y2,zval]
result = np.array(vals)
print(result)
plt.plot([result[0]],[result[1]],[result[2]], "rx")
for i in range(5):
x = [18.80452816482531,18.80452816482531]
y = [6.810999963323182, 6.810999963323182]
z = [-400,400]
plt.plot(x,y,z,'k--',alpha=0.8, linewidth=0.5)
Related
I have a contourf plot and I want to replace every value where x and y < 0 with 0s or nans. For example, if I had a contourf plot like this:
and I want it to look like this:
How would I do that? I have always had trouble with indexing meshgrid output... The first figure was generated with this code:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.interpolate import griddata
x = np.linspace(0, 10 * np.pi, num=600)
n = np.random.normal(scale=8, size=x.size)
time = []
lons = []
quant = []
for a in np.arange(0,100,1):
y1 = 100 * np.sin(x+a) + n
y2 = 50 * np.sin(x-1.5*a) + n
time += [a] * len(y1)
quant += list((y1+y2)/2)
lons += list(x)
dict = {'time': time, 'lons': lons, 'quant': quant}
df = pd.DataFrame(dict)
def grid(x, y, z, resX=24, resY=24):
#prepare to create grid
xi = np.linspace(0, 30, resX)
yi = np.linspace(0, 95, resY)
#grid x, y, z
X, Y = np.meshgrid(xi, yi)
Z = griddata((x, y), z, (X, Y), method='linear')
return X, Y, Z
X,Y,Z = grid(df['lons'],df['time'],df['quant'])
fft2 = (np.fft.fft2(Z))
FreqCompRows = np.fft.fftfreq(fft2.shape[0],d=1)
FreqCompCols = np.fft.fftfreq(fft2.shape[1],d=1)
xi = np.linspace(1/FreqCompRows[-1], -1/FreqCompRows[-1], 24)
yi = np.linspace(1/FreqCompCols[-1], -1/FreqCompCols[-1], 24)
X, Y = np.meshgrid(xi, yi)
plt.contourf(X,Y,fft2)
plt.show()
I have a multivariate function
X is (2, 1), A(100, 2), and b is (100, 1).
How can I plot the contour plot of it in python? for a simple case like Z = X^2 + Y^2 I have this code, but I don't think it can work for my problem.
x, y = np.linspace(-3, 3, 400), np.linspace(-3, 3, 400)
XX, YY = np.meshgrid(x, y)
Z = (XX ** 2 + YY ** 2)
fig, ax = plt.subplots()
ax.contour(XX, YY, Z)
How can I plot contour of my function?
I assumed that in the pictured formula x is the same as X, which represents the coordinates of a point in a 2D plane.
Then, with np.meshgrid you create the usual grid of coordinates over which evaluating the function, xx and yy. At this points, you need to combine them into a matrix X of 2 rows (representing x and y coordinates) and as many columns as necessary.
Finally, you evaluate the function over each column of X.
import numpy as np
import matplotlib.pyplot as plt
# create example data
x = y = np.linspace(0, 2 * np.pi, 100)
A = np.stack([np.cos(x), np.sin(x * y)]).T
b = np.cbrt(x * y)
print("A.shape = ", A.shape)
print("b.shape = ", b.shape)
# define the function
f = lambda X: np.linalg.norm(b - A # X, 2)**2
# creates the coordinates over which the function
# will be evaluated
x = y = np.linspace(-np.pi, np.pi, 200)
xx, yy = np.meshgrid(x, y)
# assemble the coordinates into a "vector of coordinates"
X = np.stack([xx, yy]).reshape(2, -1)
print("X.shape = ", X.shape)
# apply the function f on each pair of coordinates
Z = np.apply_along_axis(f, 0, X).reshape(xx.shape)
print("Z.shape = ", Z.shape)
fig, ax = plt.subplots()
c = ax.contourf(xx, yy, Z)
fig.colorbar(c)
plt.show()
The task:
I am trying to interpolate a vector field on a regular grid, i.e.:
The issue:
I am using the RegularGridInterpolator from scipy to do this. However, it seems that the resulting vector field is turned with respect to the original:
Anyone knows why?
Python code to reproduce example:
from scipy.interpolate import RegularGridInterpolator
import matplotlib.pyplot as plt
import numpy as np
# ORIGINAL
# Number of points (NxN)
N = 50
# Boundaries
ymin = -2.; ymax = 2.
xmin = -2.; xmax = 2.
# Create Meshgrid
x = np.linspace(xmin,xmax, N)
y = np.linspace(ymin,ymax, N)
xx, yy = np.meshgrid(x, y)
# Vector Field
Fx = np.cos(xx + 2*yy)
Fy = np.sin(xx - 2*yy)
# Plot vector field
fig, ax = plt.subplots()
ax.quiver(x, y, Fx, Fy)
plt.title("Original")
plt.show()
# REDUCED
# Number of points (NxN)
N = 10
# Boundaries
ymin = -2.; ymax = 2.
xmin = -2.; xmax = 2.
# Create Meshgrid
x = np.linspace(xmin,xmax, N)
y = np.linspace(ymin,ymax, N)
xx, yy = np.meshgrid(x, y)
# Vector Field
Fx = np.cos(xx + 2*yy)
Fy = np.sin(xx - 2*yy)
# Plot vector field
fig, ax = plt.subplots()
ax.quiver(x, y, Fx, Fy)
plt.title("Reduced")
plt.show()
# INTERPOLATED VERSION BASED ON REDUCED
# Iterpolate
my_interpolating_function_x = RegularGridInterpolator((x, y), Fx)
my_interpolating_function_y = RegularGridInterpolator((x, y), Fy)
# Create Meshgrid
N = 50
x = np.linspace(xmin,xmax, N)
y = np.linspace(ymin,ymax, N)
grid = np.meshgrid(x, y)
new_points = np.vstack(list(map(np.ravel, grid))).T
# Interpolate
F_x_inter = my_interpolating_function_x(new_points)
F_y_inter = my_interpolating_function_y(new_points)
# reshape
F_x_inter = np.reshape(F_x_inter,(50,50))
F_y_inter = np.reshape(F_y_inter,(50,50))
#plot
fig, ax = plt.subplots()
ax.quiver(x, y, F_x_inter, F_y_inter)
plt.title("Interpolated")
plt.show()
I have the following code:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi/2, np.pi/2, 30)
y = np.linspace(-np.pi/2, np.pi/2, 30)
x,y = np.meshgrid(x,y)
z = np.sin(x**2+y**2)[:-1,:-1]
fig,ax = plt.subplots()
ax.pcolormesh(x,y,z)
Which gives this image:
Now lets say I want to highlight the edge certain grid boxes:
highlight = (z > 0.9)
I could use the contour function, but this would result in a "smoothed" contour. I just want to highlight the edge of a region, following the edge of the grid boxes.
The closest I've come is adding something like this:
highlight = np.ma.masked_less(highlight, 1)
ax.pcolormesh(x, y, highlight, facecolor = 'None', edgecolors = 'w')
Which gives this plot:
Which is close, but what I really want is for only the outer and inner edges of that "donut" to be highlighted.
So essentially I am looking for some hybrid of the contour and pcolormesh functions - something that follows the contour of some value, but follows grid bins in "steps" rather than connecting point-to-point. Does that make sense?
Side note: In the pcolormesh arguments, I have edgecolors = 'w', but the edges still come out to be blue. Whats going on there?
EDIT:
JohanC's initial answer using add_iso_line() works for the question as posed. However, the actual data I'm using is a very irregular x,y grid, which cannot be converted to 1D (as is required for add_iso_line().
I am using data which has been converted from polar coordinates (rho, phi) to cartesian (x,y). The 2D solution posed by JohanC does not appear to work for the following case:
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
def pol2cart(rho, phi):
x = rho * np.cos(phi)
y = rho * np.sin(phi)
return(x, y)
phi = np.linspace(0,2*np.pi,30)
rho = np.linspace(0,2,30)
pp, rr = np.meshgrid(phi,rho)
xx,yy = pol2cart(rr, pp)
z = np.sin(xx**2 + yy**2)
scale = 5
zz = ndimage.zoom(z, scale, order=0)
fig,ax = plt.subplots()
ax.pcolormesh(xx,yy,z[:-1, :-1])
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xmin, xmax = xx.min(), xx.max()
ymin, ymax = yy.min(), yy.max()
ax.contour(np.linspace(xmin,xmax, zz.shape[1]) + (xmax-xmin)/z.shape[1]/2,
np.linspace(ymin,ymax, zz.shape[0]) + (ymax-ymin)/z.shape[0]/2,
np.where(zz < 0.9, 0, 1), levels=[0.5], colors='red')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
This post shows a way to draw such lines. As it is not straightforward to adapt to the current pcolormesh, the following code demonstrates a possible adaption.
Note that the 2d versions of x and y have been renamed, as the 1d versions are needed for the line segments.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(-np.pi / 2, np.pi / 2, 30)
y = np.linspace(-np.pi / 2, np.pi / 2, 30)
xx, yy = np.meshgrid(x, y)
z = np.sin(xx ** 2 + yy ** 2)[:-1, :-1]
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z)
def add_iso_line(ax, value, color):
v = np.diff(z > value, axis=1)
h = np.diff(z > value, axis=0)
l = np.argwhere(v.T)
vlines = np.array(list(zip(np.stack((x[l[:, 0] + 1], y[l[:, 1]])).T,
np.stack((x[l[:, 0] + 1], y[l[:, 1] + 1])).T)))
l = np.argwhere(h.T)
hlines = np.array(list(zip(np.stack((x[l[:, 0]], y[l[:, 1] + 1])).T,
np.stack((x[l[:, 0] + 1], y[l[:, 1] + 1])).T)))
lines = np.vstack((vlines, hlines))
ax.add_collection(LineCollection(lines, lw=1, colors=color))
add_iso_line(ax, 0.9, 'r')
plt.show()
Here is an adaption of the second answer, which can work with only 2d arrays:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from scipy import ndimage
x = np.linspace(-np.pi / 2, np.pi / 2, 30)
y = np.linspace(-np.pi / 2, np.pi / 2, 30)
x, y = np.meshgrid(x, y)
z = np.sin(x ** 2 + y ** 2)
scale = 5
zz = ndimage.zoom(z, scale, order=0)
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z[:-1, :-1] )
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xmin, xmax = x.min(), x.max()
ymin, ymax = y.min(), y.max()
ax.contour(np.linspace(xmin,xmax, zz.shape[1]) + (xmax-xmin)/z.shape[1]/2,
np.linspace(ymin,ymax, zz.shape[0]) + (ymax-ymin)/z.shape[0]/2,
np.where(zz < 0.9, 0, 1), levels=[0.5], colors='red')
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
plt.show()
I'll try to refactor add_iso_line method in order to make it more clear an open for optimisations. So, at first, there comes a must-do part:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(-np.pi/2, np.pi/2, 30)
y = np.linspace(-np.pi/2, np.pi/2, 30)
x, y = np.meshgrid(x,y)
z = np.sin(x**2+y**2)[:-1,:-1]
fig, ax = plt.subplots()
ax.pcolormesh(x,y,z)
xlim, ylim = ax.get_xlim(), ax.get_ylim()
highlight = (z > 0.9)
Now highlight is a binary array that looks like this:
After that we can extract indexes of True cells, look for False neighbourhoods and identify positions of 'red' lines. I'm not comfortable enough with doing it in a vectorised manner (like here in add_iso_line method) so just using simple loop:
lines = []
cells = zip(*np.where(highlight))
for x, y in cells:
if x == 0 or highlight[x - 1, y] == 0: lines.append(([x, y], [x, y + 1]))
if x == highlight.shape[0] or highlight[x + 1, y] == 0: lines.append(([x + 1, y], [x + 1, y + 1]))
if y == 0 or highlight[x, y - 1] == 0: lines.append(([x, y], [x + 1, y]))
if y == highlight.shape[1] or highlight[x, y + 1] == 0: lines.append(([x, y + 1], [x + 1, y + 1]))
And, finally, I resize and center coordinates of lines in order to fit with pcolormesh:
lines = (np.array(lines) / highlight.shape - [0.5, 0.5]) * [xlim[1] - xlim[0], ylim[1] - ylim[0]]
ax.add_collection(LineCollection(lines, colors='r'))
plt.show()
In conclusion, this is very similar to JohanC solution and, in general, slower. Fortunately, we can reduce amount of cells significantly, extracting contours only using python-opencv package:
import cv2
highlight = highlight.astype(np.uint8)
contours, hierarchy = cv2.findContours(highlight, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cells = np.vstack(contours).squeeze()
This is an illustration of cells being checked:
I am trying to find a slope of a line in 3D space. The solution for plotting such line is given in this post
Here's the given code from the link above:
import numpy as np
pts = np.add.accumulate(np.random.random((10,3)))
x,y,z = pts.T
# this will find the slope and x-intercept of a plane
# parallel to the y-axis that best fits the data
A_xz = np.vstack((x, np.ones(len(x)))).T
m_xz, c_xz = np.linalg.lstsq(A_xz, z)[0]
# again for a plane parallel to the x-axis
A_yz = np.vstack((y, np.ones(len(y)))).T
m_yz, c_yz = np.linalg.lstsq(A_yz, z)[0]
# the intersection of those two planes and
# the function for the line would be:
# z = m_yz * y + c_yz
# z = m_xz * x + c_xz
# or:
def lin(z):
x = (z - c_xz)/m_xz
y = (z - c_yz)/m_yz
return x,y
#verifying:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
zz = np.linspace(0,5)
xx,yy = lin(zz)
ax.scatter(x, y, z)
ax.plot(xx,yy,zz)
plt.savefig('test.png')
plt.show()
Math-wise I know how to find intersections of two planes and the slope of a given line but I am having trouble putting it in code.
How can I find the slope of resulting regression line using this solution?
The "slope" of a 3D line is generally taken to be slopes of the line "projected" onto the x, y and z planes. See the second answer to this question
If this is what you intended then it's easy enough to calculate these; this modified version of your code below does this into the sx, sy and sz variables:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from math import pow, sqrt
pts = np.add.accumulate(np.random.random((10,3)))
x, y, z = pts.T
# plane parallel to the y-axis
A_xz = np.vstack((x, np.ones(len(x)))).T
m_xz, c_xz = np.linalg.lstsq(A_xz, z, rcond=None)[0]
# plane parallel to the x-axis
A_yz = np.vstack((y, np.ones(len(y)))).T
m_yz, c_yz = np.linalg.lstsq(A_yz, z, rcond=None)[0]
# the intersection of those two planes and
# the function for the line would be:
# z = m_yz * y + c_yz
# z = m_xz * x + c_xz
# or:
def lin(z):
x = (z - c_xz)/m_xz
y = (z - c_yz)/m_yz
return x,y
# get 2 points on the intersection line
za = z[0]
zb = z[len(z) - 1]
xa, ya = lin(za)
xb, yb = lin(zb)
# get distance between points
len = sqrt(pow(xb - xa, 2) + pow(yb - ya, 2) + pow(zb - za, 2))
# get slopes (projections onto x, y and z planes)
sx = (xb - xa) / len # x slope
sy = (yb - ya) / len # y slope
sz = (zb - za) / len # z slope
# integrity check - the sum of squares of slopes should equal 1.0
# print (pow(sx, 2) + pow(sy, 2) + pow(sz, 2))
fig = plt.figure()
ax = Axes3D(fig)
ax.set_xlabel("x, slope: %.4f" %sx, color='blue')
ax.set_ylabel("y, slope: %.4f" %sy, color='blue')
ax.set_zlabel("z, slope: %.4f" %sz, color='blue')
ax.scatter(x, y, z)
ax.plot([xa], [ya], [za], markerfacecolor='k', markeredgecolor='k', marker = 'o')
ax.plot([xb], [yb], [zb], markerfacecolor='k', markeredgecolor='k', marker = 'o')
ax.plot([xa, xb], [ya, yb], [za, zb], color = 'r')
plt.show()
The output graph below shows the line in question, which is just drawn between the 2 extreme xyz points.
I hope this may help