Python: Wählen Sie eine Teilmenge aus der Liste basierend auf der Indexmenge aus

94

Ich habe mehrere Listen mit der gleichen Anzahl von Einträgen (von denen jede eine Objekteigenschaft angibt):

property_a = [545., 656., 5.4, 33.]
property_b = [ 1.2,  1.3, 2.3, 0.3]
...

und Liste mit Flags gleicher Länge

good_objects = [True, False, False, True]

(die leicht durch eine äquivalente Indexliste ersetzt werden könnte:

good_indices = [0, 3]

Was ist der einfachste Weg , um neue Listen zu generieren property_asel, property_bsel... , die nur die Werte enthalten , angegeben entweder durch die TrueEinträge oder die Indizes?

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]
fuenfundachtzig
quelle

Antworten:

123

Sie könnten einfach das Listenverständnis verwenden :

property_asel = [val for is_good, val in zip(good_objects, property_a) if is_good]

oder

property_asel = [property_a[i] for i in good_indices]

Letzteres ist schneller, weil es weniger good_indicesals die Länge von gibt property_a, vorausgesetzt, sie good_indiceswerden vorberechnet anstatt im laufenden Betrieb generiert.


Bearbeiten : Die erste Option entspricht der itertools.compressseit Python 2.7 / 3.1 verfügbaren Option . Siehe die Antwort von @Gary Kerr .

property_asel = list(itertools.compress(property_a, good_objects))
kennytm
quelle
1
@fuen: Ja. Verursacht viel in Python 2 (verwenden Sie stattdessen itertools.izip ), nicht so sehr in Python 3. Dies liegt daran, dass zipin Python 2 eine neue Liste erstellt wird, in Python 3 jedoch nur ein (fauler) Generator zurückgegeben wird.
Kennytm
OK, dann sollte ich mich dann an Ihren zweiten Vorschlag halten, da dies den zentralen Teil meines Codes ausmacht.
Fuenfundachtzig
4
@ 85: Warum machst du dir Sorgen um die Leistung? Schreiben Sie, was Sie tun müssen. Wenn es langsam ist, testen Sie, um Engpässe zu finden.
Gary Kerr
1
@PreludeAndFugue: Wenn es zwei gleichwertige Optionen gibt, ist es gut zu wissen, welche schneller ist, und diese sofort zu verwenden.
Fuenfundachtzig
1
Sie können dies einfach verwenden from itertools import izipund verwenden, anstatt zipim ersten Beispiel. Das schafft einen Iterator, genau wie Python 3.
Chris B.
26

Ich sehe 2 Optionen.

  1. Verwenden von numpy:

    property_a = numpy.array([545., 656., 5.4, 33.])
    property_b = numpy.array([ 1.2,  1.3, 2.3, 0.3])
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = property_a[good_objects]
    property_bsel = property_b[good_indices]
  2. Verwenden Sie ein Listenverständnis und komprimieren Sie es:

    property_a = [545., 656., 5.4, 33.]
    property_b = [ 1.2,  1.3, 2.3, 0.3]
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = [x for x, y in zip(property_a, good_objects) if y]
    property_bsel = [property_b[i] for i in good_indices]
Wolph
quelle
2
Die Verwendung von Numpy ist ein guter Vorschlag, da das OP offenbar Nummern in Listen speichern möchte. Ein zweidimensionales Array wäre noch besser.
Philipp
Dies ist auch ein guter Vorschlag, da dies für Benutzer von R eine sehr vertraute Syntax ist, bei der diese Art der Auswahl sehr leistungsfähig ist, insbesondere wenn sie verschachtelt und / oder mehrdimensional ist.
Thomas Browne
[property_b[i] for i in good_indices]ist eine gute für die Verwendung ohnenumpy
Ilya Rusin
16

Verwenden Sie die integrierte Funktion zip

property_asel = [a for (a, truth) in zip(property_a, good_objects) if truth]

BEARBEITEN

Schauen Sie sich nur die neuen Funktionen von 2.7 an. Es gibt jetzt eine Funktion im itertools-Modul, die dem obigen Code ähnlich ist.

http://docs.python.org/library/itertools.html#itertools.compress

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F
Gary Kerr
quelle
1
Ich bin überwältigt von der Verwendung von itertools.compresshier. Das Listenverständnis ist weitaus besser lesbar, ohne herausfinden zu müssen, was die Heckkompresse tut.
PaulMcG
5
Hm, ich finde den Code mit Compress viel besser lesbar :) Vielleicht bin ich voreingenommen, weil er genau das macht, was ich will.
Fuenfundachtzig
8

Angenommen, Sie haben nur die Liste der Elemente und eine Liste der wahren / erforderlichen Indizes, sollte dies die schnellste sein:

property_asel = [ property_a[index] for index in good_indices ]

Dies bedeutet, dass die Eigenschaftsauswahl nur so viele Runden ausführt, wie echte / erforderliche Indizes vorhanden sind. Wenn Sie viele Eigenschaftslisten haben, die den Regeln einer einzelnen Tag-Liste (wahr / falsch) folgen, können Sie eine Indexliste mit denselben Grundsätzen für das Listenverständnis erstellen:

good_indices = [ index for index, item in enumerate(good_objects) if item ]

Dies durchläuft jedes Element in good_objects (wobei der Index mit enumerate gespeichert wird) und gibt nur die Indizes zurück, bei denen das Element wahr ist.


Für alle, die das Listenverständnis nicht verstehen, gibt es hier eine englische Prosa-Version mit fett hervorgehobenem Code:

Liste den Index für jede Gruppe von Index, Artikel , das existiert in einer Aufzählung von guten Objekten , wenn (wo) das Element True

Eyrofire
quelle
0

Matlab- und Scilab-Sprachen bieten eine einfachere und elegantere Syntax als Python für die von Ihnen gestellte Frage. Ich denke, das Beste, was Sie tun können, ist, Matlab / Scilab mithilfe des Numpy-Pakets in Python nachzuahmen. Auf diese Weise ist die Lösung Ihres Problems sehr präzise und elegant:

from numpy import *
property_a = array([545., 656., 5.4, 33.])
property_b = array([ 1.2,  1.3, 2.3, 0.3])
good_objects = [True, False, False, True]
good_indices = [0, 3]
property_asel = property_a[good_objects]
property_bsel = property_b[good_indices]

Numpy versucht, Matlab / Scilab nachzuahmen, aber es ist mit Kosten verbunden: Sie müssen jede Liste mit dem Schlüsselwort "array" deklarieren, was Ihr Skript überladen wird (dieses Problem besteht bei Matlab / Scilab nicht). Beachten Sie, dass diese Lösung auf Arrays mit Zahlen beschränkt ist, was in Ihrem Beispiel der Fall ist.

FredAndre
quelle
3
Nirgendwo in der Frage erwähnt er NumPy - es besteht keine Notwendigkeit, Ihre Meinung zu NumPy vs Matlab zu äußern. Python-Listen sind nicht dasselbe wie NumPy-Arrays, auch wenn beide ungefähr Vektoren entsprechen. (Python-Listen ähneln Matlab-Zell-Arrays - jedes Element kann einen anderen Datentyp haben. NumPy-Arrays sind eingeschränkter, um bestimmte Optimierungen zu ermöglichen.) Sie können eine ähnliche Syntax wie in Ihrem Beispiel über die integrierte Python- filterBibliothek oder die externe Bibliothek erhalten pandas. Wenn Sie Sprachen tauschen möchten, können Sie auch R ausprobieren, aber das ist nicht die Frage .
Livius