11 votes

Méthode pythonique pour créer un dictionnaire à partir d'une liste où les clés sont les éléments qui se trouvent dans une autre liste et les valeurs sont les éléments entre les clés.

Considérant que j'ai deux listes comme :

l1 = ['a', 'c', 'b', 'e', 'f', 'd']
l2 = [
    'x','q','we','da','po',
    'a', 'el1', 'el2', 'el3', 'el4',
    'b', 'some_other_el_1', 'some_other_el_2',
    'c', 'another_element_1', 'another_element_2',
    'd', '', '', 'another_element_3', 'd4'
]

et j'ai besoin de créer un dictionnaire où les clés sont les éléments de la deuxième liste qui sont trouvés dans la première et les valeurs sont des listes d'éléments trouvés entre les "clés", par exemple :

result = {
    'a': ['el1', 'el2', 'el3', 'el4'],
    'b': ['some_other_el_1', 'some_other_el_2'],
    'c': ['another_element_1', 'another_element_2'],
    'd': ['', '', 'another_element_3', 'd4']
}

Quelle est la façon la plus pythique de faire cela ?

Actuellement, je fais ceci :

# I'm not sure that the first element in the second list
# will also be in the first so I have to create a key
k = ''
d[k] = []
for x in l2:
    if x in l1:
        k = x
        d[k] = []
    else:
        d[k].append(x)

Mais je suis certain que ce n'est pas la meilleure façon de faire et que ce n'est pas très joli :)

Éditer : Je dois également mentionner qu'aucune liste n'est nécessairement ordonnée et que la seconde liste ne doit pas non plus commencer par un élément de la première.

10voto

FHTMitchell Points 8959

Je ne pense pas que vous ferez beaucoup mieux si c'est l'énoncé le plus spécifique du problème. Je veux dire que je le ferais de cette façon, mais ce n'est pas beaucoup mieux.

import collections

d = collections.defaultdict(list)
k = ''

for x in l2:
    if x in l1:
        k = x
    else:
        d[k].append(x)

5voto

jpp Points 83462

Pour le plaisir, vous pouvez aussi le faire avec itertools et de tiers numpy :

import numpy as np
from itertools import zip_longest, islice

arr = np.where(np.in1d(l2, l1))[0]
res = {l2[i]: l2[i+1: j] for i, j in zip_longest(arr, islice(arr, 1, None))}

print(res)

{'a': ['el1', 'el2', 'el3', 'el4'],
 'b': ['some_other_el_1', 'some_other_el_2'],
 'c': ['another_element_1', 'another_element_2'],
 'd': ['', '', 'another_element_3', 'd4']}

3voto

mkrieger1 Points 4461

Voici une version utilisant itertools.groupby . Il peut ou non être plus efficace que la version simple de votre message, en fonction de la façon dont le système de gestion de l'information est utilisé. groupby est mis en œuvre, car le for a moins d'itérations.

from itertools import groupby
from collections import defaultdict, deque

def group_by_keys(keys, values):
    """
    >>> sorted(group_by_keys('abcdef', [
    ...          1, 2, 3,
    ...     'b', 4, 5,
    ...     'd',
    ...     'a', 6, 7,
    ...     'c', 8, 9,
    ...     'a', 10, 11, 12
    ... ]).items())
    [('a', [6, 7, 10, 11, 12]), ('b', [4, 5]), ('c', [8, 9])]
    """
    keys = set(keys)
    result = defaultdict(list)
    current_key = None
    for is_key, items in groupby(values, key=lambda x: x in keys):
        if is_key:
            current_key = deque(items, maxlen=1).pop()  # last of items
        elif current_key is not None:
            result[current_key].extend(items)
    return result

Cela ne permet pas de faire la distinction entre les clés qui n'apparaissent pas dans le fichier values du tout (comme e y f ), et les clés pour lesquelles il n'existe pas de valeurs correspondantes (telles que d ). Si ces informations sont nécessaires, l'une des autres solutions pourrait être mieux adaptée.

2voto

Justin Engel Points 880

Mise à jour ... Encore une fois

J'ai mal interprété la question. Si vous utilisez de grandes listes, alors les compréhensions de listes sont la voie à suivre et elles sont assez simples une fois que vous avez appris à les utiliser.

Je vais utiliser deux compréhensions de liste.

idxs = [i for i, val in enumerate(l2) if val in l1] + [len(l2)+1]
res = {l2[idxs[i]]: list(l2[idxs[i]+1: idxs[i+1]]) for i in range(len(idxs)-1)}
print(res)

Résultats :

{'a': ['el1', 'el2', 'el3', 'el4'],
 'b': ['some_other_el_1', 'some_other_el_2'],
 'c': ['another_element_1', 'another_element_2'],
 'd': ['', '', 'another_element_3', 'd4']}

Test de vitesse pour les grandes listes :

import collections

l1 = ['a', 'c', 'b', 'e', 'f', 'd']
l2 = [
    'x','q','we','da','po',
    'a', 'el1', 'el2', 'el3', 'el4', *(str(i) for i in range(300)),
    'b', 'some_other_el_1', 'some_other_el_2', *(str(i) for i in range(100)),
    'c', 'another_element_1', 'another_element_2', *(str(i) for i in range(200)),
    'd', '', '', 'another_element_3', 'd4'
]

def run_comp():
    idxs = [i for i, val in enumerate(l2) if val in l1] + [len(l2)+1]
    res = {l2[idxs[i]]: list(l2[idxs[i]+1: idxs[i+1]]) for i in range(len(idxs)-1)}

def run_other():
    d = collections.defaultdict(list)
    k = ''

    for x in l2:
        if x in l1:
            k = x
        else:
            d[k].append(x)

import timeit
print('For Loop:', timeit.timeit(run_other, number=1000))
print("List Comprehension:", timeit.timeit(run_comp, number=1000))

Résultats :

For Loop: 0.1327093063242541
List Comprehension: 0.09343156142774986

vieux trucs ci-dessous

C'est assez simple avec les compréhensions de listes.

{key: [val for val in l2 if key in val] for key in l1}

Résultats :

{'a': ['a', 'a1', 'a2', 'a3', 'a4'],
 'b': ['b', 'b1', 'b2', 'b3', 'b4'],
 'c': ['c', 'c1', 'c2', 'c3', 'c4'],
 'd': ['d', 'd1', 'd2', 'd3', 'd4'],
 'e': [],
 'f': []}

Le code ci-dessous montre ce qui se passe ci-dessus.

d = {}
for key in l1:
    d[key] = []
    for val in l2:
        if key in val:
            d[key].append(val)

La compréhension de la liste / du dictionnaire (premier morceau de code) est en fait beaucoup plus rapide. La compréhension de liste crée la liste sur place, ce qui est beaucoup plus rapide que de parcourir la liste et d'y ajouter des éléments. L'ajout fait que le programme parcourt la liste, alloue plus de mémoire et ajoute les données à la liste, ce qui peut être très lent pour les grandes listes.

Références :

1voto

Ajax1234 Points 42210

Vous pouvez utiliser itertools.groupby :

import itertools
l1 = ['a', 'c', 'b', 'e', 'f', 'd']
l2 = ['x', 'q', 'we', 'da', 'po', 'a', 'el1', 'el2', 'el3', 'el4', 'b', 'some_other_el_1', 'some_other_el_2', 'c', 'another_element_1', 'another_element_2', 'd', '', '', 'another_element_3', 'd4']
groups = [[a, list(b)] for a, b in itertools.groupby(l2, key=lambda x:x in l1)]
final_dict = {groups[i][-1][-1]:groups[i+1][-1] for i in range(len(groups)-1) if groups[i][0]}

Sortie :

{'a': ['el1', 'el2', 'el3', 'el4'], 'b': ['some_other_el_1', 'some_other_el_2'], 'c': ['another_element_1', 'another_element_2'], 'd': ['', '', 'another_element_3', 'd4']}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X