Подтвердить что ты не робот

Каков самый быстрый способ вывода большого DataFrame в файл CSV?

Для 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

Примечание: увеличение производительности зависит от типа dtypes. Но это всегда верно (по крайней мере, в моих тестах) что to_csv() выполняет гораздо медленнее, чем не оптимизированный питон.

Если у меня есть файл csv размером 45 миллионов строк, то:

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 wrong with to_csv() ? Why is it soooo slow ?

Примечание. Мои тесты проводились с использованием pandas 0.9.1 на локальном диске на сервере Linux.

4b9b3361

Ответ 1

Лев. 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 (например, float), не слишком широкая, составляет около 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

Ответ 2

используйте chunksize. Я обнаружил, что чертовски много. Если у вас есть память в руке, используйте хороший chunksize (no of rows), чтобы попасть в память, а затем написать один раз.

Ответ 3

В 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).

PS. Если вам нужно включить индекс, savetxt будет работать нормально - просто передайте df.rest_index().values и соответственно отрегулируйте строку форматирования.

Ответ 4

Ваша функция 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 вместо прямого вывода, а затем выгружать его в реальный файл каждый раз в то время.