Сохраните pandas DataFrame с помощью h5py для взаимодействия с другими считывателями hdf5

Вот пример фрейма данных:

import pandas as pd

NaN = float('nan')
ID = [1, 2, 3, 4, 5, 6, 7]
A = [NaN, NaN, NaN, 0.1, 0.1, 0.1, 0.1]
B = [0.2, NaN, 0.2, 0.2, 0.2, NaN, NaN]
C = [NaN, 0.5, 0.5, NaN, 0.5, 0.5, NaN]
columns = {'A':A, 'B':B, 'C':C}
df = pd.DataFrame(columns, index=ID)
df.index.name = 'ID'
print(df)

      A    B    C
ID               
1   NaN  0.2  NaN
2   NaN  NaN  0.5
3   NaN  0.2  0.5
4   0.1  0.2  NaN
5   0.1  0.2  0.5
6   0.1  NaN  0.5
7   0.1  NaN  NaN

Я знаю, что у pandas есть HDFStore на основе pytables, который представляет собой простой способ эффективно сериализовать / десериализовать фрейм данных. Но эти наборы данных не очень легко загрузить напрямую с помощью считывателя h5py или Matlab. Как я могу сохранить фрейм данных с помощью h5py, чтобы я мог легко загрузить его обратно с помощью другого считывателя hdf5?


person Phil    schedule 11.06.2015    source источник


Ответы (3)


Формат pandas HDFStore - это стандартный HDF5, только с соглашением о том, как интерпретировать метаданные. Документы находятся здесь

In [54]: df.to_hdf('test.h5','df',mode='w',format='table',data_columns=True)

In [55]: h = h5py.File('test.h5')

In [56]: h['df']['table']
Out[56]: <HDF5 dataset "table": shape (7,), type "|V32">

In [64]: h['df']['table'][:]
Out[64]: 
array([(1, nan, 0.2, nan), (2, nan, nan, 0.5), (3, nan, 0.2, 0.5),
       (4, 0.1, 0.2, nan), (5, 0.1, 0.2, 0.5), (6, 0.1, nan, 0.5),
       (7, 0.1, nan, nan)], 
      dtype=[('index', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])


In [57]: h['df']['table'].attrs.items()
Out[57]: 
[(u'CLASS', 'TABLE'),
 (u'VERSION', '2.7'),
 (u'TITLE', ''),
 (u'FIELD_0_NAME', 'index'),
 (u'FIELD_1_NAME', 'A'),
 (u'FIELD_2_NAME', 'B'),
 (u'FIELD_3_NAME', 'C'),
 (u'FIELD_0_FILL', 0),
 (u'FIELD_1_FILL', 0.0),
 (u'FIELD_2_FILL', 0.0),
 (u'FIELD_3_FILL', 0.0),
 (u'index_kind', 'integer'),
 (u'A_kind', "(lp1\nS'A'\na."),
 (u'A_meta', 'N.'),
 (u'A_dtype', 'float64'),
 (u'B_kind', "(lp1\nS'B'\na."),
 (u'B_meta', 'N.'),
 (u'B_dtype', 'float64'),
 (u'C_kind', "(lp1\nS'C'\na."),
 (u'C_meta', 'N.'),
 (u'C_dtype', 'float64'),
 (u'NROWS', 7)]

In [58]: h.close()

Данные будут полностью читаемы на любом считывателе HDF5. Некоторые метаданные обработаны, поэтому необходимо соблюдать осторожность.

person Jeff    schedule 11.06.2015
comment
Я использовал аргументы по умолчанию для «фиксированного» формата без установки data_columns, который имеет гораздо другой и более абстрактный набор данных hdf5, чем при использовании format = 'table', data_columns = True. Что касается документации pandas о внешней совместимости с HDFStore, я ее перечитал и никогда не увижу вашего ответа из этого описания. Спасибо за ясный и очень полезный ответ! - person Phil; 11.06.2015
comment
Если вы хотите добавить еще несколько комментариев к этому документу, было бы здорово! пожалуйста, отправьте запрос на включение - person Jeff; 12.06.2015
comment
Я хотел бы добавить к примеру документа, но у меня проблемы. Дополнительный вопрос см. В stackoverflow.com/questions/30807270. - person Phil; 12.06.2015

Вот мой подход к решению этой проблемы. Я надеюсь, что либо у кого-то есть лучшее решение, либо мой подход будет полезен другим.

Сначала определите функцию для создания массива структур numpy (не массива записей) из DataFrame pandas.

import numpy as np
def df_to_sarray(df):
    """
    Convert a pandas DataFrame object to a numpy structured array.
    This is functionally equivalent to but more efficient than
    np.array(df.to_array())

    :param df: the data frame to convert
    :return: a numpy structured array representation of df
    """

    v = df.values
    cols = df.columns
    types = [(cols[i].encode(), df[k].dtype.type) for (i, k) in enumerate(cols)]
    dtype = np.dtype(types)
    z = np.zeros(v.shape[0], dtype)
    for (i, k) in enumerate(z.dtype.names):
        z[k] = v[:, i]
    return z

Используйте reset_index, чтобы создать новый фрейм данных, который включает индекс как часть своих данных. Преобразуйте этот фрейм данных в массив структур.

sa = df_to_sarray(df.reset_index())
sa

array([(1L, nan, 0.2, nan), (2L, nan, nan, 0.5), (3L, nan, 0.2, 0.5),
       (4L, 0.1, 0.2, nan), (5L, 0.1, 0.2, 0.5), (6L, 0.1, nan, 0.5),
       (7L, 0.1, nan, nan)], 
      dtype=[('ID', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

Сохраните этот структурированный массив в файл hdf5.

import h5py
with h5py.File('mydata.h5', 'w') as hf:
            hf['df'] = sa

Загрузите набор данных h5

with h5py.File('mydata.h5') as hf:
            sa2 = hf['df'][:]

Извлеките столбец ID и удалите его из sa2

ID = sa2['ID']
sa2 = nprec.drop_fields(sa2, 'ID')

Сделайте фрейм данных с идентификатором индекса, используя sa2

df2 = pd.DataFrame(sa2, index=ID)
df2.index.name = 'ID'

print(df2)

      A    B    C
ID               
1   NaN  0.2  NaN
2   NaN  NaN  0.5
3   NaN  0.2  0.5
4   0.1  0.2  NaN
5   0.1  0.2  0.5
6   0.1  NaN  0.5
7   0.1  NaN  NaN
person Phil    schedule 11.06.2015
comment
Довольно хороший ответ и именно то, что я искал. Когда я использую стандартный pandas для создания файла hdf5, он создает файл hdf5 с большим количеством таблиц, что не совсем удобно, поэтому я предпочитаю делать это с h5py, но обрабатывать данные с помощью панд. Если бы я только мог поставить более одного голоса за ваш ответ, я бы сделал это, спасибо! ;) - person silgon; 12.11.2015
comment
Спасибо за поддержку и положительный отзыв! Мой ответ более низкоуровневый и индивидуальный, чем ответ Джеффа, чей ответ я принял, потому что он не требует специального кода и лучше подходит для моего варианта использования, чем этот индивидуальный подход. Однако я очень рад, что этот код был вам полезен. - person Phil; 12.11.2015
comment
dtype = np.dtype(types) дает TypeError: data type not understood. - person Tushar Jain; 04.05.2020
comment
Удаление encode() заставляет его работать. Я предполагаю, что это связано с изменением Unicode в python3. - person Tushar Jain; 04.05.2020

Если это кому-то будет полезно, я взял этот пост из Guillaume и Phil, и немного изменил его для моих нужд с помощь ankostis. Мы читаем фрейм данных pandas из файла CSV.

В основном я адаптировал его для Strings, потому что вы не можете сохранить объект в файле HDF5 (я считаю). Сначала проверьте, какие типы столбцов numpy objects. Затем проверьте, какая длина этого столбца самая длинная, и исправьте этот столбец как String такой длины. Остальное очень похоже на другой пост.

def df_to_sarray(df):
    """
    Convert a pandas DataFrame object to a numpy structured array.
    Also, for every column of a str type, convert it into 
    a 'bytes' str literal of length = max(len(col)).

    :param df: the data frame to convert
    :return: a numpy structured array representation of df
    """

    def make_col_type(col_type, col):
        try:
            if 'numpy.object_' in str(col_type.type):
                maxlens = col.dropna().str.len()
                if maxlens.any():
                    maxlen = maxlens.max().astype(int) 
                    col_type = ('S%s' % maxlen, 1)
                else:
                    col_type = 'f2'
            return col.name, col_type
        except:
            print(col.name, col_type, col_type.type, type(col))
            raise

    v = df.values            
    types = df.dtypes
    numpy_struct_types = [make_col_type(types[col], df.loc[:, col]) for col in df.columns]
    dtype = np.dtype(numpy_struct_types)
    z = np.zeros(v.shape[0], dtype)
    for (i, k) in enumerate(z.dtype.names):
        # This is in case you have problems with the encoding, remove the if branch if not
        try:
            if dtype[i].str.startswith('|S'):
                z[k] = df[k].str.encode('latin').astype('S')
            else:
                z[k] = v[:, i]
        except:
            print(k, v[:, i])
            raise

    return z, dtype

Итак, рабочий процесс будет таким:

import h5py
import pandas as pd

# Read a CSV file
# Here we assume col_dtypes is a dictionary that contains the dtypes of the columns
df = pd.read_table('./data.csv', sep='\t', dtype=col_dtypes)
# Transform the DataFrame into a structured numpy array and get the dtype
sa, saType = df_to_sarray(df)

# Open/create the HDF5 file
f = h5py.File('test.hdf5', 'a')
# Save the structured array
f.create_dataset('someData', data=sa, dtype=saType)
# Retrieve it and check it is ok when you transform it into a pandas DataFrame
sa2 = f['someData'][:]
df2 = pd.DataFrame(sa2)
print(df2.head())
f.close()

Кроме того, таким образом вы можете увидеть его из HDFView даже при использовании gzip сжатия например.

person iipr    schedule 03.04.2017