如何按元素移动数据框以填充 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()
生成的,您可能还会从索引中注意到这一点。我之所以这样说,是因为在单行中比在多行中更容易做到。
我发现了一些使用 shift
、combine_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
我有一个 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()
生成的,您可能还会从索引中注意到这一点。我之所以这样说,是因为在单行中比在多行中更容易做到。
我发现了一些使用 shift
、combine_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