2012-01-18 12 views
29

Ho un dataframe df in panda che è stato creato utilizzando pandas.read_table da un file csv. Il dataframe ha diverse colonne ed è indicizzato da una delle colonne (che è univoco, in quanto ogni riga ha un valore univoco per quella colonna utilizzata per l'indicizzazione.)selezionando tra più colonne con i panda Python?

Come posso selezionare le righe del mio dataframe in base a un filtro "complesso" applicato a più colonne? Posso facilmente selezionare la fetta di dataframe dove colonna colA è maggiore di 10, per esempio:

df_greater_than10 = df[df["colA"] > 10] 

Ma se volevo un filtro simile: selezionare la porzione di df dove qualsiasi delle colonne sono maggiori di 10?

O il valore per colA è maggiore di 10 ma il valore per colB è inferiore a 5?

Come vengono implementati nei panda? Grazie.

risposta

36

Vi incoraggio a porre queste domande su mailing list, ma in ogni caso, è ancora un affare di livello molto basso che funziona con gli array NumPy sottostanti. Ad esempio, per selezionare le righe in cui il valore in qualsiasi colonna superi, per esempio, 1.5 in questo esempio:

In [11]: df 
Out[11]: 
      A  B  C  D  
2000-01-03 -0.59885 -0.18141 -0.68828 -0.77572 
2000-01-04 0.83935 0.15993 0.95911 -1.12959 
2000-01-05 2.80215 -0.10858 -1.62114 -0.20170 
2000-01-06 0.71670 -0.26707 1.36029 1.74254 
2000-01-07 -0.45749 0.22750 0.46291 -0.58431 
2000-01-10 -0.78702 0.44006 -0.36881 -0.13884 
2000-01-11 0.79577 -0.09198 0.14119 0.02668 
2000-01-12 -0.32297 0.62332 1.93595 0.78024 
2000-01-13 1.74683 -1.57738 -0.02134 0.11596 
2000-01-14 -0.55613 0.92145 -0.22832 1.56631 
2000-01-17 -0.55233 -0.28859 -1.18190 -0.80723 
2000-01-18 0.73274 0.24387 0.88146 -0.94490 
2000-01-19 0.56644 -0.49321 1.17584 -0.17585 
2000-01-20 1.56441 0.62331 -0.26904 0.11952 
2000-01-21 0.61834 0.17463 -1.62439 0.99103 
2000-01-24 0.86378 -0.68111 -0.15788 -0.16670 
2000-01-25 -1.12230 -0.16128 1.20401 1.08945 
2000-01-26 -0.63115 0.76077 -0.92795 -2.17118 
2000-01-27 1.37620 -1.10618 -0.37411 0.73780 
2000-01-28 -1.40276 1.98372 1.47096 -1.38043 
2000-01-31 0.54769 0.44100 -0.52775 0.84497 
2000-02-01 0.12443 0.32880 -0.71361 1.31778 
2000-02-02 -0.28986 -0.63931 0.88333 -2.58943 
2000-02-03 0.54408 1.17928 -0.26795 -0.51681 
2000-02-04 -0.07068 -1.29168 -0.59877 -1.45639 
2000-02-07 -0.65483 -0.29584 -0.02722 0.31270 
2000-02-08 -0.18529 -0.18701 -0.59132 -1.15239 
2000-02-09 -2.28496 0.36352 1.11596 0.02293 
2000-02-10 0.51054 0.97249 1.74501 0.20525 
2000-02-11 0.10100 0.27722 0.65843 1.73591 

In [12]: df[(df.values > 1.5).any(1)] 
Out[12]: 
      A  B  C  D  
2000-01-05 2.8021 -0.1086 -1.62114 -0.2017 
2000-01-06 0.7167 -0.2671 1.36029 1.7425 
2000-01-12 -0.3230 0.6233 1.93595 0.7802 
2000-01-13 1.7468 -1.5774 -0.02134 0.1160 
2000-01-14 -0.5561 0.9215 -0.22832 1.5663 
2000-01-20 1.5644 0.6233 -0.26904 0.1195 
2000-01-28 -1.4028 1.9837 1.47096 -1.3804 
2000-02-10 0.5105 0.9725 1.74501 0.2052 
2000-02-11 0.1010 0.2772 0.65843 1.7359 

Condizioni multiple devono essere combinati utilizzando & o | (e le parentesi!):

In [13]: df[(df['A'] > 1) | (df['B'] < -1)] 
Out[13]: 
      A  B  C  D  
2000-01-05 2.80215 -0.1086 -1.62114 -0.2017 
2000-01-13 1.74683 -1.5774 -0.02134 0.1160 
2000-01-20 1.56441 0.6233 -0.26904 0.1195 
2000-01-27 1.37620 -1.1062 -0.37411 0.7378 
2000-02-04 -0.07068 -1.2917 -0.59877 -1.4564 

Sarei molto interessato ad avere un qualche tipo di API di query per semplificare questo tipo di cose

+1

Grazie ancora. Pubblicheremo domande future sulla mailing list. Ma per ora, e se volessi farlo a livello di programmazione? Hai avuto un elenco di etichette di colonne ... come hai potuto inserirlo nel '|' notazione? Per esempio. se 'labels = ['A', 'B', 'C', ... ']' – user248237dfsf

+0

Per chiarire: L'approccio 'any (1)' non funzionerebbe se tu avessi altri valori nella tabella che non hai voglio filtrare Supponiamo che ci siano molte colonne e vuoi solo che 'any' si applichi a un sottoinsieme di esse (conosci le etichette del sottoinsieme). – user248237dfsf

5

Ci sono almeno alcuni approcci per accorciare la sintassi per questo in Pandas, fino a quando non ottiene un'intera API di query lungo il strada (perhap s Proverò ad unirmi al progetto github e fare questo è tempo permesso e se nessun altro è già partito).

Un metodo per accorciare la sintassi un po 'è il seguente:

inds = df.apply(lambda x: x["A"]>10 and x["B"]<5, axis=1) 
print df[inds].to_string() 

Per risolvere completamente questo, uno avrebbe bisogno di costruire qualcosa come l'SQL selezionare e dove le clausole in Panda. Questo non è affatto banale, ma una pugnalata che penso possa funzionare per questo è usare il modulo integrato Python operator. Questo ti permette di trattare cose più grandi che funzioni invece che simboli. Così si potrebbe effettuare le seguenti operazioni:

def pandas_select(dataframe, select_dict): 

    inds = dataframe.apply(lambda x: reduce(lambda v1,v2: v1 and v2, 
          [elem[0](x[key], elem[1]) 
          for key,elem in select_dict.iteritems()]), axis=1) 
    return dataframe[inds] 

Poi un esempio di prova come il vostro potrebbe essere quella di effettuare le seguenti operazioni:

import operator 
select_dict = { 
       "A":(operator.gt,10), 
       "B":(operator.lt,5)     
       } 

print pandas_select(df, select_dict).to_string() 

è possibile abbreviare la sintassi ancora di più da una costruzione in più argomenti per pandas_select a gestire automaticamente i diversi operatori logici comuni o importarli nello spazio dei nomi con nomi più brevi.

Si noti che la funzione pandas_select sopra funziona solo con logiche e catene di vincoli. Dovresti modificarlo per ottenere un comportamento logico diverso. Oppure usa not e le leggi di DeMorgan.

+0

Se ho una lista ['Alice', 'Bob', 'Carl'] come posso generare il dizionario per selezionare gli elementi in cui dataframe ['A'] è nella mia lista? –

+1

Se la lista è 'a = ['Alice', 'Bob', 'Carl']' e il frame di dati complessivo è chiamato 'df', allora puoi fare questo:' df [df.A.isin (a) ] 'e selezionerà gli indici di riga in cui la condizione di appartenenza impostata è vera per gli elementi della colonna' A'. Espandere il linguaggio mini-dominio specifico che ho fatto sopra per esprimere i logici per avere questa opzione con sintassi semplice sarà probabilmente un compito scomodo. – ely

+0

forse vedi anche il metodo di ricerca imminente (pandas 0.13): http://pandas.pydata.org/pandas-docs/dev/indexing.html?highlight=query#the-query-method-experimental e anche http : //stackoverflow.com/questions/18521037/pandas-iterative-filtering-a-dataframes-rows – RuiDC

1

Una funzione di query è stata aggiunta a Pandas poiché questa domanda è stata posta e ha risposto. Un esempio è dato seguito.

Dato questo frame di dati di esempio:

periods = 8 
dates = pd.date_range('20170101', periods=periods) 
rand_df = pd.DataFrame(np.random.randn(periods,4), index=dates, 
     columns=list('ABCD')) 

La sintassi di query come segue vi permetterà di utilizzare più filtri, come una clausola "WHERE" in una dichiarazione prescelta.

rand_df.query("A < 0 or B < 0") 

Vedere il Pandas documentation per ulteriori dettagli.