【问题标题】:Keep all the column values from first row and any updates in subsequent rows in a Pandas group?保留 Pandas 组中第一行的所有列值以及后续行中的任何更新?
【发布时间】:2020-10-23 16:29:37
【问题描述】:

我有一个 Pandas 数据框,其中包含一个 id 列、一个日期列 dt 和一些可能包含 0 或 1(或根本没有值)的二进制列。

import pandas as pd 
import datetime
  
# initialize list of lists 
data = [
    ['A', None, None, 0, 0, datetime.date(2020,5,1)],
    ['A', 1, None, 0, 1, datetime.date(2020,5,2)],
    ['A', None, 1, 0, 0, datetime.date(2020,5,3)],
    ['B', 1, 1, 0, None, datetime.date(2020,5,3)]
] 
  
# Create the pandas DataFrame 
df = pd.DataFrame(data, columns = ['id', 'd1', 'd2', 'd3', 'd4', 'dt']) 
  
# print dataframe. 
df

   id   d1   d2  d3   d4          dt
0   A  NaN  NaN   0  0.0  2020-05-01
1   A  1.0  NaN   0  1.0  2020-05-02
2   A  NaN  1.0   0  0.0  2020-05-03
3   B  1.0  1.0   0  NaN  2020-05-03

对于每个 ID,在任何给定日期,我都想跟踪二进制列从最早日期到最近日期的变化,保持最新值,但 忽略从数字到无的任何变化.

每个 ID 将有一行显示最新的已知值。

为了澄清,对于每个 ID,从最早日期到最近日期逐行:

  • 如果列值从无变为数字,则使用数字作为最新的已知值
  • 如果列值从 0 变为 1,则使用 1 作为最新的已知值
  • 如果列值从 1 变为 0,则使用 0 作为最新的已知值
  • 但是,如果列从数字更改为无,则将数字保留为最新的已知值

例如,在上面的数据框中,对于id ='A'

2020 年 5 月 2 日,该值将是 'A', 1, None, 0, 1(因为 d1 从 None 变为 1,d4 从 0 变为 1)

在 2020 年 5 月 3 日,该值将为 'A', 1, 1, 0, 0(因为 d2 从 None 变为 1 并且 d4 从 1 变为 0,但是 d1 从 1 变为 None 所以仍然保留 1 而不是无,因为这是最后一个已知值)

我对如何跟踪这样的更改有点迷茫。有什么方法可以在 lambda 中应用 shift 还是编写自定义函数来逐行比较?

df.groupby('id').apply(lambda x: x.sort_values(by='dt'). ???)

编辑添加:最后,我只想要一个数据框,每个 ID 有一行,所有二进制列的最新已知值。

    id  d1   d2  d3   d4
0   A   1    1   0    0
1   B   1    1   0    NaN

任何帮助将不胜感激!

【问题讨论】:

  • 我不确定这个决定的标准是什么,所以我无法回应,但很容易让它成为一个垂直持有记录。我要对你说的就这些了吗?
  • @r-beginners 标准相当简单 - 对于每个组(按 ID),我必须将每一行与前一行进行比较......所以我从 5 月 1 日开始,初始化每个 @987654335 @;然后在 5 月 2 日,将这些列的值与上一行(即 5 月 1 日)进行比较,如果有任何更改为数字,我使用新值,否则保持不变;然后在 5 月 3 日做同样的事情,与已知值进行比较,依此类推。我只在列中的值更改为数字时更新(所以 null 到 0 或 1 是可以的。0 到 1 反之亦然反之亦然.. 但如果数字变为 null,则不更新)
  • 为了响应cmet,我又写了代码,但是有一个地方有一个决策逻辑无法改进,原来是这样。如果你愿意,我仍然会发布。很抱歉我帮不了你。
  • 没问题,@r-beginners - 感谢您对我的耐心和帮助 :) 我想出了一个解决方案并将其发布在答案下方。

标签: python pandas dataframe aggregate pandas-groupby


【解决方案1】:

我已经找到了解决问题的方法 - pandas.DataFrame.ffill

正如我在问题中提到的,我想在任何给定日期跟踪每个id 的每一列的最新“已知”数值。

所以,如果我这样做,我会得到我需要的:

df.groupby('id').apply(lambda x: x.sort_values(by='dt').ffill().tail(1))

这是因为:

  • 对于每个 ID(例如 AB),行按日期排序 dt
  • 前向填充将确保较近日期的空值将由前面的数值填充。
  • 数值不受影响。如果我选择最新的日期,最新的数值将可用,以及现在用最新的已知数值填充的空值。
  • 最后,我使用tail(1) 获取最新日期的行

这给了我想要的输出:

    id  d1   d2  d3   d4
0   A   1    1   0    0
1   B   1    1   0    NaN

如果我想查看最新日期之前的任何日期,我可以简单地过滤并保留少于所需日期的行,然后做同样的事情。这将为我提供直到该日期为止的最新已知值。

【讨论】:

  • 很高兴您解决了这个问题。我发布了我的方法。
【解决方案2】:

我尝试将样本数据转换为垂直格式并通过id与前一天的数据进行比较,当我尝试使用循环过程来处理它时遇到了挑战。

df = df.melt(id_vars=['id','dt'], var_name='D', value_name='Value')
df.sort_values(['id','D'], ascending=[True,True], ignore_index=True, inplace=True)
df
0   A   2020-05-01  d1  NaN
1   A   2020-05-02  d1  1.0
2   A   2020-05-03  d1  NaN
3   A   2020-05-01  d2  NaN
4   A   2020-05-02  d2  NaN
5   A   2020-05-03  d2  1.0
6   A   2020-05-01  d3  0.0
7   A   2020-05-02  d3  0.0
8   A   2020-05-03  d3  0.0
9   A   2020-05-01  d4  0.0
10  A   2020-05-02  d4  1.0
11  A   2020-05-03  d4  0.0
12  B   2020-05-03  d1  1.0
13  B   2020-05-03  d2  1.0
14  B   2020-05-03  d3  0.0
15  B   2020-05-03  d4  NaN

cnt = 0
for idx,d,val in zip(df.index,df['D'],df['Value']):
    if cnt == 0:
        df.at[idx, 'new'] = val
    if cnt >= 1:
        print(idx, d, pre_d, val, pre_val)
        if d == pre_d:
            if val == 0.0 or pre_val == 1.0:
                df.at[idx, 'new'] = 1.0
            if val == 1.0 or pre_val == 0.0:
                df.at[idx, 'new'] = 1.0 
        (....)
        else:
            df.at[idx, 'new'] = val

    pre_idx = idx
    pre_val = val
    pre_d = d
    cnt += 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-23
    • 2021-06-24
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 2019-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多