pandas 棘手的列内逻辑

pandas tricky intra-column logic

我有一个包含三列的 DataFrame,tbh:

              t          b           h
0           NaN      False           6
1      6.023448      False          38
2     12.996233      False          46
3      2.484907      False          67
4      5.062595      False          81
5      4.624973      False          82
6      3.367296      False          38
7      3.688879      False          53
8      6.926577       True          38
9     14.972346      False          81
10    14.442651      False          78
11     3.367296      False          67
12     5.236442      False          46
13     5.298317       True           8

并且我想生成一个新列,它传播 h 每个实例的值,b==True,向后,并且只传播到下一个这样的实例或第一次出现t>9.5。其余的填充 NaN 的。这是我需要的输出示例:

              t          b           h       i
0           NaN      False           6     NaN
1      6.023448      False          38     NaN
2     12.996233      False          46      38
3      2.484907      False          67      38
4      5.062595      False          81      38
5      4.624973      False          82      38
6      3.367296      False          38      38
7      3.688879      False          53      38
8      6.926577       True          38      38
9     14.972346      False          81     NaN
10    14.442651      False          78       8
11     3.367296      False          67       8
12     5.236442      False          46       8
13     5.298317       True           8       8

我想避免遍历行,因为我有数百万行。我尝试使用 where 获取 b==True 实例,然后使用 bfill 选项获取 fillna 实例,但无法告诉他何时开始填充。此外,这将被 apply 编辑到 groupby 中的各个组,因此我需要一个函数,将一列添加到它的参数和 returns 整个框架

def get_i(x):
    x['i']=x['h'].where(x['b']==True).fillna(value=None,method='backfill').dropna()
    return x

首先,我颠倒数据帧的顺序。它使我更简单,但这不是必需的:

df = df.iloc[::-1]

为了隔离 b == True 所在的实例,我添加了一个新列:

df['cum_b'] = df['b'].cumsum()

这意味着我可以按 cum_b 分组以分别处理每个实例。

我定义了一个函数,用于查找 t > 9.5 处的第一个索引,并填充列 i 直到该索引:

def func(dfg):
    idx = max(dfg[dfg.t > 9.5].index, default=-1)
    dfg.loc[:, 'i'] = dfg.h.iloc[0]
    dfg.loc[dfg.index < idx, 'i'] = np.nan
    return dfg.i

请注意我是如何使用 maxindex < idx 的,因为我在恢复其顺序后没有重置数据帧的索引。

当我应用这个函数时,我得到了你想要的结果:

In [44]: df.groupby('cum_b').apply(func)
Out[44]: 
cum_b    
1      13     8.0
       12     8.0
       11     8.0
       10     8.0
       9      NaN
2      8     38.0
       7     38.0
       6     38.0
       5     38.0
       4     38.0
       3     38.0
       2     38.0
       1      NaN
       0      NaN

您可以使用:

#create NaN where False values
df['i'] = np.where(df.b, df.h, np.nan)
#bfill all NaN
df['i'] = df.i.fillna(method='bfill')

#create NaN by condition
a = df[::-1].groupby('i')['t'].apply(lambda x: (x > 9.5).shift().cumsum()) >= 1
df['i'] = df.i.mask(a, np.nan)

print (df)
            t      b   h     i
0         NaN  False   6   NaN
1    6.023448  False  38   NaN
2   12.996233  False  46  38.0
3    2.484907  False  67  38.0
4    5.062595  False  81  38.0
5    4.624973  False  82  38.0
6    3.367296  False  38  38.0
7    3.688879  False  53  38.0
8    6.926577   True  38  38.0
9   14.972346  False  81   NaN
10  14.442651  False  78   8.0
11   3.367296  False  67   8.0
12   5.236442  False  46   8.0
13   5.298317   True   8   8.0