数据分析06
符号数组
sign函数可以把样本数组的变成对应的符号数组,正数变为1,负数变为-1,0则变为0。
ary = np.sign(源数组)
净额成交量(OBV)
成交量可以反映市场对某支股票的人气,而成交量是一只股票上涨的能量。一支股票的上涨往往需要较大的成交量。而下跌时则不然。
能量潮指标(On Balance Volume,OBV)是葛兰维(Joe Granville)于上世纪60年代提出的,并被广泛使用。股市技术分析的四大要素:价、量、时、空。OBV指标就是从“量”这个要素作为突破口,来发现热门股票、分析股价运动趋势的一种技术指标。它是将股市的人气——成交量与股价的关系数字化、直观化,以股市的成交量变化来衡量股市的推动力,从而研判股价的走势。关于成交量方面的研究,OBV能量潮指标是一种相当重要的分析指标之一。
能量潮理论成立的依据重要是:
1、投资者对股价的评论越不一致,成交量越大;反之,成交量就小。因此,可用成交量来判断市场的人气和多空双方的力量。
2、重力原理。上升的物体迟早会下跌,而物体上升所需的能量比下跌时多。涉及到股市则可解释为:一方面股价迟早会下跌;另一方面,股价上升时所需的能量大,因此股价的上升特别是上升初期必须有较大的成交量相配合;股价下跌时则不必耗费很大的能量,因此成交量不一定放大,甚至有萎缩趋势。
3、惯性原则——动则恒动、静则恒静。只有那些被投资者或主力相中的热门股会在很大一段时间内成交量和股价的波动都比较大,而无人问津的冷门股,则会在一段时间内,成交量和股价波幅都比较小。
若相比上一天的收盘价上涨,则为正成交量;若相比上一天的收盘价下跌,则为负成交量。
计算方法
以某日为基期,逐日累计每日上市股票总成交量,若隔日指数或股票上涨,则基期OBV加上本日成交量为本日OBV。隔日指数或股票下跌,则基期OBV减去本日成交量为本日OBV。一般来说,只是观察OBV的升降并无多大意义,必须配合K线图的走势才有实际的效用。
由于OBV的计算方法过于简单化,所以容易受到偶然因素的影响,为了提高OBV的准确性,可以采取多空比率净额法对其进行修正。
多空比率净额= [(收盘价-最低价)-(最高价-收盘价)] ÷( 最高价-最低价)×V
该方法根据多空力量比率加权修正成交量,比单纯的OBV法具有更高的可信度。
应用法则
1、当股价上升而OBV线下降,表示买盘无力,股价可能会回跌。
2、股价下降时而OBV线上升,表示买盘旺盛,逢低接手强股,股价可能会止跌回升。
3、OBV线缓慢上升,表示买气逐渐加强,为买进信号。
4、OBV线急速上升时,表示力量将用尽为卖出信号。
5、OBV线从正的累积数转为负数时,为下跌趋势,应该卖出持有股票。反之,OBV线从负的累积数转为正数时,应该买进股票。
6、OBV线最大的用处,在于观察股市盘局整理后,何时会脱离盘局以及突破后的未来走势,OBV线变动方向是重要参考指数,其具体的数值并无实际意义。
7、OBV线对双重顶第二个高峰的确定有较为标准的显示,当股价自双重顶第一个高峰下跌又再次回升时,如果OBV线能够随股价趋势同步上升且价量配合,则可持续多头市场并出现更高峰。相反,当股价再次回升时OBV线未能同步配合,却见下降,则可能形成第二个顶峰,完成双重顶的形态,导致股价反转下跌。
绘制OBV柱状图
dates, closing_prices, volumes = np.loadtxt(
'../../data/bhp.csv', delimiter=',',
usecols=(1, 6, 7), unpack=True,
dtype='M8[D], f8, f8', converters={1: dmy2ymd})
diff_closing_prices = np.diff(closing_prices) #将后一天的减去前一天 得到正负0的值
sign_closing_prices = np.sign(diff_closing_prices) # 将正数变为1,负数变为-1,0则变为0。得到符号
obvs = volumes[1:] * sign_closing_prices #得到正负的成交量
mp.figure('On-Balance Volume', facecolor='lightgray')
mp.title('On-Balance Volume', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('OBV', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(axis='y', linestyle=':')
dates = dates[1:].astype(md.datetime.datetime)
mp.bar(dates, obvs, 1.0, color='dodgerblue',
edgecolor='white', label='OBV')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()
'''
obv完整代码
'''
import numpy as np
import matplotlib.pyplot as mp
import matplotlib.dates as md
import datetime as dt
def dmy2ymd(dmy):
dmy = str(dmy, encoding='utf-8')
time = dt.datetime.strptime(dmy, '%d-%m-%Y').date()
t = time.strftime('%Y-%m-%d')
return t
dates, closing_prices, volumes = np.loadtxt(
'../da_data/bhp.csv', delimiter=',',
usecols=(1, 6, 7), unpack=True,
dtype='M8[D], f8, f8', converters={1: dmy2ymd})
diff_closing_prices = np.diff(closing_prices)
sign_closing_prices = np.sign(diff_closing_prices)
print(sign_closing_prices)
obvs = volumes[1:] * sign_closing_prices
mp.figure('On-Balance Volume', facecolor='lightgray')
mp.title('On-Balance Volume', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('OBV', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(axis='y', linestyle=':')
dates = dates[1:].astype(md.datetime.datetime)
mp.bar(dates, obvs, 1.0, color='dodgerblue',
edgecolor='white', label='OBV')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()
数组处理函数
ary = np.piecewise(源数组, 条件序列, 取值序列)
针对源数组中的每一个元素,检测其是否符合条件序列中的每一个条件,符合哪个条件就用取值系列中与之对应的值,表示该元素,放到目标 数组中返回。
np.piecewise 比 np.sign 更加智能。
条件序列: [a < 0, a == 0, a > 0]
取值序列: [-1, 0, 1]
a = np.array([70, 80, 60, 30, 40])
d = np.piecewise(
a, #原数组
[a < 60, a == 60, a > 60], #条件
[-1, 0, 1]) #在条件下的分别取值
# d = [ 1 1 0 -1 -1]
np.where(条件)
np.where( c < 0) 返回的是c数组中小于0 的值的序列坐标。
矢量化
矢量化指的是用数组代替标量来操作数组里的每个元素。
numpy提供了vectorize函数,可以把处理标量的函数矢量化,返回的函数可以直接处理ndarray数组。
就是将原本只能处理标量的函数,例如只能处理单个数字之间的运算,转变为处理数组的函数的方法。可以传入数组到函数。
如果给的是一个数组和标量的运算,会将数组中的每个元素和这个标量做运算。
import math as m
import numpy as np
def foo(x, y):
return m.sqrt(x**2 + y**2)
x, y = 1, 4
print(foo(x, y))
X, Y = np.array([1, 2, 3]), np.array([4, 5, 6])
vectorized_foo = np.vectorize(foo) #矢量化后的数组foo函数已经可以处理矢量化的数组
print(vectorized_foo(X, Y))
print(np.vectorize(foo)(X, Y)) #先生成一个函数,再操作后面的变量
numpy还提供了frompyfuc函数,也可以完成与vectorize相同的功能:
# 把foo转换成矢量函数,该矢量函数接收2个参数,返回一个结果
fun = np.frompyfunc(foo, 2, 1)
fun(X, Y)
注意:任何一个数和nan进行操作返回的数组还是nan,np.isnan()得到是nan的True,False列表。
案例:定义一种买进卖出策略,通过历史数据判断这种策略是否值得实施。
dates, opening_prices, highest_prices, \
lowest_prices, closing_prices = np.loadtxt(
'../../data/bhp.csv', delimiter=',',
usecols=(1, 3, 4, 5, 6), unpack=True,
dtype='M8[D], f8, f8, f8, f8',
converters={1: dmy2ymd})
# 定义一种投资策略
def profit(opening_price, highest_price,
lowest_price, closing_price):
buying_price = opening_price * 0.99
if lowest_price <= buying_price <= highest_price:
return (closing_price - buying_price) * \
100 / buying_price
return np.nan # 无效值
# 矢量化投资函数
profits = np.vectorize(profit)(opening_prices,
highest_prices, lowest_prices, closing_prices)
nan = np.isnan(profits) #得到数组中是nan的那些变为True
dates, profits = dates[~nan], profits[~nan] #~按位取反,将True变成False,得到非nan的日期和收益率
gain_dates, gain_profits = dates[profits > 0], profits[profits > 0]
loss_dates, loss_profits = dates[profits < 0], profits[profits < 0]
mp.figure('Trading Simulation', facecolor='lightgray')
mp.title('Trading Simulation', fontsize=20)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Profit', fontsize=14)
ax = mp.gca()
ax.xaxis.set_major_locator(md.WeekdayLocator(byweekday=md.MO))
ax.xaxis.set_minor_locator(md.DayLocator())
ax.xaxis.set_major_formatter(md.DateFormatter('%d %b %Y'))
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
if dates.size > 0:
dates = dates.astype(md.datetime.datetime)
mp.plot(dates, profits, c='gray',
label='Profit')
mp.axhline(y=profits.mean(), linestyle='--',
color='gray')
if gain_dates.size > 0:
gain_dates = gain_dates.astype(md.datetime.datetime)
mp.plot(gain_dates, gain_profits, 'o',
c='orangered', label='Gain Profit')
mp.axhline(y=gain_profits.mean(), linestyle='--',
color='orangered')
if loss_dates.size > 0:
loss_dates = loss_dates.astype(md.datetime.datetime)
mp.plot(loss_dates, loss_profits, 'o',
c='limegreen', label='Loss Profit')
mp.axhline(y=loss_profits.mean(), linestyle='--',
color='limegreen')
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()
"""
demo04_profit.py 自定义策略 使用历史数据进行策略回测
"""
import numpy as np
import matplotlib.pyplot as mp
import datetime as dt
def dmy2ymd(dmy):
dmy = str(dmy, encoding='utf-8')
time = dt.datetime.strptime(dmy, '%d-%m-%Y').date()
t = time.strftime('%Y-%m-%d')
return t
dates, opening_prices, highest_prices, \
lowest_prices, closing_prices = \
np.loadtxt('../da_data/aapl.csv',
delimiter=',', usecols=(1,3,4,5,6),
unpack=True, dtype='M8[D],f8,f8,f8,f8',
converters={1:dmy2ymd})
def profit(open, high, low, close):
# 定义函数 描述一种投资策略
buy_price = open * 1.00
if low <= buy_price <= high:
return (close - buy_price) / buy_price
else:
return np.nan
# 求30天的收益率,矢量化profit函数
profits = np.vectorize(profit)(
opening_prices, highest_prices,
lowest_prices, closing_prices)
print(profits)
# 判断无效值
isnan = np.isnan(profits)
# 绘制收盘价折线图
mp.figure('Profits', facecolor='lightgray')
mp.title('Profits', fontsize=18)
mp.xlabel('Date', fontsize=14)
mp.ylabel('Profit Return', fontsize=14)
mp.grid(linestyle=':')
# 设置刻度定位器
import matplotlib.dates as md
ax = mp.gca()
ax.xaxis.set_major_locator( # 每周一为主刻度
md.WeekdayLocator(byweekday=md.MO))
# 每天一个次刻度
ax.xaxis.set_minor_locator(md.DayLocator())
# 设置主刻度文本格式
ax.xaxis.set_major_formatter(
md.DateFormatter('%Y/%m/%d'))
dates = dates.astype(md.datetime.datetime)
# 绘制收益率折线
dates = dates[~isnan]
profits = profits[~isnan]
mp.plot(dates, profits, 'o-', color='dodgerblue',
label='Profits')
print(profits.mean())
mp.legend()
mp.gcf().autofmt_xdate()
mp.show()
矩阵
矩阵是numpy.matrix类类型的对象,该类继承自numpy.ndarray,任何针对多维数组的操作,对矩阵同样有效,但是作为子类矩阵又结合其自身的特点,做了必要的扩充,比如:乘法计算、求逆等。
矩阵对象的创建
'''如果copy的值为True(缺省),所得到的矩阵对象与参数中的源容器各自拥有独立的数据拷贝。二者独立'''
numpy.matrix(
ary, # 任何可被解释为矩阵的二维容器
copy=True # 是否复制数据(缺省值为True,即复制数据)
)
'''等价于:numpy.matrix(..., copy=False)
由该函数创建的矩阵对象与参数中的源容器一定共享数据,无法拥有独立的数据拷贝'''
numpy.mat(任何可被解释为矩阵的二维容器)
'''# 该函数可以接受字符串形式的矩阵描述:
# 数据项通过空格分隔,数据行通过分号分隔。例如:"1 2 3; 4 5 6"'''
numpy.mat(拼块规则)
矩阵的乘法运算
矩阵的乘法的点乘
# 矩阵的乘法:乘积矩阵的第i行第j列的元素等于
# 被乘数矩阵的第i行与乘数矩阵的第j列的点积
#
# 1 2 6
# X----> 3 5 7
# | 4 8 9
# |
# 1 2 6 31 60 74
# 3 5 7 46 87 116
# 4 8 9 64 120 161
e = np.mat('1 2 6; 3 5 7; 4 8 9')
print(e * e)
矩阵的逆矩阵
若两个矩阵A、B满足:AB = BA = E (E为单位矩阵),则成为A、B为逆矩阵。
e.I直接获得e的逆矩阵
e = np.mat('1 2 6; 3 5 7; 4 8 9')
print(e.I) #逆矩阵
print(e * e.I) #得到单位阵
ndarray提供了方法让多维数组替代矩阵的运算:
a = np.array([
[1, 2, 6],
[3, 5, 7],
[4, 8, 9]])
# 点乘法求ndarray的点乘结果,与矩阵的乘法运算结果相同
k = a.dot(a)
print(k)
# linalg模块中的inv方法可以求取a的逆矩阵
l = np.linalg.inv(a)
print(l)
案例:假设一帮孩子和家长出去旅游,去程坐的是bus,小孩票价为3元,家长票价为3.2元,共花了118.4;回程坐的是Train,小孩票价为3.5元,家长票价为3.6元,共花了135.2。分别求小孩和家长的人数。使用矩阵求解。
[
3
3.2
3.5
3.6
]
×
[
x
y
]
=
[
118.4
135.2
]
\left[ \begin{array}{ccc} 3 & 3.2 \\ 3.5 & 3.6 \\ \end{array} \right] \times \left[ \begin{array}{ccc} x \\ y \\ \end{array} \right] =\left[ \begin{array}{ccc} 118.4 \\ 135.2 \\ \end{array} \right]
[33.53.23.6]×[xy]=[118.4135.2]
import numpy as np
prices = np.mat('3 3.2; 3.5 3.6')
totals = np.mat('118.4; 135.2')
persons = prices.I * totals
print(persons)
把逆矩阵的概念推广到非方阵,即称为广义逆矩阵。
案例:斐波那契数列
1 1 2 3 5 8 13 21 34 …
X 1 1 1 1 1 1
1 0 1 0 1 0
--------------------------------
1 1 2 1 3 2 5 3
1 0 1 1 2 1 3 2
F^1 F^2 F^3 F^4 ... f^n
代码
import numpy as np
n = 35
# 使用递归实现斐波那契数列
def fibo(n):
return 1 if n < 3 else fibo(n - 1) + fibo(n - 2)
print(fibo(n))
# 使用矩阵实现斐波那契数列
print(int((np.mat('1. 1.; 1. 0.') ** (n - 1))[0, 0]))
通用函数
裁剪、压缩
数组的裁剪
'''# 将调用数组中小于和大于下限和上限的元素替换为下限和上限,返回裁剪后的数组,调
# 用数组保持不变。'''
ndarray.clip(min=下限, max=上限)
数组的压缩
# 返回由调用数组中满足条件的元素组成的新数组。
ndarray.compress(条件)
案例:
from __future__ import unicode_literals
import numpy as np
a = np.array([10, 20, 30, 40, 50])
print(a)
b = a.clip(min=15, max=45)
print(b)
c = a.compress((15 <= a) & (a <= 45))
print(c)
拿到数组中a>3,且a<7的数组
加法与乘法通用函数
np.add(a, a) # 两数组相加
np.add.reduce(a) # a数组元素累加和
np.add.accumulate(a) # 累加和的过程
np.prod(a) #阶乘
np.cumprod(a) #阶乘的过程的值
np.add.outer([10, 20, 30], a) # 外和
np.outer([10, 20, 30], a) #外积
累加np.add.accumulate(a)
累乘:np.prod(a) 阶乘
外和
外积: 数组的行和给的数组的列进行乘积
案例:
a = np.arange(1, 7)
print(a)
b = a + a
print(b)
b = np.add(a, a)
print(b)
c = np.add.reduce(a)
print(c)
d = np.add.accumulate(a)
print(d)
# + 1 2 3 4 5 6
# --------------------
# 10 |11 12 13 14 15 16 |
# 20 |21 22 23 24 25 26 |
# 30 |31 32 33 34 35 36 |
--------------------
f = np.add.outer([10, 20, 30], a)
print(f)
# x 1 2 3 4 5 6
# -----------------------
# 10 |10 20 30 40 50 60 |
# 20 |20 40 60 80 100 120 |
# 30 |30 60 90 120 150 180 |
-----------------------
g = np.outer([10, 20, 30], a)
print(g)
除法与取整通用函数
np.divide(a, b) # a 真除 b
np.floor(a / b) # (真除的结果向下取整)
np.ceil(a / b) # (真除的结果向上取整)
np.trunc(a / b) # (真除的结果截断取整)
np.round(a / b) # (真除的结果四舍五入取整)
案例:
import numpy as np
a = np.array([20, 20, -20, -20])
b = np.array([3, -3, 6, -6])
# 真除
c = np.true_divide(a, b)
c = np.divide(a, b)
c = a / b
print('array:',c)
# 对ndarray做floor操作
d = np.floor(a / b)
print('floor_divide:',d)
# 对ndarray做ceil操作
e = np.ceil(a / b)
print('ceil ndarray:',e)
# 对ndarray做trunc操作
f = np.trunc(a / b)
print('trunc ndarray:',f)
# 对ndarray做around操作
g = np.around(a / b)
print('around ndarray:',g)
位运算通用函数
不考虑正负的情况,可以4位可以表示16个数字,但是无法表示负数
所以将后面一半转为负数表示,0表示正数,1表示负数
-1 与 1 相加 所得的为10000,但是最大的内存为4为,所以表示为0
当7+1 的时候值就一出来了所以等于-8 ,不等于8
范围:8位 -128~127 16:-32768 ~ 32767 32:-2147483648
异或:相同为0,不同为1。
位异或:
c = a ^ b
c = np.bitwise_xor(a, b)
位与:
e = a & b
e = np.bitwise_and(a, b)
位或:
e = a | b
e = np.bitwise_or(a, b)
位反:
e = ~a
e = np.bitwise_or(a, b)
移位:
<< __lshift__ left_shift
>> __rshift__ right_shift
位异或:
按位异或操作可以很方便的判断两个数据是否同号。 因为如果异号的话开头分别为1和0,异或后第一位还是1
同号为0,异号为1
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
a = np.array([0, -1, 2, -3, 4, -5])
b = np.array([0, 1, 2, 3, 4, 5])
print(a, b)
c = a ^ b
# c = a.__xor__(b)
# c = np.bitwise_xor(a, b)
print(np.where(c < 0)[0])
位与运算,都为1才是1
利用位与运算计算某个数字是否是2的幂
2的幂和减1后的数进行与运算后的值为0
# 1 2^0 00001 0 00000
# 2 2^1 00010 1 00001
# 4 2^2 00100 3 00011
# 8 2^3 01000 7 00111
# 16 2^4 10000 15 01111
# ...
d = np.arange(1, 21)
print(d)
e = d & (d - 1)
e = d.__and__(d - 1)
e = np.bitwise_and(d, d - 1)
print(e)
三角函数通用函数
numpy.sin()
傅里叶定理:
法国科学家傅里叶提出傅里叶定理,任何一条周期曲线,无论多么跳跃或不规则,都能表示成一组光滑正弦曲线叠加之和。
合成方波
一个方波由如下参数的正弦波叠加而成:
y
=
4
π
×
s
i
n
(
x
)
y
=
4
π
3
×
s
i
n
(
3
x
)
.
.
.
.
.
.
y
=
4
π
2
n
−
1
×
s
i
n
(
(
2
n
−
1
)
x
)
y = 4\pi \times sin(x) \\ y = \frac{4\pi}{3} \times sin(3x) \\ ...\\ ...\\ y = \frac{4\pi}{2n-1} \times sin((2n-1)x)
y=4π×sin(x)y=34π×sin(3x)......y=2n−14π×sin((2n−1)x)
曲线叠加的越多,越接近方波。所以可以设计一个函数,接收曲线的数量n作为参数,返回一个矢量函数,该函数可以接收x坐标数组,返回n个正弦波叠加得到的y坐标数组。
x = np.linspace(-2*np.pi, 2*np.pi, 1000)
y = np.zeros(1000)
n = 1000
for i in range(1, n+1):
y += 4 / ((2 * i - 1) * np.pi) * np.sin((2 * i - 1) * x)
mp.plot(x, y, label='n=1000')
mp.legend()
mp.show()
特征值和特征向量
对于n阶方阵A,如果存在数a和非零n维列向量x,使得Ax=ax,则称a是矩阵A的一个特征值,x是矩阵A属于特征值a的特征向量
N阶方阵可以拿到N个特征向量和N和特征值,相配套
特征就是最能表现当前事物的信息,可以通过特征来还原事物。无非就是要提取图像或者矩阵的特征信息。矩阵的特征就是数值的大小
#已知n阶方阵A, 求特征值与特征数组
# eigvals: 特征值数组
# eigvecs: 特征向量数组
eigvals, eigvecs = np.linalg.eig(A)
#已知特征值与特征向量,求方阵
S = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs逆)
对角阵
案例:
import numpy as np
A = np.mat('3 -2; 1 0')
print(A)
eigvals, eigvecs = np.linalg.eig(A)
print(eigvals)
print(eigvecs)
print(A * eigvecs[:, 0]) # 方阵*特征向量
print(eigvals[0] * eigvecs[:, 0]) #特征值*特征向量
S = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs.I)
案例:读取图片的亮度矩阵,提取特征值与特征向量,保留部分特征值,重新生成新的亮度矩阵,绘制图片。
原数组转为特征值和特征向量后,会有复数,将其抹掉一部分后,再还原的原数组的时候就会有复数,所以再imshow()的时候就不能识别复数,所以需要用ndarray.real去得实数再还原
'''
特征值与特征向量
'''
import numpy as np
import scipy.misc as sm
import matplotlib.pyplot as mp
original = sm.imread('../data/lily.jpg', True) #True代表提取灰度图片,将3维的变成2维度的,去掉了颜色信息
#提取特征值
eigvals, eigvecs = np.linalg.eig(original)
eigvals[50:] = 0
print(np.diag(eigvals).shape)
original2 = np.mat(eigvecs) * np.mat(np.diag(eigvals)) * np.mat(eigvecs).I
mp.figure("Lily Features")
mp.subplot(121)
mp.xticks([])
mp.yticks([])
mp.imshow(original, cmap='gray') #imshow 默认有自己的颜色映射,自己可以自定义,gray就是灰度图的映射
mp.subplot(122)
mp.xticks([])
mp.yticks([])
mp.imshow(original2, cmap='gray')
mp.tight_layout()
mp.show()