本节目标:能够正确读入和输出以及处理时间类型的数据。
本节技术点:pandas,datetime
本节阅读需要(15)min。
本节实操需要(15)min。
时间往往是表型数据中常见的有意义和价值的一类数据。
对于时间的计算以及格式化输入输出是十分重要的。毕竟上班时间和工资关系可密切了
谁也不希望变少是不是?
我个人认为时间数据是介于数值型数据和字符串数据之间的。
既要满足时间的运算,有需要满足格式相关的处理!!
当我们列名有如下的标志时,pandas会自动的转换这一列的数据为时间数据
it ends with '_at'
it ends with '_time'
it begins with 'timestamp'
it is 'modified'
it is 'date'
怎么说呢,与其期望读入的数据是这么标准的列名,不如输出或者处理的时候把列名替换一下。
如果没法识别,如下
# 格式标准,直接跟换列名
df.rename(columns={'timestamp':'time_list'}, inplace = True)
# 不标准就需要清洗之后再转化,自动识别
s1 = pd.to_datetime(pd.Series(["Jul 31, 2009", "2010-01-10", None]))
清洗就像字符串清洗那样。常用到replace。
我们可以像等差数列那样产生时间数据。然后一般都是作为index或者特殊的列存在的。
dti = pd.date_range("2018-01-01", periods=5, freq="H") # 按小时
dti1 = pd.date_range("2018-01-01", periods=5, freq="D") # 按天
dti2 = pd.date_range("2018-01-01", periods=5, freq="W") # 按周
ts = pd.Series(range(len(idx)), index=dti)
ts.resample("2H").mean() # return type :Series
dtype=datetime64[ns],这就是pandas内置的时间类型和datetime还是比较像的。
df.rename(columns={‘A’:‘a’,‘B’:‘b’,‘C’:‘c’}) #columns可以接受一个字典, 键为 旧索引 ,值为 新索引
periods相当于个数。
Pandas中的resample,重新采样,是对原样本重新处理的一个方法,是一个对常规时间序列数据重新采样和频率转换的便捷的方法。相当于简单的groupby。是一个根据时间快速分组统计的方法。
示例:ts.resample(“5Min”, closed=“right”).mean()
格式的统一
pd.Timestamp(datetime.datetime(2012, 5, 1)) #统一为时间戳
pd.to_datetime("12-11-2010 00:00", format="%d-%m-%Y %H:%M") # 统一为固定的格式
# epoch时间转换为DatetimeIndex,精确到秒
pd.to_datetime(
[1349720105, 1349806505, 1349892905, 1349979305, 1350065705], unit="s"
)
时间的forma请参考官方文档。一般照抄"%d-%m-%Y %H:%M"就行,精准到分钟这个。
format的官方文档
freq也可以参考官方文档,字母的意思是固定的
我们统计时间数据的时候一般可以将时间作为index,从数据统计的角度就是作为主键去生成新的df
互联网上很多是英国的格林尼治时间,我们最常用的是北京时间。
UTC时间在不影响结果的情况下看做GMT时间还是可行的。
dti = pd.date_range("2018-01-01", periods=3, freq="H")
dti = dti.tz_localize("UTC")
dti
dti = dti.tz_convert("Asia/Shanghai") # 北京时间就是这个。。。
dti
根据datetime的学习,我们知道必然要有一个时间差对象,才能计算。
pd.Timedelta("-1 days 2 min 3us") # 用不到,好蠢的格式
pd.Timedelta(days=1, seconds=1) # 用这个
pd.Timedelta(datetime.timedelta(days=1, seconds=1))
剩下的四则运算都是可行的。过程并不复杂。
月份会自动计算不要担心天数不同的问题
epoch计算起来更方便一些尤其是底层。timestamps 更直观
stamps = pd.date_range("2012-10-08 18:15:05", periods=4, freq="D")
(stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s")
请记住这个公式不要问为什么!!!
start = datetime.datetime(2011, 1, 1)
end = datetime.datetime(2012, 1, 1)
index = pd.date_range(start, end)
freq='D’这个是自动识别的。
可见pandas对于datetime的支持是很棒的。
df = pd.DataFrame(
{"year": [2015, 2016], "month": [2, 3], "day": [4, 5], "hour": [2, 3]}
)
idx=pd.to_datetime(df[["year", "month", "day"]]) # 列名必须如此,才能自动识别
提前改成这些
required: year, month, day
optional: hour, minute, second, millisecond, microsecond, nanosecond
拆分如下:
idx.isocalendar()
为什么强调可以把时间作为行号构建一个新的df来数据处理,就是因为可以有很多的时间相关的统计方式。
rng = pd.date_range(start, end, freq="BM")
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts["1/31/2011"] # 时间作为索引
ts["10/31/2011":"12/31/2011"]
ts["2011"] # 相当于在2011年范围内的都选中
从某种程度上说自动支持了时间的聚类。
2013-2-28这种格式也是支持的。pandas风格的都是支持的。
这里以json格式为例,其他的简单点,基本类似。
date_format输出函数基本都有这个字段,只要选择具体的时间类型就可以了
date_format : string, type of date conversion, ‘epoch’ for timestamp, ‘iso’ for ISO8601.
dfd = pd.DataFrame(np.random.randn(5, 2), columns=list("AB"))
dfd["date"] = pd.Timestamp("20130101")
dfd = dfd.sort_index(axis=1, ascending=False)
json = dfd.to_json(date_format="iso")
'{"date":{"0":"2013-01-01T00:00:00.000Z","1":"2013-01-01T00:00:00.000Z","2":"2013-01-01T00:00:00.000Z","3":"2013-01-01T00:00:00.000Z","4":"2013-01-01T00:00:00.000Z"},"B":{"0":0.403309524,"1":0.3016244523,"2":-1.3698493577,"3":1.4626960492,"4":-0.8265909164},"A":{"0":0.1764443426,"1":-0.1549507744,"2":-2.1798606054,"3":-0.9542078401,"4":-1.7431609117}}'
这就是iso时间的格式
json = dfd.to_json(date_format="epoch", date_unit="s")
当然也有epoch时间可以选择。
date_unit : The time unit to encode to, governs timestamp and ISO8601 precision. One of ‘s’, ‘ms’, ‘us’ or ‘ns’ for seconds, milliseconds, microseconds and nanoseconds respectively. Default ‘ms’.
总结如下:
子在川上曰逝者如斯夫不舍昼夜
论语