如何按每秒预设时间区间统计CSV事件并绘制直方图?
问题描述
需求:统计CSV数据文件中每秒的事件数量,并根据结果绘制直方图,但不清楚如何正确获取每秒的事件数。
现有代码:
from matplotlib import pyplot as pl import pandas as pd import numpy as np def read_data(): df = pd.read_csv("test.csv", usecols=['time', 'unix_time', 'name']) df['time'] = pd.to_datetime(df['time']) df['unix_time'] = (df['unix_time']).astype(int) df.info() i = 1 time_counts = df.groupby((3600 * df.time.dt.minute + df.time.dt.second) // i * i)['time'].count() print(time_counts) if __name__ == "__main__": read_data()
异常输出:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33 entries, 0 to 32
Data columns (total 3 columns):Column Non-Null Count Dtype0 time 33 non-null datetime64[ns]
1 unix_time 33 non-null int32
2 name 33 non-null object
dtypes: datetime64ns, int32(1), object(1)
memory usage: 788.0+ bytes
time
18 1
25217 1
43209 1
43219 1
46804 1
54047 1
61241 1
64815 1
64833 1
68402 1
75620 1
79235 1
82806 1
82837 2
86407 1
86446 1
93625 1
97254 1
104446 1
140438 1
144050 1
162025 1
169250 1
180050 1
183623 1
183658 1
194404 1
194412 2
194433 1
194438 1
205219 1
Name: time, dtype: int64
问题分析
原代码的分组键计算逻辑错误:3600 * df.time.dt.minute + df.time.dt.second 用小时转秒的系数去乘分钟,得到的不是从0开始的有效秒数,而是混乱的累计值,导致分组结果的索引毫无意义。
正确实现方法
以下两种方式可可靠统计每秒事件数,并完成直方图绘制:
方法1:使用resample按秒重采样
pandas的resample是时间序列频率转换的专用工具,直接按秒聚合即可,还会自动补全无事件的秒数(计数为0):
from matplotlib import pyplot as plt import pandas as pd def read_data(): df = pd.read_csv("test.csv", usecols=['time', 'unix_time', 'name']) df['time'] = pd.to_datetime(df['time']) # 将time列设为索引,方便resample操作 df = df.set_index('time') # 按秒统计事件数 time_counts = df.resample('S')['name'].count() print(time_counts) # 绘制直方图 plt.figure(figsize=(12, 6)) time_counts.plot(kind='bar') plt.title('每秒事件数量分布') plt.xlabel('时间(秒)') plt.ylabel('事件数') plt.xticks(rotation=45) plt.tight_layout() plt.show() if __name__ == "__main__": read_data()
方法2:按秒级时间戳分组
若不想设置索引,可直接提取秒级时间作为分组键:
from matplotlib import pyplot as plt import pandas as pd def read_data(): df = pd.read_csv("test.csv", usecols=['time', 'unix_time', 'name']) df['time'] = pd.to_datetime(df['time']) # 提取到秒级的时间作为分组键(格式如:2024-05-20 12:34:56) df['second'] = df['time'].dt.floor('S') # 按秒分组统计事件数 time_counts = df.groupby('second')['name'].count() print(time_counts) # 绘制直方图 plt.figure(figsize=(12, 6)) time_counts.plot(kind='bar') plt.title('每秒事件数量分布') plt.xlabel('时间(秒)') plt.ylabel('事件数') plt.xticks(rotation=45) plt.tight_layout() plt.show() if __name__ == "__main__": read_data()
补充说明
- 若
unix_time列是精确到秒的时间戳,也可直接用它分组:df.groupby('unix_time')['name'].count(),效果一致; - 若想展示事件数的分布频率,可把
plot(kind='bar')换成plt.hist(time_counts.values)来绘制传统直方图。
内容的提问来源于stack exchange,提问作者ditrauth

