Как сохранить несколько словарей в hdf5 и объединить информацию для общих ключей из разных словарей? - PullRequest
0 голосов
/ 21 апреля 2020

Вкратце, меня интересует, существует ли эффективный способ сохранения нескольких словарей с общими ключами в файл hdf5.

Возможно, у меня есть N словарей со следующей структурой (в следующем примере я буду использовать 2 слова) ):

sample_1 = {'gene1_gene1': 1,
            'gene1_gene2': -0.385,
            'gene1_gene3': 0.25,
            'gene2_gene2': 1,
            'gene2_gene3': 0.004,
            'gene3_gene3': 1
           }
sample_2 = {'gene1_gene1': 1,
            'gene1_gene2': -0.0035,
            'gene1_gene4': 0.0421,
            'gene2_gene2': 1,
            'gene2_gene4': -0.0783,
            'gene4_gene4': 1
           }

Каждый словарь содержит M ключей. Некоторые ключи являются общими для нескольких словарей (например, gene1_gene2), некоторые ключи уникальны (например, gene2_gene3 и gene2_gene4). Я хочу сохранить каждый словарь в один файл hdf5 со структурой, подобной этой:

{
    'gene1_gene2': {
         'sample_1': -0.385,
         'sample_2': -0.0035,
         ....
    }
    ....
}

Таким образом, файл должен содержать оба значения для каждого ключа и соответствующие имена образцов.

После что я хочу извлечь имена и значения образцов из файла hdf5, используя целевые пары geneX_geneY.

Чтобы сделать это, я пишу этот код:

with h5py.File("out_file.hdf5", 'a') as f:
    for gene_pair, cor_val in sample_N.items():
        try:
            grp = f.create_group(gene_pair)
            grp.create_dataset('data', (0,), maxshape=(None,), dtype=h5py.string_dtype(encoding='ascii'),
                               compression="gzip", compression_opts=9)
            grp.create_dataset('cor', (0,), maxshape=(None,), dtype='f4', compression="gzip", compression_opts=9)
            data = f[gene_pair]['data']
            cor = f[gene_pair]['cor']
            cor.resize(cor.shape[0] + 1, axis=0)
            cor[-1] = cor_val
            data.resize(data.shape[0] + 1, axis=0)
            data[-1] = 'sample_N'
        except:
            data = f[gene_pair]['data']
            cor = f[gene_pair]['cor']
            cor.resize(cor.shape[0] + 1, axis=0)
            cor[-1] = cor_val
            data.resize(data.shape[0] + 1, axis=0)
            data[-1] = 'sample_N'

Вкратце, код создает группы соответствующий ключу geneX_geneY и сохраните значение (f[gene_pair]['cor']) и имя набора данных (f[gene_pair]['data']) в соответствующих наборах данных с массивами numpy.

Мне интересно, есть ли эффективный способ сделать это?

1 Ответ

0 голосов
/ 24 апреля 2020

Как отмечается в комментариях, HDF5 / h5py использует NumPy структуры массивов и не поддерживает словари. Честно говоря, я думаю, что массивы намного превосходят словари для управления такого рода данными. (Лично я бы НЕ сохранял данные gene_pair в словарях ... грязно, чтобы загружать и выгружать ... но это только я.)

Нет необходимости создавать группу gene_pair с отдельным наборы данных для cor и data. Фактически, ваш метод усложняет схему, требуя доступа к 2 различным наборам данных для получения cor и data. Вы можете сделать все это в одном наборе данных / таблице для каждого gene_pair (эквивалентно массиву записей NumPy). Есть много способов сделать это. Если вы можете изменить структуру данных gene_pair перед этим процессом, я настоятельно рекомендую массив записей NumPy. Он имеет естественную привязку к наборам данных HDF5 / h5py, и их создание - одношаговый процесс. Если вы не можете (или не хотите) сделать это, у меня есть 3 предложения.

Примечание. Код для создания словаря sample_N одинаков для всех методов и приведен в конце.

Кроме того, я последовал вашему примеру и создал каждый набор данных / таблицу как расширяемый набор данных с нулем строки изначально, добавляя по одной строке за раз. Это нормально для небольших выборок данных, но перераспределение больших наборов данных станет медленным, если у вас есть миллионы строк. Если вы планируете добавить много данных, я предлагаю другой подход. Выделите начальный блок больших строк (10e4?), Затем проследите за текущей строкой и расширите при необходимости дополнительными большими блоками. Этот метод только «немного» сложнее. : -)

Метод 1: Этот формат таблицы соответствует вашему запросу. Он создает набор данных (таблицу) для каждого gene_pair имени. Имя набора данных - это имя gene_pair, и оно имеет 3 поля: Sample_ID (текст), ID (извлеченное целое число) и тестовое значение, связанное с этим образцом.

with h5py.File("SO_61351577_1.h5", 'w') as h5f:
  for sample_N in sample_dict:
    for gene_pair, cor_val in sample_dict.get(sample_N).items():
        gene_dt = np.dtype ([ ('Sample', 'S20'), ('ID', int), ('Value', float) ]) 
        if gene_pair not in h5f.keys() :
            gene_pair_ds = h5f.create_dataset(gene_pair, (1,), maxshape=(None,), dtype=gene_dt,
                           compression="gzip", compression_opts=9)
        else:
            gene_pair_ds = h5f[gene_pair]
            gene_pair_ds.resize(gene_pair_ds.shape[0] + 1, axis=0)
        gene_pair_ds[-1,'Sample'] = sample_N
        gene_pair_ds[-1,'ID'] = int(sample_N[7:] )
        gene_pair_ds[-1,'Value']  = cor_val

Метод 2: Используется другой формат таблицы. Он содержит одну строку данных для каждого образца и тестовые значения под соответствующим столбцом / полем. Это просто предложение для вашего рассмотрения. Не имеет смысла, если количество пар велико (скажем,> 100). Кроме того, в таблице есть 0.0 для значений, которые не были введены, поэтому это может быть проблемой.
Как работает процесс: Сначала я получаю gene_pair имен из словарей sample_N и строю список уникальных имен. Я использую этот список для создания NumPy dtype для хранения всех данных. Затем я l oop в словарях sample_N и записываю все значения gene_pair для этого образца в той же строке. Сопоставление с полем / столбцом выполняется с помощью имени gene_pair.

#Create a list of unique dictionary keys:
gene_list = []
for sample_N in sample_dict:
    for gene_pair in sample_dict.get(sample_N):
        if gene_pair not in gene_list:
            gene_list.append(gene_pair)
gene_list.sort()
# Create dictionary of names and formats to use for dtype
# Then use as NumPy dtype to create the dataset
dt_genetable = {'names':['Sample','ID'], 'formats':['S20', int] }
for gene_name in gene_list:
     dt_genetable['names'].append(gene_name)
     dt_genetable['formats'].append(float)

with h5py.File("SO_61351577_2.h5", 'w') as h5f:
    genetable_ds = h5f.create_dataset('GeneTable', (0,), maxshape=(None,), 
                        dtype=dt_genetable, compression="gzip", compression_opts=9)

    for sample_N in sample_dict:
        genetable_ds.resize(genetable_ds.shape[0] + 1, axis=0)
        genetable_ds[-1,'Sample'] = sample_N
        genetable_ds[-1,'ID'] = int(sample_N[7:] )
        for gene_pair, cor_val in sample_dict.get(sample_N).items():
            print (sample_N, ':', gene_pair, ':', cor_val)
            genetable_ds[-1,gene_pair]  = cor_val

Метод 3: Я создал этот пример, потому что этот формат набора данных / таблицы наиболее точно имитирует исходный формат словаря. Он похож на метод 2 с немного отличающимся типом и таблицей. Как и в методе 2, он имеет одну строку данных для каждой выборки с 12 дополнительными полями для значений gene_pair и cor_val. (Я предполагал, что у вас всегда есть 6 пар.)

# Create dictionary of names and formats to use for dtype
# Then use as NumPy dtype to create dataset
sample_table_dt = {'names':['Sample','ID'], 'formats':['S20', int] }
for cnt in range(1,7,1):
     sample_table_dt['names'].append('gene_name_'+str(cnt))
     sample_table_dt['formats'].append('S20')
     sample_table_dt['names'].append('cor_val_'+str(cnt))
     sample_table_dt['formats'].append(float)

with h5py.File("SO_61351577_3.h5", 'w') as h5f:
    sample_table_ds = h5f.create_dataset('SampleTable', (0,), maxshape=(None,), 
                        dtype=sample_table_dt, compression="gzip", compression_opts=9)

    for sample_N in sample_dict:
        sample_table_ds.resize(sample_table_ds.shape[0] + 1, axis=0)
        sample_table_ds[-1,'Sample'] = sample_N
        sample_table_ds[-1,'ID'] = int(sample_N[7:] )
        gcnt = 1
        for gene_pair, cor_val in sample_dict.get(sample_N).items():
            sample_table_ds[-1,'gene_name_'+str(gcnt)]  = gene_pair
            sample_table_ds[-1,'cor_val_'+str(gcnt)]  = cor_val
            gcnt += 1

Код для создания словаря sample_N:

sample_1 = {'gene1_gene1': 1,
            'gene1_gene2': -0.385,
            'gene1_gene3': 0.25,
            'gene2_gene2': 1,
            'gene2_gene3': 0.004,
            'gene3_gene3': 1
           }
sample_2 = {'gene1_gene1': 1,
            'gene1_gene2': -0.0035,
            'gene1_gene4': 0.0421,
            'gene2_gene2': 1,
            'gene2_gene4': -0.0783,
            'gene4_gene4': 1
           }
sample_3 = {'gene1_gene1': 1,
            'gene1_gene2': -0.065,
            'gene1_gene4': 0.0567,
            'gene2_gene2': 1,
            'gene2_gene3': -0.0378,
            'gene4_gene4': 1
           }

sample_dict = dict( sample_1=sample_1,
                    sample_2=sample_2,
                    sample_3=sample_3 )
...