数据准备是一项必须具备的技术,是一个迭代且灵活的过程,可以用于查找、组合、清理、转换和共享数据集,包括用于分析/商业智能(BI)、数据科学/机器学习(ML)和自主数据集成中。具体来说,数据准备是在处理和分析之前对原始数据进行清洗和转换的过程,通常包括重新格式化数据、更正数据和组合数据集来丰富数据等。
本次数据分析实战系列运用股市金融数据,并对其进行一些列分析处理。处理金融数据是量化分析的基础,当然方法都是通用的,换做其他数据也同样适用。本文回顾数据分析常用模块Pandas
和NumPy
,回顾DataFrame
、array
、matrix
基本操作。
股市数据获取的几个模块
Tushare
Tushare是一个免费、开源的python财经数据接口包。主要实现对股票等金融数据从数据采集、清洗加工到数据存储的过程,能够为金融分析人员提供快速、整洁、和多样的便于分析的数据,为他们在数据获取方面极大地减轻工作量,使他们更加专注于策略和模型的研究与实现上。
- 首先使用pip安装第三方依赖库
tushare
下载股市数据。(国内)
pip install tushare -i https://pypi.douban.com/simple/
- 然后在
tushare.pro
注册用户,注意获取你自己的token
。使用daily
函数获取日线数据。
# 导入tushare库
import tushare as ts
# 设置token
ts.set_token('your token here')
# 初始化pro接口
pro = ts.pro_api()
# 获取日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20180701', end_date='20180718'
Baostock
证券宝(http://www.baostock.com)是一个免费、开源的证券数据平台(无需注册)。提供大量准确、完整的证券历史行情数据、上市公司财务数据等。通过python API获取证券数据信息,满足量化交易投资者、数量金融爱好者、计量经济从业者数据需求。
- 首先使用pip安装第三方依赖库
baostock
下载股市数据。(国内)
pip install baostock -i https://pypi.douban.com/simple/
- 然后使用
query_history_k_data_plus
函数获取日线数据
import baostock as bs
import pandas as pd
# 登陆系统
lg = bs.login()
# 获取沪深A股历史K线数据
rs_result = bs.query_history_k_data_plus("sh.600000",
fields="date,open,high, low, close,preclose,volume,amount,adjustflag",
start_date='2017-07-01',
end_date='2017-12-31',
frequency="d",
adjustflag="3")
df_result = rs_result.get_data()
# 登出系统
bs.logout()
Yfinance
yfinance
的老版本是fix_yahoo_finance
,二者都可以使用,推荐使用新版本。
- 首先使用pip安装第三方依赖库
fix_yahoo_finance
下载yahoo
股市数据。(国外)
pip install fix_yahoo_finance -i https://pypi.douban.com/simple/
如果发生报错:ModuleNotFoundError: No module named 'yfinance'
,则需要事先安装'yfinance'
,最新版本已经将fix_yahoo_finance
调整'yfinance'
为
pip install yfinance -i https://pypi.douban.com/simple/
- 然后使用
pdr_override
函数获取日线数据
import yfinance as yf
# 输入
symbol = 'AMD'
start = '2014-01-01'
end = '2018-08-27'
dataset=yf.pdr_override(symbol,start,end)
dataset.head()
下面开始本节主要内容,运用数据处理最常用的第三方模块Pandas
和NumPy
获取数据,为后续数据分析、机器学习做数据准备。
pandas
pandas 是基于NumPy 的一种工具,该工具是为解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。你很快就会发现,它是使Python成为强大而高效的数据分析环境的重要因素之一。
代码语言:javascript复制# Pandas Library
>>> import pandas as pd
>>> import warnings
>>> warnings.filterwarnings("ignore")
获取中国平安股票数据
代码语言:javascript复制>>> import baostock as bs
# 登陆系统
>>> lg = bs.login()
# 获取沪深A股历史K线数据
>>> rs_result = bs.query_history_k_data_plus("sh.601318",
fields="date,open,high,low,close,volume",
start_date='2018-01-01',
end_date='2021-03-31',
frequency="d",
adjustflag="3")
>>> df_result = rs_result.get_data()
# 登出系统
>>> bs.logout()
login success!
logout success!
<baostock.data.resultset.ResultData at 0x28d05a44ac8>
>>> df_result.head()
获取腾讯股票数据
代码语言:javascript复制>>> import yfinance as yf
>>> symbol = 'TCEHY'
>>> start = '2016-01-01'
>>> end = '2021-03-31'
>>> yf.pdr_override()
>>> dataset = yf.download(symbol,start,end)
[*********************100%***********************] 1 of 1 completed
>>> dataset.head()
查看尾部数据
代码语言:javascript复制dataset.tail()
代码语言:javascript复制>>> dataset.shape
(1320, 6)
滚动窗口计算
代码语言:javascript复制dataset.rolling(window=5).mean() # 求最后4行的均值
- 函数解析
dataset.rolling(
window,
min_periods=None,
center=False,
win_type=None,
on=None,
axis=0,
closed=None,)
提供滚动窗口计算。 window:也可以省略不写。表示时间窗的大小,注意有两种形式(int or offset)。如果使用int,则数值表示计算统计量的观测值的数量即向前几个数据。 如果是offset类型,表示时间窗的大小。 min_periods:每个窗口最少包含的观测值数量,小于这个值的窗口结果为NA。值可以是int,默认None。offset情况下,默认为1。 center: 把窗口的标签设置为居中。布尔型,默认False,居右 win_type: 窗口的类型。截取窗的各种函数。字符串类型,默认为None。各种类型 on: 可选参数。对于dataframe而言,指定要计算滚动窗口的列。值为列名。 axis: int、字符串,默认为0,即对列进行计算 closed:定义区间的开闭,支持int类型的window。对于offset类型默认是左开右闭的即默认为right。可以根据情况指定为left both等。
指数加权函数ewm
代码语言:javascript复制>>> dataset['ewm'] = dataset['Adj Close'].ewm(span=20,
min_periods=0,
adjust=False,
ignore_na=False).mean()
>>> dataset.head()
- 函数解析
DataFrame.ewm(com=None,
span=None,
halflife=None,
alpha=None,
min_periods=0,
adjust=True,
ignore_na=False,
axis=0,
times=None)
提供指数加权(EW)函数。可用EW功能:mean()
,var()
,std()
,corr()
,cov()
。参数:com
,span
,halflife
,或alpha
必须提供。
com float, optional 根据质心指定衰减,
span float, optional 跨度,根据跨度指定衰减,
halflife float, str, timedelta, optional 半衰期,根据半衰期指定衰减,
如果times
指定,则观察值衰减到其值一半的时间单位(str或timedelta)。仅适用于mean()
,半衰期值不适用于其他功能。
alpha float, optional
直接地指定平滑系数
,
min_periods int, default 0 窗口中具有值的最小观察数(否则结果为NA)。 adjust bool, default True 调整,在开始期间除以递减的调整因子,以解决相对权重的不平衡问题(将EWMA视为移动平均值)。
- 当
adjust=True
(默认)时,EW功能是使用权重计算的
。例如,该系列的EW移动平均值
将会
- 当
adjust=False
为时,将以递归方式计算指数加权函数:
ignore_na bool, default False
计算权重时忽略缺失值;指定True
重现0.15.0之前的行为。
- 当
ignore_na=False
(默认)时,权重基于绝对位置。例如,权重
和
用于计算
的最终加权平均数,如果adjust=True
,则权重分别是
和 1。如果adjust=False
,权重分别是
和
- 当
ignore_na=True
时,权重基于相对位置。例如,权重
和
用于计算
的最终加权平均数,如果adjust=True
,则权重分别是
1。如果adjust=False
,权重分别是
和
提取数据
代码语言:javascript复制# iloc[]
>>> print(dataset.iloc[0][0])
19.59000015258789
# iat[]
>>> print(dataset.iat[0,0])
19.59000015258789
代码语言:javascript复制
at
的使用方法与loc类似,但是比loc
有更快的访问数据的速度,而且只能访问单个元素,不能访问多个元素。iat
与iloc
类似。
# loc[]
>>> print(df.loc[0]['High'])
19.65999984741211
# iloc[]
>>> print(df.iloc[0])
Date 2015-12-31 00:00:00 Open 19.59 High 19.66 Low 19.51 Close 19.62 Adj Close 19.3691 Volume 382600 ewm 19.3691 Name: 0, dtype: object
重置索引
代码语言:javascript复制>>> df = dataset.reset_index()
>>> df.head()
删除列
代码语言:javascript复制# 删除数据
>>> new_df = df.drop(['Date'], axis=1)
>>> new_df.head()
添加列
代码语言:javascript复制# 添加日期
>>> new_column = df['Date']
>>> new_df['Date'] = new_column
>>> new_df.head()
移动列
代码语言:javascript复制# 将 Date 移动至第一列
>>> cols = list(new_df)
>>> cols.insert(0, cols.pop(cols.index('Date')))
>>> cols
['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume', 'ewm']
# loc是DataFrame有索引标记的值的
>>> new_df = new_df.loc[:, cols]
>>> new_df.head()
删除列
代码语言:javascript复制>>> del new_df['Close']
>>> new_df.head()
重命名列
- 重命名
>>> new_df = new_df.rename(index=str,
columns={'Adj Close':'Close'})
>>> new_df.head()
- 列名改为大写
>>> new_df.rename(str.upper, axis='columns')
- 列名改为小写
# column name to lower case
new_df.rename(str.lower, axis='columns')
选择多个列
代码语言:javascript复制>>> new_df[new_df.columns[1:5]]
选择多个行
代码语言:javascript复制>>> new_df[1:4]
创建Dataframe
代码语言:javascript复制>>> stock_df = pd.DataFrame([[123.50, 145.35, 165.50],
[152.35, 154.67, 160.35],
[201.25, 236.54, 254.69]],
columns=['IBM', 'Apple', 'Tesla'])
>>> stock_df
IBM | Apple | Tesla | |
---|---|---|---|
0 | 123.50 | 145.35 | 165.50 |
1 | 152.35 | 154.67 | 160.35 |
2 | 201.25 | 236.54 | 254.69 |
替换数据
代码语言:javascript复制>>> stock_df = stock_df.replace([201.25, 145.35], [888, 888])
>>> stock_df
IBM | Apple | Tesla | |
---|---|---|---|
0 | 123.50 | 888.00 | 165.50 |
1 | 152.35 | 154.67 | 160.35 |
2 | 888.00 | 236.54 | 254.69 |
>>> stock_df = stock_df.replace([165.50, 160.35], ['NaN', 'NaN'])
>>> stock_df
IBM | Apple | Tesla | |
---|---|---|---|
0 | 123.5 | 888 | NaN |
1 | 152.35 | 154.67 | NaN |
2 | 888 | 236.54 | 254.69 |
获取数据框数据
代码语言:javascript复制>>> for index, row in stock_df.iterrows() :
... print(row['IBM'], row['Apple'])
123.5 888.0
152.35 154.67
888.0 236.54
当然,前面这篇pandas/numpy高效数据处理方法总结里列举了很多实用方法,大家可随时查看。
NumPy
NumPy是专为简化Python中的数组运算而设计的,每个NumPy数组都具有以下属性:
- ndim:维数。
- shape:每一维的大小。
- size:数组中元素的总数。
- dtype:数组的数据类型(例如int、float、string等)。
# Numpy 模块
>>> import numpy as np
将数据集转换为numpy
代码语言:javascript复制# 将打开的DataFrame转换为numpy数组
>>> Open_array = np.array(dataset['Open'])
>>> Open_array
array([19.59000015, 19.12999916, 19.09000015, ..., 79.44000244,
79.19000244, 78.56999969])
查看单个元素
代码语言:javascript复制>>> print("First element:", Open_array [0])
>>> print("Second element:", Open_array [1])
>>> print("Second last element:", Open_array[-1])
First element: 19.59000015258789
Second element: 19.1299991607666
Second last element: 78.56999969482422
查看多个元素
代码语言:javascript复制>>> print(Open_array[2:5]) # 第3个到第5个
[19.09000015 18.63999939 18.04999924]
>>> print(Open_array[:-5]) # 从开始到最后第4个
[19.59000015 19.12999916 19.09000015 ... 80.69999695 81.90000153
81.65000153]
>>> print(Open_array[5:]) # 第6个到最后
[18.32999992 17.76000023 17.64999962 ... 79.44000244 79.19000244
78.56999969]
>>> print(Open_array[:]) # 开始到最后
[19.59000015 19.12999916 19.09000015 ... 79.44000244 79.19000244
78.56999969]
修改元素
- 改变第一个元素
>>> volume = np.array(dataset['Volume'])
>>> volume[0] = 0
>>> print(volume)
[ 0 469100 170300 ... 5923100 3468900 1715900]
- 改变多个元素
# 更改第3到5个元素
>>> volume[2:5] = np.array([4, 6, 8])
>>> print(volume)
[ 0 469100 4 ... 5923100 3468900 1715900]
增加元素
代码语言:javascript复制>>> add_numbers = np.array([1, 2, 3])
>>> np.concatenate((volume, add_numbers)
array([ 0, 469100, 4, ..., 1, 2, 3], dtype=int64)
>>> np.append(volume, add_numbers, axis=0)
array([ 0, 469100, 4, ..., 1, 2, 3], dtype=int64)
删除元素
- 直接删除
>>> print(volume)
[ 0 469100 4 ... 5923100 3468900 1715900]
>>> np.delete(volume, 1)
array([ 0, 4, 6, ..., 5923100, 3468900, 1715900], dtype=int64)
- 转换为list()来使用remove
>>> volume = volume.tolist() # Convert tolist() to use remove
>>> volume
代码语言:javascript复制>>> volume.remove(0)
>>> print(volume)
代码语言:javascript复制>>> print(volume.pop(2))
>>> print(volume)
Matrix
在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合。由 m × n
个数aij
排成的m行n列
的数表称为m行n列
的矩阵,简称m × n
矩阵。矩阵运算在科学计算中非常重要,而矩阵的基本运算包括矩阵的加法,减法,数乘,转置,共轭和共轭转置 。
>>> import numpy as np # 对于矩阵和线性代数
>>> import numpy.matlib
代码语言:javascript复制报错提示: 这里用到
Matrix
时,需要将含有Matrix
的子包matlib
导入,不然会报错。module 'numpy' has no attribute 'matlib'
这是因为numpy。matlib是numpy的可选子包,必须单独导入。如果您只导入numpy而不导入子包matlib,那么Python将把matlib作为numpy包的属性来查找。如果没有导入numpy.matlib,这个属性没有被分配给numpy。
>>> import yfinance as yf
>>> symbol = 'TCEHY'
>>> start = '2021-03-20'
>>> end = '2021-03-31'
>>> yf.pdr_override()
>>> dataset = yf.download(symbol,start,end)
>>> df = dataset.drop(['High', 'Low', 'Close', 'Volume'], axis=1)
>>> df.head()
Open | Adj Close | |
---|---|---|
Date | ||
2021-03-19 | 80.699997 | 82.639999 |
2021-03-22 | 81.900002 | 82.769997 |
2021-03-23 | 81.650002 | 80.930000 |
2021-03-24 | 80.930000 | 76.809998 |
2021-03-25 | 77.309998 | 77.419998 |
转换为矩阵
代码语言:javascript复制>>> A = np.matrix(df)
>>> A
matrix([[80.69999695, 82.63999939],
[81.90000153, 82.76999664],
[81.65000153, 80.93000031],
[80.93000031, 76.80999756],
[77.30999756, 77.41999817],
[79.44000244, 80.98000336],
[79.19000244, 78.69000244],
[78.56999969, 78.83000183]])
因为matrix
很多操作不方便,如添加修改某个元素。这里可以先用array
函数读写完毕以后,再用matrix
函数让它恢复矩阵类型。
转换数组
代码语言:javascript复制>>> A = np.array(df)
>>> A
array([[80.69999695, 82.63999939],
[81.90000153, 82.76999664],
[81.65000153, 80.93000031],
[80.93000031, 76.80999756],
[77.30999756, 77.41999817],
[79.44000244, 80.98000336],
[79.19000244, 78.69000244],
[78.56999969, 78.83000183]])
访问元素
代码语言:javascript复制>>> type(A)
numpy.ndarray
>>> print("A =", A)
A = [[80.69999695 82.63999939]
[81.90000153 82.76999664]
[81.65000153 80.93000031]
[80.93000031 76.80999756]
[77.30999756 77.41999817]
[79.44000244 80.98000336]
[79.19000244 78.69000244]
[78.56999969 78.83000183]]
>>> print("A[1] =", A[1]) # 第2行
A[1] = [81.90000153 82.76999664]
>>> print("A[1][2] =", A[1][1]) # 第2行的第2个元素
A[1][2] = 82.7699966430664
>>> print("A[0][-1] =", A[0][-1]) # 第1行的最后第1个元素A[0][-1] = 82.63999938964844
取出元素放到列表中
代码语言:javascript复制>>> column = []; # 空列表
>>> for row in A:
... column.append(row[1])
>>> print("Second column =", column)
Second column = [82.63999938964844, 82.7699966430664, 80.93000030517578, 76.80999755859375,
77.41999816894531, 80.9800033569336,8.69000244140625, 78.83000183105469]
arange and shape
代码语言:javascript复制
>>> A = np.arange(4)
>>> print('A =', A)
A = [0 1 2 3]
>>> B = np.arange(12).reshape(2, 6)
>>> print('B =', B)
B = [[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
矩阵的运算
代码语言:javascript复制>>> A = np.array(dataset['High'], dataset['Low'])
>>> B = np.array(dataset['Open'], dataset['Close'])
>>> print(A)
array([82.63999939, 82.84999847, 81.94000244, 81.16000366,
78.19000244, 80.98000336, 79.80000305, 79.41000366])
- 矩阵加法
>>> C = A B
>>> print("矩阵加法: n", C)
矩阵加法:
[163.33999634 164.75 163.59000397 162.09000397
155.5 160.4200058 158.99000549 157.98000336]
- 矩阵减法
>>> D = A - B
>>> print("矩阵减法: n", D)
矩阵减法:
[1.94000244 0.94999695 0.29000092 0.23000336
0.88000488 1.54000092 0.61000061 0.84000397]
- 两个矩阵的乘法
>>> E = A.dot(B)
>>> print("两矩阵点乘: n", E)
两矩阵点乘:
51749.67010773317
- 矩阵转置
>>> T = A.transpose()
>>> print("矩阵转置: n", T)
矩阵转置:
[82.63999939 82.84999847 81.94000244 81.16000366
78.19000244 80.98000336 79.80000305 79.41000366]
访问矩阵元素、行和列
- 访问一维矩阵单个元素
# 第一个元素
>>> print("A[0] =", A[0])
A[0] = 82.63999938964844
# 第三个元素
>>> print("A[2] =", A[2])
A[2] = 81.94000244140625
# 最后一个元素
>>> print("A[-1] =", A[-1])
A[-1] = 79.41000366210938
- 访问多维矩阵单个元素
>>> A = np.array(dataset[['Open', 'Low', 'High', 'Close']])
>>> print("矩阵A: n", A)
矩阵A:
[[80.69999695 80.09999847 82.63999939 82.63999939]
[81.90000153 81.62999725 82.84999847 82.76999664]
[81.65000153 80.91000366 81.94000244 80.93000031]
[80.93000031 76.59999847 81.16000366 76.80999756]
[77.30999756 76.80000305 78.19000244 77.41999817]
[79.44000244 77.81999969 80.98000336 80.98000336]
[79.19000244 78.06999969 79.80000305 78.69000244]
[78.56999969 78.02999878 79.41000366 78.83000183]]
# 第一行的第一个元素
>>> print("A[0][0] =", A[0][0])
A[0][0] = 80.69999694824219
# 第二行的第三个元素
>>> print("A[1][2] =", A[1][2])
A[1][2] = 82.8499984741211
# 最后一行的最后一个元素
>>> print("A[-1][-1] =", A[-1][-1])
A[-1][-1] = 78.83000183105469
- 访问矩阵的行
# 第一行
>>> print("A[0] =", A[0])
A[0] = [80.69999695 80.09999847 82.63999939 82.63999939]
# 第三行
>>> print("A[2] =", A[2])
A[2] = [81.65000153 80.91000366 81.94000244 80.93000031]
# 最后一行
>>> print("A[-1] =", A[-1])
A[-1] = [78.56999969 78.02999878 79.41000366 78.83000183]
- 访问矩阵的列
# 第一列
>>> print("A[:,0] =",A[:,0])
A[:,0] = [80.69999695 81.90000153 81.65000153 80.93000031 77.30999756 79.44000244 79.19000244 78.56999969]
# 第四列
>>> print("A[:,3] =", A[:,3])
A[:,3] = [82.63999939 82.76999664 80.93000031 76.80999756 77.41999817 80.98000336 78.69000244 78.83000183]
# 最后一列
>>> print("A[:,-1] =", A[:,-1])
A[:,-1] = [82.63999939 82.76999664 80.93000031 76.80999756 77.41999817 80.98000336 78.69000244 78.83000183]
- 访问多维矩阵多个元素
# 没有逗号相隔,默认获取行
# 第3到第5个元素
>>> print(A[2:5])
[[81.65000153 80.91000366 81.94000244 80.93000031]
[80.93000031 76.59999847 81.16000366 76.80999756]
[77.30999756 76.80000305 78.19000244 77.41999817]]
# 第1到第3个元素
>>> print(A[:-5])
[[80.69999695 80.09999847 82.63999939 82.63999939]
[81.90000153 81.62999725 82.84999847 82.76999664]
[81.65000153 80.91000366 81.94000244 80.93000031]]
# 第6行到最后一个元素
>>> print(A[5:])
[[79.44000244 77.81999969 80.98000336 80.98000336]
[79.19000244 78.06999969 79.80000305 78.69000244]
[78.56999969 78.02999878 79.41000366 78.83000183]]
# 第1个到最后一个元素
>>> print(A[:])
[[80.69999695 80.09999847 82.63999939 82.63999939]
[81.90000153 81.62999725 82.84999847 82.76999664]
[81.65000153 80.91000366 81.94000244 80.93000031]
[80.93000031 76.59999847 81.16000366 76.80999756]
[77.30999756 76.80000305 78.19000244 77.41999817]
[79.44000244 77.81999969 80.98000336 80.98000336]
[79.19000244 78.06999969 79.80000305 78.69000244]
[78.56999969 78.02999878 79.41000366 78.83000183]]
# 反转一个列表
>>> print(A[::-1])
[[78.56999969 78.02999878 79.41000366 78.83000183]
[79.19000244 78.06999969 79.80000305 78.69000244]
[79.44000244 77.81999969 80.98000336 80.98000336]
[77.30999756 76.80000305 78.19000244 77.41999817]
[80.93000031 76.59999847 81.16000366 76.80999756]
[81.65000153 80.91000366 81.94000244 80.93000031]
[81.90000153 81.62999725 82.84999847 82.76999664]
[80.69999695 80.09999847 82.63999939 82.63999939]]
# 前2行,前4列
>>> print(A[:2, :4])
[[80.69999695 80.09999847 82.63999939 82.63999939]
[81.90000153 81.62999725 82.84999847 82.76999664]]
# 前1行,所有列
>>> print(A[:1,])
[[80.69999695 80.09999847 82.63999939 82.63999939]]
# 所有行,第3列
>>> print(A[:,2])
[82.63999939 82.84999847 81.94000244 81.16000366 78.19000244 80.98000336 79.80000305 79.41000366]
# 所有行,第3到5列
>>> print(A[:, 2:5]) # 共4列,只能取到第3和第4列
[[82.63999939 82.63999939]
[82.84999847 82.76999664]
[81.94000244 80.93000031]
[81.16000366 76.80999756]
[78.19000244 77.41999817]
[80.98000336 80.98000336]
[79.80000305 78.69000244]
[79.41000366 78.83000183]]