如何从数据框中删除空白/ NA并将值向上移动

时间:2017-03-30 13:35:24

标签: python pandas numpy

我有一个巨大的数据框,其中包含值和空白/ 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语句并达到了预期的结果,但有没有更好的方法来完成它。

4 个答案:

答案 0 :(得分:11)

您可以将applydropna

一起使用
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

答案 1 :(得分:4)

numpy方法
我们的想法是按np.isnan对列进行排序,以便将np.nan放在最后。我使用kind='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的速度

天真时间测试

init()

答案 2 :(得分:1)

通过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],
 ...

这里发生的事情是,vm x n,我同时制作了ab m x { {1}},我们正在做的是,将ni,j中的每个条目a配对,以获取行中的元素,其值为b处的元素位于i,j的{​​{1}}和a中元素值的列中i,j。因此,如果我们ba看起来像上面的矩阵,那么b会返回一个矩阵,其中第一行包含v[a,b]n个副本,第二行行包含v[0][0]n个副本,依此类推。

在解决方案piRSquared中,他的v[1][1]是一个列表而不是矩阵。所以该列表用于i次,也就是每行一次。同样,我们可以做到:

v.shape[0]

如果有什么不清楚,请告诉我 谢谢:))

答案 3 :(得分:0)

作为熊猫初学者,我无法立即理解@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))

(drop = True)reset_index可以防止旧索引变成新列。

我会以对此发表评论作为对@jezrael答案的评论,但我的代表不够高!