2015-11-04 18 views
5

Ich habe einen DataFrame, wo mehrere Zeilen jeden Index überspannen. Unter dem ersten Index zum Beispiel hat ein solche Struktur:Pandas zwingen NaN zum Ende jeder Spalte bei jedem Index

df = pd.DataFrame([["A", "first", 1.0, 1.0, np.NaN], 
      [np.NaN, np.NaN, 2.0, np.NaN, 2.0], 
      [np.NaN, np.NaN, np.NaN, 3.0, 3.0]], 
      columns=["ID", "Name", "val1", "val2", "val3"], 
      index=[0, 0, 0]) 

Out[4]: 
    ID Name val1 val2 val3 
0 A first  1  1 NaN 
0 NaN NaN  2 NaN  2 
0 NaN NaN NaN  3  3 

Ich mag jede Spalte sortieren/bestellen, so dass die NaN s am unteren Ende jeder Spalte in diesem gegebenen Index ist - ein Ergebnis, das aussieht dies wie:

ID Name val1 val2 val3 
0 A first  1  1  2 
0 NaN NaN  2  3  3 
0 NaN NaN NaN  NaN NaN 

Ein explizites Beispiel könnte wie folgt aussehen:

df = pd.DataFrame([["A", "first", 1.0, 1.0, np.NaN], 
      [np.NaN, np.NaN, 2.0, np.NaN, 2.0], 
      [np.NaN, np.NaN, np.NaN, 3.0, 3.0], 
      ["B", "second", 4.0, 4.0, np.NaN], 
      [np.NaN, np.NaN, 5.0, np.NaN, 5.0], 
      [np.NaN, np.NaN, np.NaN, 6.0, 6.0]], 
      columns=[ "ID", "Name", "val1", "val2", "val3"], 
      index=[0, 0, 0, 1, 1, 1]) 

Out[5]: 
    ID Name val1 val2 val3 
0 A first  1  1 NaN 
0 NaN  NaN  2 NaN  2 
0 NaN  NaN NaN  3  3 
1 B second  4  4 NaN 
1 NaN  NaN  5 NaN  5 
1 NaN  NaN NaN  6  6 

mit dem gewünschten Ergebnis wie folgt aussehen:

ID Name val1 val2 val3 
0 A first  1  1  2 
0 NaN  NaN  2  3  3 
0 NaN  NaN NaN NaN NaN 
1 B second  4  4  5 
1 NaN  NaN  5  6  6 
1 NaN  NaN NaN NaN NaN 

Ich habe viele tausend Zeilen in diesem Datenrahmen, wobei jeder Index bis zu ein paar hundert Zeilen enthält. Mein gewünschtes Ergebnis wird sehr hilfreich sein, wenn ich den Datenrahmen to_csv habe.

Ich habe versucht, sort_values(['val1','val2','val3']) auf den gesamten Datenrahmen zu verwenden, aber dies führt zu den ungeordneten Indizes. Ich habe versucht, durch jeden Index zu iterieren und an der richtigen Stelle zu sortieren, aber das beschränkt auch nicht den NaN auf den Boden der Spalte jeder Indizes. Ich habe auch versucht, fillna zu einem anderen Wert, wie 0, aber ich war auch hier nicht erfolgreich.

Während ich es sicherlich falsch benutze, produziert der na_position Parameter in sort_values nicht das gewünschte Ergebnis, obwohl es scheint, dass dies wahrscheinlich was ist.

Edit:

der Index des endgültigen df ist nicht wie in meinem zweiten Beispiel in numerischer Reihenfolge sein, benötigt.

von ignore_index zu False in der einzelnen Zeile von @ Leb dritten Codeblock zu ändern,

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True) 

zu

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=False) 

und durch eine temporäre df für alle Zeilen in einem bestimmten Index zu erstellen, I war in der Lage, diese Arbeit zu machen - nicht schön, aber es befiehlt Dinge, wie ich sie brauche. Wenn jemand (sicherlich) einen besseren Weg hat, lass es mich wissen.

new_df = df.ix[0] 
new_df = pd.concat([new_df[col].sort_values().reset_index(drop=True) for col in new_df], axis=1, ignore_index=False) 
max_index = df.index[-1] 
for i in range(1, max_index + 1): 
    tmp = df.ix[i] 
    tmp = pd.concat([tmp[col].sort_values().reset_index(drop=True) for col in tmp], axis=1, ignore_index=False) 
    new_df = pd.concat([new_df,tmp]) 


In [10]: new_df 
Out[10]: 
    ID Name val1 val2 val3 
0 A first  1  1  2 
1 NaN  NaN  2  3  3 
2 NaN  NaN NaN NaN NaN 
0 B second  4  4  5 
1 NaN  NaN  5  6  6 
2 NaN  NaN NaN NaN NaN 

Antwort

4

Ich weiß, dass die Frage der nans zu einem Rand drängen hat auf Github diskutiert. Für Ihren speziellen Frame würde ich es wahrscheinlich manuell auf der Python-Ebene machen und mich nicht viel um die Performance kümmern. Etwas wie

>>> df.groupby(level=0, sort=False).transform(lambda x: sorted(x,key=pd.isnull)) 
    ID Name val1 val2 val3 
0 A first  1  1  2 
0 NaN  NaN  2  3  3 
0 NaN  NaN NaN NaN NaN 
1 B second  4  4  5 
1 NaN  NaN  5  6  6 
1 NaN  NaN NaN NaN NaN 

sollte funktionieren. Beachten Sie, dass sorted eine stabile Sortierung ist und wir pd.isnull als Schlüssel verwenden (wobei False < True ist), schieben wir die NaNs bis zum Ende und behalten dabei die Reihenfolge der verbleibenden Objekte bei. Beachten Sie auch, dass ich hier nur auf den Index gruppiere; Wir könnten uns alternativ auf was auch immer wir wollten gruppieren.

+1

Ich lösche meine Antwort, denn es sieht aus wie ein kleines verschrobenes Kind dagegen. :-P – Kartik

2

Gegeben df:

pd.DataFrame([["A","first",1.0,1.0,np.NaN], 
      [np.NaN,np.NaN,2.0,np.NaN,2.0], 
      [np.NaN,np.NaN,np.NaN,3.0,3.0]], 
      columns=[ "ID", "Name", "val1", "val2", "val3"],index=[0,1,2]) 

Ich änderte Index sicher um Aufenthalte zu machen.

df 
Out[127]: 
    ID Name val1 val2 val3 
0 A first  1  1 NaN 
1 NaN NaN  2 NaN  2 
2 NaN NaN NaN  3  3 

Verwendung:

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True) 

geben:

Out[130]: 
    0  1 2 3 4 
0 A first 1 1 2 
1 NaN NaN 2 3 3 
2 NaN NaN NaN NaN NaN 

Das Gleiche gilt für:

df = pd.DataFrame([["A","first",1.0,1.0,np.NaN], 
      [np.NaN,np.NaN,2.0,np.NaN,2.0], 
      [np.NaN,np.NaN,np.NaN,3.0,3.0], 
      ["B","second",4.0,4.0,np.NaN], 
      [np.NaN,np.NaN,5.0,np.NaN,5.0], 
      [np.NaN,np.NaN,np.NaN,6.0,6.0]], 
      columns=[ "ID", "Name", "val1", "val2", "val3"],index=[0,0,0,1,1,1]) 

df 
Out[132]: 
    ID Name val1 val2 val3 
0 A first  1  1 NaN 
0 NaN  NaN  2 NaN  2 
0 NaN  NaN NaN  3  3 
1 B second  4  4 NaN 
1 NaN  NaN  5 NaN  5 
1 NaN  NaN NaN  6  6 

pd.concat([df[col].sort_values().reset_index(drop=True) for col in df], axis=1, ignore_index=True) 
Out[133]: 
    0  1 2 3 4 
0 A first 1 1 2 
1 B second 2 3 3 
2 NaN  NaN 4 4 5 
3 NaN  NaN 5 6 6 
4 NaN  NaN NaN NaN NaN 
5 NaN  NaN NaN NaN NaN 

Nach weiteren Kommentare

new = pd.concat([df[col].sort_values().reset_index(drop=True) for col in df.iloc[:,2:]], axis=1, ignore_index=True) 
new.index = df.index 
cols = df.iloc[:,2:].columns 
new.columns = cols 
df.drop(cols,inplace=True,axis=1) 
df = pd.concat([df,new],axis=1) 

df 
Out[37]: 
    ID Name val1 val2 val3 
0 A first  1  1  2 
0 NaN  NaN  2  3  3 
0 NaN  NaN  4  4  5 
1 B second  5  6  6 
1 NaN  NaN NaN NaN NaN 
1 NaN  NaN NaN NaN NaN 
+0

Dies sortiert die gesamte Spalte, es sendet nicht nur die NaNs nach unten, obwohl, so kann es die Reihenfolge der nicht-NaN-Werte verschlüsseln, nicht wahr? – vmg

+0

Ja, aber 'NaN'-Werte sind bereits im gesamten Datenrahmen verschlüsselt. Sie können sie nicht einfach nach unten verschieben und den Index anderer Werte beibehalten. Wenn die NaN's gruppiert sind, ist es möglich. – Leb

+0

Ich denke, du * kannst * es tun, wenn du bereit bist, genug Schummeleien zu machen. Sie könnten zum Beispiel für jede Spalte: die Serie kopieren; zähle die 'NaN's; Streifen Sie die 'NaN' ab, die die Werte in Ordnung halten, fügen Sie am Ende die gleiche Anzahl von' NaN' an. Erstellen Sie dann einen neuen Datenrahmen mit diesen verfälschten Reihen als Spalten. Es ist nicht das, was @AGS gefragt hat, obwohl ich gerade über die Möglichkeit nachgedacht habe – vmg

1
In [219]: 
df.groupby(level=0).transform(lambda x : x.sort(na_position = 'last' , inplace = False)) 
Out[219]: 
    ID Name val1 val2 val3 
0 A first  1 1  2 
0 NaN NaN  2 3  3 
0 NaN NaN  NaN NaN  NaN 
1 B second  4 4  5 
1 NaN NaN   5 6  6 
1 NaN NaN  NaN NaN  NaN 
+0

Der Grund, dass ich dies nicht verwendet habe, sondern auf die Python-Ebene herabgefallen ist, ist, dass es auch die * non * -NaN-Werte sortiert. – DSM

+0

Ich habe Ihren Standpunkt nicht verstanden? –

+2

Sagen wir, dass val1 die Zahlen 1,2,6,5 anstelle von 1,2,4,5 enthielt, wie es jetzt ist. Ihre Herangehensweise wird die NaNs nicht nur bis zum Ende drücken, sie wird 6,5 in 5,6 verwandeln. – DSM

Verwandte Themen