【问题标题】:Fill NaN with the mean of row from specific column more efficiently更有效地用特定列中的行的平均值填充 NaN
【发布时间】:2018-12-12 21:34:46
【问题描述】:

感谢您抽出时间阅读我的问题。

我想用名称以“A”开头的列的平均值填充下面的 df 中的 NaN。

例如,第一个NaN应该用2.5填充,也就是2和3的平均值。最后一个NaN应该用1.5代替。列中以 'B' 开头的任何值都不应被考虑,尽管它们在同一个 df 中。

A.1.a  A.3.d  A.6.i  B.2.b
NaN    2      3      12
1      2      3      12
1      NaN    3      12
1      2      3      12
NaN    2      3      12
1      2      NaN    12

这是我成功的尝试。

# read only columns which names started with A. 
cols_A = [col for col in df if col.startswith('A')]   
cols_A = df[cols_A]

cols_A = cols_A.apply(lambda row: row.fillna(row.mean()), axis=1)
cols_A

我正在寻找更有效的方法,因为我的 df 有更多列。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    IIUC,你可以试试broadcasting和fillna

    cols = [x for x in df.columns if x.startswith('A')]
    df.fillna(pd.DataFrame((df[cols].sum(1)/df[cols].notnull().sum(1)).values[:,None] * np.ones([len(cols),1]).T, columns=cols))
    
        A.1.a   A.3.d   A.6.i   B.2.b
    0   2.5     2.0     3.0     12
    1   1.0     2.0     3.0     12
    2   1.0     2.0     3.0     12
    3   1.0     2.0     3.0     12
    4   2.5     2.0     3.0     12
    5   1.0     2.0     1.5     12
    

    好时机

    df = pd.concat([df]*1000).reset_index(drop=True)
    
    %timeit df.fillna(pd.DataFrame(df[cols].sum(1).div(df[cols].notnull().sum(1)).values[:,None] * np.ones([len(cols),1]).T, columns=cols))
    5.73 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %timeit df.combine_first(df.groupby(lambda x: x[0], axis=1).transform('mean'))
    856 ms ± 22.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    【讨论】:

    • 很有意思,因为当索引为DateTime格式时,会返回错误~Cannot compare type 'Timestamp' 和 type 'int'~
    • @k.koen 不确定索引是日期时间是问题(它应该可以工作)...我建议您用新数据和新方法提出另一个问题 :)
    【解决方案2】:

    仅用 A 替换的 Numpy 解决方案开始列:

    #select only A starting columns
    mask = df.columns.str.startswith('A')
    df1 = df.loc[:, mask]
    print (df1)
       A.1.a  A.3.d  A.6.i
    0    NaN    2.0    3.0
    1    1.0    2.0    3.0
    2    1.0    NaN    3.0
    3    1.0    2.0    3.0
    4    NaN    2.0    3.0
    5    1.0    2.0    NaN
    

    #convert to 2d array
    arr = df1.values
    #broadcast to 2d array by df1 shape
    a = np.broadcast_to(np.nanmean(arr, axis=1)[:, None], df1.shape)
    #check missing values
    m = np.isnan(arr)
    #replace them by mask
    arr[m] = a[m]
    print (arr)
    [[2.5 2.  3. ]
     [1.  2.  3. ]
     [1.  2.  3. ]
     [1.  2.  3. ]
     [2.5 2.  3. ]
     [1.  2.  1.5]]
    
    #assign back
    df.loc[:, mask] = arr
    print (df)
       A.1.a  A.3.d  A.6.i  B.2.b
    0    2.5    2.0    3.0     12
    1    1.0    2.0    3.0     12
    2    1.0    2.0    3.0     12
    3    1.0    2.0    3.0     12
    4    2.5    2.0    3.0     12
    5    1.0    2.0    1.5     12
    

    如果需要将NaNs 替换为由列的第一个值定义的组:

    df = df.combine_first(df.groupby(lambda x: x[0], axis=1).transform('mean'))
    #alternative
    #df = df.combine_first(df.groupby(df.columns.str[0], axis=1).transform('mean'))
    print (df)
       A.1.a  A.3.d  A.6.i  B.2.b
    0    2.5    2.0    3.0     12
    1    1.0    2.0    3.0     12
    2    1.0    2.0    3.0     12
    3    1.0    2.0    3.0     12
    4    2.5    2.0    3.0     12
    5    1.0    2.0    1.5     12
    

    另一个想法是创建Series 的字典并替换为DataFrame.fillna:

    df1 = df.groupby(df.columns.str[0], axis=1).mean()
    
    df = df.fillna({x: df1[x[0]] for x in df.columns})
    print (df)
       A.1.a  A.3.d  A.6.i  B.2.b
    0    2.5    2.0    3.0     12
    1    1.0    2.0    3.0     12
    2    1.0    2.0    3.0     12
    3    1.0    2.0    3.0     12
    4    2.5    2.0    3.0     12
    5    1.0    2.0    1.5     12
    

    【讨论】:

      【解决方案3】:

      另一种选择是:

      cols_A = cols_A.T.fillna(cols_A.mean(axis=1)).T
      

      输出:

         A.1.a  A.3.d  A.6.i
      0    2.5    2.0    3.0
      1    1.0    2.0    3.0
      2    1.0    2.0    3.0
      3    1.0    2.0    3.0
      4    2.5    2.0    3.0
      5    1.0    2.0    1.5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        • 2014-02-11
        • 2018-03-28
        • 1970-01-01
        相关资源
        最近更新 更多