1
votes

Python: sélectionnez des clés dans des dictionnaires imbriqués

J'ai un dictionnaire imbriqué avec la structure suivante:

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

seqs
{}

Et je veux accéder aux touches «description» dans tous les sous-dictionnaires hitx.

Je sais que je pourrais utiliser des boucles for, et j'ai essayé la pensée suivante, je pourrais peut-être la limiter à une boucle for en parcourant les dictionnaires recordx, puis en accédant à toutes les touches de `` description '' dans les dictionnaires hitx dans un dictionnaire recordx à une fois, mais je n'ai pas de succès:

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}
}

Toute aide serait très appréciée!


0 commentaires

3 Réponses :


0
votes

Vous aurez besoin de 2 boucles pour terminer cette opération, sauf si vous insistez pour traiter chaque clé recordxxx séparément en utilisant 1 boucle.
Et aussi les compréhensions de listes sont un peu plus faciles à lire / comprendre que les filter .

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


0 commentaires

0
votes

Je pense que vous ne comprenez pas ce que fait filter() . À partir de la documentation:

filter(function, iterable) :
Construisez un itérateur à partir des éléments de l'itérable pour lesquels la fonction renvoie true.

Alors quand tu fais

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

Vous faites essentiellement:

all_descriptions = []

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

qui ne vous donne que les touches hit* de topHitsDict['record301'] .

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

Au lieu de cela, ce que vous voulez réellement, c'est la description de ces hit* dicts. Pour cela, vous pouvez utiliser map , puis convertir l'itérateur en liste.

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

Cela équivaut à:

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

Et si vous voulez faire cela pour toutes les touches de topHitsDict , vous devrez le changer un peu. Soit en utilisant une boucle:

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'}}

Il est presque toujours plus facile de les écrire d'abord sous forme de boucle. Ensuite, vous pouvez les écrire sous forme de liste ou de dictée, puis utiliser filter() et map()

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

Ou en tant que compréhension:

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


1 commentaires

merci beaucoup pour l'excellente explication. cela m'a énormément aidé!



0
votes

Vous devez d'abord parcourir tous les enregistrements, puis pour chaque enregistrement, vérifier si la valeur est dict ou non. Si la valeur est dict, recherchez la description et ajoutez-la à la liste:

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)


0 commentaires