Python股票数据处理的一些代码细节

懵懂的女人 提交于 2020-01-27 04:34:53

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]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!