¿Cómo selecciono filas de un DataFrame según los valores de columna?

¿Cómo puedo seleccionar filas de un DataFrame según los valores en alguna columna en Pandas?

En SQL, usaría:

SELECT *
FROM table
WHERE column_name = some_value

preguntado el 12 de junio de 13 a las 18:06

Esta es una comparación con SQL: pandas.pydata.org/pandas-docs/stable/comparison_with_sql.html donde puedes ejecutar pandas como SQL. -

también puede usar DFsql, para ejecutar en memoria SQL en marcos de datos de pandas medium.com/riselab/… github.com/mindsdb/dfsql -

Fue conducido aquí buscando coincidencias basadas en una lista de valores de varias columnas. Esta publicación es solo sobre valores en una columna. Sugiera editar el título para que diga "valores en una columna" para evitar resultados de búsqueda falsos. -

Esta pregunta es sobre recuperando valores. Para pólipo valores, sugeriría mirar este hilo de preguntas SO y los comentarios que lo acompañan -

16 Respuestas

Para seleccionar filas cuyo valor de columna es igual a un escalar, some_value, Utilizar ==:

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

Para seleccionar filas cuyo valor de columna está en un iterable, some_values, Utilizar isin:

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

Combinar múltiples condiciones con &:

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

Tenga en cuenta los paréntesis. Debido a Python reglas de precedencia del operador, & se une más fuerte que <= y >=. Por lo tanto, los paréntesis en el ú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 resulta en un El valor de verdad de una serie es un error ambiguo.


Para seleccionar filas cuyo valor de columna no es igual some_value, Utilizar !=:

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

isin devuelve una serie booleana, por lo que para seleccionar filas cuyo valor es no in 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'])

los 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, póngalos en una lista (o más generalmente, cualquier iterable) y use isin:

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

los 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

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

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

los rendimientos

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

o, para incluir múltiples valores del uso del índice df.index.isin:

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

los 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

Respondido el 18 de enero de 19 a las 02:01

Hay varias formas de seleccionar filas de un dataframe de Pandas:

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

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

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


Preparar

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.

Tomando prestado 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 verdadero valor de cada fila 'A' columna siendo igual a 'foo', luego usa esos valores de verdad para identificar qué filas conservar. Por lo general, nombraríamos esta serie, una matriz de 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 simples 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 una preocupación, 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 cortar, 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

Disquera 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 presta atención a los tiempos a continuación, para datos grandes, la consulta es muy eficiente. Más que el enfoque estándar y de magnitud similar a 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 Boolean mask

Se pueden realizar mejoras reales modificando la forma en que creamos nuestros Boolean mask.

mask alternativa 1 Use la matriz NumPy subyacente y renuncie a la sobrecarga de crear otra 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 usando el marco de datos de muestra. En primer lugar, observamos la diferencia en la creación de la 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)

Evaluando el mask con la matriz NumPy 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 su correspondiente pd.Series objeto.

A continuación, veremos el tiempo para cortar con una mask contra 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 mantiene en pruebas más sólidas.


mask alternativa 2 Podríamos haber reconstruido el marco de datos también. Hay una gran advertencia al reconstruir un marco de datos: debe cuidar el 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 tipo mixto, que es nuestro ejemplo, cuando obtengamos df.values la matriz resultante es de dtype object y en consecuencia, todas las columnas del nuevo marco de datos serán de dtype object. Requiriendo así la astype(df.dtypes) y matando cualquier ganancia potencial de 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 tipo mixto, esta es una forma muy útil de hacerlo.

Dado que los

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 está en 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

Sincronización

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

Código a continuación

Cada visión de conjunto en esta tabla representa un marco de datos de longitud diferente sobre el cual probamos cada función. Cada columna muestra el tiempo relativo empleado, con la función más rápida dada 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 estar compartidos entre mask_with_values y mask_with_in1d.

res.T.plot(loglog=True)

Ingrese la descripción de la imagen aquí

clave

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)

Tiempo 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í

clave

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)

Respondido 04 Feb 21, 16:02

utilizan el df['A'].values == 'foo' apper FutureWarning: error en la comparación de elementos; devolviendo escalar en su lugar, pero en el futuro realizará una comparación por elementos: junyu

tl; dr

Los pandas equivalentes a

select * from table where column_name = some_value

is

table[table.column_name == some_value]

Múltiples condiciones:

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

or

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 basadas en el valor de la columna, 222 en este caso.

Múltiples condiciones también son posibles:

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

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

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

Respondido 05 Oct 20, 19:10

Encuentro que la sintaxis de las respuestas anteriores es redundante y difícil de recordar. Los pandas introdujeron el query() método en v0.13 y lo prefiero mucho. Para tu pregunta, podrías hacer df.query('col == val').

Reproducido de El método query() (experimental):

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

También puede acceder a las variables en el entorno anteponiendo un @.

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

Respondido el 16 de Septiembre de 22 a las 22:09

Más flexibilidad usando .query con pandas >= 0.25.0:

Como pandas >= 0.25.0 podemos usar el query método para filtrar marcos de datos con métodos pandas e incluso nombres de columnas que tienen espacios. Normalmente, los espacios en los nombres de las columnas darían un error, pero ahora podemos resolverlo usando un acento grave (`) - ver GitHub:

# Example dataframe
df = pd.DataFrame({'Sender email':['ex@example.com', "reply@shop.com", "buy@shop.com"]})

     Sender email
0  ex@example.com
1  reply@shop.com
2    buy@shop.com

Usar .query con metodo str.endswith:

df.query('`Sender email`.str.endswith("@shop.com")')

Salida

     Sender email
1  reply@shop.com
2    buy@shop.com

También podemos usar variables locales prefijándolas con un @ en nuestra consulta:

domain = 'shop.com'
df.query('`Sender email`.str.endswith(@domain)')

Salida

     Sender email
1  reply@shop.com
2    buy@shop.com

respondido 28 mar '22, 12:03

Para seleccionar solo columnas específicas de varias columnas para un valor dado en Pandas:

select col_name1, col_name2 from table where column_name = some_value.

Opciones loc:

df.loc[df['column_name'] == some_value, [col_name1, col_name2]]

or query:

df.query('column_name == some_value')[[col_name1, col_name2]]

Respondido el 05 de Septiembre de 21 a las 09:09

En versiones más nuevas de Pandas, inspiradas en la documentación (Ver datos):

df[df["colume_name"] == some_value] #Scalar, True/False..

df[df["colume_name"] == "some_value"] #String

Combine múltiples condiciones poniendo la cláusula entre paréntesis, (), y combinándolos con & y | (y/o). Como esto:

df[(df["colume_name"] == "some_value1") & (pd[pd["colume_name"] == "some_value2"])]

Otros filtros

pandas.notna(df["colume_name"]) == True # Not NaN
df['colume_name'].str.contains("text") # Search for "text"
df['colume_name'].str.lower().str.contains("text") # Search for "text", after converting  to lowercase

Respondido 08 Feb 21, 15:02

Se pueden lograr resultados más rápidos utilizando numpy donde.

Por ejemplo, con configuración de 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

Comparaciones de tiempo:

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

Respondido 03 Oct 17, 17:10

Aquí hay un ejemplo simple.

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

Respondido el 13 de junio de 13 a las 12:06

Para añadir: También puedes hacer df.groupby('column_name').get_group('column_desired_value').reset_index() para crear un nuevo marco de datos con una columna específica que tenga un valor particular. P.ej,

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)

Ejecutar esto da:

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

Respondido el 16 de Septiembre de 22 a las 23:09

También puedes usar .apply:

df.apply(lambda row: row[df['B'].isin(['one','three'])])

En realidad, funciona por filas (es decir, aplica la función a cada fila).

La salida es

   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

Los resultados son los mismos que los mencionados por @unutbu

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

Respondido el 07 de diciembre de 18 a las 17:12

Si desea realizar consultas en su marco de datos repetidamente y la velocidad es importante para usted, lo mejor es convertir su marco de datos en diccionario y, al hacerlo, puede realizar consultas miles de veces más rápido.

my_df = df.set_index(column_name)
my_dict = my_df.to_dict('index')

Después de hacer el diccionario my_dict, puede pasar por:

if some_value in my_dict.keys():
   my_result = my_dict[some_value]

Si tiene valores duplicados en column_name, no puede crear un diccionario. pero puedes usar:

my_result = my_df.loc[some_value]

respondido 27 nov., 21:17

Declaraciones SQL en DataFrames para seleccionar filas usando DuckDB

Con PatoDB podemos consultar pandas DataFrames con declaraciones SQL, en un forma de alto rendimiento.

Ya que la pregunta es ¿Cómo selecciono filas de un DataFrame según los valores de columna?, y el ejemplo en la pregunta es una consulta SQL, esta respuesta parece lógica en este tema.

Ejemplo:

In [1]: import duckdb

In [2]: import pandas as pd

In [3]: con = duckdb.connect()

In [4]: df = pd.DataFrame({"A": range(11), "B": range(11, 22)})

In [5]: df
Out[5]:
     A   B
0    0  11
1    1  12
2    2  13
3    3  14
4    4  15
5    5  16
6    6  17
7    7  18
8    8  19
9    9  20
10  10  21

In [6]: results = con.execute("SELECT * FROM df where A > 2").df()

In [7]: results
Out[7]:
    A   B
0   3  14
1   4  15
2   5  16
3   6  17
4   7  18
5   8  19
6   9  20
7  10  21

Respondido el 16 de Septiembre de 22 a las 23:09

Puedes usar loc (corchetes) con una función:

# Series
s = pd.Series([1, 2, 3, 4]) 
s.loc[lambda x: x > 1]
# s[lambda x: x > 1]

Salida:

1    2
2    3
3    4
dtype: int64

or

# DataFrame
df = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30]})
df.loc[lambda x: x['A'] > 1]
# df[lambda x: x['A'] > 1]

Salida:

   A   B
1  2  20
2  3  30

La ventaja de este método es que puede encadenar la selección con operaciones anteriores. Por ejemplo:

df.mul(2).loc[lambda x: x['A'] > 3, 'B']
# (df * 2).loc[lambda x: x['A'] > 3, 'B']

vs

df_temp = df * 2
df_temp.loc[df_temp['A'] > 3, 'B']

Salida:

1    40
2    60
Name: B, dtype: int64

Respondido 02 ago 22, 21:08

Grandes respuestas. Sólo, cuando el el tamaño del marco de datos se acerca a millones de filas, muchos de los métodos tienden a tomar años cuando se usan df[df['col']==val]. Quería tener todos los valores posibles de "otra_columna" que correspondan a valores específicos en "alguna_columna" (en este caso en un diccionario). Esto funcionó y rápido.

s=datetime.datetime.now()

my_dict={}

for i, my_key in enumerate(df['some_column'].values): 
    if i%100==0:
        print(i)  # to see the progress
    if my_key not in my_dict.keys():
        my_dict[my_key]={}
        my_dict[my_key]['values']=[df.iloc[i]['another_column']]
    else:
        my_dict[my_key]['values'].append(df.iloc[i]['another_column'])
        
e=datetime.datetime.now()

print('operation took '+str(e-s)+' seconds')```

Respondido 16 Feb 22, 21:02

1. Instalar numexpr acelerar query() llamadas

La documentación de los pandas. recomienda instalar numexpr para acelerar el cálculo numérico cuando se utiliza query(). Utilizar pip install numexpr (o conda, sudo etc. dependiendo de su entorno) para instalarlo.

Para marcos de datos más grandes (donde el rendimiento realmente importa), df.query() a numexpr El motor funciona mucho más rápido que df[mask]. En particular, funciona mejor para los siguientes casos.

Operadores lógicos y/o de comparación en columnas de cadenas

Si una columna de cadenas se compara con otra(s) cadena(s) y se van a seleccionar filas coincidentes, incluso para una sola operación de comparación, query() funciona más rápido que df[mask]. Por ejemplo, para un marco de datos con filas de 80k, es un 30% más rápido1 y para un marco de datos con filas de 800k, es un 60% más rápido.2

df[df.A == 'foo']
df.query("A == 'foo'")  # <--- performs 30%-60% faster

Esta brecha aumenta a medida que aumenta el número de operaciones (si se encadenan 4 comparaciones df.query() es 2-2.3 veces más rápido que df[mask])1,2 y/o la longitud de la trama de datos aumenta.2

Múltiples operaciones en columnas numéricas

Si es necesario calcular múltiples operaciones aritméticas, lógicas o de comparación para crear una máscara booleana para filtrar df, query() realiza más rápido. Por ejemplo, para un marco con 80k filas, es un 20 % más rápido1 y para un marco con filas de 800k, es 2 veces más rápido.2

df[(df.B % 5) **2 < 0.1]
df.query("(B % 5) **2 < 0.1")  # <--- performs 20%-100% faster.

Esta brecha en el rendimiento aumenta a medida que aumenta el número de operaciones y/o aumenta la longitud del marco de datos.2

El siguiente gráfico muestra cómo funcionan los métodos a medida que aumenta la longitud del marco de datos.3

diagrama de rendimiento

2. Acceso .values para llamar a los métodos de pandas dentro query()

Numexpr actualmente apoya solo lógico (&, |, ~), comparación (==, >, <, >=, <=, !=) y operadores aritméticos básicos (+, -, *, /, **, %).

Por ejemplo, no admite la división de enteros (//). Sin embargo, llamando al método pandas equivalente (floordiv()) y accediendo a la values atributo en la Serie resultante hace numexpr evaluar su matriz numpy subyacente y query() obras. o ajuste engine parámetro para 'python' También funciona.

df.query('B.floordiv(2).values <= 3')  # or 
df.query('B.floordiv(2).le(3).values') # or
df.query('B.floordiv(2).le(3)', engine='python')

Lo mismo aplica para ErfanLas llamadas a métodos sugeridos también. El código en su respuesta escupe TypeError tal como está (a partir de Pandas 1.3.4) para numexpr motor pero accediendo .values atributo hace que funcione.

df.query('`Sender email`.str.endswith("@shop.com")')         # <--- TypeError
df.query('`Sender email`.str.endswith("@shop.com").values')  # OK


1: Código de referencia usando un marco con 80k filas

import numpy as np
df = pd.DataFrame({'A': 'foo bar foo baz foo bar foo foo'.split()*10000, 
                   'B': np.random.rand(80000)})

%timeit df[df.A == 'foo']
# 8.5 ms ± 104.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df.query("A == 'foo'")
# 6.36 ms ± 95.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df[((df.A == 'foo') & (df.A != 'bar')) | ((df.A != 'baz') & (df.A != 'buz'))]
# 29 ms ± 554 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
%timeit df.query("A == 'foo' & A != 'bar' | A != 'baz' & A != 'buz'")
# 16 ms ± 339 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)

%timeit df[(df.B % 5) **2 < 0.1]
# 5.35 ms ± 37.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df.query("(B % 5) **2 < 0.1")
# 4.37 ms ± 46.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

2: Código de referencia usando un marco con 800k filas

df = pd.DataFrame({'A': 'foo bar foo baz foo bar foo foo'.split()*100000, 
                   'B': np.random.rand(800000)})

%timeit df[df.A == 'foo']
# 87.9 ms ± 873 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
%timeit df.query("A == 'foo'")
# 54.4 ms ± 726 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)

%timeit df[((df.A == 'foo') & (df.A != 'bar')) | ((df.A != 'baz') & (df.A != 'buz'))]
# 310 ms ± 3.4 ms per loop (mean ± std. dev. of 10 runs, 100 loops each)
%timeit df.query("A == 'foo' & A != 'bar' | A != 'baz' & A != 'buz'")
# 132 ms ± 2.43 ms per loop (mean ± std. dev. of 10 runs, 100 loops each)

%timeit df[(df.B % 5) **2 < 0.1]
# 54 ms ± 488 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
%timeit df.query("(B % 5) **2 < 0.1")
# 26.3 ms ± 320 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)

3: código utilizado para producir los gráficos de rendimiento de los dos métodos para cadenas y números.

from perfplot import plot
constructor = lambda n: pd.DataFrame({'A': 'foo bar foo baz foo bar foo foo'.split()*n, 'B': np.random.rand(8*n)})
plot(
    setup=constructor,
    kernels=[lambda df: df[(df.B%5)**2<0.1], lambda df: df.query("(B%5)**2<0.1")],
    labels= ['df[(df.B % 5) **2 < 0.1]', 'df.query("(B % 5) **2 < 0.1")'],
    n_range=[2**k for k in range(4, 24)],
    xlabel='Rows in DataFrame',
    title='Multiple mathematical operations on numbers',
    equality_check=pd.DataFrame.equals);
plot(
    setup=constructor,
    kernels=[lambda df: df[df.A == 'foo'], lambda df: df.query("A == 'foo'")],
    labels= ["df[df.A == 'foo']", """df.query("A == 'foo'")"""],
    n_range=[2**k for k in range(4, 24)],
    xlabel='Rows in DataFrame',
    title='Comparison operation on strings',
    equality_check=pd.DataFrame.equals);

Respondido el 18 de Septiembre de 22 a las 11:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.