2015-12-15 11 views
5

bei einem Datenrahmen mit einer beschreibenden Spalte und X numerischen Spalten, für jede Zeile möchte ich die oberen N Spalten mit den höheren Werten identifizieren und sie als Zeilen auf einem speichern neuer DatenrahmenSuche nach oberen N Spalten für jede Zeile im Datenrahmen

Betrachten wir zum Beispiel den folgenden Datenrahmen:

df = pd.DataFrame() 
df['index'] = ['A', 'B', 'C', 'D','E', 'F'] 
df['option1'] = [1,5,3,7,9,3] 
df['option2'] = [8,4,5,6,9,2] 
df['option3'] = [9,9,1,3,9,5] 
df['option4'] = [3,8,3,5,7,0] 
df['option5'] = [2,3,4,9,4,2] 

enter image description here

ich ausgeben möchte (können sagen, N 3 ist, so möchte ich die Top 3):

A,option3 
A,option2 
A,option4 

B,option3 
B,option4 
B,option1 

C,option2 
C,option5 
C,option4 (or option1 - ties arent really a problem) 

D,option5 
D,option1 
D,option2 

and so on.... 

eine Idee, wie das leicht erreicht werden kann? Dank

+2

welche Art von Format, das Sie tun wollen? –

+0

Da das OP nie geantwortet hat, nehmen wir die vernünftige Annahme, dass sie einen Datenrahmen haben wollen, nicht eine Liste von Listen oder was auch immer. – smci

+0

Erneut umbenannt, da das OP anscheinend * "Top-N-Spalten suchen" * anstelle von * "Top-N-Spalten auswählen ..." * auswählen möchte, was eine Pandas-Operation mit df-Ausgabe wäre. – smci

Antwort

3

Wenn Sie nur Paarungen wollen:

from operator import itemgetter as it 
from itertools import repeat 
n = 3 

# sort_values = order pandas < 0.17 
new_d = (zip(repeat(row["index"]), map(it(0),(row[1:].sort_values(ascending=0)[:n].iteritems()))) 
       for _, row in df.iterrows()) 
for row in new_d: 
    print(list(row)) 

Ausgang:

[('B', 'option3'), ('B', 'option4'), ('B', 'option1')] 
[('C', 'option2'), ('C', 'option5'), ('C', 'option1')] 
[('D', 'option5'), ('D', 'option1'), ('D', 'option2')] 
[('E', 'option1'), ('E', 'option2'), ('E', 'option3')] 
[('F', 'option3'), ('F', 'option1'), ('F', 'option2')] 

Welche auch die Reihenfolge unterhält.

Wenn Sie eine Liste von Listen:

from operator import itemgetter as it 
from itertools import repeat 
n = 3 

new_d = [list(zip(repeat(row["index"]), map(it(0),(row[1:].sort_values(ascending=0)[:n].iteritems())))) 
       for _, row in df.iterrows()] 

Ausgang:

[[('A', 'option3'), ('A', 'option2'), ('A', 'option4')], 
[('B', 'option3'), ('B', 'option4'), ('B', 'option1')], 
[('C', 'option2'), ('C', 'option5'), ('C', 'option1')], 
[('D', 'option5'), ('D', 'option1'), ('D', 'option2')], 
[('E', 'option1'), ('E', 'option2'), ('E', 'option3')], 
[('F', 'option3'), ('F', 'option1'), ('F', 'option2')]] 

Oder mit Pythons sortiert:

new_d = [list(zip(repeat(row["index"]), map(it(0), sorted(row[1:].iteritems(), key=it(1) ,reverse=1)[:n]))) 
        for _, row in df.iterrows()] 

die tatsächlich die schnellste ist, wenn Sie wirklich wollen Strings ist es ziemlich trivial, die Ausgabe zu formatieren, wie Sie wollen.

+0

Dies ergibt die Werte und nicht die Spaltennamen. – iled

+0

@lied, das OP kann oder will nicht die Namen, es ist trivial zu ändern, wenn sie tun, fragte ich in einem Kommentar zu klären –

+0

danke Padraic, ich habe ein Beispiel für die gewünschte Ausgabe auf die Frage. Dennoch, jede Idee, warum Ihr Code mir diesen Fehler geben: AttributeError: 'Series' Objekt hat kein Attribut 'Elemente' n der "pd.DataFrame (map (it (0), sortierte (row [1:]. Items(), key = it (1), reverse = 1) [: n]) für _, Zeile in df.iterrows()) "line? – Diego

1
dfc = df.copy() 
result = {} 

#First, I would effectively transpose this 

for key in dfc: 
    if key != 'index': 
     for i in xrange(0,len(dfc['index'])): 
      if dfc['index'][i] not in result: 
       result[dfc['index'][i]] = [] 
      result[dfc['index'][i]] += [(key,dfc[key][i])] 


def get_topn(result,n): 
    #Use this to get the top value for each option 
    return [x[0] for x in sorted(result,key=lambda x:-x[1])[0:min(len(result),n)]] 


#Lastly, print the output in your desired format. 
n = 3 
keys = sorted([k for k in result]) 
for key in keys: 
     for option in get_topn(result[key],n): 
     print str(key) + ',' + str(option) 
     print 
+0

danke Adam, das war wirklich hilfreich, das einzige Problem war, dass die Reihenfolge der IDs am Ende aufgrund der Wörterbuchtransformation geändert wurde. Ich habe das gelöst, indem ich die "Schlüssel" unter Verwendung des ursprünglichen Datenrahmens sortiert habe. Kleiner Hacky, aber das ist ok – Diego

2

wir

N = 3 

Zunächst einmal gehe ich davon aus Matrix der Eingabefelder erstellen wird für jedes Feld und daran erinnern, was ursprüngliche Option für diese Zelle war:

matrix = [[(j, 'option' + str(i)) for j in df['option' + str(i)]] for i in range(1,6)] 

Das Ergebnis von dieser Linie wird sein:

[ 
[(1, 'option1'), (5, 'option1'), (3, 'option1'), (7, 'option1'), (9, 'option1'), (3, 'option1')], 
[(8, 'option2'), (4, 'option2'), (5, 'option2'), (6, 'option2'), (9, 'option2'), (2, 'option2')], 
[(9, 'option3'), (9, 'option3'), (1, 'option3'), (3, 'option3'), (9, 'option3'), (5, 'option3')], 
[(3, 'option4'), (8, 'option4'), (3, 'option4'), (5, 'option4'), (7, 'option4'), (0, 'option4')], 
[(2, 'option5'), (3, 'option5'), (4, 'option5'), (9, 'option5'), (4, 'option5'), (2, 'option5')] 
] 

Dann können wir easly Transformationsmatrix Zip-Funktion, sortieren Reihen führen durch das erste Element des Tupels und nehmen erste Artikel N:

transformed = [sorted(l, key=lambda x: x[0], reverse=True)[:N] for l in zip(*matrix)] 

Liste aussehen wird, verwandelt:

[ 
[(9, 'option3'), (8, 'option2'), (3, 'option4')], 
[(9, 'option3'), (8, 'option4'), (5, 'option1')], 
[(5, 'option2'), (4, 'option5'), (3, 'option1')], 
[(9, 'option5'), (7, 'option1'), (6, 'option2')], 
[(9, 'option1'), (9, 'option2'), (9, 'option3')], 
[(5, 'option3'), (3, 'option1'), (2, 'option2')] 
] 

Der letzte Schritt Spaltenindex und Ergebnis Tupel wird durch beitreten:

for id, top in zip(df['index'], transformed): 
    for option in top: 
     print id + ',' + option[1] 
    print '' 
+0

Das sieht gut aus! –

+0

das ist eine interessante Lösung, aber es beruht auf vordefinierten Spaltennamen. Ich habe Option1, Option2, ... von Einfachheit verwendet, die Namen folgen keiner Logik und können je nach Situation unterschiedlich sein. Aber danke für die Hilfe – Diego

0

Dies könnte nicht so elegant sein, aber ich denke, es ist ziemlich viel bekommt, was Sie wollen:

n = 3 
df.index = pd.Index(df['index']) 
del df['index'] 
df = df.transpose().unstack() 
for i, g in df.groupby(level=0): 
    g = g.sort_values(ascending=False) 
    print i, list(g.index.get_level_values(1)[:n]) 
+1

Dies ändert den ursprünglichen Datenrahmen, I bin mir nicht sicher, ob das OP etwas will –

0

Noch einen verrückten Einzeiler, da n = 3

{index:option for (index, option) in zip(df['index'], 
    [df.columns[pd.notnull(x[1].where(x[1][1:].sort_values()[-n:]))].tolist() 
     for x in df.iterrows()])} 

{'A': ['option2', 'option3', 'option4'], 
'C': ['option2', 'option4', 'option5'], 
'B': ['option1', 'option3', 'option4'], 
'E': ['option1', 'option2', 'option3'], 
'D': ['option1', 'option2', 'option5'], 
'F': ['option1', 'option3', 'option5']} 
Verwandte Themen