Python股票数据处理的一些代码细节
Numpy和Pandas相关
Numpy和Pandas比较
Pandas和Numpy各有所长。
在处理股票数据的基础阶段,Pandas能够清晰地展示股票代码、交易日期、价格等信息,且对于信息的分类、归集、整理有很多针对性强的功能函数,非常便捷。
在处理股票数据的高级阶段,尤其在涉及机器学习领域,Numpy用处更多。首先因为Keras等函数库要求的输入类型多为numpy.ndarray,更深层的原因是Numpy在多维数据储存和处理上更具优势,而且由于Numpy的数据为单一类型,处理速度比Pandas快很多。
numpy.ndarray和多类型数据
由于numpy.ndarray要求存储单一类型数据,用np.array()函数转换DataFrame时,如果value有多种数据类型,比如string、int、float,转换后dtype(数据表中的元素类型)会显示为object(不是很懂这是什么东西),虽然单独提取时,会“还原”为string、int或float(datatime会被转换为int),但接下来对整块数据操作时,比如遇到要求float数据类型,必须先用astype(float)进行转换,否则会报错。
i_str = [['a', 'b', 'c']]
i_int = [[1, 2, 3]]
i_float = [[1.1, 2.2, 3.3]]
i_array = np.concatenate((i_str, i_int, i_float), axis = 0)
print(i_array)
[['a' 'b' 'c']
['1' '2' '3']
['1.1' '2.2' '3.3']]
print(i_array.dtype)
output: <U32
a = i_array[0,1]
b = i_array[1,1]
c = i_array[2,1]
d = c.astype(float)
print(type(a))
output: <class 'numpy.str_'>
print(type(b))
output: <class 'numpy.str_'>
print(type(c))
output: <class 'numpy.str_'>
print(type(d))
output: <class 'numpy.float64'>
还是由于存储单一类型数据的限制,可以对提取的元素单独进行类型转换,但对整块元素进行类型转换是行不通的。
i_array[1:,:] = i_array[1:,:].astype(float)
print(i_array)
[['a' 'b' 'c']
['1.0' '2.0' '3.0']
['1.1' '2.2' '3.3']]
转换后由于有第一行string类型的限制,二、三行在被转换成float后,又被转换回string了。
Numpy神奇的取值方法
不同于DataFrame,numpy.ndarray本身是没有索引的,但它的索引方式相当灵活。
比如,以长度相同的,元素为布尔值的numpy.ndarray作为索引。
y = np.array([1, 2, 3, 4, 5])
z = np.array([False,False,False,True,True])
x = y[z]
print(x)
output: [4 5]
再此基础上,又可以用各种方法生成这种布尔值索引,比如以下:
y = np.array([1, 2, 3, 4, 5])
z = np.array([11, 12, 13, 14, 15])
x = y[z>13]
print(x)
output: [4 5]
Pandas透视表
在获取股票数据,尤其是从一些金融数据库获取数据时,数据的原始索引方式往往不符合我们的需求,这时我们需要对数据做一些变换处理。
比如下列张原始的DataFrame数据表(仅展示了表尾部分),它包含了股票代码、日期和因子三个维度。
instrument turn volume date high low \
3958753 603016.SHA 2.866226 4246600 2019-12-30 17.138489 15.550813
3958863 603217.SHA 4.498146 1023778 2019-12-30 48.560001 47.790001
3958928 603348.SHA 15.438673 10807071 2019-12-30 22.463787 20.058041
3958999 603578.SHA 1.548533 503583 2019-12-30 20.669724 20.160618
3959111 603787.SHA 8.877831 6293495 2019-12-30 14.495040 13.518423
open amount close
3958753 15.698014 66799268.0 16.833570
3958863 48.349998 49327120.0 48.560001
3958928 20.261057 227867088.0 22.463787
3958999 20.252256 10112697.0 20.608631
3959111 13.939911 85766672.0 14.330557
由于DataFrame擅长处理二维数据,我需要将它提取转换为多张,每张展示一个因子,以日期为行索引,股票代码为列索引的DataFrame。
最直观的方式是采取For循环的办法,先构建一张空DataFrame,然后把各个元素依次提取填入空DataFrame,但是处理效率非常低。
我起初采取的方法是依靠DataFrame的切片和拼接功能,尽量整块操作,减少循环次数:
df_close = pd.concat([df.loc[df['instrument']==i].set_index('date')['close'] for i in stock_codes], axis=1)
直到发现pd.pivot_table()这个函数可以方便直接地处理这些变换:
df_close = pd.pivot_table(df_all,values='close',index=['date'],columns=['instrument'])
print(df_close.tail())
instrument 002233.SZA 002394.SZA 002454.SZA 002567.SZA 002581.SZA \
date
2019-12-24 30.662279 40.128410 13.877932 44.442993 18.995348
2019-12-25 31.158939 40.042206 13.764643 43.988461 18.879875
2019-12-26 31.524900 40.257717 13.764643 44.240978 19.053085
2019-12-27 31.786301 40.300819 13.623031 45.655075 19.053085
2019-12-30 32.335243 40.559433 13.764643 44.493496 18.995348
instrument ... 601015.SHA 601117.SHA 601336.SHA 601872.SHA \
date ...
2019-12-24 ... 6.118216 7.116693 50.495480 8.824105
2019-12-25 ... 6.098415 7.071863 50.623913 8.885554
2019-12-26 ... 6.118216 7.195144 50.837967 9.561496
2019-12-27 ... 6.138015 7.139107 50.955696 9.389438
2019-12-30 ... 6.217216 7.206352 52.518295 9.672104
如何删除numpy.ndarray中指定位置的元素
首先利用numpy.ndarray索引的特性创建一个list或numpy.ndarray,列出想要删除元素的位置(不必按顺序),然后用np.delete()函数删除。注意这里输入的a可以是list或numpy.ndarray类型,输出的c是numpy.ndarray类型。
a = [1,2,3,4,5]
b = [3,1,4]
c = np.delete(a, b, axis=0)
print(c)
output: [1 3]
如何确定numpy.ndarray中指定值的元素的位置
我没有找过直接删除指定值的方法,而是先用np.where()函数确定指定值所在的位置,再用np.delete()函数删除。这样做的好处是,在中间环节生成了一个索引,可以在机器学习中用于同步处理矩阵x和其对应的label 矩阵y。
a = [1,2,3,1,1]
b = np.where(np.array(a)==1)
np.where()函数要求输入类型为numpy.ndarray,如果是一个list或其他类型,需要进行转换。
print(b)
print(type(b))
output: (array([0, 3, 4]),)
output: <class 'tuple'>
比较吊诡的是np.where()函数的输出类型为tuple,当遇到有些函数比如np.random.choice()只接受一维的array类型或int类型,我们还需要对tuple进行转换。
如何对多维numpy.ndarray进行降维
接着上一个例子:
c = list(b)
print(c)
output: [array([0, 3, 4])]
当我们将b从tuple转换成list后,发现它仍是一个包含array的list。
d = np.array(b)
print(d)
print(type(d))
print(np.shape(d))
output: [[0 3 4]]
output: <class 'numpy.ndarray'>
output: (1, 3)
当我们将b从tuple转换成numpy.ndarray后,发现它是二维,还需继续转换成一维,这里用flatten()函数。
e = d.flatten()
print(e)
output: [0 3 4]
map()、np.apply_along_axis()和pd.apply()
标题中分别是Python、Numpy和Pandas的函数,向下兼容。当需要对list、numpy.ndarray或DataFrame进行操作时,效率会大大高于for循环。
其中map()的对象是list、numpy.ndarray或DataFrame中的单个元素;np.apply_along_axis()的对象是numpy.ndarray或DataFrame中的整列;pd.apply()的对象是DataFrame中的整列。
三者中效率map() > np.apply_along_axis() > pd.apply()
在np.apply_along_axis()和pd.apply()中指定axis=0或1或2……会选定行或列,然后延其他轴方向运算。
如果需要指定三维及以上操作,还有np.apply_over_axis()函数可用。
a = np.array([[[1,2,3,4],
[1,2,3,4],
[1,2,3,4]],
[[4,3,2,1],
[4,3,2,1],
[4,3,2,1]],
[[6,6,6,6],
[6,6,6,6],
[6,6,6,6]]])
b = map(lambda x: x ** 2, a)
c = np.array(list(map(lambda x: x ** 2, a)))
print(b)
print(c)
输入一个3x3x4的三维array,这里func用的是匿名函数lambda,map()会将a中的元素逐个带入x,计算x^2。注意map()函数返回的是内存地址,需要先转换成list,再根据需要转换成array。
output: <map object at 0x7f5ba948bbe0>
[[[ 1 4 9 16]
[ 1 4 9 16]
[ 1 4 9 16]]
[[16 9 4 1]
[16 9 4 1]
[16 9 4 1]]
[[36 36 36 36]
[36 36 36 36]
[36 36 36 36]]]
未完待续……
如何向numpy.ndarray中插入元素
向已有的numpy.ndarray中添加数据或者补零,可以用np.insert(arr,obj,values,axis)函数。其中:
arr——被插入的目标数组;
obj——插入的目标位置,在这个位置之前插入;
value——要插入的值,注意如果是多个值,形状要同被插入的目标数组匹配,如果是单个值,仅需填入一个数字,这时函数会自动扩充以匹配数组的形状;
axis——目标位置的轴。
比如:在数组a的0轴位置3添加三行0:
a = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
loc = [3,3,3]
b = np.insert(a, (loc), 0, axis=0)
print(b)
[[1 2 3]
[4 5 6]
[7 8 9]
[0 0 0]
[0 0 0]
[0 0 0]]
之前还用过np.zeros()函数生成一个三维的零矩阵,后来发现全无必要。
如果要在原有的array矩阵四周添加元素,还有一种方法是将它当作一张图片,用np.pad()函数补边。
如何在三维数组中使用min()函数
a = np.arange(0,18,1).reshape(2,3,3)
print(a)
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
[[ 9 10 11]
[12 13 14]
[15 16 17]]]
b = a.min(axis=2)
c = b.min(axis=1)
print(b)
[[ 0 3 6]
[ 9 12 15]]
print(c)
[0 9]
对多维数组进行操作常常会被axis的含义困扰,以np.min()函数为例,axis=2代表按行分组取值,比如上例中取第一行的最小数0,第二行的最小数3……依次类推。
另一个困扰是取值后函数会按什么规则输出新的矩阵。比如上例中axis=2维度的结构被拆除了,0、3、6……先是依次被填入axis=1维度,再形成axis=0维度。
如何改变数组维度
np.reshape()函数的参数代表各维度上的行列数,如果不确定某一维的行列数,可以输入-1可以让函数自动补齐,但其余维度的行列数必须明确。
函数会按从上到下,从左往右的顺序依次填充矩阵。下例相当于对数组a做了90°垂直翻转。
a = np.arange(0,18,1).reshape(-1,3)
print(a)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]
[12 13 14]
[15 16 17]]
b = a.reshape(-1,1,3)
print(b)
[[[ 0 1 2]]
[[ 3 4 5]]
[[ 6 7 8]]
[[ 9 10 11]]
[[12 13 14]]
[[15 16 17]]]
如果仅需要增加维度,不需要改变数组的形状,也不清楚数组的形状,还可以用np.newaxis函数,比reshape()快很多。
c = a[:, np.newaxis]
来源:CSDN
作者:¥ifan
链接:https://blog.csdn.net/weixin_44724090/article/details/103776808