it-swarm.com.de

Ersetzen Sie effizient Werte aus einer Spalte in eine andere Spalte von Pandas DataFrame

Ich habe einen Pandas-DataFrame wie den folgenden: 

   col1 col2 col3
1   0.2  0.3  0.3
2   0.2  0.3  0.3
3     0  0.4  0.4
4     0    0  0.3
5     0    0    0
6   0.1  0.4  0.4

Ich möchte die col1-Werte durch die Werte in der zweiten Spalte (col2) nur ersetzen, wenn col1-Werte gleich 0 sind, und danach (für die verbleibenden Nullwerte) noch einmal, jedoch mit der dritten Spalte (col3). Das gewünschte Ergebnis ist das nächste:

   col1 col2 col3
1   0.2  0.3  0.3
2   0.2  0.3  0.3
3   0.4  0.4  0.4
4   0.3    0  0.3
5     0    0    0
6   0.1  0.4  0.4

Ich habe es mit der pd.replace-Funktion gemacht, aber es scheint zu langsam. Ich denke, es muss ein schnellerer Weg sein, um das zu erreichen. 

df.col1.replace(0,df.col2,inplace=True)
df.col1.replace(0,df.col3,inplace=True)

gibt es einen schnelleren Weg, dies zu tun? Verwenden Sie statt der pd.replace-Funktion eine andere Funktion?

6
Pablo

Die Verwendung von np.where ist schneller. Verwenden Sie ein ähnliches Muster wie bei replace:

df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])

Die Verwendung eines verschachtelten np.where ist jedoch etwas schneller:

df['col1'] = np.where(df['col1'] == 0, 
                      np.where(df['col2'] == 0, df['col3'], df['col2']),
                      df['col1'])

Timings

Verwenden Sie das folgende Setup, um ein umfangreicheres Beispiel für DataFrame- und Timing-Funktionen zu erstellen:

df = pd.concat([df]*10**4, ignore_index=True)

def root_nested(df):
    df['col1'] = np.where(df['col1'] == 0, np.where(df['col2'] == 0, df['col3'], df['col2']), df['col1'])
    return df

def root_split(df):
    df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
    df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])
    return df

def pir2(df):
    df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
    return df

def pir2_2(df):
    slc = (df.values != 0).argmax(axis=1)
    return df.values[np.arange(slc.shape[0]), slc]

def andrew(df):
    df.col1[df.col1 == 0] = df.col2
    df.col1[df.col1 == 0] = df.col3
    return df

def pablo(df):
    df['col1'] = df['col1'].replace(0,df['col2'])
    df['col1'] = df['col1'].replace(0,df['col3'])
    return df

Ich bekomme die folgenden Zeiten:

%timeit root_nested(df.copy())
100 loops, best of 3: 2.25 ms per loop

%timeit root_split(df.copy())
100 loops, best of 3: 2.62 ms per loop

%timeit pir2(df.copy())
100 loops, best of 3: 6.25 ms per loop

%timeit pir2_2(df.copy())
1 loop, best of 3: 2.4 ms per loop

%timeit andrew(df.copy())
100 loops, best of 3: 8.55 ms per loop

Ich habe das Timing Ihrer Methode ausprobiert, aber sie wurde mehrere Minuten lang ausgeführt, ohne den Vorgang abzuschließen. Zum Vergleich: Das Timing Ihrer Methode für nur das 6-reihige Beispiel DataFrame (nicht das viel größere, das oben getestet wurde) hat 12,8 ms gedauert.

13
root

Ich bin nicht sicher, ob es schneller ist, aber Sie haben Recht, dass Sie den Datenrahmen in Scheiben schneiden können, um das gewünschte Ergebnis zu erhalten.

df.col1[df.col1 == 0] = df.col2
df.col1[df.col1 == 0] = df.col3
print(df)

Ausgabe:

   col1  col2  col3
0   0.2   0.3   0.3
1   0.2   0.3   0.3
2   0.4   0.4   0.4
3   0.3   0.0   0.3
4   0.0   0.0   0.0
5   0.1   0.4   0.4

Wenn Sie möchten, dass es knapper wird (obwohl ich nicht weiß, ob es schneller ist), können Sie das, was Sie getan haben, mit dem kombinieren, was ich getan habe.

df.col1[df.col1 == 0] = df.col2.replace(0, df.col3)
print(df)

Ausgabe:

   col1  col2  col3
0   0.2   0.3   0.3
1   0.2   0.3   0.3
2   0.4   0.4   0.4
3   0.3   0.0   0.3
4   0.0   0.0   0.0
5   0.1   0.4   0.4
5
Andrew

ansatz mit pd.DataFrame.where und pd.DataFrame.bfill

df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
df

 enter image description here

Ein anderer Ansatz mit np.argmax

def pir2(df):
    slc = (df.values != 0).argmax(axis=1)
    return df.values[np.arange(slc.shape[0]), slc]

Ich weiß, dass es eine bessere Möglichkeit gibt, numpy zum Schneiden zu verwenden. Ich kann gerade nicht daran denken.

2
piRSquared