Saltar al contenido

Cómo seleccionar filas de un DataFrame basado en valores de columna

Ya no busques más en otras páginas porque estás al lugar perfecto, poseemos la respuesta que necesitas encontrar y sin problema.

Solución:

Para seleccionar filas cuyo valor de columna sea igual a un escalar, some_value, usar ==:

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

Para seleccionar filas cuyo valor de columna sea iterable, some_values, usar isin:

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

Combine múltiples condiciones con &:

df.loc[(df['column_name'] >= A) & (df['column_name'] <= B)]

Tenga en cuenta los paréntesis. Debido a las reglas de precedencia de operadores de Python, & se une más fuerte que <= y >=. Por tanto, los paréntesis del último ejemplo son necesarios. Sin los paréntesis

df['column_name'] >= A & df['column_name'] <= B

se analiza como

df['column_name'] >= (A & df['column_name']) <= B

lo que da como resultado un valor de Verdad de una Serie es un error ambiguo.


Para seleccionar filas cuyo valor de columna no es igualsome_value, usar !=:

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

isin devuelve una serie booleana, por lo que para seleccionar filas cuyo valor es no en some_values, niega la serie booleana usando ~:

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

Por ejemplo,

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'])

rendimientos

     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

Si tiene varios valores que desea incluir, colóquelos en una lista (o más generalmente, cualquier iterable) y use isin:

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

rendimientos

     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

Sin embargo, tenga en cuenta que si desea hacer esto muchas veces, es más eficiente hacer un índice primero y luego usar df.loc:

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

rendimientos

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

o, para incluir varios valores del uso del índice df.index.isin:

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

rendimientos

       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

Hay varias formas de seleccionar filas de un marco de datos de Pandas:

  1. Indexación booleana (df[df['col'] == value])
  2. Indexación posicional (df.iloc[...])
  3. Indexación de etiquetas (df.xs(...))
  4. df.query(...) API

A continuación les muestro ejemplos de cada uno, con consejos sobre cuándo utilizar determinadas técnicas. Supongamos que nuestro criterio es columna 'A' == 'foo'

(Nota sobre el rendimiento: para cada tipo de base, podemos simplificar las cosas mediante el uso de la API de Pandas o podemos aventurarnos fuera de la API, generalmente en NumPy, y acelerar las cosas).


Configuración

Lo primero que necesitaremos es identificar una condición que actuará como nuestro criterio para seleccionar filas. Empezaremos con el caso del OP column_name == some_valuee incluyen algunos otros casos de uso comunes.

Préstamo de @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)

1. Indexación booleana

... La indexación booleana requiere encontrar el true valor de cada fila 'A' columna siendo igual a 'foo'y luego usar esos valores de verdad para identificar qué filas conservar. Normalmente, llamaríamos a esta serie, una array de los valores de verdad, mask. Lo haremos aquí también.

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

Luego podemos usar esta máscara para dividir o indexar el marco de datos

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

Esta es una de las formas más sencillas de realizar esta tarea y si el rendimiento o la intuición no son un problema, este debería ser su método elegido. Sin embargo, si el rendimiento es un problema, es posible que desee considerar una forma alternativa de crear el mask.


2. Indexación posicional

Indexación posicional (df.iloc[...]) tiene sus casos de uso, pero este no es uno de ellos. Para identificar dónde dividir, primero debemos realizar el mismo análisis booleano que hicimos anteriormente. Esto nos deja realizando un paso adicional para lograr la misma tarea.

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. Indexación de etiquetas

Etiqueta La indexación puede ser muy útil, pero en este caso, nuevamente estamos haciendo más trabajo sin ningún 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. df.query() API

pd.DataFrame.query es una forma muy elegante / intuitiva de realizar esta tarea, pero suele ser más lenta. Sin embargo, si prestas atención a los siguientes tiempos, para datos grandes, la consulta es muy eficiente. Más que el enfoque estándar y de similar magnitud como mi mejor sugerencia.

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

Mi preferencia es usar el Booleanmask

Se pueden realizar mejoras reales modificando la forma en que creamos nuestro Booleanmask.

mask alternativa 1Utilice el NumPy subyacente array y renunciar a la sobrecarga de crear otro pd.Series

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

Mostraré pruebas de tiempo más completas al final, pero solo eche un vistazo a las ganancias de rendimiento que obtenemos con el marco de datos de muestra. Primero, miramos la diferencia en la creación de 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)

Evaluar el mask con el NumPy array es ~ 30 veces más rápido. Esto se debe en parte a que la evaluación de NumPy suele ser más rápida. También se debe en parte a la falta de gastos generales necesarios para construir un índice y un correspondiente pd.Series objeto.

A continuación, veremos el momento para cortar con uno mask versus el otro.

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)

Las ganancias de rendimiento no son tan pronunciadas. Veremos si esto se sostiene sobre pruebas más sólidas.


mask alternativa 2
También podríamos haber reconstruido el marco de datos. Hay una gran advertencia al reconstruir un marco de datos: debe ocuparse de la dtypes al hacerlo!

En lugar de df[mask] nosotros haremos esto

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

Si el marco de datos es de mixed tipo, que es nuestro ejemplo, entonces cuando obtenemos df.values la resultante array es de dtypeobject y en consecuencia, todas las columnas del nuevo marco de datos serán de dtypeobject. Por lo tanto, requiriendo el astype(df.dtypes) y eliminar cualquier posible mejora en el rendimiento.

%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)

Sin embargo, si el marco de datos no es de mixed type, esta es una forma muy útil de hacerlo.

Dado

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)

Versus

%%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)

Reducimos el tiempo a la mitad.


mask alternativa 3

@unutbu también nos muestra cómo usar pd.Series.isin para dar cuenta de cada elemento de df['A'] estar en un conjunto de valores. Esto se evalúa como lo mismo si nuestro conjunto de valores es un conjunto de un valor, a saber 'foo'. Pero también se generaliza para incluir conjuntos de valores más grandes si es necesario. Resulta que esto sigue siendo bastante rápido a pesar de que es una solución más general. La única pérdida real es la intuición para aquellos que no están familiarizados con el concepto.

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

Sin embargo, como antes, podemos utilizar NumPy para mejorar el rendimiento sin sacrificar prácticamente nada. Usaremos 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

Momento

Incluiré otros conceptos mencionados en otras publicaciones también como referencia.

Código a continuación

Cada columna en esta tabla representa un marco de datos de diferente longitud sobre el cual probamos cada función. Cada columna muestra el tiempo relativo tomado, con la función más rápida dado un índice base de 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

Notarás que los tiempos más rápidos parecen compartirse entre mask_with_values y mask_with_in1d.

res.T.plot(loglog=True)

Ingrese la descripción de la imagen aquí

Funciones

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]

Pruebas

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)

Sincronización especial

Mirando el caso especial cuando tenemos un solo no objeto dtype para todo el marco de datos.

Código a continuación

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

Resulta que la reconstrucción no vale la pena más allá de unos cientos de filas.

spec.T.plot(loglog=True)

Ingrese la descripción de la imagen aquí

Funciones

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
)

Pruebas

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)

tl; dr

Los Pandas equivalentes a

select * from table where column_name = some_value

es

table[table.column_name == some_value]

Varias condiciones:

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

o

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

Ejemplo de código

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

En el código anterior es la línea df[df.foo == 222] que da las filas según el valor de la columna, 222 en este caso.

También son posibles múltiples condiciones:

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

Pero en ese punto, recomendaría usar la función de consulta, ya que es menos detallada y produce el mismo resultado:

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

Reseñas y calificaciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *