[英]Number of arrowheads on matplotlib streamplot
Is there anyway to increase the number of arrowheads on a matplotlib streamplot? 无论如何,有没有增加matplotlib流图上箭头的数量? Right now it appears as if three is only one arrowhead per streamline, which is a problem if I want to change to x/y axes limits to zoom in on the data. 现在,似乎每个流线只有三个箭头,如果我想更改为x / y轴限制以放大数据,这是一个问题。
I'm not sure about just increasing the number of arrowheads - but you can increase the density of streamlines with the density parameter in the streamplot function, here's the documentation: 我不确定只是增加箭头的数量-但您可以使用streamplot函数中的density参数来增加流线的密度 ,这是文档:
*density* : float or 2-tuple
Controls the closeness of streamlines. When `density = 1`, the domain
is divided into a 30x30 grid---*density* linearly scales this grid.
Each cell in the grid can have, at most, one traversing streamline.
For different densities in each direction, use [density_x, density_y].
Here is an example: 这是一个例子:
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,20,1)
y = np.arange(0,20,1)
u=np.random.random((x.shape[0], y.shape[0]))
v=np.random.random((x.shape[0], y.shape[0]))
fig, ax = plt.subplots(2,2)
ax[0,0].streamplot(x,y,u,v,density=1)
ax[0,0].set_title('Original')
ax[0,1].streamplot(x,y,u,v,density=4)
ax[0,1].set_xlim(5,10)
ax[0,1].set_ylim(5,10)
ax[0,1].set_title('Zoomed, higher density')
ax[1,1].streamplot(x,y,u,v,density=1)
ax[1,1].set_xlim(5,10)
ax[1,1].set_ylim(5,10)
ax[1,1].set_title('Zoomed, same density')
ax[1,0].streamplot(x,y,u,v,density=4)
ax[1,0].set_title('Original, higher density')
fig.show()
Building on @Richard_wth's answer, I wrote a function to provide control on the location of the arrows on a streamplot. 在@Richard_wth的答案的基础上,我编写了一个函数来控制流图上箭头的位置。 One can choose n
arrows per streamline, or choose to have the arrows equally spaced on a streamline. 每个流线可以选择n
箭头,也可以选择使箭头在流线上等距分布。
First, you do a normal streamplot
, until you are happy with the location and number of streamlines. 首先,进行普通的streamplot
,直到streamplot
线的位置和数量感到满意为止。 You keep the returned argument sp
. 您保留返回的参数sp
。 For instance: 例如:
sp = ax.streamplot(x,y,u,v,arrowstyle='-',density=10)
What's important here is to have arrowstyle='-'
so that arrows are not displayed. 在这里重要的是使用arrowstyle='-'
以便不显示箭头。
Then, you can call the function streamQuiver
(provided below) to control the arrows on the each streamline. 然后,您可以调用函数streamQuiver
(下面提供)来控制每个流线上的箭头。 If you want 3 arrows per streamline: 如果您希望每个流线3个箭头:
streamQuiver(ax, sp, n=3, ...)
If you want a streamline every 1.5
curvilinear length: 如果要每1.5
曲线长度使用一条流线:
streamQuiver(ax, sp, spacing=1.5, ...)
where ...
are options that would be passed to quiver
. 其中...
是将传递到quiver
选项。 The function streamQuiver
is probably not fully bulletproof and may need some additional handling for particular cases. 函数streamQuiver
可能不是完全防弹的,在某些情况下可能需要一些其他处理。 It relies on 4 subfunctions: 它依赖于4个子功能:
curve_coord
to get the curvilinear length along a path curve_coord
获取沿路径的曲线长度 curve extract
to extract equidistant point along a path curve extract
以提取沿路径的等距点 seg_to_lines
to convert the segments from streamplot into continuous lines. seg_to_lines
将段从流图转换为连续的线。 There might be a better way to do that! 可能会有更好的方法! lines_to_arrows
: this is the main function that extract arrows on each lines lines_to_arrows
:这是提取每行箭头的主要功能 Here's an example where the arrows are at equidistant points on each streamlines. 这是一个示例,其中箭头在每条流线上的等距点处。
import numpy as np
import matplotlib.pyplot as plt
def streamQuiver(ax,sp,*args,spacing=None,n=5,**kwargs):
""" Plot arrows from streamplot data
The number of arrows per streamline is controlled either by `spacing` or by `n`.
See `lines_to_arrows`.
"""
def curve_coord(line=None):
""" return curvilinear coordinate """
x=line[:,0]
y=line[:,1]
s = np.zeros(x.shape)
s[1:] = np.sqrt((x[1:]-x[0:-1])**2+ (y[1:]-y[0:-1])**2)
s = np.cumsum(s)
return s
def curve_extract(line,spacing,offset=None):
""" Extract points at equidistant space along a curve"""
x=line[:,0]
y=line[:,1]
if offset is None:
offset=spacing/2
# Computing curvilinear length
s = curve_coord(line)
offset=np.mod(offset,s[-1]) # making sure we always get one point
# New (equidistant) curvilinear coordinate
sExtract=np.arange(offset,s[-1],spacing)
# Interpolating based on new curvilinear coordinate
xx=np.interp(sExtract,s,x);
yy=np.interp(sExtract,s,y);
return np.array([xx,yy]).T
def seg_to_lines(seg):
""" Convert a list of segments to a list of lines """
def extract_continuous(i):
x=[]
y=[]
# Special case, we have only 1 segment remaining:
if i==len(seg)-1:
x.append(seg[i][0,0])
y.append(seg[i][0,1])
x.append(seg[i][1,0])
y.append(seg[i][1,1])
return i,x,y
# Looping on continuous segment
while i<len(seg)-1:
# Adding our start point
x.append(seg[i][0,0])
y.append(seg[i][0,1])
# Checking whether next segment continues our line
Continuous= all(seg[i][1,:]==seg[i+1][0,:])
if not Continuous:
# We add our end point then
x.append(seg[i][1,0])
y.append(seg[i][1,1])
break
elif i==len(seg)-2:
# we add the last segment
x.append(seg[i+1][0,0])
y.append(seg[i+1][0,1])
x.append(seg[i+1][1,0])
y.append(seg[i+1][1,1])
i=i+1
return i,x,y
lines=[]
i=0
while i<len(seg):
iEnd,x,y=extract_continuous(i)
lines.append(np.array( [x,y] ).T)
i=iEnd+1
return lines
def lines_to_arrows(lines,n=5,spacing=None,normalize=True):
""" Extract "streamlines" arrows from a set of lines
Either: `n` arrows per line
or an arrow every `spacing` distance
If `normalize` is true, the arrows have a unit length
"""
if spacing is None:
# if n is provided we estimate the spacing based on each curve lenght)
spacing = [ curve_coord(l)[-1]/n for l in lines]
try:
len(spacing)
except:
spacing=[spacing]*len(lines)
lines_s=[curve_extract(l,spacing=sp,offset=sp/2) for l,sp in zip(lines,spacing)]
lines_e=[curve_extract(l,spacing=sp,offset=sp/2+0.01*sp) for l,sp in zip(lines,spacing)]
arrow_x = [l[i,0] for l in lines_s for i in range(len(l))]
arrow_y = [l[i,1] for l in lines_s for i in range(len(l))]
arrow_dx = [le[i,0]-ls[i,0] for ls,le in zip(lines_s,lines_e) for i in range(len(ls))]
arrow_dy = [le[i,1]-ls[i,1] for ls,le in zip(lines_s,lines_e) for i in range(len(ls))]
if normalize:
dn = [ np.sqrt(ddx**2 + ddy**2) for ddx,ddy in zip(arrow_dx,arrow_dy)]
arrow_dx = [ddx/ddn for ddx,ddn in zip(arrow_dx,dn)]
arrow_dy = [ddy/ddn for ddy,ddn in zip(arrow_dy,dn)]
return arrow_x,arrow_y,arrow_dx,arrow_dy
# --- Main body of streamQuiver
# Extracting lines
seg = sp.lines.get_segments() # list of (2, 2) numpy arrays
lines = seg_to_lines(seg) # list of (N,2) numpy arrays
# Convert lines to arrows
ar_x, ar_y, ar_dx, ar_dy = lines_to_arrows(lines,spacing=spacing,n=n,normalize=True)
# Plot arrows
qv=ax.quiver(ar_x, ar_y, ar_dx, ar_dy, *args, angles='xy', **kwargs)
return qv
# --- Example
x = np.linspace(-1,1,100)
y = np.linspace(-1,1,100)
X,Y=np.meshgrid(x,y)
u = -np.sin(np.arctan2(Y,X))
v = np.cos(np.arctan2(Y,X))
xseed=np.linspace(0.1,1,4)
fig=plt.figure()
ax=fig.add_subplot(111)
sp = ax.streamplot(x,y,u,v,color='k',arrowstyle='-',start_points=np.array([xseed,xseed*0]).T,density=30)
qv = streamQuiver(ax,sp,spacing=0.5, scale=60)
plt.show()
I have found a way to customize the number of arrowheads on streamline plot. 我找到了一种自定义流线图中箭头数量的方法。
The idea is to plot streamline and arrows separately: 这个想法是分别绘制流线和箭头:
plt.streamplot
returns a stream_container with two attributes: lines
and arrows
. plt.streamplot
返回具有两个属性的stream_container: lines
和arrows
。 The lines
contain line segments that can be used to reconstruct streamline without arrows. 这些lines
包含可用于重建无箭头的流线的线段。 plt.quiver
can be used to plot gradient fields. plt.quiver
可用于绘制梯度场。 With the proper scaling, the length of the arrows is neglectable, leaving only arrowheads. 使用适当的缩放比例,箭头的长度可以忽略不计,仅留下箭头。 Thus, we only need to define the positions of arrows using the line segments and pass them to plt.quiver
. 因此,我们只需要使用线段定义箭头的位置,然后将它们传递到plt.quiver
。
Here is a toy example: 这是一个玩具示例:
import matplotlib.pyplot as plt
from matplotlib import collections as mc
import numpy as np
# get line segments
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
sp = ax.streamplot(x, y, u, v, start_points=start_points, density=10)
seg = sps.lines.get_segments() # seg is a list of (2, 2) numpy arrays
lc = mc.LineCollection(seg, ...)
# define arrows
# here I define one arrow every 50 segments
# you could also select segs based on some criterion, e.g. intersect with certain lines
period = 50
arrow_x = np.array([seg[i][0, 0] for i in range(0, len(seg), period)])
arrow_y = np.array([seg[i][0, 1] for i in range(0, len(seg), period)])
arrow_dx = np.array([seg[i][1, 0] - seg[i][0, 0] for i in range(0, len(seg), period)])
arrow_dy = np.array([seg[i][1, 1] - seg[i][0, 1] for i in range(0, len(seg), period)])
# plot the final streamline
fig = plt.figure(figsize=(12.8, 10.8))
ax = fig.add_subplot(1, 1, 1)
ax.add_collection(lc)
ax.autoscale()
ax.quiver(
arrow_x, arrow_y, arrow_dx, arrow_dy, angles='xy', # arrow position
scale=0.2, scale_units='inches', units='y', minshaft=0, # arrow scaling
headwidth=6, headlength=10, headaxislength=9) # arrow style
fig.show()
There is more than one way to scale the arrows so that they appear to have zero length. 缩放箭头的方式不只一种,因此箭头的长度看起来为零。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.