时间序列分析:犯罪数据的深入洞察
在数据分析领域,时间序列分析是一项强大的工具,它能帮助我们从按时间顺序排列的数据中挖掘出有价值的信息。本文将围绕犯罪数据,详细介绍时间序列分析的多种方法和技巧。
数据准备与索引操作
在处理犯罪数据时,我们首先要关注数据的存储和索引。HDF5 文件是一种很好的数据存储格式,它能保留每列的数据类型,从而显著减少内存需求。例如,将
OFFENSE_TYPE_ID
、
OFFENSE_CATEGORY_ID
和
NEIGHBORHOOD_ID
列存储为
pandas
的
category
类型,比存储为
object
类型能节省大量内存:
mem_cat = crime.memory_usage().sum()
mem_obj = crime.astype({'OFFENSE_TYPE_ID':'object',
'OFFENSE_CATEGORY_ID':'object',
'NEIGHBORHOOD_ID':'object'}) \
.memory_usage(deep=True).sum()
mb = 2 ** 20
round(mem_cat / mb, 1), round(mem_obj / mb, 1)
为了能按日期智能选择和切片行,索引必须包含日期值。我们将
REPORTED_DATE
列移到索引中,创建一个
DatetimeIndex
:
crime = pd.read_hdf('data/crime.h5', 'crime') \
.set_index('REPORTED_DATE')
print(type(crime.index))
有了
DatetimeIndex
,我们可以使用
.loc
索引器通过各种字符串来选择行,甚至索引操作符本身也能达到相同效果。不过,个人更推荐使用
.loc
索引器,因为它更明确,传入的第一个值总是用于选择行。
排序索引能显著提高性能。对比未排序和排序后的
DataFrame
在切片操作上的性能:
%timeit crime.loc['2015-3-4':'2016-1-1']
crime_sort = crime.sort_index()
%timeit crime_sort.loc['2015-3-4':'2016-1-1']
排序后的
DataFrame
性能提升了约 50 倍。
基于
DatetimeIndex
的方法使用
有许多
DataFrame
或
Series
方法只能在
DatetimeIndex
上使用。以下是一些具体操作:
1.
按时间范围选择
:使用
between_time
方法选择所有在凌晨 2 点到 5 点发生的犯罪,无论日期如何:
crime.between_time('2:00', '5:00', include_end=False).head()
-
按特定时间选择
:使用
at_time方法选择所有在特定时间的日期:
crime.at_time('5:47').head()
-
选择前 n 个时间片段
:使用
first方法选择前 n 个时间片段,这里的时间片段由DateOffset对象表示。例如,选择前六个月的犯罪数据:
crime_sort = crime.sort_index()
crime_sort.first(pd.offsets.MonthBegin(6))
但这种方法可能会因为索引的时间组件而出现意外结果。可以使用
normalize
参数将时间组件设置为零,或者直接使用切片操作来获得更准确的结果:
crime_sort.first(pd.offsets.MonthBegin(6, normalize=True))
crime_sort.loc[:'2012-06']
-
使用偏移别名
:除了直接使用
DateOffset对象,还可以使用偏移别名。例如:
crime_sort.first('5D') # 5 天
crime_sort.first('5B') # 5 个工作日
crime_sort.first('7W') # 7 周,周日结束
crime_sort.first('3QS') # 第三季度开始
crime_sort.first('A') # 一年结束
每周犯罪数量统计
统计每周的犯罪数量是时间序列分析中的常见需求。我们可以使用
resample
和
groupby
方法来实现:
1.
数据准备
:读取犯罪数据,设置索引并排序:
crime_sort = pd.read_hdf('data/crime.h5', 'crime') \
.set_index('REPORTED_DATE') \
.sort_index()
-
使用
resample方法 :使用resample方法按周分组,并使用size方法统计每周的犯罪数量:
crime_sort.resample('W')
weekly_crimes = crime_sort.resample('W').size()
weekly_crimes.head()
默认情况下,周日被选为一周的最后一天,并作为结果
Series
中每个元素的标签。我们可以手动切片数据来验证结果的准确性:
len(crime_sort.loc[:'2012-1-8'])
len(crime_sort.loc['2012-1-9':'2012-1-15'])
还可以使用锚定偏移来选择不同的周结束日:
crime_sort.resample('W-THU').size().head()
-
使用
groupby方法 :groupby方法也能实现类似功能,但需要传入pd.Grouper对象:
weekly_crimes_gby = crime_sort.groupby(pd.Grouper(freq='W')) \
.size()
weekly_crimes_gby.head()
weekly_crimes.equal(weekly_crimes_gby)
-
更多操作
:即使索引不包含时间戳,也可以使用
resample和groupby方法,通过on或key参数指定包含时间戳的列。还可以绘制每周犯罪数量的折线图:
crime = pd.read_hdf('data/crime.h5', 'crime')
weekly_crimes2 = crime.resample('W', on='REPORTED_DATE').size()
weekly_crimes2.equals(weekly_crimes)
weekly_crimes_gby2 = crime.groupby(pd.Grouper(key='REPORTED_DATE',
freq='W')).size()
weekly_crimes_gby2.equals(weekly_crimes_gby)
weekly_crimes.plot(figsize=(16, 4), title='All Denver Crimes')
犯罪和交通事故的季度聚合
在丹佛的犯罪数据中,犯罪和交通事故记录在同一个表中,通过
IS_CRIME
和
IS_TRAFFIC
列区分。我们可以按季度对这两类数据分别进行聚合:
1.
数据准备
:读取数据,设置索引并排序:
crime_sort = pd.read_hdf('data/crime.h5', 'crime') \
.set_index('REPORTED_DATE') \
.sort_index()
-
按季度聚合
:使用
resample方法按季度分组,并对IS_CRIME和IS_TRAFFIC列求和:
crime_quarterly = crime_sort.resample('Q')['IS_CRIME',
'IS_TRAFFIC'].sum()
crime_quarterly.head()
-
使用不同偏移别名
:
Q表示季度结束,QS表示季度开始。可以使用不同的偏移别名来调整季度的计算方式:
crime_sort.resample('QS')['IS_CRIME',
'IS_TRAFFIC'].sum().head()
- 结果验证 :手动切片数据验证结果的准确性:
crime_sort.loc['2012-4-1':'2012-6-30',
['IS_CRIME', 'IS_TRAFFIC']].sum()
-
使用
groupby方法 :同样可以使用groupby方法实现相同的聚合:
crime_quarterly2 = crime_sort.groupby(pd.Grouper(freq='Q')) \
['IS_CRIME', 'IS_TRAFFIC'].sum()
crime_quarterly2.equals(crime_quarterly)
- 可视化分析 :绘制犯罪和交通事故的趋势图,以便更好地分析趋势:
plot_kwargs = dict(figsize=(16,4),
color=['black', 'lightgrey'],
title='Denver Crimes and Traffic Accidents')
crime_quarterly.plot(**plot_kwargs)
还可以绘制犯罪和交通事故的百分比增长图,以获得不同的视觉视角:
crime_begin = crime_quarterly.iloc[0]
crime_quarterly.div(crime_begin) \
.sub(1) \
.round(2) \
.plot(**plot_kwargs)
通过以上方法,我们可以深入分析犯罪数据的时间特征,为决策提供有力支持。在实际应用中,我们可以根据具体需求选择合适的方法和参数,以获得更准确和有价值的结果。
以下是一个简单的流程图,展示了统计每周犯罪数量的主要步骤:
graph TD;
A[读取数据] --> B[设置索引并排序];
B --> C[使用 resample 方法分组];
C --> D[使用 size 方法统计数量];
D --> E[验证结果];
B --> F[使用 groupby 方法分组];
F --> D;
| 操作 | 方法 | 示例代码 |
|---|---|---|
| 按时间范围选择 | between_time | crime.between_time(‘2:00’, ‘5:00’, include_end=False).head() |
| 按特定时间选择 | at_time | crime.at_time(‘5:47’).head() |
| 选择前 n 个时间片段 | first | crime_sort.first(pd.offsets.MonthBegin(6)) |
| 统计每周犯罪数量 | resample + size | crime_sort.resample(‘W’).size() |
| 按季度聚合 | resample + sum | crime_sort.resample(‘Q’)[‘IS_CRIME’, ‘IS_TRAFFIC’].sum() |
时间序列分析:犯罪数据的深入洞察
深入理解
resample
和
groupby
方法
在前面的内容中,我们已经介绍了使用
resample
和
groupby
方法进行时间序列分析的基本操作。下面我们将进一步深入理解这两个方法的工作原理和更多细节。
resample
方法默认与
DatetimeIndex
配合使用,它的第一个参数是规则,用于确定如何对索引中的时间戳进行分组。例如,使用偏移别名
W
将数据按周分组,默认以周日为一周的结束日。我们可以通过以下代码查看
resample
对象可用的属性和方法:
r = crime_sort.resample('W')
resample_methods = [attr for attr in dir(r) if attr[0].islower()]
print(resample_methods)
这些方法包括
agg
、
aggregate
、
apply
等,可用于对分组后的数据进行各种操作。
虽然
resample
方法在按时间戳分组时很方便,但实际上
groupby
方法也能实现相同的功能。使用
groupby
方法时,需要传入
pd.Grouper
对象,并通过
freq
参数指定偏移量。例如:
weekly_crimes_gby = crime_sort.groupby(pd.Grouper(freq='W')) \
.size()
这两种方法的结果是等价的:
weekly_crimes.equal(weekly_crimes_gby)
自定义
DateOffset
对象
当现有的
DateOffset
对象不能满足需求时,我们可以自定义
DateOffset
对象。例如:
dt = pd.Timestamp('2012-1-16 13:40')
dt + pd.DateOffset(months=1)
还可以使用多个日期和时间组件来创建自定义的
DateOffset
对象:
do = pd.DateOffset(years=2, months=5, days=3,
hours=8, seconds=10)
pd.Timestamp('2012-1-22 03:22') + do
不同季度锚定偏移的应用
在按季度聚合犯罪和交通事故数据时,不同的锚定偏移可以满足不同的业务需求。默认情况下,偏移别名
Q
以 12 月 31 日为一年的最后一天,计算季度范围,并以季度的最后一天作为聚合结果的标签。而
QS
以 1 月 1 日为一年的第一天。
如果我们希望季度从 3 月 1 日开始,可以使用
QS - MAR
来锚定偏移别名:
crime_sort.resample('QS-MAR')['IS_CRIME', 'IS_TRAFFIC'] \
.sum().head()
这对于那些财务年度不从 1 月 1 日开始的企业或组织非常有用。
总结与建议
通过对犯罪数据的时间序列分析,我们可以总结出以下几点:
1.
数据准备
:确保索引为
DatetimeIndex
,并对数据进行排序,以提高性能。
2.
方法选择
:
resample
方法提供了更简洁的按时间分组方式,而
groupby
方法结合
pd.Grouper
也能实现相同功能。
3.
DateOffset
对象
:灵活使用
DateOffset
对象和偏移别名,可以精确控制时间范围的选择。
4.
结果验证
:在进行数据分析时,手动切片数据验证结果的准确性是很重要的。
以下是一个总结表格,对比了不同方法的特点和适用场景:
| 方法 | 特点 | 适用场景 |
| — | — | — |
| resample | 专门用于时间序列分组,使用偏移别名方便 | 按固定时间间隔分组统计 |
| groupby + pd.Grouper | 通用分组方法,结合
pd.Grouper
实现时间分组 | 复杂分组需求,可结合其他分组条件 |
| between_time | 按时间范围选择 | 选择特定时间段内的数据 |
| at_time | 按特定时间选择 | 选择特定时间点的数据 |
| first | 选择前 n 个时间片段 | 选择数据的起始部分 |
下面是一个流程图,展示了整个犯罪数据时间序列分析的主要步骤:
graph TD;
A[读取数据] --> B[设置索引并排序];
B --> C[按时间选择数据];
C --> D[按时间分组统计];
D --> E[验证结果];
E --> F[可视化分析];
C --> G[自定义时间范围];
G --> D;
通过以上的分析和操作,我们可以更好地理解犯罪数据的时间特征,为城市安全规划、资源分配等提供有价值的参考。在实际应用中,我们可以根据具体问题进一步扩展和优化分析方法,以获得更深入的洞察。
希望本文能帮助你掌握时间序列分析的基本方法和技巧,在实际项目中灵活运用这些方法解决问题。如果你有任何疑问或建议,欢迎在评论区留言。
超级会员免费看
1698

被折叠的 条评论
为什么被折叠?



