Python自学笔记——Matplotlib风羽自定义
对于气象专业的小学生来说,风场是预报重要的参考数据,我们所知的风羽有四种:短线代表风速2m/s,长线代表风速4m/s,空心三角代表风速20m/s,实心三角代表风速50m/s。而matplotlib的风羽只有短线、长线、三角三种,而这里的三角不分空心实心,但是可通过改变风羽颜色为白色使三角变为空心形状,虽然这三种可以自定义各自代表的风速,但是仍与我们的使用习惯不符,即使把三角设成20m/s,原本一个实心三角就能表示的50m/s的风在matplotlib中需要两个三角外加两条长线一条短线。为了迎合预报员的需求,我在研究了matplotlib的风场函数barbs()的源代码quiver.py文件后,对quiver.py做了适当的调整,使得matplotlib也有了空心三角和实心三角之分。
一、函数barbs的使用 barb(X, Y, U, V,, **kw) X:风场数据X坐标 Y:风场数据Y坐标 U:风的水平方向分量 V:风的垂直方向分量 '''
Demonstration of wind barb plots '''
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-5, 5, 5) X, Y = np.meshgrid(x, x) U, V = 12*X, 12*Y
data = [(-1.5, .5, -6, -6),(1, -1, -46, 46),(-3, -1, 11, -11),(1, 1.5, 80, 80),(0.5, 0.25, 25, 15),(-1.5, -0.5, -5, 40)] data = np.array(data, dtype=[('x', np.float32), ('y', np.float32), ('u', np.float32), ('v', np.float32)]) # Default parameters, uniform grid ax = plt.subplot(2, 2, 1) ax.barbs(X, Y, U, V)
# Arbitrary set of vectors, make them longer and change the pivot point #(point around which they're rotated) to be the middle ax = plt.subplot(2, 2, 2)
ax.barbs(data['x'], data['y'], data['u'], data['v'], length=8, pivot='middle') # Showing colormapping with uniform grid. Fill the circle for an empty barb, # don't round the values, and change some of the size parameters ax = plt.subplot(2, 2, 3)
ax.barbs(X, Y, U, V, np.sqrt(U*U + V*V), fill_empty=True, rounding=False,sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3)) # Change colors as well as the increments for parts of the barbs ax = plt.subplot(2, 2, 4)
ax.barbs(data['x'], data['y'], data['u'], data['v'], flagcolor='r',barbcolor=['b', 'g'], barb_increments=dict(half=10, full=20, flag=100),flip_barb=True) plt.show()
二、源代码解读 1.class Barbs()
class Barbs(mcollections.PolyCollection):
@docstring.interpd
def __init__(self, ax, *args, **kw): '...'
def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50): '...'
def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length,pivot, sizes, fill_empty, flip): '...'
def set_UVC(self, U, V, C=None): '...'
def set_offsets(self, xy): '...'
通过读源代码可知类Barbs有五个方法分别为__init__、_find_tails、_make_barbs、set_UVC、set_offsets。 2.__init__
@docstring.interpd
def __init__(self, ax, *args, **kw): \
The constructor takes one required argument, an Axes instance, followed by the args and kwargs described by the following pylab interface documentation: %(barbs_doc)s \
self._pivot = kw.pop('pivot', 'tip') self._length = kw.pop('length', 7) barbcolor = kw.pop('barbcolor', None) flagcolor = kw.pop('flagcolor', None) self.sizes = kw.pop('sizes', dict())
self.fill_empty = kw.pop('fill_empty', False)
self.barb_increments = kw.pop('barb_increments', dict()) self.rounding = kw.pop('rounding', True) self.flip = kw.pop('flip_barb', False)
transform = kw.pop('transform', ax.transData)
# Flagcolor and and barbcolor provide convenience parameters for # setting the facecolor and edgecolor, respectively, of the barb # polygon. We also work here to make the flag the same color as the # rest of the barb by default
if None in (barbcolor, flagcolor): kw['edgecolors'] = 'face' if flagcolor:
kw['facecolors'] = flagcolor elif barbcolor:
kw['facecolors'] = barbcolor else:
# Set to facecolor passed in or default to black kw.setdefault('facecolors', 'k') else:
kw['edgecolors'] = barbcolor kw['facecolors'] = flagcolor
# Parse out the data arrays from the various configurations supported x, y, u, v, c = _parse_args(*args) self.x = x self.y = y
xy = np.hstack((x[:, np.newaxis], y[:, np.newaxis]))
# Make a collection
barb_size = self._length ** 2 / 4 # Empirically determined mcollections.PolyCollection.__init__(self, [], (barb_size,), offsets=xy,
transOffset=transform, **kw) self.set_transform(transforms.IdentityTransform())
self.set_UVC(u, v, c)
__init__()方法为初始化方法,此方法中flagcolor、barbcolor为设置风羽颜色的关键字,中间的说明文字提示颜色设置是针对所有的风羽的,所以通过颜色设置达不到风羽中既有空心白色三角又有实心黑色三角。初始化方法中在对一些参数进行了初始化赋值后执行了set_UVC()方法,所以我们顺着这个set_UVC()方法往下继续读。 3.set_UVC()
def set_UVC(self, U, V, C=None):
self.u = ma.masked_invalid(U, copy=False).ravel() self.v = ma.masked_invalid(V, copy=False).ravel() if C is not None:
c = ma.masked_invalid(C, copy=False).ravel() x, y, u, v, c = delete_masked_points(self.x.ravel(), self.y.ravel(), self.u, self.v, c) else:
x, y, u, v = delete_masked_points(self.x.ravel(), self.y.ravel(), self.u, self.v)
magnitude = np.hypot(u, v)
flags, emptyflags,barbs, halves, empty = self._find_tails(magnitude, self.rounding,
**self.barb_increments)
# Get the vertices for each of the barbs
plot_barbs = self._make_barbs(u, v, flags, emptyflags,barbs, halves, empty, self._length, self._pivot, self.sizes,
self.fill_empty, self.flip) self.set_verts(plot_barbs)
# Set the color array if C is not None: self.set_array(c)
# Update the offsets in case the masked data changed xy = np.hstack((x[:, np.newaxis], y[:, np.newaxis])) self._offsets = xy self.stale = True
在此方法中,首先进行了变量的命名赋值,然后依次执行了方法_find_tails和_make_barbs。_make_barbs的输入为_find_tails的输出,_find_tails的输入中有一个为magnitude = np.hypot(u, v),np.hypot()为勾股定理方法,因此可知magnitude为风速。 4._find_tails
def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50): '''
Find how many of each of the tail pieces is necessary. Flag specifies the increment for a flag, barb for a full barb, and half for half a barb. Mag should be the magnitude of a vector (i.e., >= 0).
This returns a tuple of:
(*number of flags*, *number of barbs*, *half_flag*, *empty_flag*)
*half_flag* is a boolean whether half of a barb is needed, since there should only ever be one half on a given barb. *empty_flag* flag is an array of flags to easily tell if a barb is empty (too low to plot any barbs/flags. '''
# If rounding, round to the nearest multiple of half, the smallest # increment if rounding:
mag = half * (mag / half + 0.5).astype(np.int)
num_flags = np.floor(mag / flag).astype(np.int) mag = np.mod(mag, flag)
num_barb = np.floor(mag / full).astype(np.int) mag = np.mod(mag, full)
half_flag = mag >= half
empty_flag = ~(half_flag | (num_flags > 0) | (num_emptyflags > 0) |(num_barb > 0))
相关推荐: