时间序列分析:犯罪数据洞察与分组技巧
在数据分析领域,时间序列分析是一项强大的工具,它能帮助我们从时间维度上深入理解数据的变化规律。本文将结合犯罪数据和员工薪资数据,详细介绍如何利用Python的Pandas库进行时间序列分析,包括按工作日和年份统计犯罪数量、使用匿名函数分组以及按时间戳和其他列分组等操作。
按工作日和年份统计犯罪数量
在处理犯罪数据时,我们常常需要了解不同工作日和年份的犯罪情况。通过使用Pandas的
dt
访问器,我们可以轻松地从时间戳中提取所需信息。
操作步骤
- 读取数据 :
import pandas as pd
crime = pd.read_hdf('data/crime.h5', 'crime')
crime.head()
- 统计工作日犯罪数量 :
wd_counts = crime['REPORTED_DATE'].dt.weekday_name.value_counts()
print(wd_counts)
输出结果显示,周末的犯罪和交通事故数量明显较少。
| Weekday | Count |
| ---- | ---- |
| Monday | 70024 |
| Friday | 69621 |
| Wednesday | 69538 |
| Thursday | 69287 |
| Tuesday | 68394 |
| Saturday | 58834 |
| Sunday | 55213 |
- 绘制工作日犯罪数量水平条形图 :
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
title = 'Denver Crimes and Traffic Accidents per Weekday'
wd_counts.reindex(days).plot(kind='barh', title=title)
- 统计年份犯罪数量并绘图 :
title = 'Denver Crimes and Traffic Accidents per Year'
crime['REPORTED_DATE'].dt.year.value_counts().sort_index().plot(kind='barh', title=title)
- 按工作日和年份分组统计犯罪数量 :
weekday = crime['REPORTED_DATE'].dt.weekday_name
year = crime['REPORTED_DATE'].dt.year
crime_wd_y = crime.groupby([year, weekday]).size()
print(crime_wd_y.head(10))
- 重塑数据以方便比较 :
crime_table = crime_wd_y.rename_axis(['Year', 'Weekday']).unstack('Weekday')
print(crime_table)
- 处理2017年不完整数据 :
criteria = crime['REPORTED_DATE'].dt.year == 2017
last_day_2017 = crime.loc[criteria, 'REPORTED_DATE'].dt.dayofyear.max()
print(last_day_2017)
- 计算历史数据中前272天犯罪的平均百分比 :
crime_pct = crime['REPORTED_DATE'].dt.dayofyear.le(272).groupby(year).mean().round(3)
print(crime_pct)
median_pct = crime_pct.loc[2012:2016].median()
print(median_pct)
- 更新2017年数据并调整列顺序 :
crime_table.loc[2017] = crime_table.loc[2017].div(median_pct).astype('int')
crime_table = crime_table.reindex(columns=days)
print(crime_table)
- 绘制犯罪数量热力图 :
import seaborn as sns
sns.heatmap(crime_table, cmap='Greys')
- 考虑人口因素,计算犯罪率 :
denver_pop = pd.read_csv('data/denver_pop.csv', index_col='Year')
den_100k = denver_pop.div(100000).squeeze()
crime_table2 = crime_table.div(den_100k, axis='index').astype('int')
print(crime_table2)
- 绘制调整后犯罪率热力图 :
sns.heatmap(crime_table2, cmap='Greys')
原理分析
-
dt访问器:所有包含时间戳的DataFrame列都可以通过dt访问器访问许多额外的属性和方法。 -
groupby方法:非常灵活,可以接受多个Series或函数作为参数,形成不同的分组。 -
unstack方法:用于将多层索引的Series转换为DataFrame,方便数据比较。
使用匿名函数分组
当DataFrame具有
DatetimeIndex
时,我们可以利用
groupby
方法的特殊功能,使用匿名函数进行分组。
操作步骤
- 读取数据并设置索引 :
crime_sort = pd.read_hdf('data/crime.h5', 'crime').set_index('REPORTED_DATE').sort_index()
-
查看
DatetimeIndex和Timestamp的共同属性和方法 :
common_attrs = set(dir(crime_sort.index)) & set(dir(pd.Timestamp))
print([attr for attr in common_attrs if attr[0] != '_'])
- 使用索引统计工作日犯罪数量 :
crime_sort.index.weekday_name.value_counts()
- 使用匿名函数按工作日分组统计犯罪和交通事故数量 :
crime_sort.groupby(lambda x: x.weekday_name)['IS_CRIME', 'IS_TRAFFIC'].sum()
- 使用多个匿名函数按小时和年份分组并重塑数据 :
funcs = [lambda x: x.round('2h').hour, lambda x: x.year]
cr_group = crime_sort.groupby(funcs)['IS_CRIME', 'IS_TRAFFIC'].sum()
cr_final = cr_group.unstack()
cr_final.style.highlight_max(color='lightgrey')
原理分析
-
DatetimeIndex具有与单个Timestamp对象相似的功能,可以直接使用其属性和方法。 -
groupby方法可以接受函数作为参数,函数的返回值用于形成分组。
按时间戳和其他列分组
在处理员工薪资数据时,我们可能需要按时间戳(如入职日期)和其他列(如性别)进行分组。
操作步骤
-
读取数据并创建
DatetimeIndex:
employee = pd.read_csv('data/employee.csv', parse_dates=['JOB_DATE', 'HIRE_DATE'], index_col='HIRE_DATE')
employee.head()
- 按性别分组计算平均薪资 :
avg_salary_by_gender = employee.groupby('GENDER')['BASE_SALARY'].mean().round(-2)
print(avg_salary_by_gender)
- 按入职日期分组计算平均薪资 :
avg_salary_by_hire_date = employee.resample('10AS')['BASE_SALARY'].mean().round(-2)
print(avg_salary_by_hire_date)
- 先按性别分组,再按10年时间跨度分组计算平均薪资 :
avg_salary_by_gender_and_time = employee.groupby('GENDER').resample('10AS')['BASE_SALARY'].mean().round(-2)
print(avg_salary_by_gender_and_time)
- 尝试比较男女薪资时出现的问题 :
sal_avg = employee.groupby('GENDER').resample('10AS')['BASE_SALARY'].mean().round(-2)
sal_avg.unstack('GENDER')
结果显示,男女的10年时间段起始日期不同,导致数据无法对齐。
6.
验证最早入职员工的年份
:
earliest_male_hire = employee[employee['GENDER'] == 'Male'].index.min()
earliest_female_hire = employee[employee['GENDER'] == 'Female'].index.min()
print(earliest_male_hire)
print(earliest_female_hire)
-
使用
groupby方法同时按性别和时间分组 :
sal_avg2 = employee.groupby(['GENDER', pd.Grouper(freq='10AS')])['BASE_SALARY'].mean().round(-2)
print(sal_avg2)
- 重塑数据以方便比较 :
sal_final = sal_avg2.unstack('GENDER')
print(sal_final)
原理分析
-
resample方法只能按时间周期分组,而groupby方法可以同时按时间戳和其他列分组。 -
pd.Grouper可以复制resample的功能,通过设置freq参数指定时间周期。
通过以上操作,我们可以清晰地看到不同工作日和年份的犯罪情况,以及不同性别和入职时间的员工薪资差异。时间序列分析为我们提供了深入理解数据的视角,帮助我们做出更明智的决策。
时间序列分析:犯罪数据洞察与分组技巧(续)
后续优化与拓展
在前面的分析中,我们已经完成了按工作日和年份统计犯罪数量、使用匿名函数分组以及按时间戳和其他列分组等操作。接下来,我们将进一步优化和拓展这些分析。
封装函数进行特定犯罪类型分析
为了方便对特定类型的犯罪进行分析,我们可以封装一个函数,将前面的步骤整合在一起,并添加选择特定犯罪类型的功能。
ADJ_2017 = 0.748
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
def count_crime(df, offense_cat):
df = df[df['OFFENSE_CATEGORY_ID'] == offense_cat]
weekday = df['REPORTED_DATE'].dt.weekday_name
year = df['REPORTED_DATE'].dt.year
ct = df.groupby([year, weekday]).size().unstack()
ct.loc[2017] = ct.loc[2017].div(ADJ_2017).astype('int')
pop = pd.read_csv('data/denver_pop.csv', index_col='Year')
pop = pop.squeeze().div(100000)
ct = ct.div(pop, axis=0).astype('int')
ct = ct.reindex(columns=days)
sns.heatmap(ct, cmap='Greys')
return ct
# 示例:分析汽车盗窃犯罪
count_crime(crime, 'auto - theft')
这个函数接受一个DataFrame和一个犯罪类型作为参数,返回一个经过处理的DataFrame,并绘制相应的热力图。
多索引列数据的选择
在使用多个匿名函数分组后,得到的结果是一个具有多索引列的DataFrame。我们可以使用
xs
方法选择特定的数据。
# 选择交通事故数据
traffic_data = cr_final.xs('IS_TRAFFIC', axis='columns', level=0).head()
print(traffic_data)
# 选择2016年的数据
data_2016 = cr_final.xs(2016, axis='columns', level=1).head()
print(data_2016)
xs
方法允许我们从多索引的任何级别选择单个值,通过指定
axis
和
level
参数来确定选择的位置。
总结与流程图
通过以上的分析,我们可以总结出整个时间序列分析的流程:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(读取数据):::process
B --> C{数据类型}:::decision
C -->|犯罪数据| D(按工作日和年份统计犯罪数量):::process
D --> E(处理不完整数据):::process
E --> F(考虑人口因素计算犯罪率):::process
F --> G(绘制热力图):::process
C -->|员工数据| H(按时间戳和其他列分组):::process
H --> I(处理分组数据对齐问题):::process
I --> J(计算平均薪资):::process
J --> K(绘制图表或展示结果):::process
D --> L(使用匿名函数分组):::process
L --> M(选择特定数据):::process
G --> N([结束]):::startend
K --> N
M --> N
这个流程图展示了整个时间序列分析的主要步骤,包括数据读取、不同类型数据的处理、分组操作、数据处理和结果展示等。
关键要点回顾
-
dt访问器 :是处理时间戳数据的强大工具,可用于提取时间相关的信息,如工作日名称、年份等。 -
groupby方法 :非常灵活,可以接受Series、函数等作为参数,实现多种分组方式。 -
unstack方法 :用于将多层索引的Series转换为DataFrame,方便数据的比较和分析。 -
resample方法 :用于按时间周期对数据进行分组,但只能处理时间戳数据。 -
pd.Grouper:可以复制resample的功能,并且可以与其他列一起使用,实现更复杂的分组。 -
xs方法 :用于从多索引的DataFrame中选择特定的数据。
通过掌握这些方法和技巧,我们可以更深入地分析时间序列数据,发现数据中的规律和趋势,为决策提供有力的支持。
在实际应用中,我们可以根据具体的需求对这些方法进行组合和扩展,以满足不同的分析场景。例如,我们可以进一步分析不同时间段内犯罪类型的变化趋势,或者研究员工薪资与工作绩效之间的关系等。时间序列分析为我们打开了一扇了解数据动态变化的窗口,让我们能够更好地应对各种复杂的数据分析问题。
超级会员免费看
20

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



