I am able to plot the stair-step like plot as attached using the below code
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
x = np.array([3.45, 3.88, 3.99, 4.33])
y = np.array([14.0, 11.0, 14.0, 31.0])
y = np.cumsum(y)
y0 = np.array([0])
x0 = np.interp([0], y, x)
x = np.concatenate([x0, x])
y = np.concatenate([y0, y])
# Plot
fig, ax = plt.subplots()
ax.step(x, y, color='r', where='post')
ax.set_xlim(2, 5)
ax.set_ylim(0, y.max())
ax.invert_yaxis()
plt.show()
#####################################################
Now I need to generate more data points of x and y using interpolation function and want to generate same plot as above code produced(depicted above). I tried the below mentioned code but it produces different plot(depicted below), But definition says after interpolation, we should have the same plot, we just have more data points.
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
x = np.array([3.45, 3.88, 3.99, 4.33])
y = np.array([14.0, 11.0, 14.0, 31.0])
y = np.cumsum(y)
y0 = np.array([0])
x0 = np.interp([0], y, x)
x = np.concatenate([x0, x])
y = np.concatenate([y0, y])
# I need to generate more data points
NUM = 100 # modify the number of points
funct = interp1d(x, y, kind='next')
x_cont = np.linspace(x[0], x[-1], NUM)
y_cont = funct(x_cont)
# Plot
fig, ax1 = plt.subplots()
ax1.step(x_cont, y_cont, color='r', where='post')
ax1.set_xlim(2, 5)
ax1.set_ylim(0, y.max())
ax1.invert_yaxis()
plt.show()
Update
IIUC, now you want the points where line changes direction. So you can use np.repeat
# Original data
x = np.array([3.45, 3.88, 3.99, 4.33])
y = np.array([14.0, 11.0, 14.0, 31.0])
y = np.cumsum(y)
# Find breakpoints
x_cont = np.hstack([x[0], np.repeat(x, 2)[1:]])
y_cont = np.hstack([0, np.repeat(y, 2)[:-1]])
# Plot
fig, ax = plt.subplots()
ax.scatter(x_cont, y_cont, marker='.', color='blue')
ax.plot(x_cont, y_cont, color='red')
ax.set_xlim(2, 5)
ax.set_ylim(0, y.max())
ax.invert_yaxis()
plt.show()
Output:
>>> x_cont
array([3.45, 3.45, 3.88, 3.88, 3.99, 3.99, 4.33, 4.33])
>>> y_cont
array([ 0., 14., 14., 25., 25., 39., 39., 70.])
Old answer
You want to increase the number of points for each step. You can use a custom function to interpolate values from (x1, y1) to (x2, y1) then from (x2, y1) to (x2, y2). It means you compute x points first then y. The function take 3 couple of parameters and the step size:
x, y: your intermediate step points (your two arrays x and y)
x0, y0: start coords (here you have to specify y=0 because it does not appear in your data)
xn, yn: end coords (here you can reuse the last value of x and y arrays)
step: the step size (we approximate the value to have points evenly spaced)
def interpolate(x, y, x0, y0, xn, yn, step):
# create arrays with all points
x = np.hstack([x0, x, xn])
y = np.hstack([y0, y, yn])
x_cont = []
y_cont = []
# get points pairwise
for x1, x2, y1, y2 in zip(x, x[1:], y, y[1:]):
# approximate the number of samples per segment
numx = int(np.abs(x2 - x1) / step)
numy = int(np.abs(y2 - y1) / step)
# x-first (left or right move)
a = np.linspace(x1, x2, numx, endpoint=False)
b = np.repeat(y1, numx)
# y-second (top or down move)
c = np.repeat(x2, numy)
d = np.linspace(y1, y2, numy, endpoint=False)
# save points
x_cont.append([*a, *c])
y_cont.append([*b, *d])
# flat all computed values
return np.hstack([*x_cont, xn]), np.hstack([*y_cont, yn])
# Original data
x = np.array([3.45, 3.88, 3.99, 4.33])
y = np.array([14.0, 11.0, 14.0, 31.0])
y = np.cumsum(y) # It seems you want the cumulative sum of y
x_cont, y_cont = interpolate(x, y, x[0], 0, x[-1], y[-1], step=0.1)
Plot the values:
fig, ax = plt.subplots()
ax.step(x_cont, y_cont, color='red', where='post')
ax.set_xlim(2, 5)
ax.set_ylim(0, y.max())
ax.invert_yaxis()
plt.show()
Output:
It seems like you are looking for a function that takes as input a number of pivots p and the number of points n to put in the horizontal and vertical straight lines connecting those pivots. I assume that you want the extra points to be evenly (linearly) spaced.
Essentially, you need to:
identify the pivot points
ensure the len(p) <= n
use repeated values for x and linearly spaced values for y when the lines are vertical
use repeated values for y and linearly spaced values for x when the lines are horizontal
A simple implementation using NumPy (and specifically np.linspace() and np.repeat()) could be:
import typing as typ
import numpy as np
def upscale_steps_equi(
p_x: typ.Sequence[float],
p_y: typ.Sequence[float],
n: int,
start_hor: typ.Optional[str] = None,
dtype: np.dtype = np.float_,
):
"""
The points are equi-spaced (except near the pivots).
The pivots are, in general, not included.
Args:
p_x: Must be strictly monotonic increasing.
p_y: Must be strictly monotonic increasing.
n: Must be larger than max(len(p_x), len(p_y))
start_hor: Must be True for horizontal, False for vertical.
Only used (and needed) if len(p_x) == len(p_y)
"""
p_x_n = len(p_x)
p_y_n = len(p_y)
is_same_len = p_x_n == p_y_n
p_n = max(p_x_n, p_y_n) + 1 # number of points
if p_x_n > p_y_n:
start_hor = True
elif p_x_n < p_y_n:
start_hor = False
elif start_hor is None or not isinstance(start_hor, bool):
raise ValueError("`start_hor` required if len(p_x) == len(p_y)")
p_x_ = np.repeat(p_x, 2) # x-coords of pivots
p_y_ = np.repeat(p_y, 2) # y-coords of pivots
if is_same_len:
if start_hor:
p_x_ = p_x_[1:]
p_y_ = p_y_[:-1]
else:
p_x_ = p_x_[:-1]
p_y_ = p_y_[1:]
else:
if start_hor:
p_x_ = p_x_[1:-1]
else:
p_y_ = p_y_[1:-1]
dx = max(p_x) - min(p_x) # distance covered along x
dy = max(p_y) - min(p_y) # distance covered along y
d = dx + dy
ll = np.linspace(0, d, n, endpoint=True)
x = min(p_x) + ll
y = min(p_y) + ll
last_x_ = last_y_ = last_d_ = None
d_ = d_x_ = d_y_ = 0
for x_, y_ in zip(p_x_, p_y_):
if x_ == last_x_:
dd_y_ = y_ - last_y_
d_y_ += dd_y_
d_ += dd_y_
mask = (ll >= last_d_) & (ll < d_)
x[mask] = x_
y[mask] -= d_x_
elif y_ == last_y_:
dd_x_ = x_ - last_x_
d_x_ += dd_x_
d_ += dd_x_
mask = (ll >= last_d_) & (ll < d_)
y[mask] = y_
x[mask] -= d_y_
last_x_ = x_
last_y_ = y_
last_d_ = d_
x[-1] = p_x[-1]
y[-1] = p_y[-1]
return x, y
to be used like:
import matplotlib.pyplot as plt
x, y = upscale_steps_equi([1, 4, 5], [2, 3, 7, 9], 20)
plt.scatter(x, y, marker='o')
plt.axis('scaled')
The function above does not, in general, include the pivots.
However, if the pivots must be included, one needs to forego the exact number of points and the same spacing for x and y.
Here is a possible solution where the points are equi-spaced within each segment:
import typing as typ
import numpy as np
def upscale_steps_with_pivots(
p_x: typ.Sequence[float],
p_y: typ.Sequence[float],
n: int,
start_hor: typ.Optional[bool] = None,
dtype: np.dtype = np.float_,
):
"""
The points are equi-spaced only within each segment.
The pivots are strictly included.
Args:
p_x: Must be strictly monotonic increasing.
p_y: Must be strictly monotonic increasing.
n: Must be larger than max(len(p_x), len(p_y))
start_hor: Must be `h` for horizontal, `v` for vertical.
Only used (and needed) if len(p_x) == len(p_y)
"""
p_x_n = len(p_x)
p_y_n = len(p_y)
is_same_len = p_x_n == p_y_n
p_n = max(p_x_n, p_y_n) + 1 # number of points
if p_x_n > p_y_n:
start_hor = True
elif p_x_n < p_y_n:
start_hor = False
elif start_hor is None or not isinstance(start_hor, bool):
raise ValueError("`start_hor` required if len(p_x) == len(p_y)")
p_x_ = np.repeat(p_x, 2) # x-coords of pivots
p_y_ = np.repeat(p_y, 2) # y-coords of pivots
if is_same_len:
if start_hor:
p_x_ = p_x_[1:]
p_y_ = p_y_[:-1]
else:
p_x_ = p_x_[:-1]
p_y_ = p_y_[1:]
else:
if start_hor:
p_x_ = p_x_[1:-1]
else:
p_y_ = p_y_[1:-1]
dx = max(p_x) - min(p_x) # distance covered along x
dy = max(p_y) - min(p_y) # distance covered along y
d = dx + dy
n_x = (np.diff(p_x) / d * (n - p_n)).astype(np.int_)
n_y = (np.diff(p_y) / d * (n - p_n)).astype(np.int_)
r = np.ones(len(n_x) + len(n_y) + 1, dtype=np.int_)
if start_hor:
r[:-1 if is_same_len else None:2] = n_x + 1
r[1:-1:2] = n_y + 1
else:
r[:-1 if is_same_len else None:2] = n_y + 1
r[1:-1:2] = n_x + 1
x = np.repeat(p_x_, r).astype(dtype)
y = np.repeat(p_y_, r).astype(dtype)
j = 0
for i, r_ in enumerate(r[:-1]):
if i % 2 != start_hor:
k = np.linspace(p_x_[i], p_x_[i + 1], r_, endpoint=False)
x[j : j + len(k)] = k
else:
k = np.linspace(p_y_[i], p_y_[i + 1], r_, endpoint=False)
y[j : j + len(k)] = k
j += r_
return x, y
to be used like:
import matplotlib.pyplot as plt
x, y = upscale_steps_with_pivots([1, 4, 5], [2, 3, 7, 9], 20)
plt.scatter(x, y, marker='o')
plt.axis('scaled')
(Note that until the computation of d, the two functions are identical, and it mostly contain the logic to deal with the different possible configurations of segments.)
This line
funct = interp1d(x, y, kind='next')
needs to be changed in
funct = interp1d(x, y, kind='previous')
This because you want values in [3.99, 4.33] (e.g.) to refer to the cumsum-value of 3.99, which is ~40 (previous) instead of values related to 4.33 which is ~70 (next).
Related
I have fitted a 2-D cubic spline using scipy.interpolate.RectBivariateSpline. I would like to access/reconstruct the underlying polynomials within each rectangular cell. How can I do this? My code so far is written below.
I have been able to get the knot points and the coefficients with get_knots() and get_coeffs() so it should be possible to build the polynomials, but I do not know the form of the polynomials that the coefficients correspond to. I tried looking at the SciPy source code but I could not locate the underlying dfitpack.regrid_smth function.
A code demonstrating the fitting:
import numpy as np
from scipy.interpolate import RectBivariateSpline
# Evaluate a demonstration function Z(x, y) = sin(sin(x * y)) on a mesh
# of points.
x0 = -1.0
x1 = 1.0
n_x = 11
x = np.linspace(x0, x1, num = n_x)
y0 = -2.0
y1 = 2.0
n_y = 21
y = np.linspace(y0, y1, num = n_y)
X, Y = np.meshgrid(x, y, indexing = 'ij')
Z = np.sin(np.sin(X * Y))
# Fit the sampled function using SciPy's RectBivariateSpline.
order_spline = 3
smoothing = 0.0
spline_fit_func = RectBivariateSpline(x, y, Z,
kx = order_spline, ky = order_spline, s = smoothing)
And to plot it:
import matplotlib.pyplot as plt
# Make axes.
fig, ax_arr = plt.subplots(1, 2, sharex = True, sharey = True, figsize = (12.0, 8.0))
# Plot the input function.
ax = ax_arr[0]
ax.set_aspect(1.0)
d_x = x[1] - x[0]
x_edges = np.zeros(n_x + 1)
x_edges[:-1] = x - (d_x / 2.0)
x_edges[-1] = x[-1] + (d_x / 2.0)
d_y = y[1] - y[0]
y_edges = np.zeros(n_y + 1)
y_edges[:-1] = y - (d_y / 2.0)
y_edges[-1] = y[-1] + (d_y / 2.0)
ax.pcolormesh(x_edges, y_edges, Z.T)
ax.set_title('Input function')
# Plot the fitted function.
ax = ax_arr[1]
ax.set_aspect(1.0)
n_x_span = n_x * 10
x_span_edges = np.linspace(x0, x1, num = n_x_span)
x_span_centres = (x_span_edges[1:] + x_span_edges[:-1]) / 2.0
#
n_y_span = n_y * 10
y_span_edges = np.linspace(y0, y1, num = n_y_span)
y_span_centres = (y_span_edges[1:] + y_span_edges[:-1]) / 2.0
Z_fit = spline_fit_func(x_span_centres, y_span_centres)
ax.pcolormesh(x_span_edges, y_span_edges, Z_fit.T)
x_knot, y_knot = spline_fit_func.get_knots()
X_knot, Y_knot = np.meshgrid(x_knot, y_knot)
# Plot the knots.
ax.scatter(X_knot, Y_knot, s = 1, c = 'r')
ax.set_title('Fitted function and knots')
plt.show()
I was trying to an example of the book -"Dynamical Systems with Applications using Python" and I was asked to plot the phase portrait of Verhulst equation, then I came across this post: How to plot a phase portrait of Verhulst equation with SciPy (or SymPy) and Matplotlib?
I'm getting the same plot as the user on the previous post. Whenever, I try to use the accepted solution I get a "division by zero" error. Why doesn't the accepted solution in How to plot a phase portrait of Verhulst equation with SciPy (or SymPy) and Matplotlib? works?
Thank you very much for you help!
Edit:
Using the code from the previous post and the correction given by #Lutz Lehmann
beta, delta, gamma = 1, 2, 1
b,d,c = 1,2,1
C1 = gamma*c-delta*d
C2 = gamma*b-beta*d
C3 = beta*c-delta*b
def verhulst(X, t=0):
return np.array([beta*X[0] - delta*X[0]**2 -gamma*X[0]*X[1],
b*X[1] - d*X[1]**2 -c*X[0]*X[1]])
X_O = np.array([0., 0.])
X_R = np.array([C2/C1, C3/C1])
X_P = np.array([0, b/d])
X_Q = np.array([beta/delta, 0])
def jacobian(X, t=0):
return np.array([[beta-delta*2*X[0]-gamma*X[1], -gamma*x[0]],
[b-d*2*X[1]-c*X[0], -c*X[1]]])
values = np.linspace(0.3, 0.9, 5)
vcolors = plt.cm.autumn_r(np.linspace(0.3, 1., len(values)))
f2 = plt.figure(figsize=(4,4))
for v, col in zip(values, vcolors):
X0 = v * X_R
X = odeint(verhulst, X0, t)
plt.plot(X[:,0], X[:,1], color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )
ymax = plt.ylim(ymin=0)[1]
xmax = plt.xlim(xmin=0)[1]
nb_points = 20
x = np.linspace(0, xmax, nb_points)
y = np.linspace(0, ymax, nb_points)
X1, Y1 = np.meshgrid(x, y)
DX1, DY1 = verhulst([X1, Y1]) # compute growth rate on the gridt
M = (np.hypot(DX1, DY1)) # Norm of the growth rate
M[M == 0] = 1. # Avoid zero division errors
DX1 /= M # Normalize each arrows
DY1 /= M
plt.quiver(X1, Y1, DX1, DY1, M, cmap=plt.cm.jet)
plt.xlabel('Number of Species 1')
plt.ylabel('Number of Species 2')
plt.legend()
plt.grid()
We have:
That is still different from:
What am I missing?
With the help of #Lutz Lehmann I could rewrite the code to get want I needed.
The solutions is something like this:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 4), dpi= 80, facecolor='whitesmoke', edgecolor='k')
beta, delta, gamma = 1, 2, 1
b,d,c = 1,2,1
t = np.linspace(0, 10, 100)
C1 = gamma*c-delta*d
C2 = gamma*b-beta*d
C3 = beta*c-delta*b
def verhulst(X, t=0):
return np.array([beta*X[0] - delta*X[0]**2 -gamma*X[0]*X[1],
b*X[1] - d*X[1]**2 -c*X[0]*X[1]])
X_O = np.array([0., 0.])
X_R = np.array([C2/C1, C3/C1])
X_P = np.array([0, b/d])
X_Q = np.array([beta/delta, 0])
def jacobian(X, t=0):
return np.array([[beta-delta*2*X[0]-gamma*X[1], -gamma*x[0]],
[b-d*2*X[1]-c*X[0], -c*X[1]]])
values = np.linspace(0.05, 0.15, 5)
vcolors = plt.cm.autumn_r(np.linspace(0.3, 1., len(values)))
for v, col in zip(values, vcolors):
X0 = [v,0.2-v]
X = odeint(verhulst, X0, t)
plt.plot(X[:,0], X[:,1], color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )
for v, col in zip(values, vcolors):
X0 = [6 * v, 6 *(0.2-v)]
X = odeint(verhulst, X0, t)
plt.plot(X[:,0], X[:,1], color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )
ymax = plt.ylim(ymin=0)[1]
xmax = plt.xlim(xmin=0)[1]
nb_points = 20
x = np.linspace(0, xmax, nb_points)
y = np.linspace(0, ymax, nb_points)
X1, Y1 = np.meshgrid(x, y)
DX1, DY1 = verhulst([X1, Y1]) # compute growth rate on the gridt
M = (np.hypot(DX1, DY1)) # Norm of the growth rate
M[M == 0] = 1. # Avoid zero division errors
DX1 /= M # Normalize each arrows
DY1 /= M
plt.quiver(X1, Y1, DX1, DY1, M, cmap=plt.cm.jet)
plt.xlabel('Number of Species 1')
plt.ylabel('Number of Species 2')
plt.grid()
We get what we were looking for:
Finally, I would like to thank again #Lutz Lehnmann for the help. I wouldn't have solved without it him.
Edit 1:
I forgot $t = np.linspace(0, 10, 100)$ and if you change figsize = (8,8), we get a nicer shape in the plot. (Thank you #Trenton McKinney for the remarks)
I have to find the distance between two points inside the cluster. I have generated the clusters through the following code:
X = np.arange(0,10.2,0.16)
Y = np.arange(0,10.2,0.16)
A = np.arange(0,2, 0.005)
x, y = np.meshgrid(X, Y)
z = np.zeros(x.shape)
x0 = X.mean()
y0 = Y.mean()
b_center1 = [2,8]
b_center2 = [6,2]
r1=0.1*X.max()
r2=0.1*X.max()
for i in range(1, len(X)):
for j in range(1, len(Y)):
dist = pdist(np.array([[X[i],Y[j]],b_center1]))
if (dist[0] < r1):
z[i][j]=1
dist = pdist(np.array([[X[i],Y[j]],b_center2]))
if (dist[0] < r2):
z[i][j]=1
fig = plt.figure(figsize=(12,10))
plt.scatter(x, y, z, cmap='viridis', edgecolor='k')
plt.gca().set_aspect('equal', adjustable='box')
plt.draw()
plt.xticks([0,1,2,3,4,5,6,7,8,9,10])
plt.yticks([0,1,2,3,4,5,6,7,8,9,10])
plt.show()
This is the clusters I have obtained
d is the distance I have to find
Taking the z array, which is 64*64 matric with 0 and 1.
Get the x_index, y_index where z==1
np.where(z == 1)
(array([ 7, 7, 7, 7, 7, ...),
array([47, 48, 49, 50, 51, ...]))
So it can be seen, for x=7, z=1 for y=47, y=48, y=50 etc.
So we have to calculate the distance between (7,47) and (7,48)
So, the distance is Y[48] - Y[47] or Y[49] - Y[48]
print(Y[48] - Y[47])
>>> 0.15999999999999925 # i.e. around 0.16
And that is simply because you are line spacing by 0.16, your points are separated by 0.16, so you don't have to calculate at all. :p
X = np.arange(0,10.2,0.16)
Y = np.arange(0,10.2,0.16)
A = np.arange(0,2, 0.005)
I don't know that how make the code the three graph in damping harmonic oscillation model,
[X - t(time)], [V(velocity) - t(time)], [a(acceleration) - t(time)] graph
i can make the [X - t(time)] graph
but i don`t know how to make another graphs..
import numpy as np
from matplotlib import pyplot as plt
# mx'' = - bx' - kx
x_0 = 3
v_0 = 0
y_0 = np.array([x_0,v_0]) # first array
def Euler_Method(f,a,b,y0,step):
t = np.linspace(a,b,step)
h = t[1] - t[0]
Y = [y0]
N = len(t)
n = 0
y = y0
for n in range(0,N-1) :
y = y + h*f(y,t[n])
Y.append(y)
n = n+1
Y = np.array(Y)
return Y, t
def harmonic(y,t) :
k = 50
m = 200
b = 20 # drag coefficient
a = (-1*k/m)*y[0] - (b/m)*y[1] # x'' = a, y[0] : first position
v = y[1] # v = first velocity : y[1]
f = np.array([v,a])
return f
a = Euler_Method(harmonic, 0, 100, y_0, 100000)
X = a[0][:,0]
t = a[1]
plt.plot(t,X)
plt.show()
Why can't you just take the derivative of X to get V and A?
V = np.diff(X)
A = np.diff(V)
fig, (ax1, ax2, ax3) = plt.subplots(3)
fig.suptitle('Vertically stacked subplots')
ax1.plot(t, X)
ax2.plot(t[1:], V)
ax3.plot(t[2:], A)
plt.show()
Gives,
My code:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-2 * np.pi, 2 * np.pi, 1000)
y = np.tan(x)
y2 = 4 + x*0
y3 = -4 + x*0
fig = plt.figure(figsize=(8,5))
ax = fig.add_subplot(111)
diffs = np.sqrt(np.diff(x)**2+np.diff(y)**2)
length = diffs.sum()
numbers = [2, 4, 6, 8, 10]
p2 = []
for i in range(len(numbers)):
cumlenth = np.cumsum(diffs)
s = np.abs(np.diff(np.sign(cumlenth-numbers[i]))).astype(bool)
c = np.argwhere(s)[0][0]
p = x[c], y[c]
p2.append(p)
ax.cla()
ax = fig.add_subplot(111)
for j in range(len(p2)):
ax.scatter(p2[j][0],p2[j][1], color="crimson", s=5)
plt.plot(np.tan(x))
plt.plot(y2)
plt.plot(y3)
plt.ylim(-10, 10)
I'm trying to find the length of the tan() function cut off by two lines y2, y3 in a certain compartment. This means that only the part marked in red below should be taken to the total length of the chart:
Next, I try to mark the position of a points from list numbers = [] on this the graph, which are lying in the distance equal to the value of these points, starting from the beginning of the graph in point (x,y)=(0,0), and I want get their coordinates. Which for my sample list numbers = [] would give something like this:
What am I missing? Can this be achieved?
I will be grateful for any tips.
plt.plot takes two arguments, an x array and a y array; without providing both, pyplot assumes that you wanted to plot them against the index of the value in the array. So, your first hurdle is to change the lines at the bottom to:
plt.plot(x, np.tan(x))
plt.plot(x, y2)
plt.plot(x, y3)
Now we have the issue of the fact that your calculation is taking the whole graph into account, not just the parts between -4 and 4. You could solve this by filtering the x array by your thresholds:
x_all = np.linspace(-2 * np.pi, 2 * np.pi, 1000)
x_above = x_all[ -4 < np.tan(x_all) ]
x = x_above[ np.tan(x_above) < 4 ]
Finally, we have the issue that the diff calculation seems to be taking the jump from +4 to -4 into account. We can mitigate this by filtering out anywhere the diff in y is negative:
y_up = np.diff(y) > 0
y_diff = np.where( y_up, np.diff(y), 0 )
x_diff = np.where( y_up, np.diff(x), 0 )
diffs = np.sqrt( x_diff**2 + y_diff**2 )
My final code looks like this, and seems to be working as you expect it to:
import matplotlib.pyplot as plt
import numpy as np
x_all = np.linspace(-2 * np.pi, 2 * np.pi, 1000)
x_above = x_all[ -4 < np.tan(x_all) ]
x = x_above[ np.tan(x_above) < 4 ]
y = np.tan(x)
y2 = 4 + x*0
y3 = -4 + x*0
y_up = np.diff(y) > 0
y_diff = np.where( y_up, np.diff(y), 0 )
x_diff = np.where( y_up, np.diff(x), 0 )
diffs = np.sqrt( x_diff**2 + y_diff**2 )
length = diffs.sum()
numbers = [2, 4, 6, 8, 10]
p2 = []
for i in range(len(numbers)):
cumlenth = np.cumsum(diffs)
s = np.abs(np.diff(np.sign(cumlenth-numbers[i]))).astype(bool)
c = np.argwhere(s)[0][0]
p = x[c], y[c]
p2.append(p)
for j in range(len(p2)):
plt.scatter( p2[j][0], p2[j][1], color="crimson", s=5)
plt.plot(x, np.tan(x))
plt.plot(x, y2)
plt.plot(x, y3)
plt.ylim(-10, 10)
plt.show()