Find x value for respective y from python plot (matplotlib) [duplicate] - python

How can I get from a plot in Python an exact value on y - axis? I have two arrays vertical_data and gradient(temperature_data) and I plotted them as:
plt.plot(gradient(temperature_data),vertical_data)
plt.show()
Plot shown here:
I need the zero value but it is not exactly zero, it's a float.

I did not find a good answer to the question of how to find the roots or zeros of a numpy array, so here is a solution, using simple linear interpolation.
import numpy as np
N = 750
x = .4+np.sort(np.random.rand(N))*3.5
y = (x-4)*np.cos(x*9.)*np.cos(x*6+0.05)+0.1
def find_roots(x,y):
s = np.abs(np.diff(np.sign(y))).astype(bool)
return x[:-1][s] + np.diff(x)[s]/(np.abs(y[1:][s]/y[:-1][s])+1)
z = find_roots(x,y)
import matplotlib.pyplot as plt
plt.plot(x,y)
plt.plot(z, np.zeros(len(z)), marker="o", ls="", ms=4)
plt.show()
Of course you can invert the roles of x and y to get
plt.plot(y,x)
plt.plot(np.zeros(len(z)),z, marker="o", ls="", ms=4)
Because people where asking how to get the intercepts at non-zero values y0, note that one may simply find the zeros of y-y0 then.
y0 = 1.4
z = find_roots(x,y-y0)
# ...
plt.plot(z, np.zeros(len(z))+y0)
People were also asking how to get the intersection between two curves. In that case it's again about finding the roots of the difference between the two, e.g.
x = .4 + np.sort(np.random.rand(N)) * 3.5
y1 = (x - 4) * np.cos(x * 9.) * np.cos(x * 6 + 0.05) + 0.1
y2 = (x - 2) * np.cos(x * 8.) * np.cos(x * 5 + 0.03) + 0.3
z = find_roots(x,y2-y1)
plt.plot(x,y1)
plt.plot(x,y2, color="C2")
plt.plot(z, np.interp(z, x, y1), marker="o", ls="", ms=4, color="C1")

Related

How to Calculate the Surface Area About y-axis + Need a Python Package to Calculate Symbolic Inverse of Any Functions

After reading Calculus book I think for function that has no trouble to calculate its inverse it is easy to calculate the surface area about x-axis and y-axis. But, if the function is like this: y = (x^6 + 2)/(8x^2)
I can calculate the surface area revolve around x-axis. But not so easy about y-axis. Since I think calculating the inverse of such function is not easy.
This is the code / MWE:
import matplotlib.pyplot as plt
import numpy as np
import sympy as sy
x = sy.Symbol("x")
def f(x):
return ((x**6) + 2)/ (8*x ** 2)
def fd(x):
return sy.simplify(sy.diff(f(x), x))
def f2(x):
return sy.sqrt((1 + (fd(x)**2)))
def vx(x):
return 2*sy.pi*(f(x)*sy.sqrt(1 + (fd(x) ** 2)))
vxi = sy.Integral(vx(x), (x, 1, 3))
vxf = vxi.simplify().doit()
vxn = vxf.evalf()
n = 100
fig = plt.figure(figsize=(14, 7))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222, projection='3d')
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224, projection='3d')
x = np.linspace(1, 3, 3)
y = ((x ** 6) + 2) / (8 * x ** 2)
t = np.linspace(0, np.pi * 2, n)
xn = np.outer(x, np.cos(t))
yn = np.outer(x, np.sin(t))
zn = np.zeros_like(xn)
for i in range(len(x)):
zn[i:i + 1, :] = np.full_like(zn[0, :], y[i])
ax1.plot(x, y)
ax1.set_title("$f(x)$")
ax2.plot_surface(xn, yn, zn)
ax2.set_title("$f(x)$: Revolution around $y$")
# find the inverse of the function
y_inverse = x
x_inverse = ((y_inverse ** 6) + 2) / ( 8 * x ** 2)
xn_inverse = np.outer(x_inverse, np.cos(t))
yn_inverse = np.outer(x_inverse, np.sin(t))
zn_inverse = np.zeros_like(xn_inverse)
for i in range(len(x_inverse)):
zn_inverse[i:i + 1, :] = np.full_like(zn_inverse[0, :], y_inverse[i])
ax3.plot(x_inverse, y_inverse)
ax3.set_title("Inverse of $f(x)$")
ax4.plot_surface(xn_inverse, yn_inverse, zn_inverse)
ax4.set_title("$f(x)$: Revolution around $x$ \n Surface Area = {}".format(vxn))
plt.tight_layout()
plt.show()
The question is, can anyone help to calculate the inverse and then continuing with sympy to integrate it and calculate the surface area for this function that is revolved about y-axis?

Find the closest point to a line from an array of points [duplicate]

This question already has answers here:
Plot a point on a line closest to a point
(1 answer)
Python: point on a line closest to third point
(3 answers)
Python: Closest Point to a line
(1 answer)
Closed 3 months ago.
This post was edited and submitted for review 3 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I have the problem of finding the point which is closest to a line from an array of x- and y-data.
The line is semi-infinite originating from the origin at (0,0) and running into the direction of a given angle.
The x,y data of the points are given in relation to the origin.
How do I find the closest point (and its distance) to the line in line direction (not opposite)?
This is an example of the data I have:
import numpy as np
import matplotlib.pyplot as plt
def main():
depth = np.random.random((100))*20+50
angle = np.linspace(0, 2*np.pi, 100)
x,y = depth2xy(depth, angle)
line = np.random.random_sample()*2*np.pi
# fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
plt.scatter(x, y)
plt.plot([0,100*np.cos(line)], [0, 100*np.sin(line)], markersize=10, color = "r")
plt.show()
def depth2xy(depth, angle):
x, y = np.zeros(len(depth)), np.zeros(len(depth))
for i in range(len(depth)):
x[i] = depth[i]*np.cos(angle[i])
y[i] = depth[i]*np.sin(angle[i])
return x,y
if __name__ == "__main__": main()
I could try a brute force approach, iterating over different distances along the line to find the ultimate smallest distance.
But as time efficiency is critical my case and the algorithm would not perform as well as I think it could, I would rather try an analytical approach.
I also thought about scipy.spatial.distance, but I am not sure how this would work for a line.
Your assigned line passes through the origin, its parametric equation is
x = u cos(a)
y = u sin(a)
and you can see the parameter u is simply the (oriented) distance beteween the origin and a point on the assigned line.
Now, consider a point of coordinates X and Y, a line perpendicular to the assigned one has the parametric equation
x = X - v sin(a)
y = Y + v cos(a)
and again, the parameter v is simply the (oriented) distance between (X, Y) and a point on a line passing per (X, Y) and perpendicular to the assigned one.
The intersection is given by the equation
X = u cos(a) + v sin(a)
Y = u sin(a) - v cos(a)
you can check by inspection that the solution of the system is
u = X cos(a) + Y sin(a)
v = X sin(a) - Y cos(a)
The distance of the point (X, Y) from the assigned line is hence
d = | X sin(a) - Y cos(a) |
A Python Implementation
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(20221126)
X = 2*np.random.random(32)-1
Y = 2*np.random.random(32)-1
fig, ax = plt.subplots()
ax.set_xlim((-1.2, 1.2))
ax.set_ylim((-1.2, 1.2))
ax.grid(1)
ax.set_aspect(1)
ax.scatter(X, Y, s=80, ec='k', color='y')
a = 2*np.random.random()*np.pi
s, c = np.sin(a), np.cos(a)
plt.plot((0, c), (0, s), color='k')
plt.plot((-s, s), (c, -c), color='r')
# strike out "bad" points
bad = X*c+Y*s<0
plt.scatter(X[bad], Y[bad], marker='x', color='k')
# consider only good (i.e., not bad) points
Xg, Yg = X[~bad], Y[~bad]
# compute all distances (but for good points only)
d = np.abs(Xg*s-Yg*c)
# find the nearest point and hilight it
imin = np.argmin(d)
plt.scatter(Xg[imin], Yg[imin], ec='k', color='r')
plt.show()
An OVERDONE Example
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(20221126)
X = 2*np.random.random(32)-1
Y = 2*np.random.random(32)-1
fig, axs = plt.subplots(2, 4, figsize=(10,5), layout='constrained')
for ax, a in zip(axs.flat,
(2.8, 1.8, 1.4, 0.2,
3.4, 4.5, 4.9, 6.0)):
ax.set_xlim((-1.2, 1.2))
ax.set_xticks((-1, -0.5, 0, 0.5, 1.0))
ax.set_ylim((-1.2, 1.2))
ax.grid(1)
ax.set_aspect(1)
ax.set_title('$\\alpha \\approx %d^o$'%round(np.rad2deg(a)))
ax.scatter(X, Y, s=80, ec='k', color='yellow')
s, c = np.sin(a), np.cos(a)
ax.arrow(0, 0, 1.2*c, 1.2*s, fc='k',
length_includes_head=True,
head_width=0.08, head_length=0.1)
# divide the drawing surface in two semiplanes
if abs(c)>abs(s):
if c>0:
ax.plot((1.2*s, -1.2*s), (-1.2, 1.2))
else:
ax.plot((-1.2*s, 1.2*s), (-1.2, 1.2))
elif abs(s)>=abs(c):
if s>0:
ax.plot((-1.2, 1.2), (1.2*c, -1.2*c))
else:
ax.plot((-1.2, 1.2), (-1.2*c, 1.2*c))
# strike out "bad" points
bad = X*c+Y*s<0
ax.scatter(X[bad], Y[bad], marker='x', color='k')
# consider only good (i.e., not bad) points
Xg, Yg = X[~bad], Y[~bad]
# compute all distances (but for good points only)
d = np.abs(Xg*s-Yg*c)
# find the nearest point and hilight it
imin = np.argmin(d)
ax.scatter(Xg[imin], Yg[imin], s=80, ec='k', color='yellow')
ax.scatter(Xg[imin], Yg[imin], s= 10, color='k', alpha=1.0)
plt.show()
Let P be a point from your know data set. Let Q be the projection of this point on the line. You can use an analytic approach to determine the exact location of Q:
OQ is the segment from the origin to the Q point. It is aligned to the line.
PQ is the distance of the point P to the line.
from geometry, the dot product between QP and OQ is zero (the two segments are orthogonal to each other). From this equation we can compute the point Q.
After that, you simply compute all distances and find the shortest one.
I'm going to use SymPy for the analytical part, Numpy for the numerical part and Matplotlib for plotting:
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
xq, xp, yq, yp, m = symbols("x_Q, x_P, y_Q, y_P, m")
A = Matrix([xq - xp, yq - yp])
B = Matrix([xq, yq])
# this equations contains two unkowns: xq, yq
eq = A.dot(B)
# but we know the line equation: yq = m * xq, so we substitute it into
# eq and solve for xq
xq_expr = solve(eq.subs(yq, m * xq), xq)[1]
print(xq_expr)
# (m*y_P + x_P)/(m**2 + 1)
# generate data
mv = -0.5
xp_vals = np.random.uniform(2, 10, 30)
yp_vals = np.random.uniform(2, 10, 30)
# convert the symbolic expression to a numerical function
f = lambdify([m, xp, yp], xq_expr)
# compute the projections on the line
xq_vals = f(mv, xp_vals, yp_vals)
yq_vals = mv * xq_vals
# compute the distance
d = np.sqrt((xp_vals - xq_vals)**2 + (yp_vals - yq_vals)**2)
# find the index of the shortest distance
idx = d.argmin()
fig, ax = plt.subplots()
xline = np.linspace(0, 10)
yline = mv * xline
ax.plot(xline, yline, "k:", label="line")
ax.scatter(xq_vals, yq_vals, label="Q", marker=".")
ax.scatter(xp_vals, yp_vals, label="P", marker="*")
ax.plot([xp_vals[idx], xq_vals[idx]], [yp_vals[idx], yq_vals[idx]], "r", label="min distance")
ax.set_aspect("equal")
ax.legend()
plt.show()

Strange edge behaviour of surface plot in matplotlib

I would like to make surface plot of a function which is discontinuous at certain values in parameter space. It is near these discontinuities that the plot's coloring becomes incorrect, as shown in the picture below. How can I fix this?
My code is given below:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5*np.arctan2(2*gamma, mu_b-mu_a)
epsilon = 2*gamma**2/np.sqrt((mu_a-mu_b)**2+4*gamma**2)
y1 = np.arccos(0.5/t*(-mu_a*np.sin(theta)**2 -mu_b*np.cos(theta)**2 - epsilon))
y2 = np.arccos(0.5/t*(-mu_a*np.cos(theta)**2 -mu_b*np.sin(theta)**2 + epsilon))
return y1+y2
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-2.5, 2.5, 0.01)
Y = np.arange(-2.5, 2.5, 0.01)
X, Y = np.meshgrid(X, Y)
Z = phase(X, Y, 1, 0.6)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
An idea is to make all the arrays 1D, filter out the NaN values and then call ax.plot_trisurf:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5 * np.arctan2(2 * gamma, mu_b - mu_a)
epsilon = 2 * gamma ** 2 / np.sqrt((mu_a - mu_b) ** 2 + 4 * gamma ** 2)
with np.errstate(divide='ignore', invalid='ignore'):
y1 = np.arccos(0.5 / t * (-mu_a * np.sin(theta) ** 2 - mu_b * np.cos(theta) ** 2 - epsilon))
y2 = np.arccos(0.5 / t * (-mu_a * np.cos(theta) ** 2 - mu_b * np.sin(theta) ** 2 + epsilon))
return y1 + y2
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Make data.
X = np.linspace(-2.5, 2.5, 200)
Y = np.linspace(-2.5, 2.5, 200)
X, Y = np.meshgrid(X, Y)
X = X.ravel() # make the array 1D
Y = Y.ravel()
Z = phase(X, Y, 1, 0.6)
mask = ~np.isnan(Z) # select the indices of the valid values
# Plot the surface.
surf = ax.plot_trisurf(X[mask], Y[mask], Z[mask], cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Some remarks:
plot_trisurf will join the XY-values via triangles; this only works well if the domain is convex
to make things draw quicker, less points could be used (the original used 500x500 points, the code here reduces that to 200x200
calling fig.gca(projection='3d') has been deprecated; instead, you could call fig.add_subplot(projection='3d')
the warnings for dividing by zero or using arccos out of range can be temporarily suppressed; that way the warning will still be visible for situations when such isn't expected behavior

Trouble Shooting Python Quiverplots

I have been trying to get a plot of vector lines going using the matplotlib library and I keep getting something like this:
Not sure what is happening since the code I'm running seems to follow the syntax for how to make a basic quiver plot. I've tried messing with the array type to see if that's the issue but no luck. Some points on the plot just don't seem to be getting any vector data.
import matplotlib.pyplot as plt
import numpy as np
X = np.arange(-2,2,.1)
Y = np.arange(-2,2,.1)
x,y = np.meshgrid(X,Y)
m1 =1
m2 =2
x1 =4/3
x2 =2/3
omega = 3/8
u = -(m1/(abs(x-x1))**3)*(x-x1)-(m2/(abs(x-x2))**3)*(x-x2)+ x*omega
v = -(m1/(abs(y))**3)*(y)-(m2/(abs(y))**3)*(y)+ y*omega
fig, ax = plt.subplots()
ax.quiver(x,y,u,v)
plt.show()
A nice way, I find, to have a look at your data is to normalise the vector field and colour it by intensity. You can always mask glyphs for which the intensity is too low by using a Numpy MaskedArray. Have a look below.
import matplotlib.colors as cl
import matplotlib.pyplot as plt
import matplotlib.ticker as tck
import numpy as np
x, y = np.meshgrid(np.linspace(-2, 2, 41), np.linspace(-2, 2, 41))
m1, m2, x1, x2, omega = (1, 2, 4 / 3, 2 / 3, 3 / 8)
u = -(m1 / abs(x - x1) ** 3 * (x - x1) - m2 / abs(x - x2) ** 3 * (x - x2)
+ x * omega)
v = y * (omega - (m1 + m2) / abs(y) ** 3)
fig, (ax, bx) = plt.subplots(ncols=2, figsize=(20, 10))
ax.quiver(x, y, u, v, antialiased=True, scale=1e4, width=6e-3, headwidth=3,
headlength=4, headaxislength=3.5, pivot='tail',
edgecolors='xkcd:white', linewidths=1)
ax.set_aspect('equal')
w = np.sqrt(u ** 2 + v ** 2)
quiv = bx.quiver(x, y, u / w, v / w, w, antialiased=True, scale=3e1,
width=6e-3, headwidth=3, headlength=4, headaxislength=3.5,
pivot='tail', edgecolors='xkcd:white', linewidths=1,
norm=cl.LogNorm(vmin=1e-1, vmax=1e3))
bx.set_aspect('equal')
fig.colorbar(quiv, cax=fig.add_axes([0.93, 0.1, 0.02, 0.8]),
extend='both', ticks=tck.LogLocator(),
format=tck.LogFormatterSciNotation())
Some of the y values are close to 0 so that you get crazily large v values. I would check the equation because the plot is actually correct (the arrows are infinitely large when y ~= 0).

Arc between points in circle

I'm trying to plot a Chord diagram using Matplotlib. I am aware that already existing libraries, such as Plotly give me that functionality but I would really like to do it in matplotlib.
The code I have so far looks like this:
import itertools
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
fig, ax = plt.subplots()
ax.axhline(0, color='black', linestyle='--')
ax.axvline(0, color='black', linestyle='--')
npoints = 3
# Calculate the xy coords for each point on the circle
s = 2 * np.pi / npoints
verts = np.zeros((npoints, 2))
for i in np.arange(npoints):
angle = s * i
x = npoints * np.cos(angle)
y = npoints * np.sin(angle)
verts[i] = [x, y]
# Plot the arcs
numbers = [i for i in xrange(npoints)]
for i, j in itertools.product(numbers, repeat=2):
if i == j:
continue
x1y1 = x1, y1 = verts[i]
x2y2 = x2, y2 = verts[j]
# Calculate the centre of the Arc
mxmy = mx, my = [(x1 + x2) / 2, (y1 + y2) / 2]
r = np.sqrt((x1 - mx)**2 + (y1 - my)**2)
xy = [mx - r, my - r]
width = 2 * r
height = 2 * r
start_angle = np.arctan2(y1 - my, x1 - mx) * 180 / np.pi
end_angle = np.arctan2(y2 - my, x2 - mx) * 180 / np.pi
arc = patches.Arc(mxmy, width, height, start_angle, end_angle)
ax.add_patch(arc)
# Plot the points
x, y = verts.T
ax.scatter(x, y, marker='o', s=50, c='r')
ax.annotate("1", (x[0], y[0]), xytext=(x[0] + .5, y[0] + .5))
ax.annotate("2", (x[1], y[1]), xytext=(x[1] - 1, y[1] + .5))
ax.annotate("3", (x[2], y[2]), xytext=(x[2] - 1, y[2] - 1))
ax.set_xlim(-npoints - 5, npoints + 6)
ax.set_ylim(-npoints - 5, npoints + 6)
ax.set(aspect=1)
Is anyone able to tell me why my plot looks like this?
I'm expecting something more like the following (image taken from Plotly)
Edit 1
I would like to draw arcs between the following points:
1 and 2
1 and 3
2 and 3
These arcs should ideally be on the inside.
Edit 2
After some further investigation I figured that the end_angle seems to be the root of the problem.
After #f5r5e5d pointing out the Bézier curve used in plotly, I've decided to give this one a go. It looks like this is the way to go in my case, too.
import itertools
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
import sys
%matplotlib inline
fig, ax = plt.subplots()
npoints = 5
# Calculate the xy coords for each point on the circle
s = 2 * np.pi / npoints
verts = np.zeros((npoints, 2))
for i in np.arange(npoints):
angle = s * i
x = npoints * np.cos(angle)
y = npoints * np.sin(angle)
verts[i] = [x, y]
# Plot the Bezier curves
numbers = [i for i in xrange(npoints)]
bezier_path = np.arange(0, 1.01, 0.01)
for a, b in itertools.product(numbers, repeat=2):
if a == b:
continue
x1y1 = x1, y1 = verts[a]
x2y2 = x2, y2 = verts[b]
xbyb = xb, yb = [0, 0]
# Compute and store the Bezier curve points
x = (1 - bezier_path)** 2 * x1 + 2 * (1 - bezier_path) * bezier_path * xb + bezier_path** 2 * x2
y = (1 - bezier_path)** 2 * y1 + 2 * (1 - bezier_path) * bezier_path * yb + bezier_path** 2 * y2
ax.plot(x, y, 'k-')
x, y = verts.T
ax.scatter(x, y, marker='o', s=50, c='r')
ax.set_xlim(-npoints - 5, npoints + 6)
ax.set_ylim(-npoints - 5, npoints + 6)
ax.set(aspect=1)
The code above plots what I wanted it do to. Some modifications on the style and it should be good to go.
Since the underlying problem was "how can I draw a chord diagram in matplotlib", I just want to let you know that there is now a python library to do that: mpl-chord-diagram.
You can just do pip install mpl-chord-diagram.
[disclaimer] I am the current maintainer [/disclaimer]

Categories

Resources