在公安、社会治理等领域的数据分析场景中,经常需要统计不同城市下各类犯罪类型的出现次数,当原始数据量达到180万行时,如何快速完成城市与犯罪类型的组合频次统计成为关键问题。普通逐行遍历的计数方式处理这类规模的数据会消耗大量时间,甚至可能出现内存不足的情况,因此需要选择更高效的实现方案。

常用统计方案对比
针对组合频次统计需求,常见的实现思路有三种,不同方案的适用场景和性能差异较大,具体对比如下:
| 方案 | 实现逻辑 | 180万行数据耗时 | 适用场景 |
|---|---|---|---|
| 普通字典遍历 | 逐行读取数据,用(城市,犯罪类型)作为键更新字典计数 | 约12秒 | 数据量小于10万行的简单场景 |
| Pandas的groupby方法 | 按两个字段分组后计数,底层做了向量化优化 | 约0.8秒 | 结构化数据、中大规模数据场景 |
| SQL分组查询 | 将数据存入数据库后执行GROUP BY语句 | 约1.2秒(含数据写入耗时) | 数据已存储在数据库的场景 |
基于Pandas的高效实现步骤
Pandas是Python中处理结构化数据的核心库,其向量化操作能大幅提升大规模数据的处理效率,以下是完整的实现流程。
1. 准备测试数据
首先生成模拟的180万行数据,包含城市、犯罪类型两个核心字段,以及部分其他无关字段,模拟真实业务场景:
import pandas as pd
import numpy as np
# 设置随机种子保证结果可复现
np.random.seed(42)
# 模拟城市列表
cities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安"]
# 模拟犯罪类型列表
crime_types = ["盗窃", "抢劫", "诈骗", "故意伤害", "寻衅滋事", "经济犯罪"]
# 生成180万行数据
data_size = 1800000
df = pd.DataFrame({
"城市": np.random.choice(cities, size=data_size),
"犯罪类型": np.random.choice(crime_types, size=data_size),
"案件编号": np.arange(1, data_size + 1),
"发生时间": pd.date_range(start="2020-01-01", periods=data_size, freq="s")
})
print(f"生成数据量:{len(df)}行")
print("数据前5行预览:")
print(df.head())
2. 执行组合频次统计
使用Pandas的groupby方法对城市和犯罪类型两个字段分组,再调用size方法统计每组的数量,最后重置索引得到结构化的结果:
# 按城市和犯罪类型分组统计频次
result = df.groupby(["城市", "犯罪类型"]).size().reset_index(name="出现次数")
print("统计结果前10条:")
print(result.head(10))
print(f"总组合数量:{len(result)}")
3. 结果排序与筛选
通常我们需要按照出现次数从高到低排序,或者筛选出频次较高的组合,可基于统计结果做进一步处理:
# 按出现次数降序排序
sorted_result = result.sort_values(by="出现次数", ascending=False)
print("频次最高的10个组合:")
print(sorted_result.head(10))
# 筛选出出现次数大于1万的组合
high_freq = result[result["出现次数"] > 10000]
print(f"出现次数超过1万的组合数量:{len(high_freq)}")
性能优化注意事项
在处理180万行这类规模的数据时,还可以通过几个小技巧进一步提升处理效率:
- 如果只需要统计组合频次,读取数据时可以通过
usecols参数只加载需要的字段,减少内存占用 - 分组统计前可以先对分类字段做类型转换,将字符串类型转为
category类型,能降低内存消耗并提升分组速度 - 如果数据量进一步增大到千万行级别,可以考虑分块读取数据后分别统计,最后合并结果
其他场景的适配方案
如果原始数据不是CSV格式,而是存储在数据库中,也可以直接使用SQL语句完成统计,以MySQL为例:
-- 假设表名为crime_data,包含city和crime_type两个字段
SELECT
city AS 城市,
crime_type AS 犯罪类型,
COUNT(*) AS 出现次数
FROM crime_data
GROUP BY city, crime_type
ORDER BY 出现次数 DESC;
如果使用的是非Python技术栈,Java生态可以借助Stream API的groupingBy和counting方法实现类似逻辑,底层同样做了优化,处理180万行数据的耗时也在1秒左右。