numpy 绘制垂直于坐标的箭头

trnvg8h3  于 2023-01-05  发布在  其他
关注(0)|答案(3)|浏览(142)

我有一个像这样的图,用x和y画一个半圆

我想在每个点上添加箭头,就像这样(忽略可怕的油漆工作):

有没有一种简单的方法可以添加垂直于图的箭头?
当前代码:

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r

x = np.linspace(x0,x1,9)
y = k + np.sqrt(r**2 - (x-h)**2)

plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)

垂直于曲线的切线抱歉我忘了加上这个

jq6vz3qz

jq6vz3qz1#

空间中的一个点不知道“垂直”是什么意思,但假设y是x的某个导数函数,你可以把函数在某点的导数看作是曲线在该点的切线,要得到一个垂直向量,你只需要把向量逆时针旋转90度:
第一个月

lmvvr0a8

lmvvr0a82#

我们知道这些点来自一个圆。所以给定三个点,我们可以很容易地找到中心使用基本的几何概念。如果你需要一个复习,看看here
对于这个特殊的例子,中心在原点,知道中心坐标,每个点的法线就是从中心到点本身的向量,因为中心是原点,法线的分量就由点本身的坐标给出。

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r

x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
center = np.array([0.0, 0.0])

plt.scatter(x, y)
plt.quiver(x, y, x, y, width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()

如果您赶时间,没有时间实现公式,可以按以下方式使用scikit-spatial库:

from skspatial.objects import Circle, Vector, Points

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r

x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
points = Points(np.vstack((x, y)).T)
circle = Circle.best_fit(np.vstack((x, y)).T)
center = circle.point
normals = np.array([Vector.from_points(center, point) for point in points])
plt.scatter(x, y)
plt.quiver(x, y, normals[:, 0], normals[:, 1], width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()
gajydyqb

gajydyqb3#

假设Blunova和Simon的答案是正确的,一般来说:点没有法线,曲线有法线;所以你需要依赖于你所知道的曲线,或者,如blunova所描述的,通过知道它是一个圆,然后根据这个知识,用特别的计算来计算那些法线。
或者,正如我将要描述的,使用函数f,例如y=f(x),并使用关于什么是这样一个(x,f(x))图的法线的知识。
下面是你的代码,用这样一个函数f写的

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r
x = np.linspace(x0,x1,9)

def f(x):
   return k + np.sqrt(r**2 - (x-h)**2)
y=f(x)

plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)

所以,我在这里所做的就是把你的y=...行重写成一个函数的形式。
从那里,可以计算图表(x,f(x))的每个点的法线。
点(x,f(x))的切线是众所周知的:它是向量(1,f '(x)),其中f'(x)是f的导数。因此,垂直于它是(-f '(x),1)。除以√(f'(x)²+1)来归一化这个向量。
所以,就用它作为颤抖的入口。
首先计算函数的导数

dx=0.001
def fprime(x):
    return (f(x+dx)-f(x-dx))/(2*dx)

那就

plt.quiver(x, f(x), -fprime(x), 1)

或者,将所有矢量归一化

plt.quiver(x, f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))

(note fprime和规格化部分都是可向量化的运算,因此它在x是arange的情况下工作)
一起

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r

def f(x):
    return k+ np.sqrt(r**2 - (x-h)**2)

dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
    return (f(x+dx)-f(x-dx))/(2*dx)

plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))

plt.xlim(-4,4)
plt.ylim(-4,4)
plt.show()

除了quiver行和添加的fprime之外,这几乎是您的代码的完全相同的副本。针对您的曲线,还有一个细微的更改,即我更改了x range以确保fprime的可计算性(如果第一个x是x 0,那么fprime需要f(x 0-dx),由于sqrt,f(x 0-dx)不存在。对于x1也是如此。因此,第一个x是x 0 +dx,最后一个是x1-dx,这在视觉上是相同的)

这是该解决方案相对于blunova的主要优势:本质上,它是您代码,如果您更改f,而不假设f是圆,它也可以工作,所假设的只是f是可导的(如果不是,您就无法定义那些法线是什么)。
例如,如果你想用抛物线做同样的事情,只需改变f

import numpy as np
import matplotlib.pyplot as plt

r = 2
h = 0
k = 0

x0 = h-r
x1 = h+r

def f(x):
    return x**2

dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
    return (f(x+dx)-f(x-dx))/(2*dx)

plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))

plt.xlim(-4,4)
plt.ylim(-2,5)
plt.show()

我在这里只改变了f公式。不需要新的推理来计算法线。

最后备注:一个更精确的版本(不强制用dx近似计算f素数)是使用sympy定义f,然后计算f的真实的、符号和导数,但这对您的情况似乎没有必要。

相关问题