Pandas统计分析下(日期数据处理、时间序列、降采样、升采样、Excel多表合并、股票行情数据分析、解决中文乱码)

Source
  • 本篇博文来自《Python数据分析从入门到精通》_明日科技编著

4.8 日期数据处理

4.8.1 DataFrame的日期数据转换

  • 日常工作中,有一个非常麻烦的事情就是日期的格式可以有很多中表达,我们看到同样的2020年2月14日,可以有很多种格式,如图4.57所示。那么,我们需要先将这些格式统一后才能进行后续的工作。Pandas提供了to_datetime()方法可以帮助我们解决这一问题。
  • to_datetime()方法可以用来批量处理日期数据转换,对于处理大数据非常实用和方便,它可以将日期数据转换成你需要的各种格式。例如,将2/14/20和14-2-2020转换成日期格式2020-02-14。to_datetime()方法的语法如下:
pandas.to_datetime(arg, errors =’ignore’, dayfirst = False, yearfirst = False, utc = None, box = True, format = None,exact= True, unit = None, infer_datetime_format = False, origin =’unix ‘, cache = False)
  • arg:字符串、日期时间、字符串数组
  • errors:值为ignore、raise或coerce,具体说明如下,默认值为ignore,即忽略错误。
    – ignore:无效的解析将返回原值
    – raise:无效的解析将引发异常
    – coerce:无效的解析将被设置为NaT,即无法转换为日期的数据将转换为NaT
  • dayfirst:第一个为天,布尔型,默认值为False。例如02/09/2020,如果值为True,则解析日期的第一个为天,即2020-09-02;如果值为False,则解析日期与原日期一致,即2020-02-09
  • yearfirst:第一个为年,布尔型,默认值为False。例如14-Feb-20,如果值为True,则解析日期的第一个为年,即2014-02-20;如果值为False,则解析日期与原日期一致,即2020-02-14
  • utc:布尔型,默认值为None。返回utc即协调世界时间
  • box:布尔值,默认值为True,如果值为True,则返回DatatimeIndex;如果值为False,则返回ndarray。
  • format:格式化显示时间的格式。字符串,默认值为None
  • exact:布尔值,默认值为True,如果为True,则要求格式完全匹配;如果为False,则允许格式与目标字符串中的任何位置匹配
  • unit:默认值为 None,参数的单位(D、s、ms、us、ns)表示时间的单位
  • infer_datetime_format:默认值为False。如果没有格式,则尝试根据第一个日期时间字符串推断格式。
  • origin:默认值为unix。定义参考日期。数值将被解析为单位数
  • cache:默认值为False。如果值为True,则使用唯一、转换日期的缓存应用日期的时间转换。在解析重复日期字符串,特别是带有时区偏移的字符串时,可能会产生明显的加速。只有在至少有50个值时才使用缓存。越界值的存在将使缓存不可用,并可能减慢解析速度。
  • 返回值:日期时间

将各种日期字符串转换为指定的日期格式

  • 将2020年2月14日的各种格式转换为日期格式,程序代码如下:
import pandas as pd

df=pd.DataFrame({
    
      '原日期':['14-Feb-20', '02/14/2020', '2020.02.14', '2020/02/14','20200214']})
df['转换后的日期']=pd.to_datetime(df['原日期'])
print(df)

在这里插入图片描述

  • 还可以实现从DataFrame对象中的多列,如年、月、日各列组合成一列日期。键值时常用的日期缩略语。组合要求:
  • 必选:year、month、day
  • 可选:hour、minute、second、milisecond(毫秒)、microsecond(微秒)、nanosecond(纳秒)。

将一组数据转换为日期数据

import pandas as pd

df = pd.DataFrame({
    
      'year': [2018, 2019,2020],
                   'month': [1, 3,2],
                   'day': [4, 5,14],
                   'hour':[13,8,2],
                   'minute':[23,12,14],
                   'second':[2,4,0]})
df['组合后的日期']=pd.to_datetime(df)
print(df)

在这里插入图片描述

4.8.2 dt对象的使用

  • dt对象是Series对象中用于获取日期属性的一个访问器对象,通过它可以获取日期中的年、月、日、星期数、季节等,还可以判断日期是否处在年底。语法如下:
Series.dt()
  • 返回值:返回与原始系列相同的索引系列。如果Series不包含日期值,则引发错误。
  • dt对象提供了year、month、day、dayofweek、dayofyear、is_leap_year、quarter、weekday_name等属性和方法。例如,year可以获取"年"、quarter可以直接得到每个日期分别是第几个季度,weekday_name可以直接得到每个日期对应的是周几。

获取日期中的年、月、日、星期数等

import pandas as pd

df=pd.DataFrame({
    
      '原日期':['2019.1.05', '2019.2.15', '2019.3.25','2019.6.25','2019.9.15','2019.12.31']})
df['日期']=pd.to_datetime(df['原日期'])
df

在这里插入图片描述

df['年'],df['月'],df['日']=df['日期'].dt.year,df['日期'].dt.month,df['日期'].dt.day
df['星期几']=df['日期'].dt.day_name()
df['季度']=df['日期'].dt.quarter
df['是否年底']=df['日期'].dt.is_year_end
df

在这里插入图片描述

4.8.3 获取日期区间的数据

  • 获取日期区间的数据的方法是直接在DataFrame对象中输入日期或日期区间,但前提必须设置日期为索引,举例如下:

  • 获取2018年的数据

df1['2018']
  • 获取2017-2018年的数据
df1['2017':'2018']
  • 获取某月(2018年7月)的数据
df1['2018-07']
  • 获取具体某天(2018年5月6日)的数据
df1['2018-05-06':'2018-05-06']

获取指定日期区间的订单数据

  • 获取2018年5月11日至6月10日的订单,结果如下图所示
import pandas as pd

df = pd.read_excel('mingribooks.xls')
df1=df[['订单付款时间','买家会员名','联系手机','买家实际支付金额']]
df1=df1.sort_values(by=['订单付款时间'])
df1

在这里插入图片描述

df1 = df1.set_index('订单付款时间') # 将日期设置为索引
df1

在这里插入图片描述

#获取某个区间数据
df2=df1['2018-05-11':'2018-06-10']
df2

在这里插入图片描述

4.8.4 按不同时期统计并显示数据

1.按时期统计数据

  • 按时期统计数据主要通过DataFrame对象的resample()方法结合数据计算函数实现。resample()方法主要应用于时间序列频率转换和重采样,它可以从日期中获取年、月、日、星期、季节等,结合数据计算函数就可以实现年、月、日、星期或季度等不同时期统计数据。举例如下所示。索引必须为日期型。
  • (1)按年统计数据,代码如下:
df1=df1.resample('AS').sum()
  • (2)按季度统计数据,代码如下:
df2.resample('Q').sum()
  • (3)按月度统计数据,代码如下:
df1.resample('M').sum()
  • (4) 按星期统计数据,代码如下:
df1.resample('W').sum()
  • (5)按天统计数据,代码如下:
df1.resample('D').sum()

2.按时期显示数据

  • DataFrame对象的to_period()方法可以将时间戳转换为时期,从而实现按日期显示数据,前提是日期必须设置为索引。语法如下:
DataFrame.to_period(freq=None,axis=0,copy=True)
  • freq:字符串,周期索引的频率,默认值为None
  • axis:行列索引,axis=0表示行索引,axis=1表示列索引。默认值为0,即表示行索引。
  • copy:是否复制数据,默认值为True,如果值为False,则不复制数据。
  • 返回值:带周期索引的时间序列

从日期中获取不同的时期

  • 从日期中获取不同的时期,主要代码如下:
import pandas as pd

aa =r'TB2018.xls'
df = pd.DataFrame(pd.read_excel(aa))
df1=df[['订单付款时间','买家会员名','联系手机','买家实际支付金额']]
df1 = df1.set_index('订单付款时间') # 将date设置为index
df1

在这里插入图片描述

按月统计数据
#按月统计数据
#“MS”是每个月第一天为开始日期,“M”是每个月最后一天
df1.resample('M').sum()

在这里插入图片描述

按季统计数据
#按季统计数据
#“QS”是每个季度第一天为开始日期,“Q”是每个季度最后一天
df1.resample('QS').sum()

在这里插入图片描述

按年统计数据
#按年统计数据
#“AS”是每年第一天为开始日期,“A”是每年最后一天
df1.resample('AS').sum()

在这里插入图片描述

按年统计数据并显示数据
#按年统计数据并显示数据
#“AS”是每年第一天为开始日期,“A”是每年最后一天
df1.resample('AS').sum().to_period('A')

在这里插入图片描述

按季度统计并显示数据
# 按季度统计并显示数据
df1.resample('Q').sum().to_period('Q')

在这里插入图片描述

按月统计数据并显示数据
#按月统计数据
#“MS”是每个月第一天为开始日期,“M”是每个月最后一天
df1.resample('M').sum().to_period('M')

在这里插入图片描述

按星期统计并显示数据
df1.resample('w').sum().to_period('W').head()

在这里插入图片描述

4.9 时间序列

4.9.1 重采样(Resample()方法)

  • 通过前面的学习,我们学会了如何生成不同频率的时间索引,按小时、按天、按周、按月等,如果想对数据做不同频率的转换,该怎么办?在Pandas中对时间序列的频率的调整称为重新采样,即将时间序列从一个频率转换到另一个频率的处理过程。例如,每天一个频率转换为每5天一个频率,如图4.67所示。
    在这里插入图片描述
  • 重采样主要使用resample()方法,该方法用于对常规时间序列重新采样和频率转换,包括降采样和升采样两种。首先了解下resample()方法,语法如下:
DataFrame.resample(rule,how=None,axis=0,fill_method=None,closed=None,label=None,convention='start',kind=None,loffset=None,limit=None,base=0,level=None)
  • rule:字符串,偏移量表示目标字符串或对象转换
  • how:用于产生聚合值的函数名或数组函数。例如mean、ohlc和np.max等,默认值为mean,其他常用的值为first、last、median、max和min。
  • axis:整型,表示行列,axis=0表示列,axis=1表示行。默认值为0,即表示列
  • fill_method:升采样时所使用的填充方法,fill()方法(用前值填充)或bfill()方法(用后值填充),默认值为None
  • closed:降采样时,时间区间的开和闭,与数学里区间的概念一样,其值为right或left,right表示左开右闭,left表示左闭右开,默认值为right左开右闭
  • label:降采样时,如何设置聚合值的标签。例如,10:30-10:35会被标记成10:30还是10:35,默认值为None
  • convention:当重采样时,将低频率转换到高频率所采用的约定,其值为start或end,默认值为start
  • kind:聚合到时期(period)或时间戳(timestamp),默认聚合到时间序列的索引类型,默认值为None。
  • loffset:聚合标签的时间校正值,默认值为None。例如,-1s或second(-1)用于将聚合标签调早1秒
  • limit:向前或向后填充,允许填充的最大时期数,默认值为None
  • base:整型,默认值为0。对于均匀细分1天的频率,聚合间隔的"原点"。例如,对于5min频率,base的范围可以是0~4
  • on:字符串,可选参数,默认值为None。对DataFrame对象使用列代替索引进行重新采样。列必须与日期时间类似
  • level:字符串,可选参数,默认值为None。用于多索引,重新采样的级别或级别编号,级别必须与日期时间类似
  • 返回值:重新采样对象

一分钟的时间序列转化为3分钟的时间序列

  • 首先创建一个包含9个一分钟的时间序列,然后使用resample()方法转换为3分钟的时间序列,并对索引进行求和计算,如图4.68所示。
    在这里插入图片描述
  • 程序代码如下:
import pandas as pd
index = pd.date_range('02/02/2020', periods=9, freq='T')
series = pd.Series(range(9), index=index)
series

在这里插入图片描述

series.resample('3T').sum()

在这里插入图片描述

4.9.2 降采样处理

  • 降采样是周期由高频率转向低频率。例如,将5min股票交易数据转换为日交易,按天统计的销售数据转换按周统计。
  • 数据降采样会涉及数据的聚合。例如,天数据变成周数据,那么就要对1周7天的数据进行聚合,聚合的方式主要包括求和、求均值等。

按周统计销售数据

import pandas as pd
df=pd.read_excel('time.xls')
df1 = df.set_index('订单付款时间') #设置“订单付款时间”为索引
df1

在这里插入图片描述

df1.resample('W').sum().head()

在这里插入图片描述

#%%
df1.resample('W',closed='left').sum()

在这里插入图片描述

4.9.3 升采样处理

  • 升采样是周期由低频率转向高频率。将数据从低频率转换到高频率时,就不需要聚合了,将其重采样到日频率,默认会引入缺失值。
  • 例如,原来是按周统计的数据,现在变成按天统计。升采样会涉及数据的填充,根据填充的方法不同,填充的数据也不同。下面介绍三种填充方法。
    – 不填充。空值用NaN代替,使用asfreq()方法。
    – 用前值填充。用前面的值填充空值,使用ffill()方法或者pad()方法。为了方便记忆,ffill()方法可以使用它的第一个字母"f"代替,代表forward,向前的意思。
    – 用后值填充,使用bfill()方法,可以使用字母"b"代替,代表back,向后的意思。

每6小时统计一次数据

  • 下面创建一个时间序列,起始日期是2020-02-02,一共两天,每天对应的数值分别是1和2,通过升采样处理为每6小时统计一次数据,空值以不同的方式填充,程序代码如下:
import pandas as pd
import numpy as np
rng = pd.date_range('20200202', periods=2)
s1 = pd.Series(np.arange(1,3), index=rng)
s1

在这里插入图片描述

s1_6h_asfreq = s1.resample('6H').asfreq()
print(s1_6h_asfreq)
s1_6h_pad = s1.resample('6H').pad()
print(s1_6h_pad)
s1_6h_ffill = s1.resample('6H').ffill()
print(s1_6h_ffill)
s1_6h_bfill = s1.resample('6H').bfill()
print(s1_6h_bfill)

在这里插入图片描述

4.9.4 时间序列数据汇总(ohlc()函数)

  • 在金融领域,经常会看到开盘(open)、收盘(close)、最高价(high)和最低价(low)数据,而在Pandas中经过重新采样的数据也可以实现这样的结果,通过调用ohlc()函数得到数据汇总结果,即开始值(open)、结束值(close)、最高值(high)和最低值(low)。ohlc()函数的语法如下:
resample.ohlc()
  • ohlc()函数返回DataFrame对象,每组数据的open(开)、high(高)、low(低)和close(关)值。

统计数据的open、high、low和close值。

  • 下面每一组5分钟的时间序列,通过ohlc()函数获取该时间序列中每组时间的开始值、最高值、最低值和结束值,程序代码如下:
import pandas as pd
import numpy as np
rng = pd.date_range('2/2/2020',periods=12,freq='T')
s1 = pd.Series(np.arange(12),index=rng)
s1

在这里插入图片描述

s1.resample('5min').ohlc()

在这里插入图片描述

4.9.5 移动窗口数据计算(rolling()函数)

  • 通过重采样可以得到想要的任何低频率的数据,但是这些数据也是一共时点的数据,那么就存在这样一个问题:时点的数据波动较大,某一点的数据就不能很好地表现它本身的特性,于是就有了"移动窗口"的概念,简单地说,为了提升数据的可靠性,将某个点的取值扩大到这个点的一段区间,用区间来进行判断,这个区间就是窗口。
  • 下面举例说明,图4.74显示了移动窗口数据示意图,其中时间序列代表1号到15号每天的销量数据,接下来以3天为一个窗口,将该窗口从左至右依次移动,统计出3天的平均值作为这个点的值,如3号的销量是1号、2号和3号的平均值。
    在这里插入图片描述
  • 通过上述示意图相信您已经理解了移动窗口,在Pandas中可以通过rolling()函数实现移动窗口数据的计算,语法如下:
DataFrame.rolling(window,min_periods=None,center=False,win_type=None,on=None,axis=0,closed=None)
  • window:时间窗口的大小,有两种形式,即int或offset。如果使用int,则数值表示计算统计量的观测值的数量,即向前几个数据;如果使用offset,则表示时间窗口的大小。
  • min_periods:每个窗口最少包含的观测值数量,小于这个值的窗口结果为NA。值可以是int,默认值为None。offset情况下,默认值为1
  • center:把窗口的标签设置为剧中。布尔型,默认值为False,居右
  • win_type:窗口的类型。截取窗的各种函数。字符串类型,默认值为None
  • on:可选参数。对于DataFrame对象,是指定要计算移动窗口的列,值为列名
  • axis:整型,axis=0表示列,axis=1表示行。默认值为0,即对列进行计算
  • closed:定义区间的开闭,支持int类型的窗口。对于offset类型默认是左开右闭。可以根据情况指定left。
  • 返回值:为特定操作而生成的窗口或移动窗口子类

创建淘宝每日销售数据

  • 首先创建一组淘宝每日销售数据,程序代码如下:
import pandas as pd
index=pd.date_range('20200201','20200215')
data=[3,6,7,4,2,1,3,8,9,10,12,15,13,22,14]
s1_data=pd.Series(data,index=index)
s1_data

在这里插入图片描述

使用rolling()函数计算3天的均值

  • 下面使用rolling()函数计算2020-02-01至2020-02-15中3天的均值,窗口个数为3,代码如下:
s1_data.rolling(3).mean()

在这里插入图片描述

  • 运行程序,看下rolling()函数是如何计算的?当窗口开始移动时,第一个时间点2020-02-01和第二个时间点2020-02-02的数值为空,这是因为窗口个数为3,它们前面有空数据,所以均值为空;而到第三个时间点2020-02-03时,它前面的数据是2020-02-01至2020-02-03,所以3天的均值是5033333;以此类推。

用当天的数据代表窗口数据

  • 在计算第一个时间点2020-02-01的窗口数据时,虽然数据不够窗口长度3,但是至少有当天的数据,那么能否用当天的数据代表窗口数据呢?答案是肯定的,通过设置min_periods参数即可,它表示窗口最少包含的观测值,小于这个值的窗口长度显示为空,等于或大于时都有值,主要代码如下:
s1_data.rolling(3,min_periods=1).mean()

在这里插入图片描述

import numpy as np
import pandas as pd
index=pd.date_range('20200201','20200215')
data=[3,6,7,4,2,1,3,8,9,10,12,15,13,22,14]

np.random.seed(2)
data=np.random.randint(20,size=len(index))
ser_data=pd.Series(data,index=index)
plt.figure(figsize=(15, 5))
ser_data.plot(style='r--')
ser_data.rolling(3).mean().plot(style='b')

在这里插入图片描述

4.10 综合应用

案例1:Excel多表合并

  • 在日常工作中,几乎我们每天都有大量的数据需要处理,桌面上总是布满密密麻麻的Excel表,这样看上去非常凌乱,其实我们完全可以其中的类别相同的Excel表合并到一起,这样不但不会丢失数据,而且还可以非常有效地分析数据。下面使用concat()方法指定文件夹内的所有Excel表合并,程序代码如下:
import pandas as pd
import glob
filearray=[]
filelocation=glob.glob(r'./aa/*.xlsx')  #指定目录下的所有Excel文件
#遍历指定目录
for filename in filelocation:
    filearray.append(filename)
    print(filename)
res=pd.read_excel(filearray[0])   #读取第一个Excel文件
#顺序读取Excel文件并进行合并
for i in range(1,len(filearray)):
    A=pd.read_excel(filearray[i])
    res=pd.concat([res,A],ignore_index=True,sort=False) #忽略索引,读取文件详见pandas统计分析中
print(res.index)
#写入Excel文件,并保存
writer = pd.ExcelWriter('all.xlsx')
res.to_excel(writer,'sheet1')
writer.save()

4.10.2 案列2:股票行情数据分析

  • 股票数据包括开盘价、收盘价、最低价、成交量等多个指标。其中,收盘价是当日行情的标准,也是下一个交易日开盘价的依据,可以预测未来证券市场行情,因此当投资者对行情分析时,一般采用收盘价作为计价依据。
  • 下面使用rolling()函数计算某股票20天、50天和200天的收盘价均值并生成走势图(也称K线图),程序代码如下:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

aa =r'000001.xlsx'
df = pd.DataFrame(pd.read_excel(aa))
df['date'] = pd.to_datetime(df['date']) #将数据类型转换为日期类型
df

在这里插入图片描述

df = df.set_index('date') # 将date设置为index
df=df[['close']] #提取收盘价数据
df

在这里插入图片描述

df['20天'] = np.round(df['close'].rolling(window = 20, center = False).mean(), 2)
df['50天'] = np.round(df['close'].rolling(window = 50, center = False).mean(), 2)
df['200天'] = np.round(df['close'].rolling(window = 200, center = False).mean(), 2)
plt.rcParams['font.sans-serif']=['SimHei'] #解决中文乱码
df.plot(secondary_y = ["收盘价", "20","50","200"], grid = True)
plt.legend(('收盘价','20天', '50天', '200天'), loc='upper right')

在这里插入图片描述