2016-06-22 11 views
0

Zum Beispiel habe ich folgende Daten als Liste:Python - wie das letzte Vorkommen Erfüllung einer bestimmten Bedingung aus einer Liste extrahieren

l = [['A', 'aa', '1', '300'], 
    ['A', 'ab', '2', '30'], 
    ['A', 'ac', '3', '60'], 
    ['B', 'ba', '5', '50'], 
    ['B', 'bb', '4', '10'], 
    ['C', 'ca', '6', '50']] 

Jetzt für 'A', 'B' und 'C', ich wollte bekommen ihre letzten Vorkommen, dh:

[['A', 'ab', '3', '30'], 
['B', 'bb', '4', '10'], 
['C', 'ca', '6', '50']] 

oder weitere, die dritte Spalte in diesen Ereignissen, nämlich:

['3', '4', '6'] 
Derzeit

, die Art, wie ich damit umgehen ist:

import pandas as pd 
df = pd.DataFrame(l, columns=['u', 'w', 'y', 'z']) 
df.set_index('u', inplace=True) 
ll = [] 
for letter in df.index.unique(): 
    ll.append((df.ix[letter, 'y'][-1])) 

Dann %timeit ich, es zeigt:

>> The slowest run took 27.86 times longer than the fastest. 
>> This could mean that an intermediate result is being cached. 
>> 1000000 loops, best of 3: 887 ns per loop 

Nur frage mich, ob es eine Möglichkeit ist, dies mit weniger Zeit, als mein Code zu tun ? Vielen Dank!

+0

Was ist die ineffiziente Art, die Sie derzeit haben? – jonrsharpe

+3

Warum ist das letzte Vorkommen für 'A' das zweite, nicht das dritte Array? –

+0

Verwenden Sie umgekehrt auf Ihrer Liste und dann - Mögliche Duplikate von [Was ist der beste Weg, um das erste Element aus einem iterablen zu einer Bedingung zu bekommen?] (Http://stackoverflow.com/questions/2361426/what-is-the-) best-way-to-get-the-first-item-from-iterable-matching-a-condition) –

Antwort

2
l = [['A', 'aa', '1', '300'], 
    ['A', 'ab', '2', '30'], 
    ['A', 'ac', '3', '60'], 
    ['B', 'ba', '5', '50'], 
    ['B', 'bb', '4', '10'], 
    ['C', 'ca', '6', '50']] 

import itertools 
for key, group in itertools.groupby(l, lambda x: x[0]): 
    print key, list(group)[-1] 

Ohne Kommentar zu „Effizienz“, weil Sie nicht Ihre Bedingungen erklärt auf alle. Angenommen, die Liste wird im Voraus nach dem ersten Element der Unterliste sortiert.

Wenn die Liste sortiert ist, sollte man durchlaufen genug sein:

def tidy(l): 
    tmp = [] 
    prev_row = l[0] 

    for row in l: 
     if row[0] != prev_row[0]: 
      tmp.append(prev_row) 
     prev_row = row 
    tmp.append(prev_row) 
    return tmp 

und das ist ~ 5x schneller als itertools.groupby in einem timeit Test. Demonstration: https://repl.it/C5Af/0

[Edit: OP ihre Frage aktualisiert hat sie zu sagen, ist GROUPBY bereits Pandas mit, die schneller bereits möglicherweise Weg ist]

+0

Tut mir leid, habe das aus Versehen geändert und kann es jetzt nicht entfernen! Fühlen Sie sich frei zu entfernen, wenn Sie können, habe dies zu meiner Antwort jetzt hinzugefügt! –

+0

@NilsGudat es ist in Ordnung, ich habe die Bearbeitung abgelehnt. Ich erwarte, dass der Ansatz "itertools.groupby" langsamer ist, da er GroupInfo-Objekte und neue Listen erstellt. Es ist durchaus möglich, dies mit einem Durchlauf durch die Liste zu tun, vorausgesetzt, die Liste ist sortiert, ich denke, es ist ziemlich pythonisch und drückt deutlicher aus, was es tut. – TessellatingHeckler

1

Auch wenn ich nicht sicher bin ich Ihre Frage verstanden, dann ist hier, was Sie tun können:

li = [l[i][0] for i in range(len(l))] 
[l[j][2] for j in [''.join(li).rfind(i) for i in set(li)]] 

Beachten Sie, dass die Ausgabe [3,4,6], als das letzte Auftreten von A die dritte zu sein scheint, nicht die zweites Array.

bearbeiten, wie Sie über die Leistung sehr besorgt scheinen (obwohl Sie nicht sagen, was Sie versucht haben und was als „gut“):

%timeit li = [l[i][0] for i in range(len(l))] 
%timeit [l[j][2] for j in [''.join(li).rfind(i) for i in set(li)]] 
>> 1000000 loops, best of 3: 1.19 µs per loop 
>> 100000 loops, best of 3: 2.57 µs per loop 

%timeit [list(group)[-1][2] for key, group in itertools.groupby(l, lambda x: x[0])] 
>> 100000 loops, best of 3: 5.11 µs per loop 

So scheint es, die Liste Verständnis geringfügig schneller ist als itertools (obwohl ich kein Experte für Benchmarks bin und es könnte einen besseren Weg geben, den itertools zu starten).

0

Ein nicht sehr-pythonic Ansatz: (beachten Sie, dass Nils' Lösung ist die pythonic - mit Liste Verständnis)

def get_last_row(xs,q): 
    for i in range(len(xs)-1,-1,-1): 
     if xs[i][0] == q: 
      return xs[i][2] 

def get_third_cols(xs): 
    third_cols = [] 
    for q in ["A","B","C"]: 
     third_cols.append(get_last_row(xs,q)) 
    return third_cols 

print get_third_cols(xs) 

Dies druckt ['3', '4', '6'] wenn das, was Sie mit dem letzten Auftreten gemeint.

1

{l[0]: l[2] for l in vals} finden Sie eine Abbildung von ‚A‘ erhalten, ‚B‘ und ‚C‘, um ihre letzten Werte

+0

Hallo, würde es dir etwas ausmachen, deinen Code zu erklären? Ich verstehe nicht, wie man es benutzt, um das Ergebnis zu bekommen. Übrigens, was ist "Vals"? Vielen Dank! – Map

+0

'Vals' ist Ihre Eingabe (Ihre Liste von Listen). Wie für den Code selbst, lesen Sie auf Diktat-Verständnis und Sie werden sehen, wie es funktioniert. – acushner

+0

Wäre es möglich, eine Liste wie '['3', '4', '6']' anstelle von Wörterbuch zurückgeben zu lassen? – Map

0

Dies zu jedem Schlüssel/Wert-Standort verallgemeinern wird.Beachten Sie, dass die Ausgabe in in der Reihenfolge erfolgt, in der der erste Schlüssel beobachtet wurde. Es wäre nicht schwer sein, so einzustellen, dass die Reihenfolge der Ausgabe der Auftrag ist, dass der Ausgangswert

import operator 

l = [['A', 'aa', '1', '300'], 
    ['A', 'ab', '2', '30'], 
    ['A', 'ac', '3', '60'], 
    ['B', 'ba', '5', '50'], 
    ['B', 'bb', '4', '10'], 
    ['C', 'ca', '6', '50']] 

def getLast(data, key, value): 
    f = operator.itemgetter(key,value) 
    store = dict() 
    keys = [] 
    for row in data: 
     key, value = f(row) 
     if key not in store: 
      keys.append(key) 
     store[key] = value 
    return [store[k] for k in keys] 

Jetzt ist es Timing,

%timeit getLast(l,0,2) 

Gibt beobachtet wurde:

The slowest run took 9.44 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 2.85 µs per loop 
Ausgänge

Und die Funktion:

['3', '4', '6']