FillNaN 具有多个条件并在 Pandas 中使用 n-1 和 n+2 值

Posted

技术标签:

【中文标题】FillNaN 具有多个条件并在 Pandas 中使用 n-1 和 n+2 值【英文标题】:FillNaN with multiple conditions and using n-1 and n+2 values with Pandas 【发布时间】:2022-01-23 08:52:16 【问题描述】:

我有以下数据框:

d = 'T': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'Val1': [10, np.NaN, 14, np.NaN, np.NaN, np.NaN, 20, np.NaN, np.NaN, 30]
df = pd.DataFrame(data=d)

T   Val1
1   10.0
2   NaN
3   14.0
4   NaN
5   NaN
6   NaN
7   20.0
8   NaN
9   NaN
10  30.0

我想根据特定条件用不同的值填充 NaN:

    如果值V 是NaN 并且如果V+1V-1 不是NaN 那么V=np.mean([V+1, V-1]) 如果值 VV+1 是 NaN 并且如果 V-1V+2 不是 NaN 那么我想按照这个公式填充它们:V=np.cbrt([(V-1)*(V-1)*(V+2)]) AND V+1=np.cbrt([(V-1)*(V+2)*(V+2)]) 应删除其他 NaN

所以想要的数据表应该是这样的:

T   Val1
1   10.0
2   12.0
3   14.0
7   20.0
8   22.89
9   26.20
10  30.0

我可以通过以下命令执行V=np.mean([V+1, V-1])

df1 = pd.concat([df.ffill(), df.bfill()]).groupby(level=0).mean()

T   Val1
1   10.0
2   12.0
3   14.0
4   17.0
5   17.0
6   17.0
7   20.0
8   25.0
9   25.0
10  30.0

但我不知道如何合并不同的条件。 我尝试使用np.select(),但找不到恢复关注和先前值并将它们添加到条件的方法。

非常感谢

【问题讨论】:

【参考方案1】:

你可以使用:

def condition_2(a, b): #a = V-1, b = V+2
    return np.cbrt((a) * (a) * (b))

def condition_3(a,b): # a = V-2, b=V+1
    return np.cbrt((a) * (b) * (b))


d = 'T': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'Val1': [10, np.NaN, 14, np.NaN, np.NaN, np.NaN, 20, np.NaN, np.NaN, 30]
df = pd.DataFrame(data=d)
cond_1 = df['Val1'].isnull() & df['Val1'].shift(1).notna() & df['Val1'].shift(-1).notna()
cond_2 = df['Val1'].isnull() & df['Val1'].shift(1).notna() & df['Val1'].shift(-1).isnull() & df['Val1'].shift(-2).notna()
cond_3 = df['Val1'].isnull() & df['Val1'].shift(-1).notna() & df['Val1'].shift(1).isnull() & df['Val1'].shift(2).notna()

df['Val1'] = np.where(cond_1, (df['Val1'].shift(1) + df['Val1'].shift(-1))/2, df['Val1'])
df['Val1'] = np.where(cond_2, condition_2(df['Val1'].shift(1), df['Val1'].shift(-2)), df['Val1'])
df['Val1'] = np.where(cond_3, condition_3(df['Val1'].shift(2), df['Val1'].shift(-1)), df['Val1'])

df.dropna(subset=['Val1'], inplace=True)

OUTPUT

    T       Val1
0   1  10.000000
1   2  12.000000
2   3  14.000000
6   7  20.000000
7   8  22.894285
8   9  26.207414
9  10  30.000000

【讨论】:

非常感谢,工作正常!【参考方案2】:

这是使用np.split 和自定义函数的一种解决方案。基本上拆分非 NaN 值并迭代每个拆分以评估是否删除 NaN 或更改 NaN:

def nan2notna(arr1, arr2):
    mask = pd.isna(arr1)
    if len(arr1[mask]) > 2:
        return arr1[~mask] 
    else:
        if len(arr1[mask]) == 2:
            arr1[mask] = [np.cbrt([(arr1.iloc[0])*(arr1.iloc[0])*(arr2.iloc[0])]), np.cbrt([(arr1.iloc[0])*(arr2.iloc[0])*(arr2.iloc[0])])]
        elif len(arr1[mask]) == 1:
            arr1[mask] = np.mean([arr1.iloc[0], arr2.iloc[0]])
        else:
            pass
        return arr1

splits = np.split(df['Val1'], np.where(pd.notna(df['Val1']))[0])[1:]
out = (df.merge(pd.concat([nan2notna(arr1, arr2) for (arr1, arr2) in zip(splits, splits[1:]+[None])]).to_frame(), 
               left_index=True, right_index=True)
       .drop(columns='Val1_x')
       .rename(columns='Val1_y':'Val1')
       .round(2))    

输出:

    T   Val1
0   1  10.00
1   2  12.00
2   3  14.00
6   7  20.00
7   8  22.89
8   9  26.21
9  10  30.00

【讨论】:

哇,解决这个问题的好方法。我使用您的代码时出错,找不到“单个位置索引器越界”的原因。我也试图理解,有一点我仍然对你的代码有疑问。我不明白 np.split()np.where(pd.notna(df))[0] 的行为如何?输出如下array([0, 0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 9], dtype=int64)np.split() 返回不同的子数组,其中很少有空子数组。你能向我解释一下你的这部分代码是如何工作的吗?非常感谢!! @Lekim79 我已经包含了我的测试代码,其中包含x 而不是df['Val1']。我现在改了。我认为它现在会起作用。告诉我进展如何。 工作得很好,是解决这个问题的好方法。但是,我的数据框有大约 2M 行,这种方法比@Muhammad Hassan 的其他答案慢了大约 10 倍。谢谢你,谢谢你!

以上是关于FillNaN 具有多个条件并在 Pandas 中使用 n-1 和 n+2 值的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:过滤具有多个字符串条件的行[重复]

python pandas - 生成具有多个条件的视图/复制警告过滤数据框

pandas python中的COUNTIF在具有多个条件的多列上

如何使用for循环或条件在pandas数据框的子集中创建多个回归模型(statsmodel)?

如何比较多列,并在单个新列中生成值,在 Pandas 中使用 Apply 函数

pandas:如果在循环中遇到条件,则更新值