IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Intersection de listes avec critère de sous-liste [Python 3.X]


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2013
    Messages
    1 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 617
    Par défaut Intersection de listes avec critère de sous-liste
    Bonsoir,
    Je vous présente mon problème simplifié. Je dispose d'une liste telle que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [[['baba', 1, 25.2, 36.4], ['chouba', 236, 25.7, 48.6], ['touti', 63, 25.8, 36.9], ['quanti', 27, 58.4, 12.9]], [['baba', 9, 15.2, 63.8], ['touti', 653, 75.8, 636.9], ['quanti', 287, 588.4, 126.9]], [['baba', 12, 58.2, 326.4], ['chouba', 23, 2.8, 4.6], ['loula', 65, 36.1, 2514.3], ['touti', 683, 2.8, 4.9], ['quanti', 7, 5.4, 2.9]]]
    Je ne veux récupérer que les éléments dont le premier terme est commun, c'est-à-dire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [[['baba', 1, 25.2, 36.4], ['touti', 63, 25.8, 36.9], ['quanti', 27, 58.4, 12.9]], [['baba', 9, 15.2, 63.8], ['touti', 653, 75.8, 636.9], ['quanti', 287, 588.4, 126.9]], [['baba', 12, 58.2, 326.4], ['touti', 683, 2.8, 4.9], ['quanti', 7, 5.4, 2.9]]]
    J'ai écrit ce petit programme mais cela me semble bien compliqué, non ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    liste_1 = [["baba",1,25.2,36.4],["chouba",236,25.7,48.6],["touti",63,25.8,36.9],["quanti",27,58.4,12.9]]
    liste_2 = [["baba",9,15.2,63.8],["touti",653,75.8,636.9],["quanti",287,588.4,126.9]]
    liste_3 = [["baba",12,58.2,326.4],["chouba",23,2.8,4.6],["loula",65,36.1,2514.3],["touti",683,2.8,4.9],["quanti",7,5.4,2.9]]
    liste_origine = []
    liste_origine.append(liste_1)
    liste_origine.append(liste_2)
    liste_origine.append(liste_3)
    print(liste_origine)
     
    prem = []
    for k in range(len(liste_origine)):
        prem.append([])
        for i in range(len(liste_origine[k])):
            if liste_origine[k][i][0] not in prem:
                prem[k].append(liste_origine[k][i][0])
     
    inter = set(prem[0]).intersection(*prem)
    print("---------------------------")
    print(inter)
    print("---------------------------")
     
    liste_defi=[]
    for k in range(len(liste_origine)):
        liste_defi.append([])
        for i in range(len(liste_origine[k])):
            if liste_origine[k][i][0] in inter:
                liste_defi[k].append(liste_origine[k][i])
    print(liste_defi)
    Si vous avez plus simple (en fait, j'ai 12 listes de 400 et quelques sous-listes)...

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ai souvent traité des problèmes de "liste de listes" à cause des bases de données, et j'ai fini par faire une petite fonction de recherche dans les sous-listes (appelée ici "indicedansliste") qui a beaucoup simplifié mes codes.

    Voilà comment j'aurais codé ton problème:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    def indicedansliste(elem, lliste, indcol):
        """renvoie l'indice i (1er trouvé) de lliste si lliste[i][indcol]==elem
           si non trouvé, renvoie -1.
        """
        for i, x in enumerate(lliste):
            if elem == x[indcol]:
                return i
        return -1
     
    liste_1 = [["baba",1,25.2,36.4],["chouba",236,25.7,48.6],["touti",63,25.8,36.9],["quanti",27,58.4,12.9]]
    liste_2 = [["baba",9,15.2,63.8],["touti",653,75.8,636.9],["quanti",287,588.4,126.9]]
    liste_3 = [["baba",12,58.2,326.4],["chouba",23,2.8,4.6],["loula",65,36.1,2514.3],["touti",683,2.8,4.9],["quanti",7,5.4,2.9]]
     
    results = []
    for sliste in liste_1:
        ind2 = indicedansliste(sliste[0], liste_2, 0)
        if ind2 == -1:
            continue # le nom n'existe pas dans cette liste
        ind3 = indicedansliste(sliste[0], liste_3, 0)
        if ind3 == -1:
            continue # le nom n'existe pas dans cette liste
        # ici, on a trouvé un nom commun aux 3 listes
        results.append(sliste)
        results.append(liste_2[ind2])
        results.append(liste_3[ind3])
     
    print(results)    
    [['baba', 1, 25.2, 36.4], ['baba', 9, 15.2, 63.8], ['baba', 12, 58.2, 326.4], ['touti', 63, 25.8, 36.9], ['touti', 653, 75.8, 636.9], ['touti', 683, 2.8, 4.9], ['quanti', 27, 58.4, 12.9], ['quanti', 287, 588.4, 126.9], ['quanti', 7, 5.4, 2.9]]
    Ce petit code suppose que chaque liste n'a chaque nom qu'une seule fois (=pas de doublon), mais si le même nom pouvait être retrouvé plusieurs fois dans la même liste, il faudrait que la petite fonction de recherche renvoie la liste des indices des noms trouvés et non le 1er seulement. Si tu as ce problème, je peux te proposer un nouveau code.

    [edit] on peut gagner encore un peu en concision en remplaçant les 3 x "results.append" par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    results += [sliste] + [liste_2[ind2]] + [liste_3[ind3]]

  3. #3
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2013
    Messages
    388
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 388
    Par défaut
    Bonjour,
    Quand le nombre de listes est grand, l'utilisation de itertools.chain est avantageuse. Voici ma version :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    $ python3
    Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
    [GCC 5.4.0 20160609] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> l1 = [ ["baba", 1, 25.2, 36.4], ["chouba", 236, 25.7, 48.6], ["touti", 63, 25.8, 36.9], ["quanti", 27, 58.4, 12.9]]
    >>> l2 = [["baba", 9, 15.2, 63.8], ["touti", 653, 75.8, 636.9], ["quanti", 287, 588.4, 126.9]]
    >>> l3 = [["baba", 12, 58.2, 326.4], ["chouba", 23, 2.8, 4.6], ["loula", 65, 36.1, 2514.3], ["touti", 683, 2.8, 4.9], ["quanti", 7, 5.4, 2.9]]
    >>> data = (l1, l2, l3)
    >>> def common_items(*lists):
    ...     for idx, a_list in enumerate(lists):
    ...         keys = {item[0] for item in a_list}
    ...         common = common & keys if idx else keys
    ...     return common
    ... 
    >>> keys = common_items(*data)
    >>> keys
    {'touti', 'quanti', 'baba'}
    >>> from itertools import chain
    >>> (item for item in chain(*data) if item[0] in keys)
    <generator object <genexpr> at 0x7f07d3cdba40>
    >>> for item in _: print(item)
    ... 
    ['baba', 1, 25.2, 36.4]
    ['touti', 63, 25.8, 36.9]
    ['quanti', 27, 58.4, 12.9]
    ['baba', 9, 15.2, 63.8]
    ['touti', 653, 75.8, 636.9]
    ['quanti', 287, 588.4, 126.9]
    ['baba', 12, 58.2, 326.4]
    ['touti', 683, 2.8, 4.9]
    ['quanti', 7, 5.4, 2.9]
    >>>
    Qui fonctionne s'il n'y a pas de doublon.

  4. #4
    Membre Expert
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2013
    Messages
    1 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 617
    Par défaut
    Bon, ben merci à tous les deux : j'ai du boulot avec ce que vous avez posté.

  5. #5
    Membre Expert
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2013
    Messages
    1 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 617
    Par défaut
    L'avantage de la solution de dardanos, c'est qu'elle fonctionne pour des grandes listes (ce qui est mon cas), l'inconvénient, c'est que je n'y comprends pas grand chose.
    Le point positif, c'est que j'ai des progrès à faire

  6. #6
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2013
    Messages
    388
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2013
    Messages : 388
    Par défaut
    Citation Envoyé par marco056 Voir le message
    L'avantage de la solution de dardanos, c'est qu'elle fonctionne pour des grandes listes (ce qui est mon cas), l'inconvénient, c'est que je n'y comprends pas grand chose.
    Pas de problème pour fournir quelques infos.

    Je reprends les 3 listes que tu as fournies et les mets dans un tuple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> l1 = [ ["baba", 1, 25.2, 36.4], ["chouba", 236, 25.7, 48.6], ["touti", 63, 25.8, 36.9], ["quanti", 27, 58.4, 12.9]]
    >>> l2 = [["baba", 9, 15.2, 63.8], ["touti", 653, 75.8, 636.9], ["quanti", 287, 588.4, 126.9]]>>> l3 = [["baba", 12, 58.2, 326.4], ["chouba", 23, 2.8, 4.6], ["loula", 65, 36.1, 2514.3], ["touti", 683, 2.8, 4.9], ["quanti", 7, 5.4, 2.9]]
    >>> data = (l1, l2, l3)
    La fonction common_items recherche les éléments communs à toutes les listes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    >>> def common_items(*lists):
    ...     for idx, a_list in enumerate(lists):
    ...         keys = {item[0] for item in a_list}
    ...         common = common & keys if idx else keys
    ...     return common
    ...
    L'opérateur d'unpacking, le * dans "common_items(*lists)" permet d'avoir un nombre quelconque d'arguments, et donc de traiter un nombre quelconque de listes.
    Pour chaque liste, le set keys contient le premier élément de chacune des sous-listes. Il n'y a pas de doublon puisque c'est un set. L'opération common & keys prend l'intersection entre la liste courante et l'intersection des listes précédentes. Il ne reste à la fin que les éléments communs à toutes les listes.

    D'ailleurs on le vérifie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> keys = common_items(*data)
    >>> keys
    {'quanti', 'baba', 'touti'}
    L'opérateur d'unpacking * est utilisé pour transformer le tuple data en 3 listes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    >>> from itertools import chain
    >>> g = (item for item in chain(*data) if item[0] in keys)
    >>> for item in g: print(item)
    ... 
    ['baba', 1, 25.2, 36.4]
    ['touti', 63, 25.8, 36.9]
    ['quanti', 27, 58.4, 12.9]
    ['baba', 9, 15.2, 63.8]
    ['touti', 653, 75.8, 636.9]
    ['quanti', 287, 588.4, 126.9]
    ['baba', 12, 58.2, 326.4]
    ['touti', 683, 2.8, 4.9]
    ['quanti', 7, 5.4, 2.9]
    >>>
    La fonction itertools.chain permet de parcourir un ensemble de listes comme s'il n'y en avait qu'une seule. Le générateur g sert à sélectionner les sous-listes dont le premier élément fait partie de éléments communs à toutes les listes.
    Les sous-listes sont affichées. On pourrait les regrouper avec le slicing en fonction du besoin.

    La solution fonctionne donc quelque soit le nombre de listes.
    Il faut modifier le traitement s'il y a des doublons (par exemple plusieurs sous-listes commençant par "touti"). Mais c'est une base de travail.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [AC-2016] Requête ajout avec critère basé sur liste déroulante
    Par Lolo62000 dans le forum Access
    Réponses: 1
    Dernier message: 30/04/2017, 17h34
  2. [XL-2007] Mise à jour automatique d'une Liste, avec critères sur feuille
    Par Crachover dans le forum Excel
    Réponses: 1
    Dernier message: 05/03/2017, 12h54
  3. Réponses: 3
    Dernier message: 15/06/2016, 06h27
  4. Réponses: 0
    Dernier message: 08/12/2010, 10h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo