Pandas - 将重复值替换为NaN并保留行

7

如何将每个组的重复项替换为 NaN,同时保留行?

我需要保留行而不是删除,并且可能还要保留首次出现的原始值。

import pandas as pd
from datetime import timedelta

df = pd.DataFrame({
    'date': ['2019-01-01 00:00:00','2019-01-01 01:00:00','2019-01-01 02:00:00', '2019-01-01 03:00:00',
             '2019-09-01 02:00:00','2019-09-01 03:00:00','2019-09-01 04:00:00', '2019-09-01 05:00:00'],
    'value': [10,10,10,10,12,12,12,12],
    'ID': ['Jackie','Jackie','Jackie','Jackie','Zoop','Zoop','Zoop','Zoop',]
})

df['date'] = pd.to_datetime(df['date'], infer_datetime_format=True)


date    value   ID
0   2019-01-01 00:00:00 10  Jackie
1   2019-01-01 01:00:00 10  Jackie
2   2019-01-01 02:00:00 10  Jackie
3   2019-01-01 03:00:00 10  Jackie
4   2019-09-01 02:00:00 12  Zoop
5   2019-09-01 03:00:00 12  Zoop
6   2019-09-01 04:00:00 12  Zoop
7   2019-09-01 05:00:00 12  Zoop

期望的数据框:

date    value   ID
0   2019-01-01 00:00:00 10  Jackie
1   2019-01-01 01:00:00 NaN Jackie
2   2019-01-01 02:00:00 NaN Jackie
3   2019-01-01 03:00:00 NaN Jackie
4   2019-09-01 02:00:00 12  Zoop
5   2019-09-01 03:00:00 NaN Zoop
6   2019-09-01 04:00:00 NaN Zoop
7   2019-09-01 05:00:00 NaN Zoop

编辑:

重复值应该只在相同日期上被删除,不考虑频率。因此,如果值10在1月1日出现两次,在1月2日出现三次,则值10应该仅在1月1日和1月2日各显示一次。

3个回答

10

我假设您在列valueID上检查重复,并进一步检查date列中的date

df.loc[df.assign(d=df.date.dt.date).duplicated(['value','ID', 'd']), 'value'] = np.nan

Out[269]:
                 date  value      ID
0 2019-01-01 00:00:00   10.0  Jackie
1 2019-01-01 01:00:00    NaN  Jackie
2 2019-01-01 02:00:00    NaN  Jackie
3 2019-01-01 03:00:00    NaN  Jackie
4 2019-09-01 02:00:00   12.0    Zoop
5 2019-09-01 03:00:00    NaN    Zoop
6 2019-09-01 04:00:00    NaN    Zoop
7 2019-09-01 05:00:00    NaN    Zoop

正如@Trenton建议的那样,您可以使用pd.NA来避免导入numpy

注意:正如@rafaelc建议的那样:这里是详细解释pd.NAnp.nan之间差异的链接: https://pandas.pydata.org/pandas-docs/stable/whatsnew/v1.0.0.html#experimental-na-scalar-to-denote-missing-values

df.loc[df.assign(d=df.date.dt.date).duplicated(['value','ID', 'd']), 'value'] = pd.NA

Out[273]:
                 date value      ID
0 2019-01-01 00:00:00    10  Jackie
1 2019-01-01 01:00:00  <NA>  Jackie
2 2019-01-01 02:00:00  <NA>  Jackie
3 2019-01-01 03:00:00  <NA>  Jackie
4 2019-09-01 02:00:00    12    Zoop
5 2019-09-01 03:00:00  <NA>    Zoop
6 2019-09-01 04:00:00  <NA>    Zoop
7 2019-09-01 05:00:00  <NA>    Zoop

3
你可以使用pd.NA,无需为了使用np.nan而导入numpy。 - Trenton McKinney
重复的值应该只在相同日期下被删除,而不考虑频率。因此,如果值10在1月1日出现两次,在1月2日出现三次,则值10应该只在1月1日和1月2日各出现一次。 - Starbucks
3
值得一提的是,pd.NAnp.nan的行为可以非常不同。它们并不一定是可互换的。 - rafaelc
2
@rafaelc:添加了一个链接,解释了pd.NAnp.nan之间的区别。 - Andy L.
2
我只是在玩 pd.NA,并意识到当创建一个由 intsfloats 组成的列时,pd.NA 会导致该列成为 object 类型。同样,pd.NA 强制 'value'type 变为 object。因此,我撤回我的建议。 - Trenton McKinney

1
如果数据框已排序,则此方法有效-就像您的示例中一样:

这个方法在数据框已排序的情况下有效-就像您的示例中一样:

import numpy as np                                    # to be used for np.nan

df['duplicate'] = df['value'].shift(1)                # create a duplicate column 
df['value'] = df.apply(lambda x: np.nan if x['value'] == x['duplicate'] \
                          else x['value'], axis=1)    # conditional replace
df = df.drop('duplicate', axis=1)                     # drop helper column

1
在日期上进行分组,并取第一个观察值(不一定是按时间排序时的第一个),然后将结果合并回原始数据框。
df2 = df.groupby([df['date'].dt.date, 'ID'], as_index=False).first()
>>> df.drop(columns='value').merge(df2, on=['date', 'ID'], how='left')[df.columns]
                 date  value      ID
0 2019-01-01 00:00:00   10.0  Jackie
1 2019-01-01 01:00:00    NaN  Jackie
2 2019-01-01 02:00:00    NaN  Jackie
3 2019-01-01 03:00:00    NaN  Jackie
4 2019-09-01 02:00:00   12.0    Zoop
5 2019-09-01 03:00:00    NaN    Zoop
6 2019-09-01 04:00:00    NaN    Zoop
7 2019-09-01 05:00:00    NaN    Zoop

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