炸鸡人博客 基本上无害

Matplotlib 系列:手动设置时间序列折线图的刻度

前言

Matplotlib 中画折线图用 ax.plot(x, y),当横坐标 x 是时间数组时,例如 datetimenp.datetime64 构成的列表,xy 的组合即一条时间序列。Matplotlib 能直接画出时间序列,并自动设置刻度。下面以一条长三年的气温时间序列为例:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('test.csv', index_col=0, parse_dates=True)
series = df.loc['2012':'2014', 'T']

fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(series.index, series)
ax.set_ylabel('Temperature (℃)')

print(ax.xaxis.get_major_locator())
print(ax.xaxis.get_major_formatter())
<matplotlib.dates.AutoDateLocator object at 0x000001AC6BF89A00>
<matplotlib.dates.AutoDateFormatter object at 0x000001AC6BF89B20>

fig_1

预测的 KPI:RMSE、MAE、MAPE 和 Bias

Nicolas Vandeput 发布在 Towards Data Science 上的文章,同时也是其著作《Data Science for Supply Chain Forecasting》中的一章。

为预测任务挑选一个合适的指标并没有想象中那么简单,所以这次我们来研究一下 RMSE、MAE、MAPE 和 Bias 的优缺点。剧透:MAPE 是其中最差的,别用。

fig1

Pandas 系列:管道风格

R 语言的管道

这回来介绍一下如何利用管道(pipe)风格将 Pandas 相关的代码写得更易读,不过首先让我们看看隔壁 R 语言中管道是怎么用的。假设输入是 x,经过连续四个函数的处理后得到输出 y,代码可以按顺序写:

x1 <- func1(x, arg1)
x2 <- func2(x1, arg2)
x3 <- func3(x2, arg3)
y <- func4(x3, arg4)

Pandas 系列:图解插值

相信大伙对 NumPy 和 SciPy 里的插值比较熟:已知坐标值 xp 和变量值 fp,调用函数计算变量在目标坐标 x 上的数值。例如 np.interp 的 API 就是

np.interp(x, xp, fp)

Pandas 的 SeriesDataFrame 对象也有插值方法 interpolate,默认做线性插值。但其功能与 NumPy 和 SciPy 不太一样。以一个序列对象 s 为例:

# 缺测部分和有效部分.
invalid = s.isna()
valid = ~invalid

# 对应于xp.
s.index[valid]

# 对应于fp.
s.values[valid]

# 对应于x.
s.index

# 两式大致等价.
s.interpolate(method='index').values
np.interp(s.index, s.index[valid], s.values[valid])

即 Pandas 的插值是要利用序列的有效值当 xpfp,去填补缺测的部分。所以调用 s.interpolate 时我们不需要传入形如 x 的参数,而是应该在调用前就通过 s.reindex 之类的方法将 x 融合到 s 的索引中。这么说可能有点抽象,下面就以图像直观展示 Pandas 里插值的效果。本文不会涉及到具体的插值算法(最邻近、三次样条……),仅以线性插值为例。

Python 系列:衔尾蛇一样的取模

Python 的取模运算 r = m % n 相当于

# 或q = math.floor(m / n)
q = m // n
r = m - q * n

即取模的结果是被除数减去地板除的商和除数的乘积,这一规则对正数、负数乃至浮点数皆适用。

n 为正数时。显然任意实数 x 可以表示为 x = r + k * n,其中 0 <= r < nk 是某个整数。那么有

x // n = floor(r/n + k) = k
x % n = x - x // n = r

x % n 的结果总是一个大小在 [0, n) 之间的实数 r。当 n = 10 时,以 x = 12x = -12 为例:

number

如果以 n 为一个周期,那么 x = 12 就相当于往右一个周期再走 2 格,x % n 会消去这个周期,剩下不满一个周期的 2;x = -12 相当于往左两个周期后再往右走 8 格,x % n 会消去这两个周期,剩下不满一个周期且为正数的 8。

再本质点说,取模运算就是在 [0, 10) 的窗口内进行“衔尾蛇”移动:

  • 12 向右超出窗口两格, 12 % 10 = 2,即右边出两格那就左边进两格。
  • -12 向左超出窗口 12 格,-12 % n = 8,即左边出 12 格那就右边进 12 格,发现还是超出左边两格,再从右边进两格,最后距离零点 8 格。

PyTorch 时间序列预测入门

xkcd

最近学习用 PyTorch 做时间序列预测,发现只有 TensorFlow 官网的教程 把时间窗口的选取和模型的设置讲得直观易懂,故改编如下。本人也只是入门水平,翻译错误之处还请指正。

本文是利用深度学习做时间序列预测的入门教程,用到的模型包括卷积神经网络(CNN)和循环神经网络(RNN)。全文分为两大部分,又可以细分为:

  • 预测单个时间步:
    • 预测一个特征。
    • 预测所有特征。
  • 预测多个时间步:
    • 单发预测:模型跑一次输出所有时间步的结果。
    • 自回归:每次输出一个时间步的预测,再把结果喂给模型得到下一步的预测。

本文用到的数据和 notebook 可以在 GitHub 仓库 找到。

编写科研作图代码有更好的方法

这是物理海洋学家 Ken Hughes 在 2021 年发表的博客文章,原文标题为 A better way to code up scientific figures。以 Matplotlib 和 Matlab 为例,强调了模块化思想对于科研作图代码的帮助。我很少看到关于作图方法论的文章,所以翻译出来交流学习。

我画一张出版级别的科研配图一般需要写 100 - 200 行代码,这个长度有点点危险,因为很容易写出能正常运行但又一团糟的东西。如果代码片段都很短还可以从头重写,但如果代码有上千行,提前做好规划会更明智一些。不过在这两种极端情况之间潜藏着另一种吸引人的做法:写出一段当时感觉无比连贯,但以后会让你吃苦头的脚本。

假设你想画一张中等复杂度的图片,类似下面这张:

data_overview-1

Python 系列:测量程序的运行时间

前言

说到测量程序的运行时间这件事,我最早的做法是在桌上摆个手机,打开秒表应用,右手在命令行里敲回车的同时左手启动秒表,看屏幕上程序跑完后再马上按停秒表,最后在纸上记下时间。后来我在 Linux 上学会了在命令开头添加一个 time,终于摆脱了手动计时的原始操作。这次就想总结一下迄今为止我用过的那些测量时间的工具/代码。

Cartopy 系列:利用多边形生成掩膜数组

二维平面上一系列点的坐标由 xy 数组描述,同时准备一个形状相同的 mask 数组。若第 i 个点落入了平面上一个多边形的内部,则令 mask[i] = True;若在多边形外,则令 mask[i] = False。由此得到的 mask 数组即掩膜(mask)数组,它可以作为布尔索引分出多边形内外的点

x_in, y_in = x[mask], y[mask]
x_out, y_out = x[mask], y[mask]

它可以作为掩膜,掩盖多边形范围外的值——即把外面的值设为 NaN,以便进行后续的计算

z[~mask] = np.nan
z_mean = np.nanmean(z)

下图展示了两个应用:左小图的多边形是一个中心带洞的正方形,给定一系列散点的坐标,计算出掩膜后可以把多边形内的散点画成红色,多边形外的散点画成蓝色;右小图的多边形是中国全域,给定 (50, 50) 形状的经纬度网格,计算出掩膜后用橙色画出掩膜为 True 的部分,这张掩膜之后可以用来处理网格上的其它变量。

diagram

本文的目的是介绍如何用 Python 制作掩膜数组,并尽量优化其运行时间。从 shapefile 中读取中国国界并转化为 Shapely 中的多边形对象等操作,已经在博文 Cartopy 系列:探索 shapefile 中详细介绍过了,本文是对其的一个补充。

Cartopy 系列:探索 shapefile

前言

Cartopy 可以通过 feature 模块向地图添加国界 BORDER 和省界 STATES,因其底层采用的 Natural Earth 地图数据并不符合我国的政治主张,所以我们经常需要自备 shapefile 文件来画中国省界,以下面的代码为例

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader

extents = [70, 140, 0, 60]
crs = ccrs.PlateCarree()
fig = plt.figure()
ax = fig.add_subplot(111, projection=crs)
ax.set_extent(extents, crs)

filepath = './data/bou2_4/bou2_4p.shp'
reader = shpreader.Reader(filepath)
geoms = reader.geometries()
ax.add_geometries(geoms, crs, lw=0.5, fc='none')
reader.close()

plt.show()

图就不放了,这段代码足以应付大部分需要画省界的情况。然而我在无脑粘贴代码的过程中逐渐产生了疑惑:为什么 shapefile 会由三个文件组成?省界是以何种形式存储在文件中?Cartopy 和 Matplotlib 又是怎样将省界画出来的?调查一番源码后总结出了这段代码底层实现的流程:

  • 利用 PyShp 包读取 shapefile 文件中的每个形状。
  • 利用 Shapely 包将形状转换为几何对象。
  • 利用 Cartopy 包将几何对象投影到地图所在的坐标系上。
  • 用投影后的坐标构造 Matplotlib 的 Path 对象,最后画在地图上。

本文的目的即是从头到尾解说一下这段流程,希望加深对 shapefile 格式,Matplotlib 和 Cartopy 包的理解。令人意外的是,随着探索的不断深入,我发现自己自然而然地学会了如何实现省份填色、省份合并,地图白化等,以前看起来十分困难的操作。本文也会一并介绍这些应用。