如何按元素移动数据框以填充 NaN?

How to shift a dataframe element-wise to fill NaNs?

我有一个 DataFrame 这样的:

>>> df = pd.DataFrame({'a': list('ABCD'), 'b': ['E',np.nan,np.nan,'F']})
   a    b
0  A    E
1  B  NaN
2  C  NaN
3  D    F

我正在尝试用下一行中前一列的值填充 NaN 并删除第二行。换句话说,我想将两行与 NaN 组合起来形成一个没有 NaN 的单行,如下所示:

   a  b
0  A  E
1  B  C
2  D  F

我尝试了各种不同的 df.fillna(method="<bfill/ffill>"),但这并没有给我预期的输出。

我还没有找到关于这个问题的任何其他问题,一个。实际上 DataFrame 是由 list of DataFrame 通过 .concat() 生成的,您可能还会从索引中注意到这一点。我之所以这样说,是因为在单行中比在多行中更容易做到。

我发现了一些使用 shiftcombine_first 的建议,但没有一个对我有用。你也可以试试这些。

我也找到了this。这是一篇关于填充 nan 值的整篇文章,但我还没有找到像我这样的 problem/answer。

你试过 df[columnname] = df[columnname].ffill()

OK 第一次误解了你想做的事情。虚拟示例有点模棱两可。

这是另一个:

>>> df = pd.DataFrame({'a': list('ABCD'), 'b': ['E',np.nan,np.nan,'F']})
   a    b
0  A    E
1  B  NaN
2  C  NaN
3  D    F

据我所知,pandas 不存在此操作,因此我们将使用 numpy 来完成这项工作。

首先将数据帧转换为 numpy 数组,然后 flatten it to be one-dimensional. Then drop NaNs using pandas.isna that is working on a larger range types than numpy.isnan, and then reshape 在转换回数据帧之前将数组恢复为原始形状:

array = df.to_numpy().flatten()
pd.DataFrame(array[~pd.isna(array)].reshape(-1,df.shape[1]), columns=df.columns)

输出:

   a  b
0  A  E
1  B  C
2  D  F

它也适用于更复杂的示例,只要 NaN 模式在具有 NaN 的列中是守恒的:

In:
   a    b   c    d
0  A    H  A2   H2
1  B  NaN  B2  NaN
2  C  NaN  C2  NaN
3  D    I  D2   I2
4  E  NaN  E2  NaN
5  F  NaN  F2  NaN
6  G    J  G2   J2

Out:
   a   b   c   d
0  A   H  A2  H2
1  B  B2   C  C2
2  D   I  D2  I2
3  E  E2   F  F2
4  G   J  G2  J2
In:
   a    b    c
0  A    F    H
1  B  NaN  NaN
2  C  NaN  NaN
3  D  NaN  NaN
4  E    G    I

Out:
   a  b  c
0  A  F  H
1  B  C  D
2  E  G  I

如果 NaN 列没有相同的模式,例如:

   a    b   c    d
0  A    H  A2  NaN
1  B  NaN  B2  NaN
2  C  NaN  C2   H2
3  D    I  D2   I2
4  E  NaN  E2  NaN
5  F  NaN  F2  NaN
6  G    J  G2   J2

您可以对每组两列应用操作:

def elementwise_shift(df):
    array = df.to_numpy().flatten()
    return pd.DataFrame(array[~pd.isna(array)].reshape(-1,df.shape[1]), columns=df.columns)

(df.groupby(np.repeat(np.arange(df.shape[1]/2), 2), axis=1)
   .apply(elementwise_shift)
)

输出:

   a  b   c   d
0  A  H  A2  B2
1  B  C  C2  H2
2  D  I  D2  I2
3  E  F  E2  F2
4  G  J  G2  J2

您可以使用占位符列分两步完成此操作。首先,用下一行的 a 值填充 b 列中的所有 nans。然后你应用过滤。在此示例中,我使用限制为 1 的 ffill 来过滤第一个之后的所有 nan 值,可能有更好的方法。

import pandas as pd
import numpy as np
df=pd.DataFrame({"a":[1,2,3,3,4],"b":[1,2,np.nan,np.nan,4]})

# Fill all nans:
df['new_b'] = df['b'].fillna(df['a'].shift(-1))
df = df[df['b'].ffill(limit=1).notna()].copy() # .copy() because loc makes a view
df = df.drop('b', axis=1).rename(columns={'new_b': 'b'})

print(df)
# output:
#    a  b
# 0  1  1
# 1  2  2
# 2  3  2
# 4  4  4