时间序列
1 911紧急电话分类
现在我们有2015到2020年911的紧急电话的数据,请统计出这些数据中不同类型的紧急情况的次数
(1)先导入数据
import pandas as pd
import numpy as np
# 读取数据
data = pd.read_csv('911.csv')
print(data.info())
输出
可以看到,数据没有缺失
(2)确定要提取的列
用Excel打开数据,分类那一列是title
把紧急电话类型从title中提取出来
# 首先把紧急电话类型提取出来
emergency_data = data['title'].str.split(': ').tolist()
print(emergency_data[1:10])
# data['title'].str.split(': ')的返回值是Series对象,每个元素都是列表
# tolist()将Series对象转化成一个大列表,大列表的每个元素又是一个列表
输出
[['EMS', 'DIABETIC EMERGENCY'], ['Fire', 'GAS-ODOR/LEAK'], ['EMS', 'CARDIAC EMERGENCY'], ['EMS', 'DIZZINESS'], ['EMS', 'HEAD INJURY'], ['EMS', 'NAUSEA/VOMITING'], ['EMS', 'RESPIRATORY EMERGENCY'], ['EMS', 'SYNCOPAL EPISODE'], ['Traffic', 'VEHICLE ACCIDENT -']]
(3)构造分类列表
# 把Series转化成一个列表,即把Series中每个列表打开,构成一个新列表
temp_list = [i[0] for i in emergency_data]
# 构造分类列表
cate_list = list(set(temp_list))
(4)构造全为0的数组
# 构造全为0的数组
zeros_df = pd.DataFrame(np.zeros((data.shape[0],
len(cate_list))), columns=cate_list)
print(zeros_df.shape)
print(zeros_df.head(10))
输出
(423909, 3)
Fire Traffic EMS
0 0.0 0.0 0.0
1 0.0 0.0 0.0
2 0.0 0.0 0.0
3 0.0 0.0 0.0
4 0.0 0.0 0.0
5 0.0 0.0 0.0
6 0.0 0.0 0.0
7 0.0 0.0 0.0
8 0.0 0.0 0.0
9 0.0 0.0 0.0
(5)给数组赋值
# 赋值
for cate in cate_list:
# 由于原数据中有40多万条数据,因此一行一行的遍历,那么运行时间将非常长
# 这里我们按列遍历,因为只有三列,所以时间能节约很多
# print(data["title"].str.contains(cate).head(10))
# data["title"].str.contains(cate)是由布尔值构成的Series对象
zeros_df[cate][data["title"].str.contains(cate).values] = 1
# contains表示是否包含,返回逻辑值
# zeros_df[cate][data["title"].str.contains(cate)] = 1 没有values也可以
# 表示对同一列的多个位置同时赋值
print(zeros_df.head(10))
输出
Fire EMS Traffic
0 0.0 1.0 0.0
1 0.0 1.0 0.0
2 1.0 0.0 0.0
3 0.0 1.0 0.0
4 0.0 1.0 0.0
5 0.0 1.0 0.0
6 0.0 1.0 0.0
7 0.0 1.0 0.0
8 0.0 1.0 0.0
9 0.0 0.0 1.0
(6)统计每一列的次数
sum_ret = zeros_df.sum(axis=0) # 在0轴上折叠
print(sum_ret)
输出
EMS 208682.0
Fire 63775.0
Traffic 151458.0
dtype: float64
(7)完整程序
统计不同类型的紧急电话次数,完整程序如下
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
# 读取数据
data = pd.read_csv('911.csv')
# print(data.head(10))
# print(data.info())
# 首先把紧急电话类型提取出来
emergency_data = data['title'].str.split(': ').tolist()
# print(emergency_data.head(10))
# data['title'].str.split(': ')的返回值是Series对象,每个元素都是列表
# tolist()将Series对象转化成一个大列表,大列表的每个元素又是一个列表
# 把Series转化成一个列表,即把Series中每个列表打开,构成一个新列表
temp_list = [i[0] for i in emergency_data]
# 构造分类列表
cate_list = list(set(temp_list))
# 构造全为0的数组
zeros_df = pd.DataFrame(np.zeros((data.shape[0],
len(cate_list))), columns=cate_list)
# print(zeros_df.shape)
# print(zeros_df.head(10))
# 赋值
for cate in cate_list:
# 由于原数据中有40多万条数据,因此一行一行的遍历,那么运行时间将非常长
# 这里我们按列遍历,因为只有三列,所以时间能节约很多
# print(data["title"].str.contains(cate).head(10))
# data["title"].str.contains(cate)是由布尔值构成的Series对象
zeros_df[cate][data["title"].str.contains(cate).values] = 1
# contains表示是否包含,返回一个逻辑值
# zeros_df[cate][data["title"].str.contains(cate)] = 1 没有values也可以
# 表示对同一列的多个位置同时赋值
# print(zeros_df.head(10))
sum_ret = zeros_df.sum(axis=0) # 在0轴上折叠
print(sum_ret)
(8)第二种思路——增加一列分类
先把分类提取出来,然后将其转换为只有一类的DataFrame或者Series对象,并加到原数据的后面,即增加一列,最后使用新增的一列作为分类依据(groupby),调用count函数
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
df = pd.read_csv("./911.csv")
# print(df.head(5))
# 获取分类
# print()df["title"].str.split(": ")
temp_list = df["title"].str.split(": ").tolist()
cate_list = [i[0] for i in temp_list]
# 增加一列
df["cate"] = pd.DataFrame(np.array(cate_list).reshape((df.shape[0],1)))
# df["cate"] = pd.Series(np.array(cate_list)) 也可以
# cate_list是列表,上面的命令是吧这个列表转化为DataFrame对象
# print(df["cate"])
# print(df.head(5))
print(df.groupby(by="cate").count()["title"])
输出
[5 rows x 9 columns]
cate
EMS 320326
Fire 96177
Traffic 223395
Name: title, dtype: int64
2 时间序列
如果我们还想统计出不同月份不同类型紧急电话的次数的变化情况,应该怎么做呢?
时间序列都是一种非常重要的数据形式,很多统计数据以及数据的规律也都和时间序列有着非常重要的联系。
(1)时间序列的生成
pandas中生成一段时间的命令
pd.date_range(start=None, end=None, periods=None, freq='D')
start和end以及freq配合能够生成start和end范围内以频率freq的一组时间索引
start和periods以及freq配合能够生成从start开始的频率为freq的periods个时间索
end和periods不在一起使用
如果freq缺失,默认就是’D",‘D’表示1天,10天就是’10D’,这里有关于时间的缩写
(2)时间序列与切片的区别
与实践切片不同的是,start有可能取不到,end也未必不能取到,关键是看freq。
import pandas as pd
time_series = pd.date_range(start='20171230', end='20180501', freq='MS')
# 表示从20171230-20180501这个时间段内的每个月的第一个工作日
print(time_series)
print(type(time_series))
输出
DatetimeIndex(['2018-01-01', '2018-02-01', '2018-03-01', '2018-04-01',
'2018-05-01'],
dtype='datetime64[ns]', freq='MS')
<class 'pandas.core.indexes.datetimes.DatetimeIndex'>
除了20171230之外,2017-12-30, 2017/12/30都是能识别的
time_series1 = pd.date_range(start='2017-12-30', periods=10, freq='D')
time_series2 = pd.date_range(start='2017/12/30 10:10:20', periods=10, freq='2H')
print(time_series1)
print(50 * '*')
print(time_series2)
输出
DatetimeIndex(['2017-12-30', '2017-12-31', '2018-01-01', '2018-01-02',
'2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06',
'2018-01-07', '2018-01-08'],
dtype='datetime64[ns]', freq='D')
**************************************************
DatetimeIndex(['2017-12-30 10:10:20', '2017-12-30 12:10:20',
'2017-12-30 14:10:20', '2017-12-30 16:10:20',
'2017-12-30 18:10:20', '2017-12-30 20:10:20',
'2017-12-30 22:10:20', '2017-12-31 00:10:20',
'2017-12-31 02:10:20', '2017-12-31 04:10:20'],
dtype='datetime64[ns]', freq='2H')
(3)字符串与时间序列的相互转化
回到最开始的911数据的案例中,我们可以使用pandas提供的方法把时间字符串转化为时间序列
df["timeStamp"] = pd.to_datetime(df["timeStamp"])
当然,也可以把时间戳对象以指定格式转化为字符串,使用strftime()函数,观察下面两个程序,理解用法
1
import pandas as pd
data = pd.read_csv("911.csv")
# print(data.info())
x = pd.to_datetime(data["timeStamp"])
print(type(x))
for i in x:
print(type(i))
print(i.strftime("%Y-%m-%d"))
print(type(i.strftime("%Y-%m-%d")))
break
输出
<class 'pandas.core.series.Series'>
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
2015-12-10
<class 'str'>
2
import pandas as pd
time_series = pd.date_range(start='20171230', end='20180501', freq='MS')
# 表示从20171230-20180501这个时间段内的每个月的第一个工作日
print(time_series)
print(type(time_series))
print(50*'*')
for i in time_series:
print(type(i))
print(i.strftime("%Y-%m-%d"))
print(type(i.strftime("%Y-%m-%d")))
break
输出
DatetimeIndex(['2018-01-01', '2018-02-01', '2018-03-01', '2018-04-01',
'2018-05-01'],
dtype='datetime64[ns]', freq='MS')
<class 'pandas.core.indexes.datetimes.DatetimeIndex'>
**************************************************
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
2018-01-01
<class 'str'>
3 重采样
将时间序列从一个频率转化为另一个频率进行处理的过程,将高频率数据转化为低频率数据为降采样,低频率转化为高频率为升采样。说通俗一点,比如让数据从精确到秒转变成精确到天,这就是降采样,反之则为升采样。
(1)重采样方法
pandas提供了一个resample的方法来帮助我们实现频率转化
import pandas as pd
import numpy as np
s = np.random.uniform(10, 50, (100, 1))
t = pd.DataFrame(s, index=pd.date_range("20170101", periods=100))
print(t.head())
print(t.resample("M"))
print(type(t.resample("M")))
print(t.resample("M").mean())
print(type(t.resample("M").mean()))
print(t.resample("10D").count())
输出
0
2017-01-01 13.137071
2017-01-02 34.360108
2017-01-03 36.784323
2017-01-04 13.214508
2017-01-05 13.488609
DatetimeIndexResampler [freq=<MonthEnd>, axis=0, closed=right, label=right, convention=start, origin=start_day]
pandas.core.resample.DatetimeIndexResampler
0
2017-01-31 32.715650
2017-02-28 30.268516
2017-03-31 31.286149
2017-04-30 34.630335
<class 'pandas.core.frame.DataFrame'>
0
2017-01-01 10
2017-01-11 10
2017-01-21 10
2017-01-31 10
2017-02-10 10
2017-02-20 10
2017-03-02 10
2017-03-12 10
2017-03-22 10
2017-04-01 10
可以看到,原始数据是每天一个数字,t.resample(“M”)变成每个月一个数字,降采样之后无法输出
DatetimeIndexResampler对象,虽然无法直接输出,但是可以进行聚合操作,聚合之后的返回对象是DataFrame。
(2)重采样的聚合函数
重采样的聚合函数,统计的是采样组的数据,看下面的程序,就能明白“重采样——聚合”的原理
import pandas as pd
file_path = "./PM2.5/BeijingPM20100101_20151231.csv"
df = pd.read_csv(file_path)
period = pd.PeriodIndex(year=df["year"],month=df["month"],
day=df["day"],hour=df["hour"],freq="H")
df["datetime"] = period
df.set_index("datetime",inplace=True)
df = df.resample("7D").mean()
print(df.head(2))
输出
No year month ... Iws precipitation Iprec
datetime ...
2010-01-01 84.5 2010.0 1.0 ... 43.859821 0.066667 0.786905
2010-01-08 252.5 2010.0 1.0 ... 45.392083 0.000000 0.000000
程序中设定了采样频率是7天,聚合函数使用平均值,那么“重采样——聚合”后,得到的每一行都是7天内的平均值,这7天可以当成是采样组。例如,在输出种,第一行的No是84.5,说明从1月1日-1月7日所有数据的No的平均值为84.5
可以通过Excel把1月1日-1月7日全部筛选出来,然后看看平均值是不是84.5
(3)绘制每个月的911紧急电话分类变化图
绘制911数据中不同月份不同类型的电话的次数的变化情况,在原数据中时间都是精确到秒的,我们可以通过降采样,使其精确到月,之后根据类型进行分类,绘制变化趋势图。
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
# 把时间字符串转为时间类型设置为索引
df = pd.read_csv("./911.csv")
df["timeStamp"] = pd.to_datetime(df["timeStamp"])
# 添加列,表示分类
temp_list = df["title"].str.split(": ").tolist()
cate_list = [i[0] for i in temp_list]
# print(np.array(cate_list).reshape((df.shape[0],1)))
df["cate"] = pd.DataFrame(np.array(cate_list).reshape((df.shape[0],1)))
# 设置时间为索引
df.set_index("timeStamp",inplace=True)
# inplace表示原地修改,不生成新的对象
# print(df.head(1))
plt.figure(figsize=(20, 8), dpi=80)
# 分组
for group_name,group_data in df.groupby(by="cate"):
# 按照类型进行分组
# DataFrameGroupBy对象的每一个元素都是一个元组,所以这里用两个变量来获取值
# group_name是类型名称,group_data是类型数据
# 对不同的分类都进行绘图
count_by_month = group_data.resample("M").count()["title"]
# resample()是重采样,count()是聚合,["title"]是取字段
# “重采样——聚合”操作之后,返回值是DataFrame,操作前有多少个字段,操作之后就有多少个
# count()是计算有多少行,之所以取title字段,是因为这个字段没有缺失
# print(group_data.resample("M").count())
_x = count_by_month.index
# print(type(_x))
_y = count_by_month.values
# 将时间索引转化为字符串
_x = [i.strftime("%Y-%m-%d") for i in _x]
# strftime()是以指定格式返回时间字,返回的是字符串类型
# strftime()是to_datetime()的逆过程
# 画图(折线图)
plt.plot(range(len(_x)), _y, label=group_name)
plt.xticks(range(len(_x)), _x, rotation=45)
plt.legend(loc="best")
plt.show()
输出
4 PeriodIndex
之前所学习的DatetimeIndex可以理解为时间戳
那么现在我们要学习的PeriodIndex可以理解为时间段
periods = pd.PeriodIndex(year=data["year"],month=data["month"],day=data["day"],hour=data["hour"],freq="H")
有些数据把时间分成了多列,没有完成的时间,如下图
这个时候就可以使用PeriodIndex
import pandas as pd
file_path = "PM2.5/BeijingPM20100101_20151231.csv"
df = pd.read_csv(file_path)
# 构造PeriodIndex对象
period = pd.PeriodIndex(year=df["year"],month=df["month"],
day=df["day"],hour=df["hour"],freq="H")
print(period)
print(type(period))
输出
PeriodIndex(['2010-01-01 00:00', '2010-01-01 01:00', '2010-01-01 02:00',
'2010-01-01 03:00', '2010-01-01 04:00', '2010-01-01 05:00',
'2010-01-01 06:00', '2010-01-01 07:00', '2010-01-01 08:00',
'2010-01-01 09:00',
...
'2015-12-31 14:00', '2015-12-31 15:00', '2015-12-31 16:00',
'2015-12-31 17:00', '2015-12-31 18:00', '2015-12-31 19:00',
'2015-12-31 20:00', '2015-12-31 21:00', '2015-12-31 22:00',
'2015-12-31 23:00'],
dtype='period[H]', length=52584, freq='H')
<class 'pandas.core.indexes.period.PeriodIndex'>
从输出结果中可以看到,PeriodIndex和DateIndex是一样的,它们唯一额区别在于构造的方法不一样