【问题标题】:Pandas: Combine rows having same date different time into a single row of the same date(consolidate partial data of different time for same identity)熊猫:将具有相同日期不同时间的行合并为同一日期的一行(合并不同时间的部分数据以获得相同的身份)
【发布时间】:2021-11-17 05:57:39
【问题描述】:

我有一个示例数据框,如下所示。

import pandas as pd
import numpy as np

NaN = np.nan
data = {'ID':['A', 'A', 'A', 'B','B','B'],
    'Date':['2021-09-20 04:34:57', '2021-09-20 04:37:25', '2021-09-20 04:38:26', '2021-09-01 
    00:12:29','2021-09-01 11:20:58','2021-09-02 09:20:58'],
    'Name':['xx',NaN,NaN,'yy',NaN,NaN],
    'Height':[174,NaN,NaN,160,NaN,NaN],
    'Weight':[74,NaN,NaN,58,NaN,NaN],
    'Gender':[NaN,'Male',NaN,NaN,'Female',NaN],
    'Interests':[NaN,NaN,'Hiking,Sports',NaN,NaN,'Singing']}

 df1 = pd.DataFrame(data)
 df1 

我想将同一日期的数据合并到一行中。 “日期”列采用时间戳格式。 最终输出应如下图所示。

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

【问题讨论】:

  • 我已经编辑了这个问题。大多数列都有 np.nan 值而不是空字符串。可能是这导致了错误。
  • @SeaBean 我尝试了您的更新解决方案。现在删除具有整数/数值的列。我想在数据框中拥有所有列而不删除任何列。我已经相应地更新了问题。
  • 您修改后的数据根本无法运行。 160cm 不加引号不是合法文本。你的意思是字符串吗? '160cm' 而不是?
  • 其更新后的@SeaBean 160 是一个整数。其他是字符串

标签: python pandas dataframe data-science data-processing


【解决方案1】:

新解决方案

旧的解决方案基于问题的初始版本,其中空字符串而不是 NaN 值用于未定义的值,并且所有列都是字符串类型。使用NaN 更新未定义值的问题(即使也更新为具有不同的列数据类型的数字和字符串类型),解决方案可以简化如下:

您可以使用.groupby() + GroupBy.last()ID 和日期(没有时间)进行分组,然后将NaN 和非NaN 元素与最新(假设Date 列按时间顺序显示)ID 的非NaN 值,如下所示:

# Convert `Date` to datetime format
df1['Date'] = pd.to_datetime(df1['Date'])

# Sort `df1` with ['ID', 'Date'] order if not already in this order
#df1 = df1.sort_values(['ID', 'Date'])

df_out = (df1.groupby(['ID', pd.Grouper(key='Date', freq='D')])
             .last()
             .reset_index()
         ).replace([None], [np.nan])

结果:

print(df_out)


   ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174.0   74.0    Male  Hiking,Sports
1  B 2021-09-01   yy  160.0   58.0  Female            NaN
2  B 2021-09-02  NaN    NaN    NaN     NaN        Singing

旧解决方案

您可以使用.groupby() + .agg()ID 和日期进行分组,然后聚合NaN 和非NaN 元素,如下所示:

# Convert `Date` to datetime format
df1['Date'] = pd.to_datetime(df1['Date'])

df_out = (df1.groupby(['ID', pd.Grouper(key='Date', freq='D')])
             .agg(lambda x: ''.join(x.dropna().astype(str)))
             .reset_index()
         ).replace('', np.nan)

结果:

print(df_out)


   ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174.0   74.0    Male  Hiking,Sports
1  B 2021-09-01   yy  160.0   58.0  Female            NaN
2  B 2021-09-02  NaN    NaN    NaN     NaN        Singing

由于您的原始问题包含所有字符串类型的列,因此上述代码可以很好地将所有列的结果作为字符串类型。但是,您编辑的问题包含数字和字符串类型的数据。为了保留原有的数据类型,我们可以修改代码如下:

# Convert `Date` to datetime format
df1['Date'] = pd.to_datetime(df1['Date'])

df_out = (df1.groupby(['ID', pd.Grouper(key='Date', freq='D')])
             .agg(lambda x: np.nan if len(w:=x.dropna().reset_index(drop=True)) == 0 else w)
             .reset_index()
         )

结果:

print(df_out)


   ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174.0   74.0    Male  Hiking,Sports
1  B 2021-09-01   yy  160.0   58.0  Female            NaN
2  B 2021-09-02  NaN    NaN    NaN     NaN        Singing


print(df_out.dtypes)

ID                   object
Date         datetime64[ns]
Name                 object
Height              float64            <==== retained as numeric dtype
Weight              float64            <==== retained as numeric dtype
Gender               object
Interests            object
dtype: object

【讨论】:

  • @Corralien 这就是我必须在这里引用列名列表的原因。我认为同样的警告,但用列名解决了。
  • 是的,你是对的。奇怪的行为 ?!?我对 Grouper 没有这个警告
  • @Corralien Pandas 只是有时会有奇怪的行为,我同意! :-)
  • @SeaBean 我也尝试使用 Grouper() 实现您的解决方案。但同样,我丢失了所有包含“字符串”值的列。当我使用 .agg() 和 .sum() 时。
  • @Shiva 见我上面的编辑。使用 NaN 值而不是空字符串来满足您修改后的示例数据的增强答案。
【解决方案2】:

首先转换为日期时间和地板:

In [3]: df["Date"] = pd.to_datetime(df["Date"]).dt.floor('D')

In [4]: df
Out[4]:
  ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174cm   74kg
1  A 2021-09-20                       Male
2  A 2021-09-20                             Hiking,Sports
3  B 2021-09-01   yy  160cm   58kg
4  B 2021-09-01                     Female
5  B 2021-09-02                                   Singing

现在使用groupbysum

In [5]: df.groupby(["ID", "Date"]).sum().reset_index()
Out[5]:
  ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174cm   74kg    Male  Hiking,Sports
1  B 2021-09-01   yy  160cm   58kg  Female
2  B 2021-09-02                                   Singing

【讨论】:

  • 你的想法不错(我同时选择了同样的方法)+1。
  • @ddejohn 我尝试实现这一点。但是由于某种原因,在使用 groupby() 行之后,具有字符串值的列都在最终数据帧中被删除。
【解决方案3】:

如果您的数据作为样本被正确排序,您可以按如下方式合并您的数据:

>>> df1.groupby(['ID', pd.Grouper(key='Date', freq='D')]) \
       .sum().reset_index()

  ID       Date Name Height Weight  Gender      Interests
0  A 2021-09-20   xx  174cm   74kg    Male  Hiking,Sports
1  B 2021-09-01   yy  160cm   58kg  Female               
2  B 2021-09-02                                   Singing

【讨论】:

  • 你压得太紧了。 Singing 所在的行来自 09-02,但您已将其与 09-01 分组。
  • 是的。我看见。我正在尝试修复它。
  • 是的。此外,必须在“日期”列中删除时间。应该只剩下日期。
  • @Corralien 我试过实现这个。但是由于某种原因,在使用 groupby() 行之后,具有字符串值的列都在最终数据帧中被删除。不知道为什么会这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-12
  • 2013-07-31
  • 2021-11-20
  • 2015-10-21
相关资源
最近更新 更多