【问题标题】:What is the fastest way to output large DataFrame into a CSV file?将大型 DataFrame 输出到 CSV 文件的最快方法是什么?
【发布时间】:2013-03-03 07:00:35
【问题描述】:

对于 python / pandas,我发现 df.to_csv(fname) 以每分钟约 100 万行的速度工作。我有时可以像这样将性能提高 7 倍:

def df2csv(df,fname,myformats=[],sep=','):
  """
    # function is faster than to_csv
    # 7 times faster for numbers if formats are specified, 
    # 2 times faster for strings.
    # Note - be careful. It doesn't add quotes and doesn't check
    # for quotes or separators inside elements
    # We've seen output time going down from 45 min to 6 min 
    # on a simple numeric 4-col dataframe with 45 million rows.
  """
  if len(df.columns) <= 0:
    return
  Nd = len(df.columns)
  Nd_1 = Nd - 1
  formats = myformats[:] # take a copy to modify it
  Nf = len(formats)
  # make sure we have formats for all columns
  if Nf < Nd:
    for ii in range(Nf,Nd):
      coltype = df[df.columns[ii]].dtype
      ff = '%s'
      if coltype == np.int64:
        ff = '%d'
      elif coltype == np.float64:
        ff = '%f'
      formats.append(ff)
  fh=open(fname,'w')
  fh.write(','.join(df.columns) + '\n')
  for row in df.itertuples(index=False):
    ss = ''
    for ii in xrange(Nd):
      ss += formats[ii] % row[ii]
      if ii < Nd_1:
        ss += sep
    fh.write(ss+'\n')
  fh.close()

aa=DataFrame({'A':range(1000000)})
aa['B'] = aa.A + 1.0
aa['C'] = aa.A + 2.0
aa['D'] = aa.A + 3.0

timeit -r1 -n1 aa.to_csv('junk1')    # 52.9 sec
timeit -r1 -n1 df2csv(aa,'junk3',myformats=['%d','%.1f','%.1f','%.1f']) #  7.5 sec

注意:性能的提高取决于数据类型。 但这总是正确的(至少在我的测试中) to_csv() 的执行速度比未优化的 python 慢得多。

如果我有一个 4500 万行的 csv 文件,那么:

aa = read_csv(infile)  #  1.5 min
aa.to_csv(outfile)     # 45 min
df2csv(aa,...)         # ~6 min

问题:

What are the ways to make the output even faster?
What's wrong with to_csv() ? Why is it soooo slow ?

注意:我的测试是在 Linux 服务器的本地驱动器上使用 pandas 0.9.1 完成的。

【问题讨论】:

  • 我最近刚刚使用内置的 to_excel 和 to_csv DataFrame 方法从 DataFrame groupby 批量导出大约 1.7K 报告,以及部分报告(特别是较大的文件)出来损坏了。我现在对这些内置程序相当怀疑,并计划为我的工作流程自制我自己的导出功能。
  • @DavidMarx 您能否发布损坏文件、DataFrame 和您的代码的示例?这将极大地帮助我们调试问题。谢谢。
  • 它位于我为办公室打造的主力工具中。我会看看我是否不能用对工作场所不敏感的数据来复制问题。

标签: python performance pandas output


【解决方案1】:

您的df_to_csv 函数非常好,除了它做了很多假设并且不适用于一般情况。

如果它适合您,那很好,但请注意,这不是通用解决方案。 CSV 可以包含逗号,那么如果要写入这个元组会发生什么? ('a,b','c')

python csv 模块将引用该值,以免出现混淆,如果任何值中存在引号,则会转义引号。当然,生成适用于所有情况的东西要慢得多。但我想你只有一堆数字。

你可以试试这个,看看是否更快:

#data is a tuple containing tuples

for row in data:
    for col in xrange(len(row)):
        f.write('%d' % row[col])
        if col < len(row)-1:
            f.write(',')
    f.write('\n')

我不知道这样会不会更快。如果不是,那是因为完成了太多系统调用,所以您可能会使用StringIO 而不是直接输出,然后每隔一段时间将其转储到真实文件中。

【讨论】:

    【解决方案2】:

    列夫。 Pandas 已重写 to_csv 以大幅提高本机速度。该过程现在是 i/o 绑定的,解决了许多微妙的 dtype 问题和引用案例。这是我们与 0.10.1(即将发布的 0.11)版本相比的性能结果。这些在ms,比例越低越好。

    Results:
                                                t_head  t_baseline      ratio
    name                                                                     
    frame_to_csv2 (100k) rows                 190.5260   2244.4260     0.0849
    write_csv_standard  (10k rows)             38.1940    234.2570     0.1630
    frame_to_csv_mixed  (10k rows, mixed)     369.0670   1123.0412     0.3286
    frame_to_csv (3k rows, wide)              112.2720    226.7549     0.4951
    

    所以单个 dtype(例如浮点数)的吞吐量,不太宽约为 20M 行/分钟,这是您上面的示例。

    In [12]: df = pd.DataFrame({'A' : np.array(np.arange(45000000),dtype='float64')}) 
    In [13]: df['B'] = df['A'] + 1.0   
    In [14]: df['C'] = df['A'] + 2.0
    In [15]: df['D'] = df['A'] + 2.0
    In [16]: %timeit -n 1 -r 1 df.to_csv('test.csv')
    1 loops, best of 1: 119 s per loop
    

    【讨论】:

    • FWIW 对于这种类型的数据集,通常需要将其存储在 HDF5 中,尤其是如果您需要查询,请参阅 pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables 和 pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore
    • 我正在尝试输出一个 100,000 * 100,000 的数据帧,而且花费的时间非常长... =(
    • 我合并了 90+ 个 csv 文件,每个文件选择特定的 4 列,只有 3 个文件超过 200,000KB,其他文件平均在 10,000KB 以下,有些文件是零行,然后 to_csv,它需要17 分钟。结果csv是200,000KB,这个时间有意义吗?我该如何优化?@Jeff
    【解决方案3】:

    使用块大小。我发现这有很大的不同。如果您手头有内存,请使用好的块大小(行数)进入内存,然后写入一次。

    【讨论】:

    • 默认的chunksize是1还是什么?你使用什么块大小?即使设置 chunksize = 500000 对我来说似乎也没有什么不同
    • @wordsforthewise chunksize 如果列数 > 100000 则为 1,否则为 100000 // 列数
    • 正在尝试将 1600 万行写入 CSV。使用 chunksize=1000000,写入速度提高了 10 倍。使用 c5.9xlarge EC2 实例
    【解决方案4】:

    在 2019 年,对于这样的情况,最好只使用 numpy.看时间:

    aa.to_csv('pandas_to_csv', index=False)
    # 6.47 s
    
    df2csv(aa,'code_from_question', myformats=['%d','%.1f','%.1f','%.1f'])
    # 4.59 s
    
    from numpy import savetxt
    
    savetxt(
        'numpy_savetxt', aa.values, fmt='%d,%.1f,%.1f,%.1f',
        header=','.join(aa.columns), comments=''
    )
    # 3.5 s
    

    因此,您可以使用 numpy 将时间缩短两倍。当然,这是以降低灵活性为代价的(与 aa.to_csv 相比)。

    以 Python 3.7、pandas 0.23.4、numpy 1.15.2 为基准(xrange 已替换为 range 以使问题中的已发布函数在 Python 3 中工作)。

    附言。如果您需要包含索引,savetxt 可以正常工作 - 只需传递 df.reset_index().values 并相应地调整格式字符串。

    2021 年更新:正如 cmets 中所指出的,pandas 的性能大大提高。 savetxt 仍然是最快的选项,但差距很小:当使用 pandas 1.3.0 和 numpy 1.20.3 进行基准测试时,aa.to_csv() 用了 2.64 秒,而savetxt 用了 2.53 秒。问题 (df2csv) 中的代码花费了 2.98 秒,使其成为当今最慢的选项。

    您的里程可能会有所不同 - 2021 年的测试是在具有非常快 CPU 的 SSD 上执行的,而 2019 年我使用的是 HDD 和较慢的 CPU。

    【讨论】:

    • 我收到以下错误:ValueError: fmt has wrong number of % formats: %d,%.1f,%.1f,%.1f
    • 确保格式字符串与列一样多。答案与问题中的测试用例一起开箱即用,对于其他数据帧则需要调整。
    • 对于它的价值,pandas to_csv 并没有那么落后(我在 v 0.23.4):timeit("aa.to_csv('temp.csv', index=False)", globals=globals(), number=5) 37.9 s timeit("np.savetxt('temp.csv', aa.values, fmt='%d,%.1f,%.1f,%.1f', header=','.join(aa.columns), comments='')", globals=globals(), number=5) 33.7 s
    • 我更新了答案。我想知道 SSD 与 HDD 的区别有多大。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    相关资源
    最近更新 更多