时间信息可以按照不同的单位进行不同形式的表示,由此就会产生各种不同层次的时间信息的转换问题。比如以月份为单位时,我们是应该从月初开始算还是从月末开始算?再如1个学年通常会从9月份到第二年的6月,等等。
我们还是以带有时间索引的学生数据为例:
# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime
data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
'gender': [True, False, True, False, True, False, True],
'age': [16, 20, 18, 18, 17, 18, 16],
'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
}
dates = [datetime(2019, 11, 29), datetime(2019, 12, 5), datetime(2019, 12, 17),
datetime(2019, 12, 30), datetime(2020, 1, 1), datetime(2020, 1, 3),
datetime(2020, 1, 4)]
frame = pd.DataFrame(data, index=dates)
print(frame)
为了后续统计的方便,我们现在想将以天数为单位的时间信息转换为以月份为单位的时间信息,可以考虑将时间信息直接转换为时间段信息,因为月份相对于天而言,是一个具有多个天的时间段。
print(frame.to_period(freq=’M’))
这里使用了DataFrame的to_period方法,就是将时间点转换为时间段,其中的freq参数指定时间段的类型。
这种转换是非常灵活的,可以根据freq参数来做出有特点的调整。比如我们认为学校的学年是从9月初开始计算,那么上述时间都应该是2020学年。此时可以定义新的freq参数值:
frame = frame.to_period(freq=’A-AUG’)
参数A表示按照年来分,AUG表示以8月作为最后一个月份,发现全部都是2020学年的年度单位了。
比较复杂的是如何将时间段转换为粒度更小的时间单位,比如我们做个调整,将所有索引时间全部保存为月份单位。
frame = frame.to_period(freq=’M’)
print(frame)
那么接下来如何将其转换为日期呢?我们可以再次调用DataFrame的to_timestamp方法,就是这个反操作:
print(frame.to_timestamp())
从结果来看,我们发现所有的日期都被重置为每月的1号。这没有办法,只能由系统给出一个默认值,此时默认为月初。
那么能不能略微修改下呢?比如重置到月底:
print(frame.to_timestamp(how=’end’))
可以使用how参数指定end即可表达这个意思。
我们也可以使用另外一个更为方便的方法asfreq,它完全通过freq参数既可以粒度变大,也可以粒度变小:
print(frame.asfreq(freq=’D’, how=’end’))
效果很类似,但是转换后的时间单位可以自由指定,D表示天,T表示小时等等,大家可以自行查阅。
比如我们还可以进行进行粒度变大:
print(frame.asfreq(freq=’A’, how=’end’))
当然,此时的how其实意义并不大,从低粒度到高粒度无需指定前后。因此省略的效果也是一样的。大家能够想的出为什么吧?
我们再看看一个例子。上述两种转换为小粒度的方法都依赖于当初生成时间段的单位,比如在前面设置9月初开始的学年练习中,如果对于得到的学年也进行小粒度时间转换,那么就会使用当时设定的时间点来表达:
frame=frame.to_period(freq=’A-AUG’)
print(frame.to_timestamp(how=’end’))
比如这里既然是8月为年的最后一月,那么转换为就是显示2020年8月底。
同样,如果设置为how为start,那么转换就是2019年9月初。
frame=frame.to_period(freq=’A-AUG’)
print(frame.to_timestamp(how=’start’))