Попытка загрузить таблицу hdf5 с помощью dataframe.to_hdf, прежде чем я умру от старости

Похоже, что с помощью Google должно быть ДЕЙСТВИТЕЛЬНО легко ответить, но я считаю, что таким образом невозможно ответить на большинство моих нетривиальных вопросов о pandas/pytables. Все, что я пытаюсь сделать, это загрузить около 3 миллиардов записей из примерно 6000 различных файлов CSV в одну таблицу в одном файле HDF5. Это простая таблица, 26 полей, смесь строк, чисел с плавающей запятой и целых чисел. Я загружаю CSV с помощью df = pandas.read_csv() и добавляю их в свой файл hdf5 с помощью df.to_hdf(). Я действительно не хочу использовать df.to_hdf(data_columns = True), потому что похоже, что это займет около 20 дней против примерно 4 дней для df.to_hdf(data_columns = False). Но, очевидно, когда вы используете df.to_hdf(data_columns = False), вы получаете кучу мусора, из которого вы даже не можете восстановить структуру таблицы (по крайней мере, так кажется на мой неподготовленный взгляд). Только столбцы, которые были определены в списке min_itemsize (4 строковых столбца), можно идентифицировать в таблице hdf5, остальные выгружаются по типу данных в values_block_0values_block_4:

table = h5file.get_node('/tbl_main/table')
print(table.colnames)
['index', 'values_block_0', 'values_block_1', 'values_block_2', 'values_block_3', 'values_block_4', 'str_col1', 'str_col2', 'str_col3', 'str_col4']

И любой запрос, например df = pd.DataFrame.from_records(table.read_where(condition)), завершается с ошибкой «Исключение: данные должны быть одномерными».

Итак, мои вопросы: (1) Мне действительно нужно использовать data_columns = True, который занимает в 5 раз больше времени? Я ожидал выполнить быструю загрузку, а затем проиндексировать всего несколько столбцов после загрузки таблицы. (2) Что это за куча мусора, которую я использую data_columns = False? Будет ли это хорошо, если мне нужна моя таблица обратно со столбцами, доступными для запросов? Это вообще на что-то годится?

Эй, дтс! Этот вопрос потребует некоторого редактирования, прежде чем кто-либо сможет ответить на него. Одно из основных правил — задавать по 1 конкретному вопросу за раз, так что вас, скорее всего, за это пометят. Кроме того, отображение минимальный воспроизводимый пример правильного и искаженного результата на некоторых небольших кадрах данных поможет любому читателю понять, как помочь.

Mike 31.05.2019 02:47

Я думал, что это общий вопрос о data_columns = True и False ... что-то, что должно быть хорошо освещено в документации pytables, но, насколько я могу судить, это не так.

dts 31.05.2019 15:28

Не уверен, почему pytbales dos должен покрывать это. df.to_hdf(data_columns = True) против False — это функция панд. Если все ваши файлы CSV имеют одинаковые столбцы данных, и все, что вам нужно, это файл HDF5 с одним и тем же файлом, возможно, вам следует пропустить pandas и вместо этого использовать pytables или h5py. Есть ТАК ответы о том, как это сделать.

kcw78 31.05.2019 16:26

Правильно, я исправлен, должен был сказать документацию pandas. Я потратил довольно много времени на просмотр pytables, чтобы пропустить шаг read_csv кадра данных, но не смог получить скорость read_csv вместе с богатством того, что read_csv может делать во время чтения. Но если pytables намного быстрее, я бы согласился просто сосать CSV без какой-либо причудливой обработки. Итак, никто ничего не знает о df.to_hdf(data_columns = True vs False)? Одна из величайших тайн жизни?

dts 31.05.2019 17:17

Я собирался указать вам на мой ответ на предыдущий вопрос об этом. Затем я обнаружил, что «вопрос был добровольно удален его автором». Я нашел свой старый код и изменил его, чтобы решить вашу ситуацию с несколькими CSV. Смотрите ответ ниже.

kcw78 31.05.2019 20:50
1
5
213
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот как вы можете создать файл HDF5 из данных CSV с помощью pytables. Вы также можете использовать аналогичный процесс для создания файла HDF5 с помощью h5py.

  1. Используйте цикл для чтения CSV-файлов с np.genfromtxt в массив np.
  2. После прочтения первого CSV-файла запишите данные с помощью метода .create_table(), ссылаясь на массив np, созданный на шаге 1.
  3. Для дополнительных файлов CSV запишите данные с помощью метода .append(), ссылаясь на массив np, созданный на шаге 1.

Конец цикла
Обновлено 02.06.2019 для чтения поля даты (мм/дд/ГГГ) и преобразования в объект datetime. Обратите внимание на изменения в аргументах genfromtxt()! Используемые данные добавляются под обновленным кодом.

import numpy as np   
import tables as tb
from datetime import datetime
csv_list = ['SO_56387241_1.csv', 'SO_56387241_2.csv' ]

my_dtype= np.dtype([ ('a',int),('b','S20'),('c',float),('d',float),('e','S20') ])

with tb.open_file('SO_56387241.h5', mode='w') as h5f:

for  PATH_csv in csv_list:
    csv_data = np.genfromtxt(PATH_csv, names=True, dtype=my_dtype, delimiter=',', encoding=None)
    # modify date in fifth field 'e'   
    for row in csv_data :
        datetime_object = datetime.strptime(row['my_date'].decode('UTF-8'), '%m/%d/%Y' )
        row['my_date'] = datetime_object

    if h5f.__contains__('/CSV_Data') :
        dset = h5f.root.CSV_Data
        dset.append(csv_data)

    else:
        dset = h5f.create_table('/','CSV_Data', obj=csv_data)

    dset.flush()

h5f.close()

Данные для тестирования:

SO_56387241_1.csv:
my_int,my_str,my_float,my_exp,my_date
0,zero,0.0,0.00E+00,01/01/1980
1,one,1.0,1.00E+00,02/01/1981
2,two,2.0,2.00E+00,03/01/1982
3,three,3.0,3.00E+00,04/01/1983
4,four,4.0,4.00E+00,05/01/1984
5,five,5.0,5.00E+00,06/01/1985
6,six,6.0,6.00E+00,07/01/1986
7,seven,7.0,7.00E+00,08/01/1987
8,eight,8.0,8.00E+00,09/01/1988
9,nine,9.0,9.00E+00,10/01/1989


SO_56387241_2.csv:
my_int,my_str,my_float,my_exp,my_date
10,ten,10.0,1.00E+01,01/01/1990
11,eleven,11.0,1.10E+01,02/01/1991
12,twelve,12.0,1.20E+01,03/01/1992
13,thirteen,13.0,1.30E+01,04/01/1993
14,fourteen,14.0,1.40E+01,04/01/1994
15,fifteen,15.0,1.50E+01,06/01/1995
16,sixteen,16.0,1.60E+01,07/01/1996
17,seventeen,17.0,1.70E+01,08/01/1997
18,eighteen,18.0,1.80E+01,09/01/1998
19,nineteen,19.0,1.90E+01,10/01/1999

Этот подход выглядит многообещающе... например, я могу делать все, что делал с read_csv, используя вместо этого genfromtxt, и это может иметь ожидаемую скорость. Но, будучи невежественным, я не могу понять, как читать строковые поля. И мне не разрешено вводить здесь достаточно символов, чтобы показать, что я делаю. Думаю, я попытаюсь отредактировать исходный пост выше...

dts 01.06.2019 19:50

Не могу понять, как добавить свой код в редактирование... что мне делать, начать новый вопрос в новой теме "genfromtxt"?

dts 01.06.2019 19:55

Отмените это, в четвертый раз, когда я попробовал синтаксис типа «| S10», он наконец сработал. Сейчас пытаюсь разобраться с преобразователями строк в целые и преобразованием даты...

dts 01.06.2019 20:14

Что вы подразумеваете под «не могу понять, как читать строковые поля»? Когда вы используете dtype=None , genfromtxt должен автоматически создавать dtype на основе найденных данных. Затем create_table будет использовать dtype массива np для определения типов данных полей таблицы. Кроме того, помните, что вы имеете дело с объектами Unicode. Я не знаю, что произойдет, если в каждой строке у вас есть строковые поля разной длины. Кроме того, если вы знаете формат файла CSV, вы можете определить dtype и ссылку в genfromtxt.

kcw78 01.06.2019 21:02

Я должен определить dtypes в виде списка, иначе «догадки» в более ранних CSV-файлах будут неправильными для более поздних CSV-файлов. Но даже если бы мне этого не нужно было делать, dtype = None дает мусор для строковых полей. На данный момент я отказываюсь от всех своих преобразователей и пытаюсь жить ни с одним из них, кроме дат.

dts 01.06.2019 21:08

Хорошо, у меня действительно работают все мои конвертеры, кроме дат. Данные имеют формат «мм/дд/гггг», и я думаю, что хочу преобразовать их в np.datetime64 (но я возьму все, что смогу). Я пытаюсь {'date': lambda s : np.datetime64(s, 's')} и еще дюжину вариаций на этот счет и другие вещи, найденные в Google, но без любви. Любые подсказки?

dts 02.06.2019 01:13

Что ж, мой проект еще не сдвинулся с мертвой точки, но похоже, что базовый код kcw78 будет работать, чтобы получить что-то похожее на версию для панд при сохранении скорости ... поэтому я отмечу как ответ. Последний комментарий: флаг «w» у меня не работает, мне нужен «a», иначе файл hdf5 воссоздается в каждом цикле вместо добавления.

dts 02.06.2019 20:28

Режим «w» и «a» зависит от порядка открытия файла HDF5 и чтения файлов CSV. Я зацикливаюсь внутри CSV внутри with, который создает/открывает файл HDF5. Разная логика может привести к разному поведению.

kcw78 02.06.2019 21:31

Давайте посмотрим, сможем ли мы решить ваши проблемы с датой и временем. Констатация очевидного: формат ваших дат зависит от того, что вы хотите с ними делать. Строки хороши, если вы просто хотите печатать. datetime ожидает ввода строки, поэтому обработка зависит от типа данных, который вы получили от genfromtxt . Из моего тестирования по умолчанию возвращается юникод. Однако вы можете определить свой dtype для чтения в виде строк. Я изменил код в своем ответе, чтобы показать, как вы можете это сделать. Кроме того, я добавил данные, используемые в примере. Надеюсь это поможет.

kcw78 02.06.2019 21:34

Спасибо за дополнительный вклад. Я долго думал над проблемой преобразования даты и решил, что лучше всего преобразовать в YYYYMMDD int32. Это столбцы, которые необходимо индексировать, и мне не нравится тип даты и времени Python, так как мне нужна кроссплатформенность.

dts 07.06.2019 15:05

Чтобы преобразовать форматы даты и времени, проверьте datetime strftime(). Вы предоставляете объект datetime и формат вывода. Добавьте int(), если вам это нужно. Добавьте что-то вроде этого в код выше: dt_ymd = int(datetime.datetime.strftime(datetime_object , '%Y%m%d'))

kcw78 07.06.2019 16:49

Это полезно, но предполагается, что у меня есть объект datetime... это было легко использовать с пандами, потому что я мог использовать аргументы infer_datetime_format и parse_dates в методе read_csv, но я еще не нашел способа сделать это в рамках капот с numpy.genfromtxt ... есть ли способ? То, как я это делаю сейчас, использует грубую силу с пользовательским преобразователем, который берет строку байтов и делает dt = datetime.strptime(byte_str.decode("UTF-8"), '%m/%d/%Y' ) ... вероятно, очень медленный по сравнению с методом панд.

dts 08.06.2019 02:42

dts, да, я использовал объект datetime, который создал в своем ответе. Я нашел SO Q&A, который считывает объект datetime, определяя dtype и добавляя converters = {0:datefunc} к numpy.genfromtxt. Смотрите это: numpy-unicodedecodeerror-am-я-использую-правильный-подход-с-‌​genfromtxt

kcw78 10.06.2019 15:19

В этой ссылке есть почти то, что я делаю (конвертер). На самом деле это работает довольно хорошо, я закончил загрузку всех своих данных в 2 больших файла hdf5, и это заняло всего 4 дня.

dts 12.06.2019 17:58

Хорошая работа. Итак, это примерно так же быстро, как df.to_hdf(data_columns = False) и намного быстрее, чем df.to_hdf(data_columns = True), и вы закончите, прежде чем умрете от старости. :-) Хороший.

kcw78 12.06.2019 18:26

Правильно ... и большое спасибо за всю вашу помощь, это было неоценимо.

dts 15.06.2019 19:30

Другие вопросы по теме