将大型DataFrame快速输出为CSV文件的最佳方法是什么?

38 投票
5 回答
62306 浏览
提问于 2025-04-17 19:07

对于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

注意:性能的提升取决于数据类型(dtypes)。但在我的测试中,总是发现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进行的。

5 个回答

5

使用 chunksize。这个方法真的能带来很大的不同。如果你的内存足够的话,可以选择合适的 chunksize(也就是每次处理的行数),先把数据加载到内存中,然后再一次性写入。

12

在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年的更新:正如评论中提到的,pandas的性能有了很大提升。虽然savetxt仍然是最快的选择,但差距很小:在使用pandas 1.3.0和numpy 1.20.3进行基准测试时,aa.to_csv()花了2.64秒,而savetxt花了2.53秒。提问中的代码(df2csv)现在是最慢的,花了2.98秒。

你的情况可能会有所不同——2021年的测试是在SSD和非常快的CPU上进行的,而在2019年我使用的是HDD和较慢的CPU。

15

Lev. Pandas对to_csv这个功能进行了重写,提升了它的运行速度。现在这个过程主要受输入输出的限制,同时也解决了很多细微的数据类型问题和引号的处理情况。下面是我们在即将发布的0.11版本中与0.10.1版本的性能对比结果。这些数据单位是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

对于单一数据类型(比如浮点数),如果数据量不是太大,处理速度大约是每分钟2000万行,这里就是你之前提到的例子。

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

撰写回答