Pandas - 根据另一列的条件对列进行分组

7

我在pandas方面遇到了问题,具体是如何按条件分组多列值:

以下是我的数据样式,作为pandas数据帧呈现:

id      trigger     timestamp
1       started     2017-10-01 14:00:1
1       ended       2017-10-04 12:00:1
2       started     2017-10-02 10:00:1
1       started     2017-10-03 11:00:1
2       ended       2017-10-04 12:00:1    
2       started     2017-10-05 15:00:1
1       ended       2017-10-05 16:00:1
2       ended       2017-10-05 17:00:1

我的目标是找到按id分组后的日期之间的天数、小时数或分钟数的差异。

我的输出应该更像这样(以小时为单位):

id      trigger     timestamp           trigger     timestamp               diff
1       started     2017-10-01 14:00:1  ended       2017-10-04 12:00:1      70
1       started     2017-10-03 11:00:1  ended       2017-10-05 16:00:1      53
2       started     2017-10-02 10:00:1  ended       2017-10-04 12:00:1      26
2       started     2017-10-05 15:00:1  ended       2017-10-05 17:00:1      2

我尝试了很多选项,但是我找不到最有效的解决方案。

以下是我目前的代码:

首先,我尝试将数据分为“开始”和“结束”:

df['started'] = df.groupby(['id', 'timestamp'])['trigger'] == 'started'

df['ended'] = df.groupby(['id', 'timestamp'])['trigger'] == 'ended'

并且:
df.groupby(['id', 'started', 'ended'], as_index=True).sum()

但它并没有起作用。

df['started'] = df.groupby(['trigger'])['timestamp'].np.where(df['trigger']=='started')

没有良好的结果。

有人能指点一下如何使用pandas来做到这一点吗? 我的数据中还会有空值匹配,我该如何使用df.fillna(method='ffill')将NaN或缺失数据添加到新的数据框中。


你可以使用 df.iterrows() 或者更快的 df.itertuples() 来解决这个问题。至于 .fillna,问题不是很清楚,但是你发布的代码将会向前填充值,你可以像这样做 df['column_name'].fillna(value=0) 来替换名为 column_name 的特定列中的 NaN0 - joaoavf
1个回答

9
  1. idtrigger设置为索引
  2. 由于索引包含重复条目,因此附加另一个索引列以进行分组累计计数。总之,df必须具有3列的MultiIndex
  3. timestamp上执行unstack
  4. 按小时找到列之间的差异并将结果重新分配
df['timestamp'] = pd.to_datetime(df['timestamp']) # if necessary

i = df.groupby(['id', 'trigger']).cumcount()
df.set_index(['id', i, 'trigger']).timestamp.unstack().assign(
       diff=lambda d: d.ended.sub(d.started).dt.total_seconds() / 3600
)

感谢piRSquared的改进。
v

                  timestamp                      diff
trigger               ended             started      
id                                                   
1  0    2017-10-04 12:00:01 2017-10-01 14:00:01  70.0
   1    2017-10-05 16:00:01 2017-10-03 11:00:01  53.0
2  0    2017-10-04 12:00:01 2017-10-02 10:00:01  50.0
   1    2017-10-05 17:00:01 2017-10-05 15:00:01   2.0

结果可能不完全符合您问题中所示的情况,但我认为使用列的 MultiIndex 会比两个触发列更清晰地表示您的输出。

谢谢,这看起来是个好主意。现在唯一的问题是:由于数据无法找到所有开始和结束点的匹配项,我需要找到一种处理空值的方法。目前我遇到了以下错误:'ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().' code - El_Patrón
@El_Patrón 嗯,假设这些是日期时间列,你应该能够有效地处理NaN/NaT值 - 你会得到NaT作为结果。如果你得到了其他东西,那么我们可能需要更仔细地查看你的代码和数据。很可能是你的代码问题。没有进一步的上下文,调试将会很困难,所以请尝试打开另一个问题,并提供导致这个混乱的数据/代码。 - cs95
@COLDSPEED,带有时间戳的字段实际上是格式化为字符串的,您认为这会导致错误吗?抱歉,我的知识有限。 - El_Patrón
@El_Patrón 是的。首先尝试这个:df['timestamp'] = pd.to_datetime(df['timestamp']) - cs95
嗨@COLDSPEED,感谢您的帮助,我仍然遇到一些错误。可能代码格式不正确,也许我应该像这样做:df.set_index(['id', i, 'trigger']).['timestamp'].unstack().assign(diff=lambda d: d.'started'.sub(d.'ended').dt.total_secounds()/3600) 您认为呢? - El_Patrón

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