4
votes

Une manière appropriée d'ajouter les valeurs de plusieurs dictionnaires à un autre dictionnaire en utilisant la même clé

Je souhaite ajouter 3 valeurs différentes de 3 dictionnaires différents à 1 dictionnaire "all_in_one" basé sur la même clé .

J'ai 3 grands dictionnaires basés sur le même corpus de texte (chacun des fichiers qu'il contient contient les valeurs de la même ligne - plusieurs lignes, en fait - de ces fichiers, mais des colonnes différentes). Les 3 dictionnaires ont la même key .

Ils ressemblent à ceci:

all_in_one = {}

for tk, tv in tokens.items():
    for lk, lv in lemmas.items():
        for ck, cv in categs.items():
            if tk == lk == ck:
                all_in_one[tk] = [tv, lv, cv]

Je veux ajouter ces valeurs à un autre dictionnaire pour qu'il ressemble à ceci:

all_in_one = {"token1": [tokens[value1], lemmas[value1], categs[value1]],
              "token2": [tokens[value2], lemmas[value2], categs[value2]], ... } 

J'ai une telle boucle:

tokens = {"token1": 10, "token2": 56, "token3": 90, ...}

lemmas = {"token1": "lemma1", "token2": "lemma2", "token2": "lemma3", ...}

categs = {"token1": "categX", "token2": "categY", "token3": "categZ", ...}

Le problème est que cela fonctionne (je ne sais pas si ça va), mais avec une petite quantité de fichiers. J'ai 500k fichiers. Je n'ai pas essayé de l'exécuter avec les corpus finaux, car même le premier essai avec 100 fichiers a pris quelques heures et n'a pas été terminé (100 fichiers = 6500 jetons, donc je suppose que c'est 6500 ^ 3 boucles ...). Je ne l'ai testé qu'avec 10 et 20 fichiers.

Est-ce même une boucle appropriée pour faire cela (ajouter des valeurs de 3 dics dans un autre dic)? Si oui (je doute, en fonction du temps nécessaire), peut-être y a-t-il un moyen de l'optimiser?


7 commentaires

Est-ce possible et que devrait-il se passer si certaines clés ne sont pas présentes dans tous les dictionnaires?


@buran, OP a spécifié Les 3 dictionnaires ont la même clé , mais la réponse de RoadRunner couvre le cas où cette hypothèse est assouplie.


Dans le cas ci-dessus, ils sont tous présents dans les 3 dictionnaires, mais votre question m'a fait penser que je ne l'ai pas construit correctement. Je pense que j'ai besoin de le réécrire, donc les lemmes sont les valeurs clés, et le dictionnaire lemmes devrait inclure un dictionnaire imbriqué de jetons pour chaque lemme avec chaque occurrence de jeton ... Merci!


@jpp, désolé, mais "tous les dictionnaires ont les mêmes clés" ne veut pas dire aussi "et seulement ces / mêmes clés". Quand j'ai posé la question, la réponse de RoadRunner n'était pas présente.


@buran, Assez juste, toutes les réponses énoncent la ou les hypothèses sous-jacentes, donc je ne pense pas que nous perdions (beaucoup) en traduction.


@jpp, d'accord. La première réponse a été donnée au moment où j'ai posé la question, le reste a été ajouté après ma question. Vous ne voyez pas pourquoi je pose une question valide est un problème?


@buran, je ne commente pas pour critiquer, c'est pour fournir des explications aux autres . Aucun problème ici, l'accent doit être mis sur le problème, pas sur les gens.


3 Réponses :


3
votes

Ma réponse suppose que les trois dictionnaires ont des clés identiques et identiques . Dans ce cas, je ne pense pas que vous ayez besoin de 3 boucles for ici. Vous avez juste besoin d'une seule boucle for. Étant donné que les clés sont les mêmes et qu'il vous suffit de regrouper les valeurs des mêmes clés, vous pouvez simplement boucler sur l'une des clés du dictionnaire et faire

all_in_one = {}

for tk, tv in tokens.items():
    all_in_one[tk] = [tv, lemmas[tk], categs[tk]]


1 commentaires

Merci, cette solution fonctionne très vite (ainsi que celle ci-dessous, merci @jpp). Ma boucle était évidemment un débutant. J'ai encore besoin de réécrire tout comme mentionné dans le commentaire ci-dessous le commentaire de @ buran.



2
votes

Puisque les clés sont identiques dans tous les dictionnaires, vous pouvez utiliser une compréhension de dictionnaire itérant sur les clés de n'importe lequel de ces dictionnaires. Pour réduire la logique répétée, vous pouvez utiliser operator.itemgetter:

# define list explicitly
all_in_one = {k: [tokens[k], lemmas[k], categs[k]] for k in tokens}

# use list comprehension
all_in_one = {k: [lst[k] for lst in (tokens, lemmas, categs)] for k in tokens}

D'autres alternatives incluent la définition explicite d'une liste ou en utilisant une compréhension de liste:

from operator import itemgetter

tokens = {"token1": 10, "token2": 56, "token3": 90}
lemmas = {"token1": "lemma1", "token2": "lemma2", "token3": "lemma3"}
categs = {"token1": "categX", "token2": "catehY", "token3": "categZ"}

all_in_one = {k: list(map(itemgetter(k), (tokens, lemmas, categs))) for k in tokens}

# {'token1': [10, 'lemma1', 'categX'],
#  'token2': [56, 'lemma2', 'catehY'],
#  'token3': [90, 'lemma3', 'categZ']}


0 commentaires

2
votes

Si les clés entre les trois dictionnaires ne sont pas les mêmes, vous pouvez utiliser un collections.defaultdict () ici:

{k: [tokens.get(k), lemmas.get(k), categs.get(k)] for k in tokens.keys() & lemmas.keys() & categs.keys()}

De plus, vous pouvez condenser ce qui précède avec itertools.chain également:

from itertools import chain

for k, v in chain(tokens.items(), lemmas.items(), categs.items()):
    result[k].append(v)

print(result)
# defaultdict(<class 'list'>, {'token1': [10, 'lemma1', 'categX'], 'token2': [56, 'lemma2', 'catehY'], 'token3': [90, 'lemma3', 'categZ']})


4 commentaires

Il convient de souligner que c'est la meilleure réponse si les clés de vos dictionnaires ne sont pas toujours égales.


@jpp J'ai inclus un exemple d'intersection d'ensemble qui fonctionne également si les clés sont les mêmes. J'ai oublié la Les 3 dictionnaires ont la même hypothèse de clé faite par l'OP.


Si toutes les clés sont identiques, il n'est pas nécessaire de prendre l'intersection, il suffit de parcourir l'un des dictionnaires (n'importe lequel le ferait).


@jpp C'est vrai, mais si pour une raison quelconque, tous les dictionnaires n'ont pas les mêmes clés, utiliser cette approche lèvera une KeyError .