Wie kann ich eine Liste basierend auf einer Bedingung partitionieren (aufteilen, teilen)?

Lesezeit: 8 Minuten

Benutzeravatar von Parand
Teil

Ich habe einen Code wie:

good = [x for x in mylist if x in goodvals]
bad = [x for x in mylist if x not in goodvals]

Ziel ist es, den Inhalt aufzuteilen mylist in zwei andere Listen, je nachdem, ob sie eine Bedingung erfüllen oder nicht.

Wie kann ich das eleganter machen? Kann ich vermeiden, zwei separate Iterationen zu machen? mylist? Kann ich dadurch die Leistung verbessern?

  • Ich bin hier gelandet und habe nach einer Möglichkeit gesucht, eine Bedingung in der Set-Builder-Anweisung zu haben. Ihre Frage hat meine Frage beantwortet 🙂

    – Anuvrat Parashar

    21. Juni 2012 um 13:27 Uhr

  • Teilt ist eine unglückliche Beschreibung dieser Operation, da sie bereits eine spezifische Bedeutung in Bezug auf Python-Strings hat. Ich finde teilen ist ein präziseres (oder im Kontext von Python-Iterables zumindest weniger überladenes) Wort zur Beschreibung dieser Operation. Ich bin hier gelandet und habe nach einem Listenäquivalent gesucht str.split()Zu Teilt die Liste in eine geordnete Sammlung aufeinanderfolgender Unterlisten. Z.B split([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6])im Gegensatz zu Teilen Elemente einer Liste nach Kategorie.

    – Eintopf

    17. Dezember 2015 um 16:24 Uhr


  • Diskussion des gleichen Themas auf python-list.

    – Xiong Tschamiow

    10. Oktober 2016 um 19:13 Uhr

  • IMAGE_TYPES sollte eine Menge anstelle eines Tupels sein: IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png'). n(1) statt n(o/2), praktisch ohne Unterschied in der Lesbarkeit.

    – ChaimG

    5. März 2017 um 17:58 Uhr


Benutzeravatar von dbr
dbr

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

Wie kann ich das eleganter machen?

Dieser Code ist bereits vollkommen elegant.

Es kann zu leichten Leistungsverbesserungen kommen sets, aber der Unterschied ist trivial. set basierte Ansätze werden auch Duplikate verwerfen und die Reihenfolge der Elemente nicht beibehalten. Ich finde das Listenverständnis auch viel einfacher zu lesen.

Tatsächlich könnten wir sogar noch einfacher einfach a verwenden for Schleife:

good, bad = [], []

for x in mylist:
    if x in goodvals:
        good.append(f)
    else:
        bad.append(f)

Dieser Ansatz erleichtert das Hinzufügen zusätzlicher Logik. Zum Beispiel kann der Code leicht geändert werden, um ihn zu verwerfen None Werte:

good, bad = [], []

for x in mylist:
    if x is None:
        continue
    if x in goodvals:
        good.append(f)
    else:
        bad.append(f)

  • Gibt es keine Möglichkeit, die Liste zu verstehen, ohne die Liste zweimal durchlaufen zu müssen?

    – Balki

    21. Juli 2012 um 15:42 Uhr

  • Das Problem ist, dass dies gegen das DRY-Prinzip verstößt. Es wäre schön, wenn es einen besseren Weg gäbe, dies zu tun.

    – Antimon

    9. Mai 2013 um 18:03 Uhr

  • Sobald der Appetit auf funktionale Programmierung (Haskell) oder funktionalen Stil (LINQ) geweckt ist, fangen wir an, Python für sein Alter zu riechen – [x for x in blah if ...] – ausführlich, lambda ist plump und limitiert… Es fühlt sich an, als würde man heute das coolste Auto von 1995 fahren. Nicht mehr so ​​wie damals.

    – Tomasz Gandor

    24. Mai 2015 um 13:52 Uhr

  • @TomaszGandor FTR, Haskell ist älter als Python (und tatsächlich sein Design beeinflusst). Ich denke, die Syntax für Listenverständnis und Lambdas wurde absichtlich ein wenig ausführlich gehalten, vielleicht um zu verhindern, dass sie übermäßig verwendet werden. Was in der Tat ein gewisses Risiko darstellt … So sehr ich Haskell mag, kann ich verstehen, warum viele Leute Python im Allgemeinen besser lesbar finden.

    – linksherum

    30. September 2015 um 20:04 Uhr

  • die einfache for-Schleife ist der beste Weg, dies zu tun … eine einzelne Schleife, sehr klar und lesbar

    – Anentrop

    21. April 2016 um 10:04 Uhr

  • Sie haben wahrscheinlich Recht, dass dies gegen das YAGNI-Prinzip verstößt. Es basiert auf der Annahme, dass die Anzahl verschiedener Listen, in die Dinge partitioniert werden können, in Zukunft wachsen wird.

    – Ameisen Aasma

    4. Juni 2009 um 15:33 Uhr

  • Es kann eine Menge Code sein, aber wenn [ x for x in my_list if ExpensiveOperation(x) ] dauert lange, um es zu laufen, Sie wollen es sicher nicht zweimal machen!

    – Dash-Tom-Bang

    10. Januar 2013 um 22:29 Uhr

  • +1 für das Angebot mehrerer Varianten, einschließlich iteratorbasierter und einer spezifischen „in X“-Lösung. Die “in goodvals” des OP sind möglicherweise klein, aber das Ersetzen durch ein sehr großes Wörterbuch oder ein teures Prädikat kann teuer werden. Außerdem reduziert es die Notwendigkeit, das Listenverständnis zweimal zu schreiben überall Es ist erforderlich, wodurch die Wahrscheinlichkeit von Tipp-/Benutzerfehlern verringert wird. Schöne Lösung. Danke!

    – cod3monk3y

    16. November 2013 um 21:08 Uhr

  • Beachten Sie, dass tee speichert alle Werte zwischen den zurückgegebenen Iteratoren, sodass nicht wirklich Speicherplatz gespart wird, wenn Sie einen ganzen Generator durchlaufen und dann den anderen.

    – John LaRooy

    8. August 2014 um 4:51 Uhr

windens Benutzeravatar
winden

Das Problem bei allen vorgeschlagenen Lösungen besteht darin, dass die Filterfunktion zweimal gescannt und angewendet wird. Ich würde eine einfache kleine Funktion wie diese machen:

def split_into_two_lists(lst, f):
    a = []
    b = []
    for elem in lst:
        if f(elem):
            a.append(elem)
        else:
            b.append(elem)
    return a, b

Auf diese Weise verarbeiten Sie nichts zweimal und wiederholen auch keinen Code.

  • Ich stimme zu. Ich suchte nach einem “eleganten” (dh hier bedeutet kurz und eingebaut/implizit) Weg, dies zu tun, ohne die Liste zweimal zu scannen, aber dies scheint (ohne Profilerstellung) der richtige Weg zu sein. Natürlich würde es sowieso nur bei großen Datenmengen eine Rolle spielen.

    – Matthäus Flaschen

    4. Juni 2009 um 8:32 Uhr

  • IMHO, wenn Sie einen Weg kennen, dies mit weniger CPU-Auslastung (und damit weniger Stromverbrauch) zu tun, gibt es keinen Grund, ihn nicht zu verwenden.

    – winden

    4. Juni 2009 um 19:46 Uhr

  • @winden … Alle meine Pythons nach C portieren. 😉

    – Elliot Cameron

    27. April 2016 um 21:52 Uhr

Toms Benutzeravatar
Tom

Meine Meinung dazu. Ich schlage einen faulen Single-Pass vor, partition -Funktion, die die relative Reihenfolge in den ausgegebenen Untersequenzen beibehält.

1. Anforderungen

Ich gehe davon aus, dass die Anforderungen sind:

  • Beibehaltung der relativen Reihenfolge der Elemente (daher keine Sätze und Wörterbücher)
  • werte Bedingung nur einmal für jedes Element aus (also nicht mit (i)filter oder groupby)
  • Ermöglichen Sie den faulen Konsum beider Sequenzen (wenn wir es uns leisten können, sie vorab zu berechnen, ist die naive Implementierung wahrscheinlich auch akzeptabel).

2. split Bibliothek

Mein partition Funktion (unten eingeführt) und andere ähnliche Funktionen haben es in eine kleine Bibliothek geschafft:

Es kann normalerweise über PyPI installiert werden:

pip install --user split

Um eine Liste nach Bedingung zu teilen, verwenden Sie partition Funktion:

>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]

3. partition Funktion erklärt

Intern müssen wir zwei Untersequenzen gleichzeitig erstellen, sodass die Verwendung von nur einer Ausgabesequenz dazu führt, dass auch die andere berechnet wird. Und wir müssen den Status zwischen Benutzeranfragen beibehalten (verarbeitete, aber noch nicht angeforderte Elemente speichern). Um den Zustand beizubehalten, verwende ich zwei doppelseitige Warteschlangen (deques):

from collections import deque

SplitSeq Klasse kümmert sich um den Haushalt:

class SplitSeq:
    def __init__(self, condition, sequence):
        self.cond = condition
        self.goods = deque([])
        self.bads = deque([])
        self.seq = iter(sequence)

Magie passiert in seinem .getNext() Methode. Es ist fast wie .next()
der Iteratoren, erlaubt aber die Angabe, welche Art von Element wir dieses Mal wollen. Hinter den Kulissen werden die abgelehnten Elemente nicht verworfen, sondern in eine der beiden Warteschlangen gestellt:

    def getNext(self, getGood=True):
        if getGood:
            these, those, cond = self.goods, self.bads, self.cond
        else:
            these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
        if these:
            return these.popleft()
        else:
            while 1: # exit on StopIteration
                n = self.seq.next()
                if cond(n):
                    return n
                else:
                    those.append(n)

Der Endbenutzer soll verwenden partition Funktion. Es nimmt eine Bedingungsfunktion und eine Sequenz (genau wie map oder filter) und gibt zwei Generatoren zurück. Der erste Generator bildet eine Teilfolge von Elementen, für die die Bedingung gilt, der zweite bildet die komplementäre Teilfolge. Iteratoren und Generatoren ermöglichen ein faules Aufteilen sogar langer oder unendlicher Sequenzen.

def partition(condition, sequence):
    cond = condition if condition else bool  # evaluate as bool if condition == None
    ss = SplitSeq(cond, sequence)
    def goods():
        while 1:
            yield ss.getNext(getGood=True)
    def bads():
        while 1:
            yield ss.getNext(getGood=False)
    return goods(), bads()

Ich habe die Testfunktion als erstes Argument gewählt, um zukünftig eine partielle Anwendung zu ermöglichen (ähnlich how map Und filter
haben die Testfunktion als erstes Argument).

  • Ich stimme zu. Ich suchte nach einem “eleganten” (dh hier bedeutet kurz und eingebaut/implizit) Weg, dies zu tun, ohne die Liste zweimal zu scannen, aber dies scheint (ohne Profilerstellung) der richtige Weg zu sein. Natürlich würde es sowieso nur bei großen Datenmengen eine Rolle spielen.

    – Matthäus Flaschen

    4. Juni 2009 um 8:32 Uhr

  • IMHO, wenn Sie einen Weg kennen, dies mit weniger CPU-Auslastung (und damit weniger Stromverbrauch) zu tun, gibt es keinen Grund, ihn nicht zu verwenden.

    – winden

    4. Juni 2009 um 19:46 Uhr

  • @winden … Alle meine Pythons nach C portieren. 😉

    – Elliot Cameron

    27. April 2016 um 21:52 Uhr

Benutzeravatar von Alan Isaac
Alan Isaak

Grundsätzlich gefällt mir der Ansatz von Anders, da er sehr allgemein gehalten ist. Hier ist eine Version, die das Kategorisierungsmodul an die erste Stelle setzt (um der Filtersyntax zu entsprechen) und ein defaultdict verwendet (angenommen importiert).

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d

  • Ich wollte versuchen, die Aussagen herauszupicken Zen von Python die hier zutreffen, aber es sind zu viele für einen Kommentar. =) Tolles Stück Code.

    – jpmc26

    24. Oktober 2014 um 18:49 Uhr

1443400cookie-checkWie kann ich eine Liste basierend auf einer Bedingung partitionieren (aufteilen, teilen)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy