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

Lev*_*tor 20 python performance pandas output

对于python/pandas,我发现df.to_csv(fname)的工作速度约为每分钟1百万行.我有时可以将性能提高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
Run Code Online (Sandbox Code Playgroud)

注意:性能的提高取决于dtypes.但是总是如此(至少在我的测试中)to_csv()的执行速度比非优化的python慢​​得多.

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

aa = read_csv(infile)  #  1.5 min
aa.to_csv(outfile)     # 45 min
df2csv(aa,...)         # ~6 min
Run Code Online (Sandbox Code Playgroud)

问题:

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

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

Jef*_*eff 11

列弗.熊猫已经改写,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
Run Code Online (Sandbox Code Playgroud)

因此,对于单个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
Run Code Online (Sandbox Code Playgroud)

  • 对于这种类型的数据集的FWIW,它经常支付以存储在HDF5中,特别是如果您有任何需要查询,请参阅http://pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables和http ://pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore (2认同)

kra*_*ski 11

在 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

2021 年更新:正如评论中所指出的,pandas 的性能大大提高。savetxt仍然是最快的选择,但幅度很小:当以pandas1.3.0 和numpy1.20.3 为基准时,用aa.to_csv()了 2.64 秒,而用了savetxt2.53 秒。问题 ( df2csv) 中的代码耗时 2.98 秒,使其成为当今最慢的选项。

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

  • 就其价值而言,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 秒 (3认同)

Run*_*un2 5

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

  • 如果列数 &gt; 100000,@wordsforthewise chunksize 仅为 1,否则为 100000 // 列数 (5认同)
  • 找到它,可能是1... https://github.com/pandas-dev/pandas/blob/v0.20.3/pandas/io/formats/format.py#L1555 (2认同)
  • 尝试将 1600 万行写入 CSV。使用 chunksize=1000000,将写入速度提高了 10 倍。使用 c5.9xlarge EC2 实例 (2认同)

LtW*_*orf 2

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

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

pythoncsv模块会引用该值,以便不会出现混淆,并且如果任何值中存在引号,则会转义引号。当然,生成在所有情况下都有效的东西要慢得多。但我想你只有一堆数字。

您可以尝试一下,看看是否更快:

#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')
Run Code Online (Sandbox Code Playgroud)

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