一、项目介绍
- 数据获取与存储:能够使用Python财经数据接口包tushare下载股票交易数据,并将数据保存到CSV文件或MySQL数据库中。
- 数据处理:能够用Pandas从CSV文件、Excel文件以及MySQL数据库中读取数据。能够使用Pandas对数据进行简单处理和深度处理,如数据的增删改查、数据去重、缺失处理、数据转换以及数据标准化等。
- 数据统计分析:能够通过Pandas的groupby、agg和transform方法实现数据的聚合,并完成数据的统计分析工作。
二、数据获取与存储
2.1、数据获取
tushare是一个免费、易用的Python财经数据接口包,能够快速、便捷的获取股票、基金、黄金等金融数据:
- 下载并安装tushare
pip install tushare
-
访问tushare官网 Tushare数据
-
注册用户,进入个人主页,在“接口TOKEN”选项卡中将token值复制下来。
-
新建一个Python项目,获取股票代码:
import tushare as ts
# 设置token值
ts.set_token("2022fef40058599bb7458609a6ad277edda3193a92b80513f27bb73a")
# 获取格力电器股票交易数据
df = ts.pro_bar(ts_code='000651.SZ', # 股票代码
start_date='20180101', # 起始时间
end_date='20200930') # 结束时间
# 保存为Excel文件
df.to_excel("格力电器.xlsx")
to_excel()函数的常用参数
参数 | 描述 |
---|---|
excel_wirter | 用于指定文件路径或者ExcelWirter |
sheetname | 用于设置sheet名称,默认为Sheet1 |
na_rep | 用于设置在遇到NaN值的代替字符,默认为空字符 |
columns | 用于设置需要保存的列,默认为None |
header | 用于设置是否保留列名,默认为True |
index | 用于设置是否保留索引,默认为True |
encoding | 用于指定文件编码格式 |
2.2、数据存储
2.2.1、存储到CSV文件中
to_csv()函数用于将数据存储到文本文件中,其用法与to_excel() 函数类似
to_csv()函数的常用参数
参数 | 描述 |
---|---|
path_or_buf | 用于指定文件路径 |
sep | 用于设置分隔符,默认为逗号 |
na_rep | 用于设置遇到缺失值的替代字符,默认为空字符 |
columns | 用于设置需要保存的列,默认为None(全选) |
header | 用于设置是否保留列名,默认为True |
index | 用于设置是否保留索引,默认为True |
encoding | 用于设置文件编码格式,默认为UTF-8格式 |
df.to_csv("格力电器.csv", # 文件名
sep=",", # 间隔符
na_rep="99999", # 缺失值使用99999代替
header=True, # 保留列名
index=False) # 不保留索引
2.2.2、存储到数据库中
-
搭建MySQL数据库服务器
-
python连接MySQL数据库服务器。
pip install pymysql
# 连接数据库
# 导入sqlalchemy库
from sqlalchemy import create_engine
# 创建一个MySQL连接器对象engine
# 用户名为root,密码为1234
# 地址为127.0.0.1(本地),端口为3306
# 数据库名称为stock,编码格式为UTF-8
engine = create_engine("mysql+pymysql://root:1234@127.0.0.1:3306/stock?charset=utf8")
-
将股票数据存储于MySQL数据库中。 使用to_sql()函数将数据存储到名为stock的MySQL数据库服务器中。
df.to_sql("geli", # 表名称
con=engine, # 数据库连接器对象
index=False, # 不保留索引
if_exists="replace") # 如果表已存在,则先删除再重新创建表
to_sql()函数的常用参数:
参数 | 描述 |
---|---|
name | 用于设置表名称。string型,无默认值 |
con | 用于设置数据库连接对象,无默认值 |
if_exits | 用于设置表名已存在的情况,可设为"fail","replace","append"。其中,'fail'表示引发ValueError错误,不执行写入操作,为默认选项;'replace'表示删除原有表,再重新创建新表;‘append’表示将新值追加到现有表中 |
index | 用于设置是否将索引写入到表中。值为boolean型,默认为Rrue |
index_label | 用于设置索引的名称,如果该参数为None且index为True,则使用索引名。如果为多重索引,则应该使用sequence形式。默认为None |
chunksize | 用于设置一次要写入的行数。默认将所有行一次写入。值为int,可选 |
dtype | 用于设置写入的数据类型。值为dict型(列名为key,数据格式为value)或标量(应用于所有列)。默认为None |
三、数据读取
3.1、读取CSV文件中的数据
import pandas as pd
# 读取CSV文件
data = pd.read_csv("格力电器.csv") #CSV文件路径
print(data)
read_csv()函数的常用参数:
参数名称 | 描述 |
---|---|
filepath_or_buffer | 用于设置文件路径 |
sep | 用于设置分割符。默认为逗号“,”,read_table默认为制表符"[Tab]" |
header | 用于设置第几行作为列名,默认为0(第一行)。如果文件没有标题行,则设置为None |
names | 用于设置自定义的列名,默认为None |
index_col | 用于设置某一列作为DataFrame的行名,如果没有这样的列,则设置为None |
usercols | 读取按列划分的子集,有以下两种取值。None:读取所有列,默认值;列表:如[0,2,4]表示列的索引,["ts_code","high"]表示列名 |
dtype | 用于设置读取的数据类型(列名为key,数据格式为values)。默认为None |
skiprows | 用于设置开头要跳过的行数,默认为None(不跳过任何行) |
nrows | 用于设置要读取数据的条数,默认为None(全部读取) |
encoding | 编码格式 |
# 例子:自定义DataFrame的列名为“A","B","C","D"...
data = pd.read_csv("格力电器.csv",
skiprows=1, # 忽略第一列的列名
names=[chr(i) for i in range(65,65+11)] ) # 自定义列名
print(data)
# 例子:跳过前5条数据
data = pd.read_csv("格力电器.csv",
header=None, #设置成无标题格式
skiprows=5) # 忽略前5条数据
print(data)
# 读取前10条数据中的trade_date、open、high和low这几列数据
data = pd.read_csv("格力电器.csv",
nrows=10, #获取10条数据
usecols=['trade_date','open','high','low']) # 按名称获取相应列
print(data)
3.2、读取Excel文件中的数据
import pandas as pd
data =pd.read_excel("格力电器.xlsx", # Excel文件路径
sheet_name='Sheet1', # Excel中sheet名称
index_col=0) # 将第一列作为列名
print(data)
read_excel()函数的常用参数:
参数名称 | 描述 |
---|---|
io | 文件路径 |
sheet_name | Excel内sheet的名称或位置,默认为0。接收string、int或list型 |
header | 设置第几列作为列名,默认为0(第一行),如果文件没有标题行,则设置为None |
names | 设置自定义的列名,默认为None |
index_col | 设置某一列作为DataFrame的行名,如果没有这样的列,默认为None |
usecol | 读取按列划分的子集,划分范围的方法有以下几种:None:读取所有列,默认;字符串:如"A:E"表示读取列A到列E子集所有的数据。列表:如[0,2,4]表示列的索引,["ts_code","high"]表示列名 |
dtype | 代表读取的数据类型(列名为key,数据格式为values)。默认为None |
skiprows | 开头要跳过的行数,默认为None |
nrows | 要读取数据的条数,默认为None |
3.3、获取MySQL数据库中的数据
Pandas提供了3个从数据库中获取数据的函数。
-
read_sql_table(),详细用法如下:
pandas.read_sql_table(table_name,con,scheme='None',index_col='None',coerce_float='True',parse_dates='None',columns='None',chunksize: None = 'None')
-
read_sql_query(),详细用法如下:
pandas.read_sql_query(sql,con,index_col='None',coerce_float='True',params='None',parse_dates='None',chunksize: None = 'None')
-
read_sql(),详细用法如下:
pandas.read_sql(sql,con,index_col='None',coerce_float='True',params='None',parse_dates='None',columns='None',chunksize: None = 'None')
3个数据库数据读取函数的常用参数:
参数名称 | 描述 |
---|---|
sql或table_name | string型。读取数据表的表名或SQL语句 |
con | 数据库连接对象 |
index_col | 设置某一列作为DateFrame的行名,如果没有这样的列,则设置为None |
coerce_float | boolen型。将数据库中的decimal类型的数据转换为Pandas的float64类型数据,默认为True |
parse_dates | list或dict型。解析为日期的列名 |
columns | list型。要从SQL表中读取列名的列表(仅在读取表时使用)。默认为None |
# 各使用方法例子:
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:1234@127.0.0.1:3306/stock?charset=utf8")
# 读取数据库的表geli中的所有数据
data1 = pd.read_sql_table("geli",con=engine)
# 使用SQL语句获取表中的数据
data2 = pd.read_sql_query("select * from geli",con=engine)
# 查询数据库stock中的所有表名称
data3 = pd.read_sql_query("show tables",con=engine)
# 即可读取表也可通过SQL实现查询功能
data4 = pd.read_sql("geli",con=engine)
data5 = pd.read_sql("select * from geli",con=engine)
四、数据简单处理
Pandas也有属于自己的数组对象:Series和DataFrame。
-
series:带有标签的一维数组对象,类似于DataFrame的一列
-
DataFrame:带有行名和列名的二维数组对象,类似于Excel表格。NumPy的ndarray数组只能存储同类型数据,而DataFrame支持不同数据类型的数据。
4.1、常用属性
属性 | 说明 | 属性 | 说明 |
---|---|---|---|
values | 获取元素值 | size | 获取元素个数 |
index | 获取行索引 | shape | 获取数组维度(行列数) |
columns | 获取列索引 | dtype | 获取数据类型 |
ndim | 获取维度数 | T | 转置 |
# 例子:
import pandas as pd
data = pd.read_csv("格力电器.csv")
print("1.获取所有值:\n",data.values)
print("2.获取行索引:\n",data.index)
print("3.获取列索引:\n",data.columns)
print("4.获取元素个数:\n",data.size)
print("5.获取数组维度:\n",data.shape)
print("6.获取数据类型:\n",data.dtypes)
print("7.获取数组维度:\n",data.ndim)
data1 = data.T
print("8.数据转置后的维度:\n",data.shape)
4.2、查找数据
DataFrame既有普通数组的特点,又有自己独有的特征,它拥有行名,列名,因此非常方便于查找DataFrame中不同范围的数据。
4.2.1、使用字典Key查找数据
a = [[1,2,3],
[4,5,6]]
a[1][1] # 访问第2行第2列的数 5
# 例子
import pandas as pd
data = pd.read_csv("格力电器.csv")
# 获取交易日期(trade_date)列的数据
print(data["trade_date"]) # 返回一个Series类型的数组对象
# 获取交易日期(trade_date)和收盘价(close)这两列数据。
print(data[["trade_date","close"]])
# 获取交易日期(trade_date)和收盘价(close)这两列的前5条数据
print(data[["trade_date","close"]][:5])
# 获取前5条交易数据
print(data[:5])
注意:使用字典key的形式访问DataFrame数据的一般形式如下
a[列名][行名/行索引值]
即先确定行,再确定列。另外,确定列的范围时,不能通过“:”来划定(访问所有列除外)。比如要访问前5行前5列的数据:
print(data[:"close"][:5]) # 这种方式是错误的
print(data[["trade_date","open","high","low","close"]][:5]) # 正确方法
4.2.2、使用切片查找数据
DataFrame提供了两种切片函数loc()和iloc(),使得DataFrame不仅可以截取任意行和列范围的数据,而且还可以自由选择使用行/列名或者索引值查找。
loc()和iloc()的不同之处在与loc()是针对DataFrame行/列名的切片函数,如果传入的不是行/列名,操作将无法执行。iloc()接收的必须是行索引值和列索引值(与ndarry的切片函数一样)
# 获取交易日期(trade_date)列的数据
data = pd.read_csv("格力电器.csv")
data.loc[:,"trade_date"] # 方法1
data.iloc[:,1] # 方法2
-
使用loc()函数截取数据时,要使用行名和列名,而iloc()则要使用行和列的索引值。
# 获取交易日期(trade_date)和收盘价(close)这两列数据
data.loc[:,["trade_date","close"]] # 方法1
data.iloc[:,[1,5]] # 方法2
-
要截取的行或列不是一个连续的范围,可以列举出这些行或列对应的名称或者索引。
# 获取交易日期(trade_date)和收盘价(close)这两列前5条数据
print(data.loc[:4,["trade_date","close"]]) # 方法1 4表示行名
print(data.iloc[:5,[1,5]]) # 方法2
-
获取前5行数据时,行范围:“:4”,4表示行名,并非行的索引值。
# 获取前5条交易数据
print(data.loc[:4])
print(data.iloc[:5])
4.2.3、使用条件查找
DataFrame还支持使用条件表达式来查找数据。
# 查找收盘价小于50的数据
print(data[data["close"]<50.0])
# 查找收盘价小于50时的交易日期(trade_date)和涨跌幅(pac_chg)
print(data.loc[data["close"]<50.0,["trade_date","pct_chg"]])
# 查找收盘价小于50并且开盘价大于50时的所有数据
print(data[(data["close"]<50.0) & (data["open"]>50.0)])
# 查找收盘价小于50并且开盘价大于50时的交易日期
print(data.loc[(data["close"]<50.0) & (data["open"]>50.0),"trade_date"])
4.3、组装数据
4.3.1、拼接数据
Pandas的拼接函数concat()。该函数的一般格式如下:
pandas.concat(objs,axis=0,join='outer',ignore_index=False,keys=None,levels=None,names=None,verify_integrity=False,sort=False,copy=True)
concat()函数的常用参数:
参数 | 描述 |
---|---|
objs | 参与拼接的Pandas对象的列表 |
axis | 0或1。表示拼接的方向,默认为0,即纵向拼接 |
join | “inner”或"outer"。表示内连接(inner)还是外连接(outer)。默认为外连接 |
ignore_index | 表示是否忽略原索引值,如果为True,则忽略,如果为False(默认),则保留 |
-
将两组数据实现纵向拼接:
data = pd.read_csv("../数据集/格力电器.csv")
# 获取前3条数据中的前两列
A = data.head(3)[["ts_code","trade_date"]]
B = data.tail(3)[["ts_code","trade_date"]]
all = pd.concat([A,B])
print(all)
# ts_code trade_date
# 0 000651.SZ 20200930
# 1 000651.SZ 20200929
# 2 000651.SZ 20200928
# 904 000651.SZ 20170105
# 905 000651.SZ 20170104
# 906 000651.SZ 20170103
数组all中沿用了数组A和数组B中列索引的值,要想从0开始重新设置索引,可以将参数ignore_index设置为True。
all = pd.concat([A,B],ignore_index)
print(all)
# ts_code trade_date
# 0 000651.SZ 20200930
# 1 000651.SZ 20200929
# 2 000651.SZ 20200928
# 3 000651.SZ 20170105
# 4 000651.SZ 20170104
# 5 000651.SZ 20170103
A数组和B数组的列名完全相同,纵向拼接就是将相同列名的数据纵向堆叠在一起。
若列名不完全相同:如数组A的列名为“A”和“B”,而数组B的列名是“B”和“C”。纵向拼接时,一般有以下两种拼接需求:
- 只拼接列名相同的列数据,其余列都舍弃,即按列名的交集进行纵向拼接。
- 所有列都不舍弃,即按列名的并集进行拼接。
设置concat()方法中的参数join为“inner”或“outer”就可以实现按列名的交集或并集进行拼接的功能。
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
# 按列名的交集进行拼接:
# 获取前3条数据中的前两列
A = data.head(3)[["ts_code","trade_date"]]
# 获取最后3条数据中的前两列
B = data.tail(3)[["ts_code","open"]]
all = pd.concat([A,B],join="inner")
print(all)
# ts_code
# 0 000651.SZ
# 1 000651.SZ
# 2 000651.SZ
# 904 000651.SZ
# 905 000651.SZ
# 906 000651.SZ
# 按列名的并集进行拼接:
all = pd.concat([A,B],join="outer") # 默认为outer,可以不写
设置参数axis为0或1,即可设置横向或纵向拼接功能。
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
# 获取前3条数据中的前两列
A = data.head(3)[["ts_code","trade_date"]]
# 获取最后3条数据中的前两列
B = data.head(3)[["open","high"]]
all = pd.concat([A,B],axis=1)
print(all)
# ts_code trade_date open high
# 0 000651.SZ 20200930 53.16 53.42
# 1 000651.SZ 20200929 53.40 53.61
# 2 000651.SZ 20200928 54.12 54.45
横向拼接时,也会有以下两种横向拼接需求:
1、只拼接行名相同的行数据,其余行都舍弃,即按行名的交集进行横向拼接。
# 获取前3条数据中的前两列
A = data.head(3)[["ts_code","trade_date"]]
# 获取第3-5条数据中的第3-4列
B = data.loc[2:4,["trade_date","high"]]
# 按行名的交集进行横向拼接
all = pd.concat([A,B],axis=1,join="inner")
2、所有行都不舍弃,即按行名的并集进行横向拼接。
all = pd.concat([A,B],axis=1)
4.3.2、merge拼接数据
有时需要根据一个或多个键将多个数据集的行连接起来。
Pandas中使用merge()函数实现类似SQL中的join操作功能,叫做合并数据。
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
# 获取前3条数据中的股票代码和交易日
A = data.head(3)[["ts_code","trade_date"]]
# 获取前3条数据中的交易日和收盘价
B = data.head(3)[["trade_date","close"]]
all = pd.merge(A,B,on="trade_date")
merge()函数的一般格式:
merge(left,right, how= "inner",on= None,left_on= None,right_on= None,left_inde= False,right_indexalse,sort= False,suffixes= ("_x", "_y"),copy= None,indicator= False,validate= None)
merge()函数的常用参数:
参数 | 描述 |
---|---|
left | 要合并的数组A,DataFrame或series |
right | 要合并的数组B,DataFrame或series |
how | 数据的连接方式,有内连接(inner)、外连接(outer)、左连接(left)和右连接(right),默认为inner |
on | 两个数组的主键(必须一致) |
left_on | left数组用于连接的列名,默认为None |
right_on | right数组用于连接的列名,默认为None(left_on和right_on主要用于连接2个列名不同的数组) |
left_index | 是否使用left数组的行索引作为连接键,默认为False |
right_index | 是否使用right数组的行索引作为连接键,默认为False |
sort | 是否根据连接键对合并后的数据进行排序,默认为False |
suffixes | 存在相同列名时在列名后面添加的后缀,默认为('x',"y") |
copy | 默认为True,表示将数据复制到新数组中,设置为False可以提供性能 |
indicator | 显示合并结果中的数据来自那个数组 |
merge()函数也有四种连接方式通过how来设置连接方式。
# 使用内连接合并两个数组的功能。其他方式的功能,只需要更改merge()参数how即可
A = pd.DataFrame([[1,2],[3,4]],index=[0,1],columns=["A","B"])
B = pd.DataFrame([[4,11],[6,13]],index=[2,3],columns=["B","C"])
all = pd.merge(A,B,on="B",how="inner")
print(all)
# 将左右数组的行索引作为唯一连接键进行数组合并
all = pd.merge(A,B,left_index=True,right_index=True,how="inner")
4.3.3、重叠数据的合并
若出现两条数据的内容几乎一致,但某些特征在一张表上是完整的,而在另一张表中有缺失,可以用DataFrame中的combine_first()方法进行重叠数据的合并。
A = pd.DataFrame([[1,2],[3,4],[pd.NA,13]],index=[0,1,2],columns=["A","B"])
B = pd.DataFrame([[pd.NA,4],[6,12]],index=[1,2],columns=["A","B"])
# 重叠数据的合并
all = A.combine_first(B)
print(all)
# A B
# 0 1 2
# 1 3 4
# 2 6 13
conbine_first()方法有以下特点:
- combine_first()方法是DataFrame数组自带的方法
- 合并时,除了列名外,行索引也是其中的一个连接键
-
如果待重叠合并的两个数组相同位置上的值不同,如数组A的12与数组B的12,则结果以数组A中的值为准
4.4、添加数据
4.4.1、字典形式的赋值
使用data[key]=value的形式可以添加一列数据到数据集的最后,这里的value可以是一个list、ndarray一维数组、Series数组,也可以是单个值。
import numpy as np
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
# 字典形式赋值
data["bias"] = np.arange(len(data)) # ndarray数组(0,1,2...)
如新增的列中各个值相同,则直接赋值即可:
data["bias"] = 1 # 设置相同的值
4.4.2、使用insert()函数
DataFrame的insert()函数,可以选择按行插入或按列插入,可以选择插入的位置。
- 第一个参数设置新列插入的位置的索引值
- 第二个参数设置列名
-
第三个参数设置插入列的值
# 在数据的最前列插入一列全为1的数据
data.insert(0,"bias",1) # 最前列插入一列列名bias。值为1的数据
# 在第二列插入一列列名为bias、值递增的数据
data.insert(1,"bias",np.arange(len(data)))
4.4.3、修改数据
方法是将要修改的数据提取出来,重新赋值为新的数据:
# 将股票代码(ts_code)中的字符去掉(000651.SZ->000651)
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
data["ts_code"] = "000651"
# 将当天收盘价(close)低于50的成交额(amount)设定为0
data.loc[data["close"]<50,"amount"] = 0 # loc函数提取数据
4.4.4、删除数据
DataFrame提供的drop()函数可以删除某列或某行的数据,其语法:
DataFrame.drop(labels= None,axis= 0,index= None, columns= None,level= None,inplace= False,errors= "raise",
drop()函数的常用参数:
参数 | 描述 |
---|---|
labels | str或array型,表示要删除的行或列的名称 |
axis | 表示要操作的轴向:0/index表示按行、i/columns表示按列 |
index | 按行删除 |
columns | 按列删除 |
inplace | 表示操作是否对原数据生效。默认为True |
errors | "ignore"或"raise",前者不会报错,只删除存在的行或列。默认为"raise" |
# 按列删除:
data.drop(labels=["ts_code","trade_date"], # 删除的行或列的名称
axis=1, # 操作的轴向,1:按列删除
inplace=True # 对原数据生效
)
# 按列删除简洁版:
data.drop(columns=["ts_code","reade_date"],# 删除列的代替写法
inplace=True) # 对原数据生效
如果将参数inplace设置为False,则data中的数据不会有任何变动,因此此时它会复制一份data对象,所有操作针对的都是该复制对象,
如果只想删除某一列的数据,还可以使用pop()函数和del命令实现:
data.pop("ts_code")
del data{"ts_code"}
五、数据深度处理
“脏”数据:
- 多余的数据:一些诸如“编号”之类的数据。
- 重复的数据
- 带有缺失值的数据:字段值缺失或着为空,会直接导致统计分析出错
- 非数值类型数据:将字符串这种非数值型数据无法参与数学计算。
5.1、数据去重
处理重复数据有三种方式:
- 保留第一个
- 保留最后一个
- 全不保留
Pandas的DataFrame提供了drop_duplicates()函数实现重复数据的检测和处理:
DataFrame.drop_duplicates(subset=None,keep='first',inplace=False,ignore_index=False)
drop_duplicates()函数的常用参数:
参数 | 描述 |
---|---|
subset | 列名,表示在指定列中查找重复数据,默认为None,表示查找全部列 |
keep | 表示对重复数据的处理方式:first:保留第一个(默认)。last:保留最后一个。False:只要有重复数据都不保留 |
inplace | 表示是否在原表上操作。默认为False |
# 如果数据完全一样,则全部删除
data.drop_duplicates(keep=False,inplace=True)
# 如果第1列和第2列数据完全一样,则认定为重复数据,只保留第1条数据。
data.drop_duplicates(subset=["ts_code","trade_date"],keep="first",inplace=True)
5.2、缺失值处理
拿到数据先检测数据中是否存在缺失值,如果存在缺失值,先对缺失值处理。检测缺失值的方法:
DataFrame.isnull() # 检测是否有缺失值
DataFrame.notnull() # 检测是否没有缺失值
5.2.1、删除法*
删除法就是删除缺失值所在的行或列。使用dropna()函数实现:
DataFrame.dropna(axis= 0,how='any',thresh=None, subset= None,inplace= False)
dropna()函数的常用参数:
参数 | 描述 |
---|---|
axis | 0或1.表示轴向,0:按行删除。1:按列删除 |
how | 删除方式:any:只要有缺失值就执行删除操作(默认)、all:当且仅当全部为缺失值才执行删除操作 |
subset | 去重的列/行。默认为None。即所有行/列,array型 |
inplace | 表示是否在原表上操作。默认为False |
5.2.2、替换法*
替换法就是使用某个值来填充缺失值。使用fillna()函数实现:
DataFrame.fillna(value=None,method=None,axis=None,inplace=False,limit=None,downcast=None)
fillna()函数的常用参数:
参数 | 描述 |
---|---|
value | 用于填充的值 |
method | 填充方式:pad/ffill:选择上一个非缺失值。backfill/bfill:选择下一个非缺失值 |
axis | 0或1。0:按行填充。1:按列填充 |
inplace | 表示是否在原表上操作。默认为False |
5.2.3、插值法
插值法就是通过两点估计中间点的值。Pandas使用interpolate()函数实现插值法填充缺失值功能:
DataFrame.interpolate(method: str = "linear",axis= 0,limit= None,inplace= False,limit_direction= None,limit_area= None,downcast= None,**kwargs)
interpolate()函数的常用参数“
参数 | 描述 |
---|---|
method | 使用的部分插值算法:'linear':线性(默认)。'time':在以天或者更高频率的数据中插入给定时间间隔长度的数据。'index'、'values':使用索引对应的值。'pad':使用现有值 |
axis | 0或1 |
limit | 设置最多可以向连续多少个NaN中填充其他数值,该值必须大于0 |
inplace | 表示是否在原表上操作。默认为False |
# 使用固定值-99填充所有缺失值:
import pandas as pd
data = pd.read_csv("../数据集/格力电器_缺失值.csv")
data.fillna(-99,inplace=True)
# 使用50填充开盘价(open)的缺失值,使用55填充最高价(high)的缺失值:
my_dict = {"open":50,"high":55}
data.fillna(my_dict,inplace=True)
# 使用开盘价(open)和最高价(high)的平均值填充当前列的缺失值:
data["open"].fillna(data["open"].mean(),inplace=True)
data["high"].fillna(data["high"].mean(),inplace=True)
# 使用上一个非缺失值填充开盘价(open)中的缺失值
data["open"].fillna(method="ffill",inplace=True)
# 删除存在缺失值的整列数据
data.dropna(axis=1,how="any",inplace=True)
# 使用插值法实现对缺失值的填充
data.interpolate(inplace=True)
5.3、数据转换
通常获取到的数据会包含大量非数值型数据。Pandas通过哑变量(Dummy Variable)实现将字符串转换为数值型的功能。哑变量也叫虚拟变量,引入哑变量的目的是将不能够定量处理的变量量化。根据这些因素的属性类型构造的只取0或1的人工变量通常称为哑变量。
哑变量处理的特点:
- 若一个类别特征有m个取值,则哑变量处理后就变为m个二元特征。
- 特征值互斥,每次只有一个被激活(’1‘表示激活)。
-
数据变成了稀疏矩阵的形式,加快了算法模型的运算速度。
Pandas使用get_dummies()函数实现哑变量处理的功能:
DataFrame.get_dummies(data,prefix=None,prefix_sep= "_",dummy_na= False,columns=None,sparse= False,drop_first= False,dtype= None)
get_dummies()函数的常用参数:
参数 | 描述 |
---|---|
data | 需要进行哑变量处理的数据 |
prefix | 哑变量处理后列名的前缀。默认为None |
prefix_sep | 前缀连接符,默认为"_" |
dummy_na | 是否为NaN添加一列,默认为False |
columns | 需要编码的列名,默认为None,表示对所有列进行编码 |
drop_first | 是否通过从k个分类级别中中删除第一级来获得k-1个分类级别。默认为False |
dtype | 生成的新列的数据类型,默认为Uint8 |
import pandas as pd
data = pd.read_csv("../数据集/格力电器_哑变量.csv")
data=pd.get_dummies(data,prefix=["satatus"],columns=["status"])
print(data)
# ts_code trade_date open high satatus_上涨 satatus_下跌 satatus_平盘
# 0 000651.SZ 20200927 53.40 53.61 True False False
# 1 000651.SZ 20200928 53.40 53.61 False True False
# 2 000651.SZ 20200929 54.35 54.61 False False True
# 3 000651.SZ 20200930 53.16 53.42 True False False
六、统计分析
6.1、汇总统计
常用汇总统计方法:(默认按列进行统计,要按行进行统计们可以设置参数axis=1)
方法 | 描述 | 方法 | 描述 |
---|---|---|---|
count | 计算分组中非NA值的数量 | sum | 计算非NA的和 |
mean | 计算非NA值的算术平均值 | median | 计算非NA的中位数 |
std | 计算非NA的标准差 | var | 计算非NA的方差 |
min | 计算非NA的最小值 | max | 计算非NA的最大值 |
describe | 一次性产生多少个汇总统计 |
# 求数据集中的收盘价、成交量的最大值即最小值:
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
data1 = data[["close","vol"]] # 获取收盘价和成交量
data1.max() # 按列求最大值
data1.min() # 按列求最小值
data1.describe() # 多种统计数据
6.2、groupby:数据分组聚合
若想统计每年的平均收盘价、平均成交额等统计信息,就必须将数据集按年份分组,每组分别计算平均收盘价、每天平均成交额等统计信息。
DataFrame提供的groupby()函数可以按照指定的特征进行分组,再分别计算各组的统计信息:
# 使用groupby()函数实现年平均收盘价和平均日成交额:
year = data["trade_date"].astype(str) # 提取前4位,即年份
year = year.str[0:4] # 添加到数据集中
data["year"] = year # 按年份分组
group = data[["year","close","amount"]].groupby(by="year") # 求得平均值
print(group.mean())
groupby()函数的语法:
DataFrame.groupby(by=None, axis=0,level=None,as_index=True,sort=True,group_keys=True,squeeze=<object object>,observed=False,dropna=True)
groupby()函数的常用参数:
参数 | 描述 |
---|---|
by | 用于确定进行分组的依据,必选 |
axis | 表示操作的轴向,默认对列进行操作,即取值为0 |
sort | 表示是否对分组标签进行排序。默认为True |
group_keys | 表示是否显示分组标签的名称。默认为True |
squeeze | 表示是否在允许的情况下对返回数据进行降维。默认为False |
该方法返回一个包含分组信息的DateFrameGroupBy类的对象,调用该对象中的各种方法,就可以得到分组统计的值。
DateFrameGroupBy类的统计函数:
函数 | 描述 | 函数 | 描述 |
---|---|---|---|
count | 计算分组中非NA值的数量 | sum | 计算非NA值的和 |
mean | 计算非NA值的平均值 | median | 计算非NA值的中位数 |
std | 计算非NA值的标准差 | var | 计算非NA值的方差 |
min | 计算非NA值的最小值 | max | 计算非NA值的最大值 |
prod | 计算非NA值的积 | first、last | 获取第一个和最后一个非NA值 |
6.3、agg:数据聚合
在统计数据时,不同特征值的统计策略也不相同。
事实上,针对不同特征值,可以使用Pandas或者NumPy的数据统计函数分别求得:
# 1、求得数据集中收盘价、成交额的平均值和最大值
c_a_mean = data[["close","amount"]].mean() # 收盘价和成交额的平均值
c_a_max = data[["close","amount"]].max() # 收盘价和成交额的最大值
# 2、求得数据集中收盘价的平均值,成交额的最大值
c_mean = data["close"].mean() # 收盘价的平均值
c_max = data["amount"].max() # 成交额的最大值
# 3、求得数据集中收盘价的平均值,成交额的最大值和最小值
c_a_mean = data["close"].mean() # 收盘价的平均值
o_max = data["open"].max() # 成交额的最大值
o_min = data["open"].min() # 成交额的最小值
上述方法每次只能统计一种数据。使用DataFrame的 agg()函数可以设置不同特征的不同统计需求,一次求出:
agg()函数的常用参数:
参数 | 描述 |
---|---|
func | 应用于每行或每列的函数,可以是list、dict或函数 |
axis | 表示操作的轴向,默认对列进行操作,取值为0 |
# 例子:#求得数据集中收盘价、成交额的平均值和最大值
result1 = data[["close","amount"]].agg([np.mean,np.max])
# 求得数据集中收盘价的平均值,成交额的最大值
result2 = data.agg({"close":np.mean,"amount":np.max})
# 求得数据集中收盘价的平均值,成交额的最大值和最小值
result3 = data.agg({"close":np.mean,"amount":[np.max,np.min]})
另外,有些数据是要分组后再进行统计的:
# 将数值型日期转换为字符串
year = data["trade_date"].astype(str)
# 提取前4位,即年份
year = year.str[:4]
# 添加到数据集
data["year"] = year
# 按年份分组
group = data[["year","close","amount"]].groupby(by="year")
# 求各年度收盘价与成交额的平均值和最大值
r1 = group.agg([np.mean,np.max])
print(r1)
# 其结果为
# mean amax mean amax
# year
# 2017 36.024486 47.80 2.513256e+06 5.808663e+06
# 2018 44.438230 57.40 2.607493e+06 1.712054e+07
# 2019 53.539958 65.58 2.461973e+06 1.819573e+07
# 2020 57.894426 69.88 3.209122e+06 8.947222e+06
# 如果想获取成交额(amount)的平均值和最大值:
r1["amount"]
# 获取成交额的年平均值:
r1["amount"]["mean"] # 成交额的年平均值
r1["amount"]["mean"]["2018"] # 2018年成交额的平均值。注意2018要加双引号
# 求各年度收盘价的平均值,成交额的最大值
r2 = group.agg({"close":np.mean,"amount":np.max})
# 求各年度收盘价的平均值,成交额的最大值和最小值
r3 = group.agg({"close":np.mean,"amount":[np.max,np.min]})
6.4、apply:数据聚合
agg()方法的优势在于它可以根据不同特征制定不同规则的计算策略。当所有行或列使用的统计规则都一样时,就可以使用apply()函数实现。apply()方法传入的函数只能够作用整个DataFrame或者Series,agg()函数可以对不同字段设置不同函数来获取不同结果。
DataFrame.apply(func.axis=0,raw=False,result_type=None,args=(),**kwds)
apply()函数的常用参数:
参数 | 描述 |
---|---|
func | 应用于每行或每列的函数,可以是list、dict或函数 |
axis | 表示操作的轴向,默认对列进行操作,即取值为0 |
raw | 表示是否将行或列作为Series或ndarray传递给函数,默认为False,即将每个行或列作为序列传递给函数 |
result_type | 表示apply()函数的返回类型(仅在axis=1时起效)。'expand':结果作为新列追加。'reduce':返回一个Series。'broadcast':将结果广播到DataFrame的原始形状,保留原始索引和列。None:默认值。当result_type设置为None时,apply()函数的返回类型与func参数的返回类型一致。 |
import pandas as pd
data = pd.read_csv("../数据集/格力电器.csv")
# 求得数据集中收盘价、成交额的平均值
c_a_mean = data[["close","amount"]].apply(np.mean)
# 求得数据集中收盘价、成交额的平均值和最大值
c_a_mean = data[["close","amount"]].apply([np.mean,np.max])
# 求得数据集中收盘价、成交额的平均值
# 函数作为参数求平均值
def get_mean(data):
return np.mean(data)
c_a_mean = data[["close","amount"]].apply(get_mean)
# 求各年度收盘价与成交额的平均值
year = data["trade_date"].astype(str) # 提取前4位,即年份
year = year.str[0:4] # 添加到数据集中
data["year"] = year # 按年份分组
group = data[["year","close","amount"]].groupby(by="year") # 求各年度收盘价与成交额的最大值
r1 = group.apply(np.mean)
DataFrameGroupBy对象也集成了apply(),使用方法与agg()相近,区别在于使用agg()能够实现对不同字段应用不同的函数,而
6.5、transform:数据转换
transform()函数可以按行或者列对整个DataFrame的所有元素进行操作,transform()函数的参数:
参数 | 描述 |
---|---|
func | 应用于每行或每列的函数,可以是list、dict或函数 |
axis | 表示操作的轴向。默认对列操作 |
# 将收盘价和成交额变为原来的两倍
result = data[["close","amount"]].transform(lambda x:x*2)
# 收盘价和成交额减去各自的平均值,得到与平均值的差额
result = data[["close","amount"]].transform(lambda x:x-x,mean())