2014-09-11 4 views
12

Sto usando pandas versione 0.14.1 con Python 2.7.5, e ho un frame di dati con tre colonne, ad esempio:Qual è la sintassi corretta per scambiare i valori di colonna per le righe selezionate in un frame di dati panda usando solo una riga?

import pandas as pd 

d = {'L': ['left', 'right', 'left', 'right', 'left', 'right'], 
    'R': ['right', 'left', 'right', 'left', 'right', 'left'], 
    'VALUE': [-1, 1, -1, 1, -1, 1]} 
df = pd.DataFrame(d) 

idx = (df['VALUE'] == 1) 

risultati in un frame di dati che assomiglia a questo:

 L  R VALUE 
0 left right  -1 
1 right left  1 
2 left right  -1 
3 right left  1 
4 left right  -1 
5 right left  1 

Per le righe dove VALUE == 1, vorrei scambiare il contenuto delle colonne sinistra e destra, in modo che tutti i valori "a sinistra" finiscano sotto la colonna "L" ei valori "a destra" finiscano sotto Colonna "R".

Avendo già definito la variabile idx sopra, posso facilmente fare questo in soli tre più linee, utilizzando una variabile temporanea come segue:

tmp = df.loc[idx,'L'] 
df.loc[idx,'L'] = df.loc[idx,'R'] 
df.loc[idx,'R'] = tmp 

tuttavia questo sembra come la sintassi davvero goffo e poco elegante per me; sicuramente i panda supportano qualcosa di più succinto? Ho notato che se scambiare l'ordine delle colonne nell'input all'attributo frame di dati .loc, quindi ottengo il seguente output scambiati:

In [2]: print(df.loc[idx,['R','L']]) 
     R  L 
1 left right 
3 left right 
5 left right 

Questo mi suggerisce che dovrei essere in grado di attuare la stessa scambio come sopra, utilizzando solo la seguente riga:

df.loc[idx,['L','R']] = df.loc[idx,['R','L']] 

Tuttavia quando effettivamente provare questo, non succede nulla - le colonne rimangono unswapped. È come se i Panda riconoscessero automaticamente che ho messo le colonne nell'ordine sbagliato sul lato destro dell'istruzione di assegnazione, e corregge automaticamente il problema. C'è un modo per disabilitare questa "correzione automatica dell'ordine delle colonne" nelle istruzioni di assegnazione dei panda, al fine di implementare lo swap senza creare variabili temporanee non necessarie?

+0

Hai guardato dataframe.eval? Hanno esempio in documenti panda: >>> df = DataFrame (randn (10, 2), colonne = lista ('ab')) >>> df.eval ('a + b') >>> df. eval ('c = a + b') – Rainy

+0

Per chi è curioso, ho postato una domanda successiva: http://stackoverflow.com/questions/25811529/impostando-values-on-a-subset-of-rows -indexing-boolean-setting – JohnE

risposta

16

Un modo si potrebbe evitare l'allineamento sui nomi delle colonne sarebbe quella di discesa alla matrice sottostante via .values:

In [33]: df 
Out[33]: 
     L  R VALUE 
0 left right  -1 
1 right left  1 
2 left right  -1 
3 right left  1 
4 left right  -1 
5 right left  1 

In [34]: df.loc[idx,['L','R']] = df.loc[idx,['R','L']].values 

In [35]: df 
Out[35]: 
     L  R VALUE 
0 left right  -1 
1 left right  1 
2 left right  -1 
3 left right  1 
4 left right  -1 
5 left right  1 
+0

Sembra funzionare correttamente solo se la serie _idx_ ha dtype _bool_. Se la tua serie è valori 0/1 anziché True/False, converti in dtype _bool_ usando 'idx.astype (bool)'. – ashimashi

3

La cosa fondamentale da notare qui è che i tentativi panda per allineare automaticamente le righe e le colonne che utilizzano il nomi di indici e colonne. Quindi, è necessario in qualche modo dire ai panda di ignorare i nomi delle colonne qui. Un modo è come fa @DSM, convertendolo in una matrice numpy. Un altro modo è quello di rinominare le colonne:

>>> df.loc[idx] = df.loc[idx].rename(columns={'R':'L','L':'R'}) 

     L  R VALUE 
0 left right  -1 
1 left right  1 
2 left right  -1 
3 left right  1 
4 left right  -1 
5 left right  1 
+0

Grazie per la risposta; Ho dato lo stato di "risposta accettata" al DSM poiché ha risposto per primo, ma ho trovato anche la tua risposta molto utile (e quindi in aumento!). A proposito, penso che ci possa essere un refuso nel tuo primo metodo; sul mio sistema, con panda 0.14.1 e Python 2.7.5, funziona se invece aggiungo una chiamata a 'zip'; Ad esempio, 'df.ix [idx, ['L', 'R']] = zip (df.ix [idx, 'R'], df.ix [idx, 'L'])'. Il secondo metodo funziona bene, quindi grazie per quello! Per il terzo metodo, penso che il problema è che 'df [['R', 'L']] sul lato destro è lungo 6 righe, mentre' df.loc [idx, ['L', 'R ']] 'è solo 3 righe. – stachyra

+0

Grazie per il feedback. Metodo 1 funziona bene per me senza zip (e anche se aggiungo lo zip). Non ho idea del perché otteniamo risultati diversi. Ho pandas 14.1 e python 2.7.7 (distribuzione anaconda). Funzionando sotto Windows 7. – JohnE

1

Si può anche fare questo con np.select e df.where i.e

Opzione 1: np.select

df[['L','R']] = pd.np.select(df['VALUE'] == 1, df[['R','L']].values, df[['L','R']].values) 

Opzione 2: df.where

df[['L','R']] = df[['R','L']].where(df['VALUE'] == 1, df[['L','R']].values) 

Opzione 3: df.mask

df[['L','R']] = df[['L','R']].mask(df['VALUE'] == 1, df[['R','L']].values) 

uscita:

L  R VALUE 
0 left right  -1 
1 left right  1 
2 left right  -1 
3 left right  1 
4 left right  -1 
5 left right  1 
Problemi correlati