--- jupytext: text_representation: format_name: myst kernelspec: display_name: Python 3 name: python3 --- ```{code-cell} ipython3 :tags: [remove_input] import numpy as np import pandas as pd import matplotlib.pyplot as plt np.random.seed(0) plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False np.set_printoptions(suppress = True) ``` # 第十章 ## 零、练一练 ```{admonition} 练一练 请尝试在pd.Timestamp()中传入其他格式的时刻字符串,如:“20210901”、“2021-09-01”等,体会该构造方法的灵活性。 ``` ```{code-cell} ipython3 pd.Timestamp("20210901") ``` ```{code-cell} ipython3 pd.Timestamp("2021-09-01") ``` ```{admonition} 练一练 请分别将如下格式的时间戳列表转为正确的时序Series格式。 - ["0901 2021", "0902 2021", "0903 2021"] - ["21-9-1 8-35-50", "21-9-2 9-25-45"] ``` - 1 ```{code-cell} ipython3 bad_format = ["0901 2021", "0902 2021", "0903 2021"] temp = pd.to_datetime(bad_format, format='%m%d %Y') temp ``` ```{code-cell} ipython3 bad_format = ["2021-9-1 8-35-50", "2021-9-2 9-25-45"] temp = pd.to_datetime(bad_format, format='%Y-%m-%d %H-%M-%S') temp ``` ```{admonition} 练一练 Timestamp对象上定义了1个value属性,其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数。同时,pd.Timestamp也支持通过这个整数值来构造时间戳,即它们之间一一对应。请利用这个特性设计1个随机生成给定日期区间内日期序列的函数random_dates(time1, time2, n),其中time1、time2和n分别指区间左边界、区间右边界和序列元素个数。 ``` ```{code-cell} ipython3 def random_dates(time1, time2, n): t1 = pd.Timestamp(time1).value / 1e9 t2 = pd.Timestamp(time2).value / 1e9 d = np.random.randint(t1, t2+1, n) return pd.to_datetime(d * 1e9) random_dates("20200101", "20200105", 4) ``` ```{admonition} 练一练 在上述构造的Series中,按如下要求选出子序列: - 选出每月前十天中的工作日 - 选出每月的1号、11号和21号以及每月的最后5天 ``` ```{code-cell} ipython3 s = pd.Series( np.random.randint(2,size=365), index=pd.date_range('2021-01-01','2021-12-31')) idx = pd.Series(s.index).dt s.head() ``` - 1 ```{code-cell} ipython3 res = s[(idx.day<=10).values] res.head() ``` - 2 ```{code-cell} ipython3 res = s[((idx.daysinmonth-idx.day<=4)|(idx.day%10==1)).values] res.head() ``` ```{admonition} 练一练 与时间戳序列类似,时间差序列也可以使用max()、min()和mean()来得到最大时间差、最小时间差以及平均时间差,请基于learn_pandas数据集中的Time_Record列(学生的跑步体侧成绩)解决如下问题: - 最慢的学生花费的秒数是最快学生的几倍? - 在跑步时间超过及格线4分30秒的学生中(即不及格的学生中),每10秒做一次划分(区间左闭右开),请计算每一个区间(4分30秒至4分40秒、4分40秒至4分50秒、...)的学生人数。 ``` - 1 ```{code-cell} ipython3 df = pd.read_csv("data/learn_pandas.csv") df.Time_Record = pd.to_timedelta(df.Time_Record) df.Time_Record.dt.seconds.max() / df.Time_Record.dt.seconds.min() ``` - 2 ```{code-cell} ipython s = df.Time_Record[df.Time_Record > pd.Timedelta("4m30s")].reset_index(drop=True) pd.to_timedelta((s // pd.Timedelta("10s") * 10), unit="s").value_counts().sort_index() ``` ```{admonition} 练一练 将上述的pd.offsets.BDay(30)改为pd.offsets.BDay(12),请问2021-09-06和2021-09-05经过如上的操作后结果仍然保持不一致吗?请说明理由。 ``` 由于两个日期过了12个工作日之后,都是九月二十二日,因此它们的结果一致: ```{code-cell} ipython3 pd.Timestamp('20210906') + pd.offsets.BDay(12) - pd.Timestamp('20210906') ``` ```{code-cell} ipython3 pd.Timestamp('20210905') + pd.offsets.BDay(12) - pd.Timestamp('20210905') ``` ```{admonition} 练一练 请利用10.1.4节介绍的索引方法,构造与上述bdate_range()结果一致的DatetimeIndex。(练习重点在于使用dt对象和布尔序列的组合,来模拟weekmask和holidays的筛选过程) ``` ```{code-cell} ipython s = pd.Series(pd.date_range(start='20210920', end='20211015')) s = s[((s>pd.Timestamp("20211007"))|(s7)|((idx.day<7)&(idx.day_of_week<5)))).values] cday_sum = s_cday.rolling(7, min_periods=1).sum() result = cday_sum.reindex(s.index).ffill() result.head(8) ``` ```{admonition} 练一练 对上述数据每天按照0点至12点、12点至24点进行分组极差计算。 ``` ```{code-cell} ipython3 np.random.seed(0) idx = pd.date_range("20210901", "20210902 23:59:59", freq="90min") s = pd.Series(np.random.rand(idx.shape[0]), index=idx) ``` ```{code-cell} ipython3 s.resample("12H").apply(lambda x: x.max()-x.min()) ``` ```{admonition} 练一练 对于merge_asof()而言,只需要保证键是有序且可比较的即可,因此整数和浮点的模糊匹配也是可以进行的。请举出1个非时序的例子来说明参数选择对合并结果的影响。 ``` ```{code-cell} ipython3 df1 = pd.DataFrame({ "A": [2,4,6,8,10], }) df1 ``` ```{code-cell} ipython3 df2 = pd.DataFrame({ "A": [1,3,5,6,9], "B": range(100,105) }) df2 ``` ```{code-cell} ipython3 pd.merge_asof(df1, df2, on="A", direction="backward") ``` ## 一、太阳辐射数据的时序分析 现有1份关于太阳辐射的数据集: ```{code-cell} ipython3 df = pd.read_csv('data/ch10/solar.csv', usecols=['Data','Time','Radiation','Temperature']) df.head(2) ``` - 将Datetime列和Time列合并为1个时间列Datetime,同时把它作为索引后排序。 - 每条记录时间的间隔显然并不一致,请解决如下问题: - 找出间隔时间的前3个最大值所对应的3组时间戳。 - 是否存在1个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出直方图,设置bins=50。(直方图的画法可见11.1.1节) - 求如下指标对应的Series: - 温度与辐射量的6小时滑动相关系数 - 以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列 - 每条观测记录6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量) ```text 【解答】 ``` - 1 ```{code-cell} ipython3 df = pd.read_csv('data/ch10/solar.csv', usecols=['Data','Time', 'Radiation','Temperature']) solar_date = df.Data.str.extract('([/|\w]+\s).+')[0] df['Data'] = pd.to_datetime(solar_date + df.Time) df = df.drop(columns='Time').rename(columns={'Data':'Datetime'} ).set_index('Datetime').sort_index() df.head(3) ``` - 2-1 ```{code-cell} ipython3 s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds() max_3 = s.nlargest(3).index df.index[max_3.union(max_3-1)] ``` ```{code-cell} ipython3 res = s.mask((s>s.quantile(0.99))|(s