【问题标题】:numpy array: replace nan values with average of columnsnumpy 数组:用列的平均值替换 nan 值
【发布时间】:2013-09-12 09:51:46
【问题描述】:

我有一个 numpy 数组,其中大部分填充了实数,但其中也有一些 nan 值。

如何将nans 替换为它们所在列的平均值?

【问题讨论】:

    标签: python arrays numpy nan


    【解决方案1】:

    这不是很干净,但我想不出除了迭代之外的其他方法

    #example
    a = np.arange(16, dtype = float).reshape(4,4)
    a[2,2] = np.nan
    a[3,3] = np.nan
    
    indices = np.where(np.isnan(a)) #returns an array of rows and column indices
    for row, col in zip(*indices):
        a[row,col] = np.mean(a[~np.isnan(a[:,col]), col])
    

    【讨论】:

    • 非常感谢!
    【解决方案2】:

    不需要循环:

    print(a)
    [[ 0.93230948         nan  0.47773439  0.76998063]
     [ 0.94460779  0.87882456  0.79615838  0.56282885]
     [ 0.94272934  0.48615268  0.06196785         nan]
     [ 0.64940216  0.74414127         nan         nan]]
    
    #Obtain mean of columns as you need, nanmean is convenient.
    col_mean = np.nanmean(a, axis=0)
    print(col_mean)
    [ 0.86726219  0.7030395   0.44528687  0.66640474]
    
    #Find indices that you need to replace
    inds = np.where(np.isnan(a))
    
    #Place column means in the indices. Align the arrays using take
    a[inds] = np.take(col_mean, inds[1])
    
    print(a)
    [[ 0.93230948  0.7030395   0.47773439  0.76998063]
     [ 0.94460779  0.87882456  0.79615838  0.56282885]
     [ 0.94272934  0.48615268  0.06196785  0.66640474]
     [ 0.64940216  0.74414127  0.44528687  0.66640474]]
    

    【讨论】:

    • 不错的答案。我不知道nanmean存在! (+1)
    • 你有什么理由使用 take 而不是仅仅索引?
    • @Hammer 他们在 1.8 中将 nanmean 添加到 numpy。应该很有趣。由于this 的问题,我使用 take 而不是花哨的索引。有很多证据表明索引比采用慢约 5 倍。此外,这也适用于旧版本。
    • @Jaime 你能详细说明一下吗?
    • 您现在可以使用 numpy.nanmean() 代替 import scipy:docs.scipy.org/doc/numpy-dev/reference/generated/…
    【解决方案3】:

    您可能想试试这个内置功能:

    x = np.array([np.inf, -np.inf, np.nan, -128, 128])
    np.nan_to_num(x)
    array([  1.79769313e+308,  -1.79769313e+308,   0.00000000e+000,
    -1.28000000e+002,   1.28000000e+002])
    

    【讨论】:

      【解决方案4】:

      替代方案:用列插值替换 NaN。

      def interpolate_nans(X):
          """Overwrite NaNs with column value interpolations."""
          for j in range(X.shape[1]):
              mask_j = np.isnan(X[:,j])
              X[mask_j,j] = np.interp(np.flatnonzero(mask_j), np.flatnonzero(~mask_j), X[~mask_j,j])
          return X
      

      使用示例:

      X_incomplete = np.array([[10,     20,     30    ],
                               [np.nan, 30,     np.nan],
                               [np.nan, np.nan, 50    ],
                               [40,     50,     np.nan    ]])
      
      X_complete = interpolate_nans(X_incomplete)
      
      print X_complete
      [[10,     20,     30    ],
       [20,     30,     40    ],
       [30,     40,     50    ],
       [40,     50,     50    ]]
      

      我特别将这段代码用于时间序列数据,其中列是属性,行是按时间排序的样本。

      【讨论】:

        【解决方案5】:

        如果 partial 是您的原始数据,并且 replace 是包含平均值的相同形状的数组,则此代码将使用来自 partial 的值(如果存在)。

        Complete= np.where(np.isnan(partial),replace,partial)
        

        【讨论】:

        • 这是一个比其他任何解决方案都干净得多的解决方案。
        • 除了它需要更多的内存来保存重复的平均值。
        【解决方案6】:

        为了扩展唐纳德的回答,我提供了一个最小的示例。假设a 是一个ndarray,我们想用列的平均值替换它的零值。

        In [231]: a
        Out[231]: 
        array([[0, 3, 6],
               [2, 0, 0]])
        
        
        In [232]: col_mean = np.nanmean(a, axis=0)
        Out[232]: array([ 1. ,  1.5,  3. ])
        
        In [228]: np.where(np.equal(a, 0), col_mean, a)
        Out[228]: 
        array([[ 1. ,  3. ,  6. ],
               [ 2. ,  1.5,  3. ]])
        

        【讨论】:

          【解决方案7】:

          使用掩码数组

          仅使用 numpy 执行此操作的标准方法是使用 masked array 模块。

          Scipy 是一个非常重的包,它依赖于外部库,因此值得拥有一个仅限 numpy 的方法。这借鉴了@DonaldHobson 的回答。

          编辑: np.nanmean 现在是一个 numpy 函数。但是,它不处理全南列...

          假设你有一个数组a

          >>> a
          array([[  0.,  nan,  10.,  nan],
                 [  1.,   6.,  nan,  nan],
                 [  2.,   7.,  12.,  nan],
                 [  3.,   8.,  nan,  nan],
                 [ nan,   9.,  14.,  nan]])
          
          >>> import numpy.ma as ma
          >>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=0), a)    
          array([[  0. ,   7.5,  10. ,   0. ],
                 [  1. ,   6. ,  12. ,   0. ],
                 [  2. ,   7. ,  12. ,   0. ],
                 [  3. ,   8. ,  12. ,   0. ],
                 [  1.5,   9. ,  14. ,   0. ]])
          

          请注意,掩码数组的均值不必与a 的形状相同,因为我们在行上利用了隐含的broadcasting

          还要注意如何很好地处理 all-nan 列。平均值为零,因为您取的是零元素的平均值。使用nanmean 的方法不处理全南列:

          >>> col_mean = np.nanmean(a, axis=0)
          /home/praveen/.virtualenvs/numpy3-mkl/lib/python3.4/site-packages/numpy/lib/nanfunctions.py:675: RuntimeWarning: Mean of empty slice
            warnings.warn("Mean of empty slice", RuntimeWarning)
          >>> inds = np.where(np.isnan(a))
          >>> a[inds] = np.take(col_mean, inds[1])
          >>> a
          array([[  0. ,   7.5,  10. ,   nan],
                 [  1. ,   6. ,  12. ,   nan],
                 [  2. ,   7. ,  12. ,   nan],
                 [  3. ,   8. ,  12. ,   nan],
                 [  1.5,   9. ,  14. ,   nan]])
          

          说明

          a 转换为掩码数组可为您提供

          >>> ma.array(a, mask=np.isnan(a))
          masked_array(data =
           [[0.0 --  10.0 --]
            [1.0 6.0 --   --]
            [2.0 7.0 12.0 --]
            [3.0 8.0 --   --]
            [--  9.0 14.0 --]],
                       mask =
           [[False  True False  True]
           [False False  True  True]
           [False False False  True]
           [False False  True  True]
           [ True False False  True]],
                 fill_value = 1e+20)
          

          在列上取平均值会给你正确的答案,只对非屏蔽值进行规范化:

          >>> ma.array(a, mask=np.isnan(a)).mean(axis=0)
          masked_array(data = [1.5 7.5 12.0 --],
                       mask = [False False False  True],
                 fill_value = 1e+20)
          

          此外,请注意掩码如何很好地处理 all-nan 列!

          最后,np.where 完成了替换工作。


          逐行均值

          要将nan 值替换为行均值而不是列均值,需要进行微小的更改才能使广播很好地生效:

          >>> a
          array([[  0.,   1.,   2.,   3.,  nan],
                 [ nan,   6.,   7.,   8.,   9.],
                 [ 10.,  nan,  12.,  nan,  14.],
                 [ nan,  nan,  nan,  nan,  nan]])
          
          >>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=1), a)
          ValueError: operands could not be broadcast together with shapes (4,5) (4,) (4,5)
          
          >>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=1)[:, np.newaxis], a)
          array([[  0. ,   1. ,   2. ,   3. ,   1.5],
                 [  7.5,   6. ,   7. ,   8. ,   9. ],
                 [ 10. ,  12. ,  12. ,  12. ,  14. ],
                 [  0. ,   0. ,   0. ,   0. ,   0. ]])
          

          【讨论】:

          • IMO 将 np.nan 值作为全 NaN 列情况的手段并没有错。但这确实是掩码数组的一个很好的使用案例。
          • @VlasSokolov 好吧,我认为戴上口罩会更好。即,将a 制作成一个掩码数组,并且即使在 应用平均值之后也将其保持为掩码。那么您就不必担心对其执行操作,这可能会导致nans “传播”到非nan 值。
          【解决方案8】:

          使用带有循环的简单函数:

          a=[[0.93230948, np.nan, 0.47773439, 0.76998063],
            [0.94460779, 0.87882456, 0.79615838, 0.56282885],
            [0.94272934, 0.48615268, 0.06196785, np.nan],
            [0.64940216, 0.74414127, np.nan, np.nan],
            [0.64940216, 0.74414127, np.nan, np.nan]]
          
          print("------- original array -----")
          for aa in a:
              print(aa)
          
          # GET COLUMN MEANS: 
          ta = np.array(a).T.tolist()                         # transpose the array; 
          col_means = list(map(lambda x: np.nanmean(x), ta))  # get means; 
          print("column means:", col_means)
          
          # REPLACE NAN ENTRIES WITH COLUMN MEANS: 
          nrows = len(a); ncols = len(a[0]) # get number of rows & columns; 
          for r in range(nrows):
              for c in range(ncols):
                  if np.isnan(a[r][c]):
                      a[r][c] = col_means[c]
          
          print("------- means added -----")
          for aa in a:
              print(aa)
          

          输出:

          ------- original array -----
          [0.93230948, nan, 0.47773439, 0.76998063]
          [0.94460779, 0.87882456, 0.79615838, 0.56282885]
          [0.94272934, 0.48615268, 0.06196785, nan]
          [0.64940216, 0.74414127, nan, nan]
          [0.64940216, 0.74414127, nan, nan]
          
          column means: [0.82369018599999999, 0.71331494500000003, 0.44528687333333333, 0.66640474000000005]
          
          ------- means added -----
          [0.93230948, 0.71331494500000003, 0.47773439, 0.76998063]
          [0.94460779, 0.87882456, 0.79615838, 0.56282885]
          [0.94272934, 0.48615268, 0.06196785, 0.66640474000000005]
          [0.64940216, 0.74414127, 0.44528687333333333, 0.66640474000000005]
          [0.64940216, 0.74414127, 0.44528687333333333, 0.66640474000000005]
          

          for 循环也可以用列表理解来编写:

          new_a = [[col_means[c] if np.isnan(a[r][c]) else a[r][c] 
                      for c in range(ncols) ]
                  for r in range(nrows) ]
          

          【讨论】:

            猜你喜欢
            • 2019-03-21
            • 2013-04-01
            • 2013-09-12
            • 1970-01-01
            • 2018-04-17
            • 2020-01-16
            • 2013-01-13
            相关资源
            最近更新 更多