Avatar ·

Python 3 многомерный словарь

📁 словарь, python, ошибка, пример, работа

Изучая python 3.x , прошёл курсы по основам. До этого писал на php.

Сейчас решил выполнить задуманную задачу:

  1. Прочитать csv
  2. По мере чтения строк файла, обрабатывать их и записывать в массив, создавая нужную структуру
  3. ...

Вот мой код

import csvdef gene_name(gene, polfzm, genotype):    retu '_'.join([gene, polfzm, genotype]).strip().replace("" "", ""-"")data = {}genes = []with open('example-2.csv', 'r') as file:    reader = csv.DictReader(file, delimiter=';')    for row in reader:        group = row['group']        person_id = row['person_id']        ab = row['genotype']        name = gene_name(row['gene_name'], row['polfzm_name'], ab)        data[group][person_id][name] = ab  # Возникает ошибка

И тут проблема. Если я правильно понимаю, то для хранения моих данных в структурированном виде подходит Словрь - data. В php я мог легко создать нужную мне структуру/вложенность data[group][person_id][name] = ab. Тут же возникает ошибка KeyError: '3'

В чём ошибка моего подхода. На курсах, что я проходил, не работали с многомерными структурами. Правильно ли я выбрал тип переменной для хранения данных - Словарь? Как верно тут поступить?

Структура данных, которую требуется получить

Пример данных csv

person_id;number;gene_name;polfzm_name;genotype;expr1;group1086;AT1;F11;T/C rs2036914;CC;БеремПлохо;31086;AT1;F11;С/T rs2289252;TT;БеремПлохо;31086;AT1;ITGA2;C/T rs1126643;CT;БеремПлохо;31086;AT1;ITGB3;T/C rs5918;CC;БеремПлохо;31086;AT1;VEGF;C/T rs3025039;CC;БеремПлохо;31085;AT2;F11;T/C rs2036914;TT;БеремПлохо;31085;AT2;F11;С/T rs2289252;CC;БеремПлохо;31085;AT2;ITGA2;C/T rs1126643;TT;БеремПлохо;31085;AT2;ITGB3;T/C rs5918;TT;БеремПлохо;31085;AT2;VEGF;C/T rs3025039;CC;БеремПлохо;323;МИД6;ACE;Alu Ins/Del;ID;БеремНорм;123;МИД6;F1;Thr312Ala;ThrThr;БеремНорм;123;МИД6;F11;T/C rs2036914;TC;БеремНорм;123;МИД6;F11;С/T rs2289252;CT;БеремНорм;123;МИД6;F13;Val34Leu;ValLeu;БеремНорм;123;МИД6;F2;G20210А;GG;БеремНорм;123;МИД6;F5;Arg506Gln;GG;БеремНорм;123;МИД6;ITGA2;C/T rs1126643;CC;БеремНорм;123;МИД6;ITGB3;T/C rs5918;TC;БеремНорм;123;МИД6;VEGF;G-634C;GG;БеремНорм;1

Данные - плоская выборка данных из реляционной БД. Т.к. там были связи, а данные выбирались из нескольких таблиц, многие значения от строчки к строчке повторяются. Например, первые 5 строк данных (не учитывая шапку) - относятся к одному человеку (person_id), одной группе (group), но имеют разные гены (gene_name) и т.п.

По данным планируется делать множественные сравнения, поиск комбинаций и т.п. - порядка ~ 4 млн проходов, поэтому нужна такая структура, чтобы не бегать по всем строчкам csv впустую столько раз

Avatar ·

Изначально словарь у вас пустой. При запросе данных по любому ключу из пустого словаря будет возникать исключение KeyError. В данном случае можно использовать defaultdict:

from collections import defaultdict# Словарь, в котором значение по умолчанию другой словарь,#  у которого значение по умолчанию обычный словарьdata = defaultdict(lambda: defaultdict(dict))group = 'group'person_id = '3'name = 'John Smith'ab = 'ab'data[group][person_id][name] = abprint(data[group][person_id][name])  # Выведет ab

Полный код:

import csvfrom collections import defaultdictfrom pprint import pprintdef gene_name(gene, polfzm, genotype):    retu '_'.join([gene, polfzm, genotype]).strip().replace("" "", ""-"")data = defaultdict(lambda: defaultdict(dict))genes = []with open('example-2.csv', 'r') as file:    reader = csv.DictReader(file, delimiter=';')    for row in reader:        group = row['group']        person_id = row['person_id']        ab = row['genotype']        name = gene_name(row['gene_name'], row['polfzm_name'], ab)        data[group][person_id][name] = abpprint(data)

Вывод на примере ваших данных:

defaultdict(<function <lambda> at 0x7f73a7732ee0>,            {'1': defaultdict(<class 'dict'>,                              {'23': {'ACE_Alu-Ins/Del_ID': 'ID',                                      'F11_T/C-rs2036914_TC': 'TC',                                      'F11_С/T-rs2289252_CT': 'CT',                                      'F13_Val34Leu_ValLeu': 'ValLeu',                                      'F1_Thr312Ala_ThrThr': 'ThrThr',                                      'F2_G20210А_GG': 'GG',                                      'F5_Arg506Gln_GG': 'GG',                                      'ITGA2_C/T-rs1126643_CC': 'CC',                                      'ITGB3_T/C-rs5918_TC': 'TC',                                      'VEGF_G-634C_GG': 'GG'}}),             '3': defaultdict(<class 'dict'>,                              {'1085': {'F11_T/C-rs2036914_TT': 'TT',                                        'F11_С/T-rs2289252_CC': 'CC',                                        'ITGA2_C/T-rs1126643_TT': 'TT',                                        'ITGB3_T/C-rs5918_TT': 'TT',                                        'VEGF_C/T-rs3025039_CC': 'CC'},                               '1086': {'F11_T/C-rs2036914_CC': 'CC',                                        'F11_С/T-rs2289252_TT': 'TT',                                        'ITGA2_C/T-rs1126643_CT': 'CT',                                        'ITGB3_T/C-rs5918_CC': 'CC',                                        'VEGF_C/T-rs3025039_CC': 'CC'}})})

defaultdict - это тот же самый словарь по сути, работает точно так же (ну, кроме создания значения по умолчанию при запросе отсутствующего ключа). Под описанную вами задачу подходит, только при выводе через print отображается по-другому.

Для вывода в более привычном виде можно сериализовывать в json например:

import jsonprint(json.dumps(data, indent=4, ensure_ascii=False))

Вариант решения без defaultdict:

import csvfrom pprint import pprintdef gene_name(gene, polfzm, genotype):    retu '_'.join([gene, polfzm, genotype]).strip().replace("" "", ""-"")data = dict()genes = []with open('example-2.csv', 'r') as file:    reader = csv.DictReader(file, delimiter=';')    for row in reader:        group = row['group']        person_id = row['person_id']        ab = row['genotype']        name = gene_name(row['gene_name'], row['polfzm_name'], ab)                if group not in data:            data[group] = dict()            data[group][person_id] = dict()  # микрооптимизация        elif person_id not in data[group]:            data[group][person_id] = dict()                data[group][person_id][name] = abpprint(data)

В итоге получается обычный словарь.


И еще один вариант, со своим классом вместо defaultdict, неограниченной вложенностью словарей и поддержкой pickle (правда без поддержки сериализации в json):

from collections import UserDictclass NestedDict(UserDict):    def __getitem__(self, key):        if key not in self.data:            self.data[key] = NestedDict()                retu self.data[key]d = NestedDict()d['Lorem']['ipsum']['dolor']['sit']['amet']['consectetur']['adipiscing']['elit'] = 1print(d)print(type(d))import picklep = pickle.dumps(d)d1 = pickle.loads(p)print(d1)
{'Lorem': {'ipsum': {'dolor': {'sit': {'amet': {'consectetur': {'adipiscing': {'elit': 1}}}}}}}}<class '__main__.NestedDict'>{'Lorem': {'ipsum': {'dolor': {'sit': {'amet': {'consectetur': {'adipiscing': {'elit': 1}}}}}}}}

Войдите чтобы оставить ответ

Блоги