【问题标题】:Memory efficient way for list comprehension of pandas dataframe using multiple columns使用多列对熊猫数据框进行列表理解的内存有效方法
【发布时间】:2020-02-22 07:38:43
【问题描述】:

我想在列表理解中对 pandas 数据框的行运行一个函数。 数据框可以有不同数量的列。 如何使用这些数据框列?

import  pandas as pd

df = {'chrom': ['chr1', 'chr1','chr1'], 'start': [10000, 10100, 12000], 'end':[10150,10120,12250], 'S1':[1, 1, 1],'S2':[2, 2, 2],'S3':[3, 3, 3] }
df = pd.DataFrame(data=df)
print(df)

def func(row):
    print(row)


[func(row) for row in zip(df['chrom'],df['start'],df['S1'],df['S2'],df['S3'])]

如何以一种内存有效的方式做到这一点?这样我们就不会收到大数据帧的任何内存错误。

【问题讨论】:

  • 取决于您想要的输出方式。似乎您的操作方式可能是最有效的方式之一(因为zip() 有效地生成了一个生成器)-您在这里遇到了特殊问题吗?
  • 是的,以“S”开头的列数不是恒定的。从 'S1' ... 到 'S30' 可以有 30 列,或者从 'S1' ... 到 'S60' 可以有 60 列。
  • 由于列数是一个变量,我使用了 df[list(df.columns.values)].values() 但这会产生 MemoryError
  • 同样使用 df[list(df.columns.values)].to_numpy(copy=False) 会产生 MemoryError

标签: python dataframe list-comprehension


【解决方案1】:

显示的代码非常节省内存,并且应该比基于iterrow 的解决方案更快。

但是从您的评论来看,导致内存错误的不是代码...有问题的代码是:

df[list(df.columns.values)].values()

或:

df[list(df.columns.values)].to_numpy(copy=False)

因为两者都涉及数据帧值的完整副本,除非所有列都具有相同的 dtype。

如果要处理未知数量的列,安全的方法是:

[func(row) for row in zip([df[i].values for i in df.columns])]

这里不需要复制,因为df[i].values 将返回底层的 numpy 数组。


顺便说一句,如果您只需要使用一次返回列表的值,您甚至可以通过使用生成器而不是列表来节省一些内存:

(func(row) for row in zip([df[i].values for i in df.columns]))

【讨论】:

    【解决方案2】:

    感谢您的回答。

    同时,我找到了以下解决方案:

    df_columns = list(df.columns.values)
    [func_using_list_comp(
                    row,
                    var1,
                    var2,
                    var3,
                    ...,
                    df_columns) for row in df[df_columns].values]
    

    通过这种方式,我不需要使用 zip 功能并使其适用于任意数量的列。

    我希望这也是内存效率。 顺便说一句,每次处理一行时,我都会在 var1、var2、var3 中累积。

    如果我使用生成器而不是列表,它会对我的内存使用有多大影响,我会在处理完所有行后获得所有累积的数据吗?

    因为,在处理完所有行之后,我将返回这些 var1、var2、var3。

    【讨论】:

      【解决方案3】:

      您的列表理解方法似乎比它需要的更令人困惑,尤其是考虑到 pandas 数据帧有一个 iterrows() 方法。你可以用这个替换你的版本:

      for index, row in df.iterrows():
          func(row)
      

      但我只建议使用上述方法,因为您的函数似乎只打印出该行。根据您的func 的实际用途,您可能需要考虑使用df.apply()

      df.apply(func, axis=1)
      

      【讨论】:

      【解决方案4】:

      在您的示例中,打印整行,[0] 或 * 只是再次删除 numpy 框架:

      [func(*row) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
      

      [func(row[0]) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
      
      ['chr1' 10000 1 2 3]
      ['chr1' 10100 1 2 3]
      ['chr1' 12000 1 2 3]
      

      只打印第三列:

      [func(row[0][2]) for row in zip(df[['chrom','start','S1','S2','S3']].to_numpy())]
      
      1
      1
      1
      

      p.s.:这最后也有控制台输出[None, None, None],不过那只是因为list comprehension里面print()的结果是None,不属于打印结果。

      另见:

      编辑:

      请使用 df.iloc 和 df.loc 代替 df[[...]],参见Selecting multiple columns in a pandas dataframe

      【讨论】:

        猜你喜欢
        • 2013-04-03
        • 2014-12-29
        • 2017-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-23
        • 2019-02-08
        • 2016-04-26
        相关资源
        最近更新 更多