【问题标题】:Pandas DataFrame: copy the contents of a column if it is emptyPandas DataFrame:如果为空,则复制列的内容
【发布时间】:2018-11-20 18:23:17
【问题描述】:

我有以下带有命名列和索引的 DataFrame:

  'a'     'a*'    'b'    'b*'
1  5      NaN     9      NaN
2  NaN    3       3      NaN
3  4      NaN     1      NaN
4  NaN    9       NaN    7

数据源导致某些列标题的复制略有不同。例如,如上,一些列标题是一个字符串,而一些列标题是带有附加“*”字符的相同字符串。

我想将a*b* 列中的任何值(不为空)分别复制到ab

有没有一种有效的方法来做这样的操作?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    使用np.where

    df['a']= np.where(df['a'].isnull(), df['a*'], df['a'])
    df['b']= np.where(df['b'].isnull(), df['b*'], df['b'])
    

    输出:

         a  a*  b   b*
    0   5.0 NaN 9.0 NaN
    1   3.0 3.0 3.0 NaN
    2   4.0 NaN 1.0 NaN
    3   9.0 9.0 7.0 7.0
    

    【讨论】:

      【解决方案2】:

      使用fillna()np.where 慢很多,但它的优点是只能使用pandas。如果您想要一个更快的方法并保持pandas 纯,您可以使用combine_first(),根据文档用于:

      组合 Series 值,首先选择调用 Series 的值。结果索引将是两个索引的并集

      翻译:这是一种旨在完全按照问题要求执行的方法。

      如何使用它?

      df['a'].combine_first(df['a*'])
      

      性能:

      df = pd.DataFrame({'A': [0, None, 1, 2, 3, None] * 10000, 'A*': [4, 4, 5, 6, 7, 8] * 10000})
      
      def using_fillna(df):
          return df['A'].fillna(df['A*'])
      
      def using_combine_first(df):
          return df['A'].combine_first(df['A*'])
      
      def using_np_where(df):
          return np.where(df['A'].isnull(), df['A*'], df['A'])
      
      def using_np_where_numpy(df):
          return np.where(np.isnan(df['A'].values), df['A*'].values, df['A'].values)
      
      %timeit -n 100 using_fillna(df)
      %timeit -n 100 using_combine_first(df)
      %timeit -n 100 using_np_where(df)
      %timeit -n 100 using_np_where_numpy(df)
      
      1.34 ms ± 71.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      281 µs ± 15.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      257 µs ± 16.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      166 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      

      【讨论】:

        【解决方案3】:

        为了获得更好的性能,可以使用 numpy.isnan 并通过 values 将 Series 转换为 numpy 数组:

        df['a'] = np.where(np.isnan(df['a'].values), df['a*'].values, df['a'].values)
        df['b'] = np.where(np.isnan(df['b'].values), df['b*'].values, df['a'].values)
        

        另一个通用解决方案,如果在 DataFrame 的列中只存在有/没有 * 的对,并且有必要删除 * 列:

        首先由split创建MultiIndex并附加*val

        df.columns = (df.columns + '*val').str.split('*', expand=True, n=1)
        

        然后通过DataFrame.xs 选择DataFrames,所以DataFrame.fillna 工作得很好:

        df = df.xs('*val', axis=1, level=1).fillna(df.xs('val', axis=1, level=1))
        print (df)
             a    b
        1  5.0  9.0
        2  3.0  3.0
        3  4.0  1.0
        4  9.0  7.0
        

        性能:(取决于缺失值的数量和DataFrame的长度)

        df = pd.DataFrame({'A': [0, np.nan, 1, 2, 3, np.nan] * 10000, 
                           'A*': [4, 4, 5, 6, 7, 8] * 10000})
        
        def using_fillna(df):
            df['A'] = df['A'].fillna(df['A*'])
            return df
        
        def using_np_where(df):
            df['B'] = np.where(df['A'].isnull(), df['A*'], df['A'])
            return df
        
        def using_np_where_numpy(df):
            df['C'] = np.where(np.isnan(df['A'].values), df['A*'].values, df['A'].values)
            return df
        
        def using_combine_first(df):
            df['D'] = df['A'].combine_first(df['A*'])
            return df
        
        %timeit -n 100 using_fillna(df)
        %timeit -n 100 using_np_where(df)
        %timeit -n 100 using_combine_first(df)
        %timeit -n 100 using_np_where_numpy(df)
        
        1.15 ms ± 89.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        533 µs ± 13.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        591 µs ± 38.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        423 µs ± 21.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        

        【讨论】:

        • 哦,这给了我一个'SyntaxError: invalid syntax' on the fillna(df[f'{c}*'])
        • @hamslice - 它是python 3.6 语法,低版本需要fillna(df['{}*'.format(c)])
        • 如果重要的评论有点过于明确,我认为“使用 numpy”。性能差异不是很大,结论应该是“如果性能很重要,请不要使用fillna”。
        • @user3471881 - 已删除 :)
        猜你喜欢
        • 2016-08-02
        • 1970-01-01
        • 2012-01-08
        • 2020-09-30
        • 1970-01-01
        • 2020-04-19
        • 2018-08-29
        • 2022-01-19
        • 1970-01-01
        相关资源
        最近更新 更多