如何高效筛选纽约市犯罪数据集并按月分组统计绘制时序图
优化500万+条犯罪数据集的按月统计与绘图流程
问题核心
你的原始代码反复筛选、分组500万条数据,导致计算耗时极高。已经写出的isin一次性筛选是正确的优化方向,接下来只需通过一次分组统计就能得到所有犯罪类型的月度数据,完美适配原有绘图代码。
优化后的完整处理代码
import pandas as pd # 1. 定义目标犯罪类型 crime_select = ["DANGEROUS DRUGS", "ASSAULT 3 & RELATED OFFENSES", "PETIT LARCENY", "FELONY ASSAULT", "DANGEROUS WEAPONS"] # 2. 一次性筛选目标数据(只遍历数据集1次) filtered_crime = df[df["OFNS_DESC"].isin(crime_select)] # 3. 转换日期为年月格式 filtered_crime['ARREST_YM'] = filtered_crime["ARREST_DATE"].dt.to_period('M') # 4. 一次分组完成所有统计(按年月+犯罪类型分组,统计数量) crime_monthly = filtered_crime.groupby(['ARREST_YM', 'OFNS_DESC']).size().unstack(fill_value=0)
代码详细解释
- 一次性筛选:
isin替代多次==判断,只遍历数据集1次,直接过滤出所有目标犯罪类型,比原代码少遍历4次500万条数据,大幅节省时间。 - 日期转换:给筛选后的数据集新增
ARREST_YM列,存储年月周期格式(如2020-01),方便后续分组。 - 分组统计:
groupby(['ARREST_YM', 'OFNS_DESC']):同时按「年月」和「犯罪类型」分组,把每个月的每种犯罪单独成组。size():统计每组的记录数(即该月该类犯罪的次数)。unstack(fill_value=0):把犯罪类型从行索引转成列,最终得到的crime_monthly是一个DataFrame:- 行索引是年月(和原代码的
arrest_ym一致) - 列是5种犯罪类型,每个单元格对应该月该类犯罪的次数,缺失月份自动补0。
- 行索引是年月(和原代码的
适配原有绘图代码
原绘图代码可以直接复用,只需把单独的变量替换成crime_monthly的列即可,也可以改成循环方式更简洁:
方式1:适配原有逐行绘图逻辑
import matplotlib.pyplot as plt plt.figure(figsize = (20, 10)) # 直接从crime_monthly取列数据,和原变量结构完全一致 plt.plot(crime_monthly.index.astype(str), crime_monthly["DANGEROUS DRUGS"], 'b-', label = "Dangerous Drugs") plt.plot(crime_monthly.index.astype(str), crime_monthly["ASSAULT 3 & RELATED OFFENSES"], 'g-', label = "Assault 3 & Related Offenses") plt.plot(crime_monthly.index.astype(str), crime_monthly["PETIT LARCENY"], 'r-', label = "Petit Larceny") plt.plot(crime_monthly.index.astype(str), crime_monthly["FELONY ASSAULT"], 'c-', label = "Felony Assault") plt.plot(crime_monthly.index.astype(str), crime_monthly["DANGEROUS WEAPONS"], 'm-', label = "Dangerous Weapons") # 保留原有的坐标轴设置 x_labels = crime_monthly.index.astype(str) plt.xticks(x_labels[::6], rotation=45) plt.xlabel("Month") plt.ylabel("Crime Count") plt.title("Monthly Crime Frequency") plt.legend(loc = "best") plt.show()
方式2:循环绘图更简洁(推荐)
如果后续要调整犯罪类型,只需修改crime_select列表,不需要改绘图代码:
import matplotlib.pyplot as plt # 定义颜色映射,对应5种犯罪类型 color_map = { "DANGEROUS DRUGS": 'b-', "ASSAULT 3 & RELATED OFFENSES": 'g-', "PETIT LARCENY": 'r-', "FELONY ASSAULT": 'c-', "DANGEROUS WEAPONS": 'm-' } plt.figure(figsize = (20, 10)) # 循环遍历每个犯罪类型列,自动绘图 for crime_type in crime_select: plt.plot(crime_monthly.index.astype(str), crime_monthly[crime_type], color_map[crime_type], label = crime_type.replace('_', ' ').title()) x_labels = crime_monthly.index.astype(str) plt.xticks(x_labels[::6], rotation=45) plt.xlabel("Month") plt.ylabel("Crime Count") plt.title("Monthly Crime Frequency") plt.legend(loc = "best") plt.show()
优化效果对比
- 原代码:遍历数据集5次(筛选)+ 5次分组统计,总计10次大遍历。
- 优化后:遍历数据集1次(筛选)+ 1次分组统计,总计2次大遍历。
对于500万条数据,计算速度至少提升4-5倍,内存占用也会大幅降低。
内容的提问来源于stack exchange,提问作者mooski
相关产品推荐
相关产品推荐

