我想在python中绘制带有弯曲箭头的矢量场,可以在vfplot(见下文)或IDL中完成。
您可以在matplotlib中接近,但使用quiver()
会将您限制为直线向量(请参见左下方),而streamplot()
似乎不允许有意义地控制箭头长度或箭头位置(请参见下文)右),即使更改integration_direction
,density
和maxlength
也是如此。
那么,有没有可以做到这一点的python库?还是有办法让matplotlib做到这一点?
答案 0 :(得分:4)
如果您查看matplotlib中包含的streamplot.py,则在第196-202行(如果在版本之间已更改,则为idk,如果是matklotlib 2.1.2,则为idk),我们将看到以下内容:
... (to line 195)
# Add arrows half way along each trajectory.
s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
n = np.searchsorted(s, s[-1] / 2.)
arrow_tail = (tx[n], ty[n])
arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
... (after line 196)
将该部分更改为此将可以解决问题(更改n的分配):
... (to line 195)
# Add arrows half way along each trajectory.
s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
n = np.searchsorted(s, s[-1]) ### THIS IS THE EDITED LINE! ###
arrow_tail = (tx[n], ty[n])
arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
... (after line 196)
如果您对此进行修改以将箭头放在末尾,那么您可以根据自己的喜好生成更多箭头。
此外,从函数顶部的文档中,我们看到以下内容:
*linewidth* : numeric or 2d array
vary linewidth when given a 2d array with the same shape as velocities.
线宽可以为numpy.ndarray
,如果您可以预先计算所需的箭头宽度,则可以在绘制箭头时修改铅笔宽度。看来这部分已经为您完成了。
因此,与缩短箭头的最大长度,增加密度和添加start_point以及调整函数以将箭头放在末尾而不是中间的方法结合使用,您可以获得所需的图形。
通过这些修改和以下代码,我可以使结果更接近您想要的结果:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])
grains = 10
tmp = tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
xs += x
ys = tuple(np.linspace(-2, 2, grains))*grains
seed_points = np.array([list(xs), list(ys)])
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])
strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=np.array(5*np.random.random_sample((100, 100))**2 + 1), cmap='winter', density=10,
minlength=0.001, maxlength = 0.07, arrowstyle='fancy',
integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')
plt.tight_layout()
plt.show()
tl; dr:复制源代码,然后将其更改为将箭头放在每个路径的末尾,而不是中间。然后,使用您的流图而不是matplotlib流图。
编辑:我得到了不同的线宽
答案 1 :(得分:4)
从David Culbreth's修改开始,我streamplot
函数的rewrote大块实现所需的行为。此处略微过多以至于无法全部指定它们,但是它包括长度标准化方法,并且禁用了轨迹重叠检查。我将新的curved quiver
函数与原始的streamplot
和quiver
进行了两次比较。
答案 2 :(得分:2)
这是一种在 vanilla pyplot 中获得所需输出的方法(即,无需修改 streamplot 函数或任何花哨的东西)。提醒一下,我们的目标是用弯曲的箭头可视化一个向量场,其长度与向量的范数成正比。
诀窍是:
import matplotlib.pyplot as plt
import numpy as np
w = 3
Y, X = np.mgrid[-w:w:8j, -w:w:8j]
U = -Y
V = X
norm = np.sqrt(U**2 + V**2)
norm_flat = norm.flatten()
start_points = np.array([X.flatten(),Y.flatten()]).T
plt.clf()
scale = .2/np.max(norm)
plt.subplot(121)
plt.title('scaling only the length')
for i in range(start_points.shape[0]):
plt.streamplot(X,Y,U,V, color='k', start_points=np.array([start_points[i,:]]),minlength=.95*norm_flat[i]*scale, maxlength=1.0*norm_flat[i]*scale,
integration_direction='backward', density=10, arrowsize=0.0)
plt.quiver(X,Y,U/norm, V/norm,scale=30)
plt.axis('square')
plt.subplot(122)
plt.title('scaling length, arrowhead and linewidth')
for i in range(start_points.shape[0]):
plt.streamplot(X,Y,U,V, color='k', start_points=np.array([start_points[i,:]]),minlength=.95*norm_flat[i]*scale, maxlength=1.0*norm_flat[i]*scale,
integration_direction='backward', density=10, arrowsize=0.0, linewidth=.5*norm_flat[i])
plt.quiver(X,Y,U/np.max(norm), V/np.max(norm),scale=30)
plt.axis('square')
结果如下:
答案 3 :(得分:0)
只需查看streamplot()
上的文档,就会发现here -如果您使用streamplot( ... ,minlength = n/2, maxlength = n)
这样的东西,其中n
是所需的长度怎么办–您将需要稍微玩那些数字以获得想要的图形
您可以使用start_points
控制点,如@JohnKoch提供的示例所示
这是我如何使用streamplot()
控制长度的一个示例-这几乎是上面example的直接复制/粘贴/裁剪。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])
grains = 10
tmp = tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
xs += x
ys = tuple(np.linspace(-2, 2, grains))*grains
seed_points = np.array([list(xs), list(ys)])
arrowStyle = pat.ArrowStyle.Fancy()
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])
strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=1.5, cmap='winter', density=10,
minlength=0.001, maxlength = 0.1, arrowstyle='->',
integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')
plt.tight_layout()
plt.show()
编辑:使它更漂亮,尽管仍然不是我们想要的。