如何在Pandas数据框中每分钟计算出现次数?

5

我有一个像这样的Pandas数据框:

timestamp           status  
2019-01-01 09:00:00 FAILED
2019-01-01 09:00:00 FAILED
2019-01-01 09:00:00 UNKNOWN
2019-01-01 09:00:00 PASSED
2019-01-01 09:00:00 PASSED
2019-01-01 09:01:00 PASSED
2019-01-01 09:01:00 FAILED 

我该如何按分钟对数据进行分组,并计算每分钟的状态数量以获得此数据框:
timestamp           PASSED FAILED UNKNOWN   
2019-01-01 09:00:00 2      2      1
2019-01-01 09:01:00 1      1      0

1
pd.crosstab(df['timestamp'],df['status']) - ansev
你是否会有第二个值,想要按分钟分组? - user3483203
尝试了您的建议,看起来可以工作,非常感谢!在原始数据中,时间戳将在一分钟内具有多个条目,因此将在几秒钟范围内不同。例如:时间戳 状态 2019-01-01 09:00:01 失败 2019-01-01 09:00:02 失败 2019-01-01 09:00:10 未知 2019-01-01 09:00:13 通过 2019-01-01 09:00:24 通过 2019-01-01 09:01:02 通过 2019-01-01 09:01:30 失败 - charemma
如果答案是交叉表,则问题是数据透视表。 - cs95
3个回答

4

方法1:

pd.crosstab(df['timestamp'],df['status'])

status               FAILED  PASSED  UNKNOWN
timestamp                                   
2019-01-01-09:00:00       2       2        1
2019-01-01-09:01:00       1       1        0

如果您想在表中添加类似时间戳的列:
pd.crosstab(df['timestamp'],df['status'],colnames=[None]).reset_index()

             timestamp  FAILED  PASSED  UNKNOWN
0  2019-01-01-09:00:00       2       2        1
1  2019-01-01-09:01:00       1       1        0

方法二:

df.groupby(['timestamp','status']).size().unstack(fill_value=0)

时间比较:

看起来方法2是最快的。

%%timeit
new_df=pd.crosstab(df['timestamp'],df['status'])
21 ms ± 759 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
new_df=df.groupby(['timestamp','status']).size().unstack(fill_value=0)
4.65 ms ± 290 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
df2 = (
    df
    .groupby(df['timestamp'].map(lambda x: x.replace(second=0)))['status']
    .value_counts()
    .unstack()
    .fillna(0)
    .astype(int)
    .reset_index()
)

8.5 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

3

如果时间戳有秒数,你可以先移除它们,以便按整分钟分组。

df2 = (
    df
    .groupby(df['timestamp'].map(lambda x: x.replace(second=0)))['status']
    .value_counts()
    .unstack(fill_value=0)
    .reset_index()
)
>>> df2
status           timestamp  FAILED  PASSED  UNKNOWN
0      2019-01-01 09:00:00       2       2        1
1      2019-01-01 09:01:00       1       1        0

您可能希望在该范围内填写每分钟的值。与上面相同的代码,但不要在最后重置索引。然后:

df2 = df2.reindex(pd.date_range(df2.index[0], df2.index[-1], freq='1min'), fill_value=0)

时间

时间会因数据集的大小(大 vs 小)、异构数据 vs 同质数据等而有所不同。鉴于数据集基本上是一个日志,人们预计会有很多时间戳高变异性的数据。为了创建更适合的测试数据,让我们将示例数据帧扩大100k倍,然后使时间戳唯一(每分钟一个)。

df_ = pd.concat([df] * 100000)
df_['timestamp'] = pd.date_range(df_.timestamp.iat[0], periods=len(df_), freq='1min')

以下是新的时间安排:

%timeit pd.crosstab(df_['timestamp'],df['status'])
# 4.27 s ± 150 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df_.groupby(['timestamp','status']).size().unstack(fill_value=0)
# 567 ms ± 34.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
(
    df_
    .groupby(['timestamp', 'status'])
    .size()
    .unstack(fill_value=0)
    .reset_index()
)
# 614 ms ± 27.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
(
    df_
    .groupby(df['timestamp'].map(lambda x: x.replace(second=0)))['status']
    .value_counts()
    .unstack(fill_value=0)
    .reset_index()
)
# 147 ms ± 6.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

1
我认为unstack有一个选项fill_value,可以让你免去fillna().astype()的麻烦。 - Quang Hoang
1
通常情况下,groupby().value_count().unstack() 的性能比其他方法更好。+1 - Quang Hoang
1
@ansev 你的样本数据框有多少行?只有玩具示例中的7行吗?此外,我不认为你的方法考虑了时间戳包含秒的分钟分组。 - Alexander
正如我所说,我同意对于大尺寸和优化此解决方案(如果没有优化,此解决方案会随着数据框的大小而变差),value_counts更好。 - ansev
1
你是什么意思说“恶化数据框的大小”? - Alexander
显示剩余4条评论

0

这个会起作用:

df.groupby(['timestamp', 'status']).size().unstack(level=1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接