python - index - print pandas dataframe




Seleziona le righe da un DataFrame in base ai valori in una colonna in panda (8)

Come selezionare le righe da un DataFrame in base ai valori in alcune colonne nei panda?
In SQL vorrei usare:

select * from table where colume_name = some_value. 

Ho provato a guardare la documentazione di panda ma non ho trovato immediatamente la risposta.


tl; dr

I panda equivalenti a

select * from table where column_name = some_value

è

table[table.column_name == some_value]

Condizioni multiple:

table[(table.column_name == some_value) | (table.column_name2 == some_value2)]

o

table.query('column_name == some_value | column_name2 == some_value2')

Esempio di codice

import pandas as pd

# Create data set
d = {'foo':[100, 111, 222], 
     'bar':[333, 444, 555]}
df = pd.DataFrame(d)

# Full dataframe:
df

# Shows:
#    bar   foo 
# 0  333   100
# 1  444   111
# 2  555   222

# Output only the row(s) in df where foo is 222:
df[df.foo == 222]

# Shows:
#    bar  foo
# 2  555  222

Nel codice precedente è la riga df[df.foo == 222] che dà le righe in base al valore della colonna, 222 in questo caso.

Sono possibili anche più condizioni:

df[(df.foo == 222) | (df.bar == 444)]
#    bar  foo
# 1  444  111
# 2  555  222

Ma a quel punto consiglierei di usare la funzione query , poiché è meno prolissa e produce lo stesso risultato:

df.query('foo == 222 | bar == 444')

Esistono alcuni metodi di base per selezionare le righe da un dataframe panda.

  1. Indicizzazione booleana
  2. Indicizzazione di posizione
  3. Indicizzazione delle etichette
  4. API

Per ogni tipo di base, possiamo mantenere le cose semplici limitandoci alle API pandas o possiamo avventurarci fuori dall'API, di solito in numpy , e accelerare le cose.

Ti mostrerò esempi di ciascuno e ti guiderò su quando utilizzare determinate tecniche.

Impostare
La prima cosa di cui avremo bisogno è identificare una condizione che fungerà da nostro criterio per selezionare le righe. L'OP offre up column_name == some_value . Inizieremo lì e includeremo alcuni altri casi di utilizzo comune.

Prendendo in prestito da @unutbu:

import pandas as pd, numpy as np

df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
                   'B': 'one one two three two two one three'.split(),
                   'C': np.arange(8), 'D': np.arange(8) * 2})

Supponiamo che il nostro criterio sia colonna 'A' = 'foo'

1.
L' indicizzazione booleana richiede di trovare il valore di verità della colonna 'A' di ogni riga uguale a 'foo' , quindi di utilizzare quei valori di verità per identificare quali righe conservare. In genere, chiameremmo questa serie, una serie di valori di verità, mask . Lo faremo anche qui.

mask = df['A'] == 'foo'

Possiamo quindi usare questa maschera per tagliare o indicizzare il dataframe

df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

Questo è uno dei modi più semplici per eseguire questa attività e se le prestazioni o l'intuitività non sono un problema, questo dovrebbe essere il metodo scelto. Tuttavia, se le prestazioni sono un problema, è possibile prendere in considerazione un modo alternativo di creare la mask .

2.
L' indicizzazione di posizione ha i suoi casi d'uso, ma questo non è uno di questi. Al fine di identificare dove tagliare, dobbiamo prima eseguire la stessa analisi booleana che abbiamo fatto sopra. Questo ci lascia compiere un ulteriore passo per compiere lo stesso compito.

mask = df['A'] == 'foo'
pos = np.flatnonzero(mask)
df.iloc[pos]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

3.
L' indicizzazione delle etichette può essere molto utile, ma in questo caso, stiamo ancora facendo più lavoro senza alcun beneficio

df.set_index('A', append=True, drop=False).xs('foo', level=1)

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

4.
pd.DataFrame.query è un modo molto elegante / intuitivo per eseguire questa attività. Ma è spesso più lento. Tuttavia , se si presta attenzione ai tempi indicati di seguito, per i dati di grandi dimensioni, la query è molto efficiente. Più che l'approccio standard e di entità simile al mio miglior suggerimento.

df.query('A == "foo"')

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

La mia preferenza è usare la mask Boolean

I miglioramenti effettivi possono essere apportati modificando il modo in cui creiamo la nostra mask Boolean .

mask alternativa 1
Utilizzare l'array numpy sottostante e rinunciare al sovraccarico della creazione di un altro pd.Series

mask = df['A'].values == 'foo'

Alla fine mostrerò più test completi sul tempo, ma osserveremo solo i guadagni in termini di prestazioni ottenuti utilizzando il campione di dati campione. Per prima cosa guardiamo alla differenza nella creazione della mask

%timeit mask = df['A'].values == 'foo'
%timeit mask = df['A'] == 'foo'

5.84 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
166 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

La valutazione della mask con l'array numpy è ~ 30 volte più veloce. Ciò è in parte dovuto alla valutazione numpy spesso è più veloce. In parte è anche dovuto alla mancanza di spese generali necessarie per creare un indice e un oggetto pd.Series corrispondente.

Successivamente vedremo i tempi per affettare con una mask rispetto all'altra.

mask = df['A'].values == 'foo'
%timeit df[mask]
mask = df['A'] == 'foo'
%timeit df[mask]

219 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
239 µs ± 7.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

I guadagni in termini di prestazioni non sono così pronunciati. Vedremo se questo regge su test più robusti.

mask alternativa 2
Potremmo anche aver ricostruito il dataframe. C'è un grande avvertimento quando si ricostruisce un dataframe: devi fare attenzione ai dtypes quando lo fai!

Invece di df[mask] lo faremo

pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)

Se il dataframe è di tipo misto, che è il nostro esempio, quando otteniamo df.values l'array risultante è di tipo dtype e, di conseguenza, tutte le colonne del nuovo dataframe saranno di tipo dtype . Richiede quindi l' astype(df.dtypes) e uccide qualsiasi potenziale guadagno in termini di prestazioni.

%timeit df[m]
%timeit pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)

216 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.43 ms ± 39.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Tuttavia, se il dataframe non è di tipo misto, questo è un modo molto utile per farlo.

Dato

np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))

d1

   A  B  C  D  E
0  0  2  7  3  8
1  7  0  6  8  6
2  0  2  0  4  9
3  7  3  2  4  3
4  3  6  7  7  4
5  5  3  7  5  9
6  8  7  6  4  7
7  6  2  6  6  5
8  2  8  7  5  8
9  4  7  6  1  5    
%%timeit
mask = d1['A'].values == 7
d1[mask]

179 µs ± 8.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Contro

%%timeit
mask = d1['A'].values == 7
pd.DataFrame(d1.values[mask], d1.index[mask], d1.columns)

87 µs ± 5.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Riduciamo il tempo a metà.

mask alternativa 3
@unutbu ci mostra anche come utilizzare pd.Series.isin per tenere conto che ogni elemento di df['A'] trova in un insieme di valori. Questo valuta la stessa cosa se il nostro insieme di valori è un insieme di un valore, vale a dire 'foo' . Ma generalizza anche per includere serie di valori più grandi, se necessario. Risulta, questo è ancora piuttosto veloce anche se è una soluzione più generale. L'unica vera perdita è nell'intuitività per chi non ha familiarità con il concetto.

mask = df['A'].isin(['foo'])
df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

Tuttavia, come prima, possiamo usare numpy per migliorare le prestazioni senza sacrificare praticamente nulla. Useremo np.in1d

mask = np.in1d(df['A'].values, ['foo'])
df[mask]

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

sincronizzazione
Includerò altri concetti menzionati in altri post come riferimento.
Codice sottostante

Ogni colonna in questa tabella rappresenta un dataframe di lunghezza diversa rispetto al quale testiamo ciascuna funzione. Ogni colonna mostra il tempo relativo preso, con la funzione più veloce data un indice di base di 1.0 .

res.div(res.min())

                         10        30        100       300       1000      3000      10000     30000
mask_standard         2.156872  1.850663  2.034149  2.166312  2.164541  3.090372  2.981326  3.131151
mask_standard_loc     1.879035  1.782366  1.988823  2.338112  2.361391  3.036131  2.998112  2.990103
mask_with_values      1.010166  1.000000  1.005113  1.026363  1.028698  1.293741  1.007824  1.016919
mask_with_values_loc  1.196843  1.300228  1.000000  1.000000  1.038989  1.219233  1.037020  1.000000
query                 4.997304  4.765554  5.934096  4.500559  2.997924  2.397013  1.680447  1.398190
xs_label              4.124597  4.272363  5.596152  4.295331  4.676591  5.710680  6.032809  8.950255
mask_with_isin        1.674055  1.679935  1.847972  1.724183  1.345111  1.405231  1.253554  1.264760
mask_with_in1d        1.000000  1.083807  1.220493  1.101929  1.000000  1.000000  1.000000  1.144175

Noterai che i tempi più veloci sembrano essere condivisi tra mask_with_values e mask_with_in1d

res.T.plot(loglog=True)

funzioni

def mask_standard(df):
    mask = df['A'] == 'foo'
    return df[mask]

def mask_standard_loc(df):
    mask = df['A'] == 'foo'
    return df.loc[mask]

def mask_with_values(df):
    mask = df['A'].values == 'foo'
    return df[mask]

def mask_with_values_loc(df):
    mask = df['A'].values == 'foo'
    return df.loc[mask]

def query(df):
    return df.query('A == "foo"')

def xs_label(df):
    return df.set_index('A', append=True, drop=False).xs('foo', level=-1)

def mask_with_isin(df):
    mask = df['A'].isin(['foo'])
    return df[mask]

def mask_with_in1d(df):
    mask = np.in1d(df['A'].values, ['foo'])
    return df[mask]

analisi

res = pd.DataFrame(
    index=[
        'mask_standard', 'mask_standard_loc', 'mask_with_values', 'mask_with_values_loc',
        'query', 'xs_label', 'mask_with_isin', 'mask_with_in1d'
    ],
    columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    dtype=float
)

for j in res.columns:
    d = pd.concat([df] * j, ignore_index=True)
    for i in res.index:a
        stmt = '{}(d)'.format(i)
        setp = 'from __main__ import d, {}'.format(i)
        res.at[i, j] = timeit(stmt, setp, number=50)

Tempi speciali
Osservando il caso speciale quando abbiamo un singolo dtype non-oggetto per l'intero dataframe. Codice sottostante

spec.div(spec.min())

                     10        30        100       300       1000      3000      10000     30000
mask_with_values  1.009030  1.000000  1.194276  1.000000  1.236892  1.095343  1.000000  1.000000
mask_with_in1d    1.104638  1.094524  1.156930  1.072094  1.000000  1.000000  1.040043  1.027100
reconstruct       1.000000  1.142838  1.000000  1.355440  1.650270  2.222181  2.294913  3.406735

Risulta che la ricostruzione non vale più di qualche centinaio di righe.

spec.T.plot(loglog=True)

funzioni

np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))

def mask_with_values(df):
    mask = df['A'].values == 'foo'
    return df[mask]

def mask_with_in1d(df):
    mask = np.in1d(df['A'].values, ['foo'])
    return df[mask]

def reconstruct(df):
    v = df.values
    mask = np.in1d(df['A'].values, ['foo'])
    return pd.DataFrame(v[mask], df.index[mask], df.columns)

spec = pd.DataFrame(
    index=['mask_with_values', 'mask_with_in1d', 'reconstruct'],
    columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    dtype=float
)

analisi

for j in spec.columns:
    d = pd.concat([df] * j, ignore_index=True)
    for i in spec.index:
        stmt = '{}(d)'.format(i)
        setp = 'from __main__ import d, {}'.format(i)
        spec.at[i, j] = timeit(stmt, setp, number=50)

Per aggiungere a questa famosa domanda (anche se un po 'troppo tardi): Puoi anche fare df.groupby('column_name').get_group('column_desired_value').reset_index() per creare un nuovo frame di dati con una colonna specificata con un valore particolare . Per esempio

import pandas as pd
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
                   'B': 'one one two three two two one three'.split()})
print("Original dataframe:")
print(df)

b_is_two_dataframe = pd.DataFrame(df.groupby('B').get_group('two').reset_index()).drop('index', axis = 1) 
#NOTE: the final drop is to remove the extra index column returned by groupby object
print('Sub dataframe where B is two:')
print(b_is_two_dataframe)

Esegui ciò dà:

Original dataframe:
     A      B
0  foo    one
1  bar    one
2  foo    two
3  bar  three
4  foo    two
5  bar    two
6  foo    one
7  foo  three
Sub dataframe where B is two:
     A    B
0  foo  two
1  foo  two
2  bar  two

Per selezionare le righe il cui valore di colonna è uguale a uno scalare, some_value , utilizzare == :

df.loc[df['column_name'] == some_value]

Per selezionare le righe il cui valore di colonna è in un iterable, some_values , utilizzare isin :

df.loc[df['column_name'].isin(some_values)]

Combina più condizioni con & :

df.loc[(df['column_name'] == some_value) & df['other_column'].isin(some_values)]

Per selezionare le righe il cui valore di colonna non è uguale a some_value , utilizzare != :

df.loc[df['column_name'] != some_value]

isin restituisce una serie booleana, quindi per selezionare le righe il cui valore non è in some_values , nega la serie booleana usando ~ :

df.loc[~df['column_name'].isin(some_values)]

Per esempio,

import pandas as pd
import numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
                   'B': 'one one two three two two one three'.split(),
                   'C': np.arange(8), 'D': np.arange(8) * 2})
print(df)
#      A      B  C   D
# 0  foo    one  0   0
# 1  bar    one  1   2
# 2  foo    two  2   4
# 3  bar  three  3   6
# 4  foo    two  4   8
# 5  bar    two  5  10
# 6  foo    one  6  12
# 7  foo  three  7  14

print(df.loc[df['A'] == 'foo'])

i rendimenti

     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

Se si desidera includere più valori, inserirli in un elenco (o più in generale, qualsiasi iterabile) e utilizzare isin :

print(df.loc[df['B'].isin(['one','three'])])

i rendimenti

     A      B  C   D
0  foo    one  0   0
1  bar    one  1   2
3  bar  three  3   6
6  foo    one  6  12
7  foo  three  7  14

Si noti, tuttavia, che se si desidera eseguire questa operazione molte volte, è più efficiente creare prima un indice e quindi utilizzare df.loc :

df = df.set_index(['B'])
print(df.loc['one'])

i rendimenti

       A  C   D
B              
one  foo  0   0
one  bar  1   2
one  foo  6  12

oppure, per includere più valori dall'indice, utilizzare df.index.isin :

df.loc[df.index.isin(['one','two'])]

i rendimenti

       A  C   D
B              
one  foo  0   0
one  bar  1   2
two  foo  2   4
two  foo  4   8
two  bar  5  10
one  foo  6  12

Qui c'è un semplice esempio

from pandas import DataFrame

# Create data set
d = {'Revenue':[100,111,222], 
     'Cost':[333,444,555]}
df = DataFrame(d)


# mask = Return True when the value in column "Revenue" is equal to 111
mask = df['Revenue'] == 111

print mask

# Result:
# 0    False
# 1     True
# 2    False
# Name: Revenue, dtype: bool


# Select * FROM df WHERE Revenue = 111
df[mask]

# Result:
#    Cost    Revenue
# 1  444     111

Risultati più rapidi possono essere ottenuti utilizzando numpy.where .

Ad esempio, con l'installazione di unubtu -

In [76]: df.iloc[np.where(df.A.values=='foo')]
Out[76]: 
     A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14

Confronti temporali:

In [68]: %timeit df.iloc[np.where(df.A.values=='foo')]  # fastest
1000 loops, best of 3: 380 µs per loop

In [69]: %timeit df.loc[df['A'] == 'foo']
1000 loops, best of 3: 745 µs per loop

In [71]: %timeit df.loc[df['A'].isin(['foo'])]
1000 loops, best of 3: 562 µs per loop

In [72]: %timeit df[df.A=='foo']
1000 loops, best of 3: 796 µs per loop

In [74]: %timeit df.query('(A=="foo")')  # slowest
1000 loops, best of 3: 1.71 ms per loop

Trovo la sintassi delle risposte precedenti ridondante e difficile da ricordare. Pandas ha introdotto il metodo query() in v0.13 e lo preferisco di gran lunga. Per la tua domanda, potresti fare df.query('col == val')

Riprodotto da http://pandas.pydata.org/pandas-docs/version/0.17.0/indexing.html#indexing-query

In [167]: n = 10

In [168]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))

In [169]: df
Out[169]: 
          a         b         c
0  0.687704  0.582314  0.281645
1  0.250846  0.610021  0.420121
2  0.624328  0.401816  0.932146
3  0.011763  0.022921  0.244186
4  0.590198  0.325680  0.890392
5  0.598892  0.296424  0.007312
6  0.634625  0.803069  0.123872
7  0.924168  0.325076  0.303746
8  0.116822  0.364564  0.454607
9  0.986142  0.751953  0.561512

# pure python
In [170]: df[(df.a < df.b) & (df.b < df.c)]
Out[170]: 
          a         b         c
3  0.011763  0.022921  0.244186
8  0.116822  0.364564  0.454607

# query
In [171]: df.query('(a < b) & (b < c)')
Out[171]: 
          a         b         c
3  0.011763  0.022921  0.244186
8  0.116822  0.364564  0.454607

Puoi anche accedere alle variabili nell'ambiente anteponendo un @ .

exclude = ('red', 'orange')
df.query('color not in @exclude')

df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
                   'B': 'one one two three two two one three'.split(),
                   'C': np.arange(8), 'D': np.arange(8) * 2})
df[df['A']=='foo']

OUTPUT:
   A      B  C   D
0  foo    one  0   0
2  foo    two  2   4
4  foo    two  4   8
6  foo    one  6  12
7  foo  three  7  14




dataframe