【问题标题】:Matrix completion in PythonPython中的矩阵完成
【发布时间】:2013-08-01 17:15:56
【问题描述】:

假设我有一个矩阵:

> import numpy as nap
> a = np.random.random((5,5))

array([[ 0.28164485,  0.76200749,  0.59324211,  0.15201506,  0.74084168],
       [ 0.83572213,  0.63735993,  0.28039542,  0.19191284,  0.48419414],
       [ 0.99967476,  0.8029097 ,  0.53140614,  0.24026153,  0.94805153],
       [ 0.92478   ,  0.43488547,  0.76320656,  0.39969956,  0.46490674],
       [ 0.83315135,  0.94781119,  0.80455425,  0.46291229,  0.70498372]])

我用np.NaN在上面打了一些洞,例如:

> a[(1,4,0,3),(2,4,2,0)] = np.NaN; 

array([[ 0.80327707,  0.87722234,         nan,  0.94463778,  0.78089194],
       [ 0.90584284,  0.18348667,         nan,  0.82401826,  0.42947815],
       [ 0.05913957,  0.15512961,  0.08328608,  0.97636309,  0.84573433],
       [        nan,  0.30120861,  0.46829231,  0.52358888,  0.89510461],
       [ 0.19877877,  0.99423591,  0.17236892,  0.88059185,        nan ]])

我想使用矩阵其余条目中的信息填写nan 条目。例如,使用出现nan 条目的列的平均 值。

更一般地说,Python 中有没有用于 matrix completion 的库? (例如类似于Candes & Recht's convex optimization method 的东西)。

背景:

这个问题经常出现在机器学习中。例如,在分类/回归或collaborative filtering 中处理缺少的特征(例如,请参阅Wikipediahere 上的Netflix 问题)

【问题讨论】:

    标签: python numpy machine-learning scikit-learn mathematical-optimization


    【解决方案1】:

    如果您安装最新的 scikit-learn 0.14a1 版,您可以使用其闪亮的新 Imputer 类:

    >>> from sklearn.preprocessing import Imputer
    >>> imp = Imputer(strategy="mean")
    >>> a = np.random.random((5,5))
    >>> a[(1,4,0,3),(2,4,2,0)] = np.nan
    >>> a
    array([[ 0.77473361,  0.62987193,         nan,  0.11367791,  0.17633671],
           [ 0.68555944,  0.54680378,         nan,  0.64186838,  0.15563309],
           [ 0.37784422,  0.59678177,  0.08103329,  0.60760487,  0.65288022],
           [        nan,  0.54097945,  0.30680838,  0.82303869,  0.22784574],
           [ 0.21223024,  0.06426663,  0.34254093,  0.22115931,         nan]])
    >>> a = imp.fit_transform(a)
    >>> a
    array([[ 0.77473361,  0.62987193,  0.24346087,  0.11367791,  0.17633671],
           [ 0.68555944,  0.54680378,  0.24346087,  0.64186838,  0.15563309],
           [ 0.37784422,  0.59678177,  0.08103329,  0.60760487,  0.65288022],
           [ 0.51259188,  0.54097945,  0.30680838,  0.82303869,  0.22784574],
           [ 0.21223024,  0.06426663,  0.34254093,  0.22115931,  0.30317394]])
    

    之后,您可以使用imp.transform 对其他数据进行相同的转换,使用impa 学习的平均值。 Imputers 绑定到 scikit-learn Pipeline 对象,因此您可以在分类或回归管道中使用它们。

    如果您想等待稳定版本,那么 0.14 应该会在下周发布。

    全面披露:我是 scikit-learn 核心开发人员

    【讨论】:

    • 但它无法显示所有未知值的行。再者,有没有更高级的矩阵补全方法?对于 Imputer,它仅根据中值、均值或频繁值推断值。
    【解决方案2】:

    你可以用纯 numpy 来做,但它更糟糕。

    from scipy.stats import nanmean
    >>> a
    array([[ 0.70309466,  0.53785006,         nan,  0.49590115,  0.23521493],
           [ 0.29067786,  0.48236186,         nan,  0.93220001,  0.76261019],
           [ 0.66243065,  0.07731947,  0.38887545,  0.56450533,  0.58647126],
           [        nan,  0.7870873 ,  0.60010096,  0.88778259,  0.09097726],
           [ 0.02750389,  0.72328898,  0.69820328,  0.02435883,         nan]])
    
    
    >>> mean=nanmean(a,axis=0)
    >>> mean
    array([ 0.42092677,  0.52158153,  0.56239323,  0.58094958,  0.41881841])
    >>> index=np.where(np.isnan(a))
    
    >>> a[index]=np.take(mean,index[1])
    >>> a
    array([[ 0.70309466,  0.53785006,  0.56239323,  0.49590115,  0.23521493],
           [ 0.29067786,  0.48236186,  0.56239323,  0.93220001,  0.76261019],
           [ 0.66243065,  0.07731947,  0.38887545,  0.56450533,  0.58647126],
           [ 0.42092677,  0.7870873 ,  0.60010096,  0.88778259,  0.09097726],
           [ 0.02750389,  0.72328898,  0.69820328,  0.02435883,  0.41881841]])
    

    运行一些计时:

    import time
    import numpy as np
    import pandas as pd
    from scipy.stats import nanmean
    
    a = np.random.random((10000,10000))
    col=np.random.randint(0,10000,500)
    row=np.random.randint(0,10000,500)
    a[(col,row)]=np.nan
    a1=np.copy(a)
    
    
    %timeit mean=nanmean(a,axis=0);index=np.where(np.isnan(a));a[index]=np.take(mean,index[1])
    1 loops, best of 3: 1.84 s per loop
    
    %timeit DF=pd.DataFrame(a1);col_means = DF.apply(np.mean, 0);DF.fillna(value=col_means)
    1 loops, best of 3: 5.81 s per loop
    
    #Surprisingly, issue could be apply looping over the zero axis.
    DF=pd.DataFrame(a2)
    %timeit col_means = DF.apply(np.mean, 0);DF.fillna(value=col_means)
    1 loops, best of 3: 5.57 s per loop
    

    我不相信 numpy 内置了数组完成例程;但是,熊猫可以。查看帮助主题here

    【讨论】:

      【解决方案3】:

      你可以用pandas很简单地做到这一点

      import pandas as pd
      
      DF = pd.DataFrame(a)
      col_means = DF.apply(np.mean, 0)
      DF.fillna(value=col_means)
      

      【讨论】:

      • 谢谢。顺便说一句,文档谈到了bfillbackfillpadffill。我在哪里可以阅读更多关于这些方法的信息? (pandas.pydata.org/pandas-docs/dev/generated/…)
      • bfillbackfill 的简写,ffillpad 的“简写”。我认为文档的方式不多,但代码是here
      • 此外,您应该阅读 pandas 缺失数据帮助,here
      【解决方案4】:

      您想要的确切方法(Candes 和 Recht,2008 年)可用于 Python 的 fancyimpute 库,位于此处 (link)。

      from fancyimpute import NuclearNormMinimization
      
      # X is the complete data matrix
      # X_incomplete has the same values as X except a subset have been replace with NaN
      
      X_filled_nnm = NuclearNormMinimization().complete(X_incomplete)
      

      我已经看到了很好的结果。值得庆幸的是,在过去的一年里,他们将 autodiff 和 SGD 后端从 downhill(在后台使用 Theano)更改为 keras。该算法在这个库中也可用 (link)。 SciKit-Learn 的Imputer() 不包含此算法。它不在文档中,但您可以使用pip 安装fancyimpute

      pip install fancyimpute
      

      【讨论】:

        【解决方案5】:

        类似的问题是askedherebefore。你需要的是inpaiting 的一个特例。不幸的是,numpy 或 scipy 都没有为此的内置例程。但是,OpenCV 有一个函数inpaint(),但它只适用于 8 位图像。

        OpenPIV 有一个 replace_nans 函数,您可以将其用于您的目的。 (See here 用于 Cython 版本,如果您不想安装整个库,可以重新打包。)它比其他答案中建议的纯平均值或旧值的传播更灵活(例如,您可以定义不同的权重函数、内核大小等)。

        使用来自@Ophion 的示例,我将replace_nansnanmean 和Pandas 解决方案进行了比较:

        import numpy as np
        import pandas as pd
        from scipy.stats import nanmean
        
        a = np.random.random((10000,10000))
        col=np.random.randint(0,10000,500)
        row=np.random.randint(0,10000,500)
        a[(col,row)]=np.nan
        a1=np.copy(a)
        
        %timeit new_array = replace_nans(a1, 10, 0.5, 1.)
        1 loops, best of 3: 1.57 s per loop
        
        %timeit mean=nanmean(a,axis=0);index=np.where(np.isnan(a));a[index]=np.take(mean,index[1])
        1 loops, best of 3: 2.23 s per loop
        
        %timeit DF=pd.DataFrame(a1);col_means = DF.apply(np.mean, 0);DF.fillna(value=col_means)
        1 loops, best of 3: 7.23 s per loop
        

        replace_nans 解决方案可以说更好更快。

        【讨论】:

        • 除非我遗漏了一些东西 replace_nans 用加权平均值填充 nans 并且不等于用列的平均值替换 nans。在 4 个循环中使用 4 个 if 语句,我不确定如果您的数组包含许多 nan 会快多少。如果您将 nan 的数量从 500 更改为 5000,我会很好奇时间安排。
        • @Opion:你是对的,它不是用列的平均值替换 nans。但这就是重点:列平均值并不是最好的替代品。出于好奇,我只是使用np.random.randint(0,10000,5000)colrow 重新运行了计时。 replace_nans 现在需要 1.55 秒,nanmean 需要 2.15 秒。所以,很相似……
        • 你确定它已经在 10 次迭代中替换了所有 nans 吗?对于我的怀疑,我深表歉意 - 乍一看,代码似乎不是一种有效的方法。
        • 它似乎确实在 10 次迭代中替换了所有 NaN。自己试试吧。这里的重点不是找到在替换最大数量的 NaN 时更快的代码,而是找到缺失值的最佳估计。修复不适用于包含大量 NaN 的图像。
        猜你喜欢
        • 1970-01-01
        • 2021-05-29
        • 2023-03-24
        • 2018-04-02
        • 2013-05-06
        • 1970-01-01
        • 1970-01-01
        • 2015-04-26
        • 1970-01-01
        相关资源
        最近更新 更多