如何从数据框中删除空白/NA并将值上移
- 2025-03-12 08:55:00
- admin 原创
- 14
问题描述:
我有一个巨大的数据框,里面有值和空白/NA。我想从数据框中删除空白,并将下一个值在列中向上移动。考虑下面的示例数据框。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5,4))
df.iloc[1,2] = np.NaN
df.iloc[0,1] = np.NaN
df.iloc[2,1] = np.NaN
df.iloc[2,0] = np.NaN
df
0 1 2 3
0 1.857476 NaN -0.462941 -0.600606
1 0.000267 -0.540645 NaN 0.492480
2 NaN NaN -0.803889 0.527973
3 0.566922 0.036393 -1.584926 2.278294
4 -0.243182 -0.221294 1.403478 1.574097
我希望我的输出如下所示
0 1 2 3
0 1.857476 -0.540645 -0.462941 -0.600606
1 0.000267 0.036393 -0.803889 0.492480
2 0.566922 -0.221294 -1.584926 0.527973
3 -0.243182 1.403478 2.278294
4 1.574097
我希望删除 NaN 并将下一个值上移。df.shift
没有帮助。我尝试了多个循环和 if 语句并获得了所需的结果,但有没有更好的方法来完成它。
解决方案 1:
您可以apply
使用dropna
:
np.random.seed(100)
df = pd.DataFrame(np.random.randn(5,4))
df.iloc[1,2] = np.NaN
df.iloc[0,1] = np.NaN
df.iloc[2,1] = np.NaN
df.iloc[2,0] = np.NaN
print (df)
0 1 2 3
0 -1.749765 NaN 1.153036 -0.252436
1 0.981321 0.514219 NaN -1.070043
2 NaN NaN -0.458027 0.435163
3 -0.583595 0.816847 0.672721 -0.104411
4 -0.531280 1.029733 -0.438136 -1.118318
df1 = df.apply(lambda x: pd.Series(x.dropna().values))
print (df1)
0 1 2 3
0 -1.749765 0.514219 1.153036 -0.252436
1 0.981321 0.816847 -0.458027 -1.070043
2 -0.583595 1.029733 0.672721 0.435163
3 -0.531280 NaN -0.438136 -0.104411
4 NaN NaN NaN -1.118318
然后如果需要替换为空白处,会创建混合值(带有数字的字符串),某些功能可能会被破坏:
df1 = df.apply(lambda x: pd.Series(x.dropna().values)).fillna('')
print (df1)
0 1 2 3
0 -1.74977 0.514219 1.15304 -0.252436
1 0.981321 0.816847 -0.458027 -1.070043
2 -0.583595 1.02973 0.672721 0.435163
3 -0.53128 -0.438136 -0.104411
4 -1.118318
解决方案 2:
方法
这个想法是按 来对列进行排序,numpy
以便将s 放在最后。我使用来保留非 内的顺序。最后,我对数组进行切片并重新分配。我接着使用 np.isnan
`np.nankind='mergesort'
np.nan`fillna
v = df.values
i = np.arange(v.shape[1])
a = np.isnan(v).argsort(0, kind='mergesort')
v[:] = v[a, i]
print(df.fillna(''))
0 1 2 3
0 1.85748 -0.540645 -0.462941 -0.600606
1 0.000267 0.036393 -0.803889 0.492480
2 0.566922 -0.221294 -1.58493 0.527973
3 -0.243182 1.40348 2.278294
4 1.574097
如果你不想改变数据框
v = df.values
i = np.arange(v.shape[1])
a = np.isnan(v).argsort(0, kind='mergesort')
pd.DataFrame(v[a, i], df.index, df.columns).fillna('')
这样做的目的是利用numpy
速度
简单时间测试
解决方案 3:
通过 piRSquared 添加到解决方案中:这会将所有值向左移动,而不是向上移动。
如果不是所有值都是数字,请使用pd.isnull
v = df.values
a = [[n]*v.shape[1] for n in range(v.shape[0])]
b = pd.isnull(v).argsort(axis=1, kind = 'mergesort')
# a is a matrix used to reference the row index,
# b is a matrix used to reference the column index
# taking an entry from a and the respective entry from b (Same index),
# we have a position that references an entry in v
v[a, b]
一点解释:
a
是一个长度为 的列表v.shape[0]
,它看起来像这样:
[[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
...
这里发生的事情是,v
是m
x n
,我已经使a
和b
m
x都成立n
,所以我们要做的是,将i,j
和中的每个条目配对a
,以获得行b
中的元素与元素的值以及列中的元素的值。因此,如果我们有和两者都像上面的矩阵,则返回一个矩阵,其中第一行包含的副本,第二行包含的副本,依此类推。i,j
`ai,j
ba
bv[a,b]
nv[0][0]
n`v[1][1]
在解决方案 piRSquared 中,这i
是一个列表而不是矩阵。因此,列表用于v.shape[0]
次数,即每行一次。类似地,我们可以这样做:
a = [[n] for n in range(v.shape[0])]
# which looks like
# [[0],[1],[2],[3]...]
# since we are trying to indicate the row indices of the matrix v as opposed to
# [0, 1, 2, 3, ...] which refers to column indices
如果有任何不清楚的地方请告诉我,谢谢:)
解决方案 4:
作为一个熊猫初学者,我无法立即理解@jezrael 背后的原因
df.apply(lambda x: pd.Series(x.dropna().values))
但我发现它通过重置列的索引来工作。df.apply(默认情况下)逐列工作,将每列视为一个系列。使用 df.dropna() 会删除 NaN,但不会更改剩余数字的索引,因此当将此列添加回数据框时,数字会返回到其原始位置,因为它们的索引仍然相同,空白处用 NaN 填充,重新创建原始数据框并且不会实现任何效果。
通过重置列的索引,在本例中,通过将系列更改为数组(使用 .values)并重新更改为系列(使用 pd.Series),只有所有数字后面的空白处(即列底部)才会用 NaN 填充。同样可以通过以下方式实现
df.apply(lambda x: x.dropna().reset_index(drop = True))
reset_index 的 (drop = True) 可防止旧索引成为新列。
我本来想将此作为对@jezrael 的回答的评论发布,但我的声誉不够高!