pd.Grouper() 应用于日期时间时,更改原始日期列

Posted

技术标签:

【中文标题】pd.Grouper() 应用于日期时间时,更改原始日期列【英文标题】:pd.Grouper() when applied on datetime, changes the original column of dates 【发布时间】:2021-12-18 19:42:12 【问题描述】:

我的巨大数据框中有一个示例数据框,如下所示。

import pandas as pd
import numpy as np

NaN = np.nan
data = 
    'ID':['AAQRB','AAQRB','AAQRB', 
'AHXSJ','AHXSJ','AHXSJ','GABOY','GABOY','GABOY','GHZGS','GHZGS','GHZGS'],
    'Date':['10/18/2021  10:52:53 PM','10/18/2021  10:53:55 PM', '10/25/2021  5:55:43 PM',
           '10/22/2021  10:37:06 PM','10/22/2021  10:38:22 PM','10/22/2021  10:39:56 PM',
           '11/1/2021  1:27:15 AM','11/1/2021  1:28:45 AM','11/2/2021  8:53:39 PM',
           '10/29/2021  11:13:57 PM', '10/29/2021  11:17:47 PM', '10/29/2021  11:19:15 PM'], 
    'Race_x':[NaN,NaN,NaN,NaN,NaN,1,NaN,NaN,1, NaN,NaN,1],
    'Vaccine':['TRUE',NaN,NaN,'TRUE',NaN,NaN,'TRUE',NaN,NaN,'FALSE',NaN,NaN],
    'Study_activity': 
   [NaN,'continue',NaN,NaN,'continue',NaN,NaN,'continue',NaN,NaN,'continue',NaN],
    'Who_Contacted': 
   [NaN,NaN,'WeContacted',NaN,NaN,'WeContacted',NaN,NaN,NaN,NaN,NaN,'WeContacted']

test_df = pd.DataFrame(data)

目标是获取每个 ID 的所有第一个值,并将参与者的几行过滤为包含所有信息的单行。最终的数据框应如下图所示。

代码尝试

我尝试使用 Grouper() 函数,代码如下。

test_df['Date'] = pd.to_datetime(test_df['Date'])

test_df1 = (test_df.groupby(['ID', pd.Grouper(key='Date', freq='D')])
   .agg("first")
   .reset_index())
 baseline_df = test_df1[~test_df1.duplicated(subset = ['ID'], keep='first')]

但问题是,如果我使用 freq='D',那么第二天输入的 Race_x 值就会丢失。输出如下图所示。

如果我使用 freq='M' 或 freq='Y',则会捕获其他值,但 Dates 列的值会发生变化,我们会得到每个 ID 的月末日期,如下所示。

最后的“日期”列应该是每个 ID 的“日期”的第一个条目,并且不应更改。

非常感谢任何帮助。谢谢!

【问题讨论】:

【参考方案1】:

创建一个按月分组的虚拟列:

>>> test_df.assign(month=test_df['Date'].dt.strftime('%Y-%m')) \
           .groupby(['ID', 'month']).agg('first') \
           .droplevel(1).reset_index() \
           .assign(Date=lambda x: x['Date'].dt.date)

      ID        Date  Race_x Vaccine Study_activity Who_Contacted
0  AAQRB  2021-10-18     NaN    TRUE       continue   WeContacted
1  AHXSJ  2021-10-22     1.0    TRUE       continue   WeContacted
2  GABOY  2021-11-01     1.0    TRUE       continue          None
3  GHZGS  2021-10-29     1.0   FALSE       continue   WeContacted

【讨论】:

这为每个 ID 提供 2 或 3 行。我的目标是为每个参与者获得一行。这只是一个庞大数据集的样本,每个 ID 至少有 30 个条目,必须合并到一行。 在 agg 函数中做你想做的事,但你的问题是 pd.Grouper,不是吗? 是的。它是。即使在我过滤到您上面给出的格式之后,它也会是同样的问题。 这可以在“年”而不是“月”中完成吗?并得到相同的结果?如果是这样,代码中需要更改什么? 是的。这是正确的。我做到了,而且效果很好。谢谢【参考方案2】:

看起来您只想将 groupby ID 和 Date 聚合为第一,其他一切都与您拥有有效值时一样。

假设所有Race_xVaccineStudy_activityWho_Contacted 始终是 ID 的单个非 NaN 值。聚合前可以先bfill

这会将非 NaN 值收集到参与者的第一个条目。

test_df['Date'] = pd.to_datetime('Date').dt.date
test_df.update(test_df.groupby('ID').bfill()) 

然后,尝试聚合。

test_df.groupby('ID').first().reset_index()


>>>     ID        Date Race_x Vaccine Study_activity Who_Contacted
 0   AAQRB  2021-10-18    NaN    TRUE       continue WeContacted
 1   AHXSJ  2021-10-22    1.0    TRUE       continue WeContacted
 2   GABOY  2021-11-01    1.0    TRUE       continue       NaN
 3   GHZGS  2021-10-29    1.0   FALSE       continue WeContacted

【讨论】:

不错的解决方案。但不幸的是,它没有为原始数据集提供所需的解决方案。最终的解决方案类似于 Corralien 在他的解决方案中的解决方案。 您能发布您的预期结果吗?我以为每个 ID 需要一行。 是的,但原始数据集不是单个非 NaN 值。这些值可以在后续行中重复。我认为这是 backfill() 不起作用的原因。问题中给出了预期的结果。 感谢您的更新。该解决方案是实现“获取每个 ID 的所有第一个值”。通过对每个 ID 执行 bfill,第一个非 NaN 值总是出现在 ID 的第一行。此结果看起来与您的预期结果完全相同。我错过了什么?也许,样本不能代表多个非 NaN 值的场景。

以上是关于pd.Grouper() 应用于日期时间时,更改原始日期列的主要内容,如果未能解决你的问题,请参考以下文章

pyspark:在日期和时间上重新采样 pyspark 数据帧

Git:提交日期的批量更改

当 iPad 日期时间设置更改时通知应用程序

如何在 C# 应用程序范围内更改仅日期部分将用于日期时间比较

熊猫桶时间戳到 TimeGrouper 频率组

Bootstrap 日期范围选择器移动突出显示更改日期时的前一个日期