使用卷积计算移动平均值
CSV(Comma-Separated Value,逗号分隔值)格式是一种常见的文件格式。NumPy中的loadtxt函数可以方便地读取CSV文件,自动切分字段,并将数据载入NumPy数组。
我们以某公司的历史股价数据为例展开叙述。股价数据存储在CSV文件中,第一列为yyyy-mm-dd格式的日期,随后各列依次是收盘价、成交量、开盘价、最高价、最低价。下面为前几行数据:
我们只关注股票的收盘价和成交量。我们将收盘价和成交量分别载入到两个数组中:
c, v = np.loadtxt('Stock_Prices.csv', delimiter=',', usecols=(1,2), unpack=True)
可以看到,数据存储在Stock_Prices.csv文件中,用delimiter参数指定了文件中的分隔符为英文逗号 ","; usecols的参数为一个元组,以获取第1列和第2列的数据,也就是股票的收盘价和成交量数据。 unpack参数设置为True,意思是分拆存储不同列的数据,即分别将收盘价和成交量的数组赋值给变量c和v。
1. 计算成交量平均价格
VWAP (Volume-Weighted Average Price,成交量加权平均价格)是一个非常重要的经济学量,它代表着金融资产的“平均”价格。某个价格的成交量越高,该价格所占的权重就越大。 VWAP就是以成交量为权重计算出来的加权平均值,常用于算法交易。
import numpy as np c, v = np.loadtxt('Stock_Prices.csv', delimiter=',', usecols=(1,2), unpack=True) vwap = np.average(c, weights = v) print "VWAP =", vwap
The output is
VWAP = 37.00109716
我们仅仅调用了average函数,并将v作为权重参数使用,就完成了加权平均值的计算。此外, NumPy中也有计算算术平均值的函数mean。
2. 计算算数平均值
print "mean =", np.mean(c)
mean = 37.0013015873
3. 简单移动平均线
简单移动平均线(moving average)通常用于分析时间序列上的数据。为了计算它,我们需要定义一个N个周期的移动窗口,在我们的例子中即N个交易日。我们按照时间序列滑动这个窗口,并计算窗口内数据的均值。
移动平均线只需要少量的循环和均值函数即可计算得出,但使用NumPy还有更优的选择——convolve函数。简单移动平均线只不过是计算与等权重的指示函数的卷积,当然,也可以是不等权重的。卷积是分析数学中一种重要的运算,定义为一个函数与经过翻转和平移的另一个函数的乘积的积分。
按照如下步骤计算简单移动平均线:
(1) 使用ones函数创建一个长度为N的元素均初始化为1的数组,然后对整个数组除以N,即可得到权重
N = int(sys.argv[1]) weights = np.ones(N) / N print "Weights", weights
在N = 5时,输出结果如下:
Weights [ 0.2 0.2 0.2 0.2 0.2]
(2) 使用这些权重值,调用convolve函数:
c = np.loadtxt('Stock_Prices.csv', delimiter=',', usecols=(1,), unpack=True) ma = np.convolve(weights, c)[N-1:-N+1]
(3) 使用Matplotlib进行绘图
在ipython中运行如下代码: %run calculate_ma.py 5
import numpy as np import sys from matplotlib.pyplot import plot from matplotlib.pyplot import show N = int(sys.argv[1]) weights = np.ones(N) / N c = np.loadtxt('Stock_Prices.csv', delimiter=',', usecols=(1,), unpack=True) ma = np.convolve(weights, c)[N-1:-N+1] t = np.arange(N - 1, len(c)) plot(t, c[N-1:], lw=1.0) plot(t, ma, lw=2.0) show()
在下图中,相对较平滑的粗线描绘的是5日移动平均线,而锯齿状的细线描绘的是每天的收盘价。
- 序列的基本运算
在数字信号处理中,序列通常有下面几种运算形式:
1. 序列相加
序列之间的加法,是指两个序列的同序号的值逐项对应相加。即在同一时刻n,对幅值进行叠加:
$$y(n)=x_1(n)+x_2(n)$$
2. 序列乘常数
一个常数乘以序列,是指该常数乘以序列的每一个值,如:
$$y(n)= k \cdot x(n)$$
k可以是复数也可以是实数。当k为实数,且k>1时,就是通常所说的放大作用,即把序列x(n)的幅度放大了k倍。
3. 序列相乘
序列之间的乘法,是指两个序列的同序号的值逐项对应相乘:
$$y(n)=x_1(n) \cdot x_2(n)$$
4. 序列移位
序列x(n)向右(左)平移,是将序列号减去(加上)m(m>0),如:
$y(n)=x(n-m)$,右移m
$y(n)=x(n+m)$,左移m
下图中分别表示了原序列x(n)、x(n+2)和x(n-1)的图形。由图可见,x(n+2)表示原序列左移2个单位,通常称序列的超前;x(n-1)表示原序列右移1个单位,通常称序列的延时。 作为实现序列延时的实际离散系统就是移位寄存器或存储器。
5. 序列翻转
设序列x(n)用下图a表示,其翻转序列x(-n)用图b表示。x(n)与x(-n)关于纵坐标轴对称。
6. 序列卷积
两个序列的线性卷积定义为
$$x(n)*y(n)=\sum_{k=-\infty}^{\infty}x(k)y(n-k)$$
其中,符号“*”表示一种特定的运算形式,称作“卷积”。
卷积运算的求解过程为:
①. 将x(n)和y(n)用x(k)和y(k)表示,并将y(k)翻转,形成y(-k);
②. 将y(-k)移位n,得到y(n-k)。当n>0时,序列右移;当n<0时,序列左移;
③. 将x(k)和y(n-k)相同k的序列值对应相乘后,再相加
按以上3个步骤可得到卷积结果x(n)*y(n)。
下面通过图例来说明。如计算序列h(n)与x(n)的卷积y(n),先将原坐标n换成坐标m,n成为m坐标中的参变量
将x(m)沿纵轴翻转得到x(-m)
将x(-m)的值每次右移一次,分别得到x(1-m),x(2-m),...,x(n-k)。当n<0时(如n=-1),从图中可以看出此时x(n-m)与h(m)没有相互重叠的区域,则卷积为0。
当n=0时x(-m)与h(m)在m=0点有重叠,故y(0)不为零。以后随着n的增加x(n-m)逐渐右移,x(n-m)与h(m)相互重叠的值也越来越多。当n=时3时,x(3-m)已有一个值移出h(m)所在的区间。当n>5时,x(n-m)已全部移出h(m)所在的区间,此时卷积y(n)又等于零了。
卷积y(n)波形图(f)所示:
可以使用numpy中的convolve函数进行验证:
h = np.ones(3) x = np.array([0.5,1,1.5,2]) y = np.convolve(h, x) print y
卷积运算服从交换律: x(n) * y(n) = y(n) * x(n)
卷积运算服从加法分配律: y(n) * [x1(n) + x2(n)] = y(n) * x1(n) + y(n) * x2(n)
卷积也服从结合律:x(n) * h1(n) * h2(n) = [x(n) * h1(n)] * h2(n) = [x(n) * h2(n)] * h1(n) = x(n) *[h1(n) * h2(n)]
7. 序列的能量
序列的能量定义为序列各采样值的平方和,即
$$E=\sum_{n=-\infty}^{\infty}|x(n)|^2$$
序列的能量可以是有限的,如能量信号;也可能是无限的,如功率信号。
参考:
《数字信号处理及应用》 北京邮电大学出版社