2015-08-12 9 views
6

Ich habe ein Python Pandas DataFrame, in dem jedes Element ein float oder NaN ist. Für jede Zeile muss ich die Spalte finden, die die n-te Nummer der Zeile enthält. Das heißt, ich brauche die Spalte, die das n-te Element der Zeile enthält, die nicht NaN ist. Ich weiß, dass die n-te Spalte immer existiert.Was ist der schnellste Weg, um für jede Zeile die Spalte zu finden, die das n-te Element enthält, das nicht NaN ist?

Also, wenn n 4 war und ein Pandas Datenrahmen genannt myDF war die folgende:

'A' 60 
'B' 70 
'C' 100 
'D' 40 

ich tun konnte:

 10 20 30 40 50 60 70 80 90 100 

'A' 4.5 5.5 2.5 NaN NaN 2.9 NaN NaN 1.1 1.8 
'B' 4.7 4.1 NaN NaN NaN 2.0 1.2 NaN NaN NaN 
'C' NaN NaN NaN NaN NaN 1.9 9.2 NaN 4.4 2.1 
'D' 1.1 2.2 3.5 3.4 4.5 NaN NaN NaN 1.9 5.5 

Ich würde erhalten wollen

import pandas as pd 
import math 

n = some arbitrary int 
for row in myDF.indexes: 
    num_not_NaN = 0 
    for c in myDF.columns:  
     if math.isnan(myDF[c][row]) == False: 
      num_not_NaN +=1 
     if num_not_NaN==n: 
      print row, c 
      break 

Ich bin sicher, das ist sehr langsam und nicht sehr Pythonic. Gibt es einen Ansatz, der schneller ist, wenn es sich um einen sehr großen DataFrame und große Werte von n handelt?

Antwort

5

Wenn die Geschwindigkeit Ihr Ziel ist es, ist es eine gute Idee Verwendung von Pandas' vektorisiert Methoden zu machen, wenn Sie können:

>>> (df.notnull().cumsum(axis=1) == 4).idxmax(axis=1) # replace 4 with any number you like 
'A'  60 
'B'  70 
'C' 100 
'D'  40 
dtype: object 

Die anderen Antworten sind gut und sind vielleicht ein wenig syntaktisch klarer. Was die Geschwindigkeit angeht, gibt es für Ihr kleines Beispiel keinen großen Unterschied. Bei einem etwas größeren DataFrame ist die vektorisierte Methode jedoch bereits um das 60-fache schneller:

>>> df2 = pd.concat([df]*1000) # 4000 row DataFrame 
>>> %timeit df2.apply(lambda row: get_nth(row, n), axis=1) 
1 loops, best of 3: 749 ms per loop 

>>> %timeit df2.T.apply(lambda x: x.dropna()[n-1:].index[0]) 
1 loops, best of 3: 673 ms per loop 

>>> %timeit (df2.notnull().cumsum(1) == 4).idxmax(axis=1) 
100 loops, best of 3: 10.5 ms per loop 
+0

Vielen Dank. Das wird die Dinge für mich sehr beschleunigen. –

2

Sie könnten eine Funktion erstellen und dann an eine lambda-Funktion übergeben.

Die Funktion filtert die Reihe nach Nullen und gibt dann den Indexwert des Elements n zurück (oder Kein, wenn die Indexlänge kleiner als n ist). Die lambda-Funktion benötigt axis=1, um sicherzustellen, dass sie auf jede Zeile des DataFrame angewendet wird.

def get_nth(series, n): 
    s = series[series.notnull()] 
    if len(s) >= n: 
     return s.index[n - 1] 

>>> n = 4 
>>> df.apply(lambda row: get_nth(row, n), axis=1) 
A  60 
B  70 
C 100 
D  40 
dtype: object 
2

Sie die df umsetzen können und eine Lambda-Anwendung, die ab den NaN Reihen, Scheiben vom 4. Wert fällt und gibt den ersten gültigen Index:

In [72]: 
n=4 

df.T.apply(lambda x: x.dropna()[n-1:].index[0]) 
Out[72]: 
'A'  60 
'B'  70 
'C' 100 
'D'  40 
dtype: object