【问题标题】:How to remove blanks/NA's from dataframe and shift the values up如何从数据框中删除空格/NA并将值向上移动
【发布时间】:2017-08-24 11:24:33
【问题描述】:

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

【问题讨论】:

    标签: python pandas numpy


    【解决方案1】:

    作为 pandas 初学者,我无法立即理解 @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) for reset_index 防止旧索引成为新列。

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

    【讨论】:

      【解决方案2】:

      通过 piRSquared 添加到解决方案: 这会将所有值移动到 left 而不是向上。
      如果不是所有值都是数字,请使用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,所以我们正在做的是配对将a 和b 中的每个条目i,j 向上,以获取a 中元素值为i,j 的行中的元素,以及i,j 中元素值为i,j 的列中的元素。所以如果我们有a 和b 看起来像上面的矩阵,那么v[a,b] 返回一个矩阵,其中第一行包含n 的v[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  
      

      如果有任何不清楚的地方,请告诉我, 谢谢:)

      【讨论】:

        【解决方案3】:

        numpy 方法
        这个想法是按np.isnan 对列进行排序,以便将np.nans 放在最后。我使用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('')
        

        这样做的目的是利用numpys 的速度

        幼稚时间测试

        【讨论】:

        • 如果DataFrame中的值不是数字怎么办?
        【解决方案4】:

        您可以将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
        

        【讨论】:

        • 谢谢,这正是我所看到的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多