Python: выбор ключей во вложенных словарях

#python #dictionary #nested

#python #словарь #вложенный

Вопрос:

У меня есть вложенный словарь со следующей структурой:

 topHitsDict = 
{'record301': 
    {'query': 'OBGP2018240_Oncorhynchus.clarkii',
        'hit1': 
            {'description': 'OBGP-2018-240_Oncorhynchus.clarkii',
            'score': '340',
            'eval': '2e-94'},
        'hit2': 
            {'description': 'OBGP-2017-332_Oncorhynchus.clarkii',
            'score': '340',
            'eval': '2e-94'},
    'numTopHits': 2},
'record302': 
    {'query': 'OBGP2018248_Oncorhynchus.kisutch',
        'hit1': 
            {'description': 'OBGP-2018-248_Oncorhynchus.kisutch',
            'score': '340',
            'eval': '2e-94'},
        'hit2': 
            {'description': 'OBGP-2018-038_Oncorhynchus.kisutch',
            'score': '340',
            'eval': '2e-94'},
        'hit3': 
            {'description': 'OBGP-2017-271_Oncorhynchus.kisutch',
            'score': '340',
            'eval': '2e-94'},
    'numTopHits': 3},
'record303':
    {'query': 'OBGP2019056_Oncorhynchus.tshawytscha',
       'hit1':
            {'description': 'OBGP-2019-056_Oncorhynchus.tshawytscha',
            'score': '340',
            'eval': '2e-94'},
        'hit2':
            {'description': 'OBGP-2017-356_Oncorhynchus.tshawytscha',
            'score': '340',
            'eval': '2e-94'},
        'hit3': 
            {'description': 'OBGP-2017-052_Oncorhynchus.tshawytscha',
            'score': '340',
            'eval': '2e-94'},
    'numTopHits': 3},
'record304':
    {'query': 'OBGP2019190_Oncorhynchus.nerka',
        'hit1': 
            {'description': 'OBGP-2019-191_Oncorhynchus.nerka',
            'score': '340',
            'eval': '2e-94'},
        'hit2': 
            {'description': 'OBGP-2019-190_Oncorhynchus.nerka',
            'score': '340',
            'eval': '2e-94'},
    'numTopHits': 2}
}
  

И я хочу получить доступ к ключам «описание» во всех вложенных словарях hitx.

Я знаю, что мог бы использовать циклы for, и я попробовал следующее, думая, что, возможно, я мог бы сократить его до одного цикла for, прокручивая словари recordx, а затем получая доступ ко всем ключам описания в словарях hitx в словаре recordx за один раз, но у меня нет никакого успеха:

 hits = dict(filter(lambda item: 'hit' in item[0], topHitsDict['record301'].items()))
seqs = dict(filter(lambda item: 'description' in item[0], hits.items()))

seqs
{}
  

Любая помощь будет высоко оценена!

Ответ №1:

Я думаю, вы неправильно понимаете, что filter() делает. Из документации:

filter(function, iterable) :
Создайте итератор из тех элементов iterable, для которых функция возвращает true .

Поэтому, когда вы делаете

 hits = dict(filter(lambda item: 'hit' in item[0], topHitsDict['record301'].items()))
  

По сути, вы делаете:

 hits = {}
for item in topHitsDict['record301'].items():
    if 'hit' in item[0]:
        hits[item[0]] = item[1]
  

который дает вам только hit* ключи от topHitsDict['record301'] .

 hits = {'hit1': {'description': 'OBGP-2018-240_Oncorhynchus.clarkii',
  'score': '340',
  'eval': '2e-94'},
 'hit2': {'description': 'OBGP-2017-332_Oncorhynchus.clarkii',
  'score': '340',
  'eval': '2e-94'}}
  

Вместо этого вам действительно нужно описание из этих hit* dicts. Для этого вы можете использовать map , а затем преобразовать итератор в список.

 descriptions = list(map(lambda item: item[1]['description'], hits.items())
# descriptions: ['OBGP-2018-240_Oncorhynchus.clarkii', 'OBGP-2017-332_Oncorhynchus.clarkii']
  

Это эквивалентно:

 descriptions = []
for item in hits.items():
    descriptions.append(item[1]['description'])
  

И если вы хотите сделать это для всех ключей topHitsDict , вам придется немного изменить его. Либо с помощью цикла:

 all_descriptions = []
for recordVal in topHitsDict.values():
    hits = dict(filter(lambda item: 'hit' in item[0], recordVal.items()))
    descriptions = list(map(lambda item: item[1]['description'], hits.items())
    # Add to all_descriptions
    all_descriptions = all_descriptions   descriptions
  

Почти всегда проще сначала записать их в виде цикла. Затем вы можете записать их в виде списка или диктовки, а затем использовать и filter() map()

 all_descriptions = []

for record in topHitsDict.values():
    for hitname, hitval in record.items():
        if "hit" in hitname:
            all_descriptions.append(hitval['description'])
  

Или как понимание:

 all_descriptions = [hitval["description"] for record in topHitsDict.values() for hitname, hitval in record.items() if "hit" in hitname]
  

Ответ №2:

Для выполнения этого вам потребуется 2 цикла, если вы не настаиваете на обработке каждого recordxxx ключа отдельно, используя 1 цикл.
А также понимание списков немного легче читать / понимать, чем filter .

 desc = [
    v["description"]
    for _, value in topHitsDict.items()
    for k, v in value.items()
    if "hit" in k
]
  

Ответ №3:

Вам нужно сначала перебрать все записи, а затем для каждой записи проверить, является ли значение dict или нет. Если значение равно dict, выполните поиск description и добавьте его в список:

 description = list()

for key,records in topHitsDict.items():
    for inner_key, inner_value in records.items(): 
        if type(inner_value) is dict and "description" in inner_value.keys():   
            description.append(inner_value['description'])

print(description)
  

Ответ №4:

Вы могли бы использовать NestedDict

 from ndicts.ndicts import NestedDict

all_descriptions = {
    'record301': {'hit1': {'description': 'OBGP-2018-240_Oncorhynchus.clarkii',
                           'eval': '2e-94',
                           'score': '340'},
                  'hit2': {'description': 'OBGP-2017-332_Oncorhynchus.clarkii',
                           'eval': '2e-94',
                           'score': '340'},
                  'numTopHits': 2,
                  'query': 'OBGP2018240_Oncorhynchus.clarkii'},
    'record302': {'hit1': {'description': 'OBGP-2018-248_Oncorhynchus.kisutch',
                           'eval': '2e-94',
                           'score': '340'},
                  'hit2': {'description': 'OBGP-2018-038_Oncorhynchus.kisutch',
                           'eval': '2e-94',
                           'score': '340'},
                  'hit3': {'description': 'OBGP-2017-271_Oncorhynchus.kisutch',
                           'eval': '2e-94',
                           'score': '340'},
                  'numTopHits': 3,
                  'query': 'OBGP2018248_Oncorhynchus.kisutch'}
}

nd = NestedDict(all_descriptions)
nd_description = nd.extract["", "", "description"]
  

Здесь это результат в виде словаря

 >>> nd_description.to_dict()
{'record301': {'hit1': {'description': 'OBGP-2018-240_Oncorhynchus.clarkii'},
               'hit2': {'description': 'OBGP-2017-332_Oncorhynchus.clarkii'}},
 'record302': {'hit1': {'description': 'OBGP-2018-248_Oncorhynchus.kisutch'},
               'hit2': {'description': 'OBGP-2018-038_Oncorhynchus.kisutch'},
               'hit3': {'description': 'OBGP-2017-271_Oncorhynchus.kisutch'}},
 'record303': {'hit1': {'description': 'OBGP-2019-056_Oncorhynchus.tshawytscha'},
               'hit2': {'description': 'OBGP-2017-356_Oncorhynchus.tshawytscha'},
               'hit3': {'description': 'OBGP-2017-052_Oncorhynchus.tshawytscha'}},
 'record304': {'hit1': {'description': 'OBGP-2019-191_Oncorhynchus.nerka'},
               'hit2': {'description': 'OBGP-2019-190_Oncorhynchus.nerka'}}}
  

Если вам нужны значения

 >>> list(nd_description.values())
['OBGP-2018-240_Oncorhynchus.clarkii', 
 'OBGP-2017-332_Oncorhynchus.clarkii', 
 ... 
 'OBGP-2019-190_Oncorhynchus.nerka']