Pandas 删除每组中前几行包含 nan

Pandas drop the first few rows contain nan in each group

我有一个面板数据,我想删除每组中包含 NaN 的第一(几)行。 (或者一些通用的方法,可以根据组内的索引和其他条件进行下降。)

df = pd.DataFrame(
{'ID': [10001, 10001, 10001, 10002, 10002, 10002, 10003, 10003, 10003, 10003],
 'PRICE': [None, 11.5, 14.31, 15.125, 14.44, None, None, None, None, 23.55],
 'date': [19920103, 19920106, 19920107, 19920108, 19920109, 19920110,
          19920113, 19920114, 19920115, 19920116]},
index = range(1,11))

数据看起来像:

    ID      PRICE   date
1   10001   NaN     19920103
2   10001   11.500  19920106
3   10001   14.310  19920107
4   10002   15.125  19920108
5   10002   14.440  19920109
6   10002   NaN     19920110
7   10003   NaN     19920113
8   10003   NaN     19920114
9   10003   NaN     19920115
10  10003   23.550  19920116

我想删除第 1 行和第 7 行,但不想删除第 9 行,因为第 9 行不是前几个缺失的观察结果之一,我试过了

def mask_first_missing(x):
    result = x.notnull() & x.rank()==1
    return result

mask = df.groupby(['ID'])['PRICE'].transform(mask_first_missing).astype(bool)
print(df[mask])

但它删除了第 1、7 和 9 行,显然第 9 行不是第 3 组中的第一个观察值,

如果我这样做

df[df.groupby('ID', as_index=False)['PRICE'].nth(0).notnull()]

那么groupby对象创建的索引与原始dataframe不对齐

有人可以帮我解决这个问题吗?谢谢

这是一种方法:

notnull = df.PRICE.notnull()
protected = df.index > df.PRICE.last_valid_index()

df[notnull | protected]

使用自定义排名的替代方法:

In [49]: %paste
df[df.assign(x=np.where(pd.isnull(df.PRICE), 1, np.nan))
     .groupby('ID').x.cumsum().fillna(np.inf) > 1
]
## -- End pasted text --
Out[49]:
      ID   PRICE      date
2  10001  11.500  19920106
3  10001  14.310  19920107
4  10002  15.125  19920108
5  10002  14.440  19920109
6  10002  14.120  19920110
8  10003  16.500  19920114
9  10003     NaN  19920115

解释:

In [50]: df.assign(x=np.where(pd.isnull(df.PRICE), 1, np.nan))
Out[50]:
      ID   PRICE      date    x
1  10001     NaN  19920103  1.0
2  10001  11.500  19920106  NaN
3  10001  14.310  19920107  NaN
4  10002  15.125  19920108  NaN
5  10002  14.440  19920109  NaN
6  10002  14.120  19920110  NaN
7  10003     NaN  19920113  1.0
8  10003  16.500  19920114  NaN
9  10003     NaN  19920115  1.0

In [51]: df.assign(x=np.where(pd.isnull(df.PRICE), 1, np.nan)).groupby('ID').x.cumsum().fillna(np.inf)
Out[51]:
1    1.000000
2         inf
3         inf
4         inf
5         inf
6         inf
7    1.000000
8         inf
9    2.000000
Name: x, dtype: float64

In [52]: df.assign(x=np.where(pd.isnull(df.PRICE), 1, np.nan)).groupby('ID').x.cumsum().fillna(np.inf) > 1
Out[52]:
1    False
2     True
3     True
4     True
5     True
6     True
7    False
8     True
9     True
Name: x, dtype: bool

感谢您的帮助,但我认为这两个答案都不适合我的任务。

我自己想出了一个解决方案,方法是创建一个子索引列。

df = pd.DataFrame(
{'ID': [10001, 10001, 10001, 10001, 10002, 10002, 10002, 10003, 10003, 10003, 10003],
 'PRICE': [None, 11.5, None, 14.31, 15.125, 14.44, None, None, None, None, 23.55],
 'date': [19920103, 19920106, 19920107, 19920108, 19920109, 19920110,
          19920113, 19920114, 19920115, 19920116, 19920122]},
index = range(1,12)) 

df.loc[:, 'subindex'] = df.groupby('ID').cumcount()

然后获得

    ID      PRICE   date    subindex
1   10001   NaN     19920103    0
2   10001   11.500  19920106    1
3   10001   NaN     19920107    2
4   10001   14.310  19920108    3
5   10002   15.125  19920109    0
6   10002   14.440  19920110    1
7   10002   NaN     19920113    2
8   10003   NaN     19920114    0
9   10003   NaN     19920115    1
10  10003   NaN     19920116    2
11  10003   23.550  19920122    3

现在我可以 select 基于列 'subindex' 每个组的第 n 个观察,而不是基于 groupby 做所有事情。

现在,如果我想删除每组 'PRICE' 的前两个 NaN 观察值,我可以创建一个掩码

mask_first_few_nan = (df.loc[:, 'PRICE'].isnull()) & (df.loc[:, 'subindex'] <= 1)
df[~mask_first_few_nan]

结果是

    ID      PRICE   date    subindex
2   10001   11.500  19920106    1
3   10001   NaN     19920107    2
4   10001   14.310  19920108    3
5   10002   15.125  19920109    0
6   10002   14.440  19920110    1
7   10002   NaN     19920113    2
10  10003   NaN     19920116    2
11  10003   23.550  19920122    3