Python Pandas: obtenga el índice de filas donde la columna coincide con cierto valor

5 minutos de lectura

quiero el avatar de usuario de badges
quiero insignias

Dado un DataFrame con una columna “BoolCol”, queremos encontrar los índices del DataFrame en los que los valores de “BoolCol” == True

Actualmente tengo la forma iterativa de hacerlo, que funciona perfectamente:

for i in range(100,3000):
    if df.iloc[i]['BoolCol']== True:
         print i,df.iloc[i]['BoolCol']

Pero esta no es la forma correcta de pandas para hacerlo. Después de algunas investigaciones, actualmente estoy usando este código:

df[df['BoolCol'] == True].index.tolist()

Este me da una lista de índices, pero no coinciden, cuando los verifico haciendo:

df.iloc[i]['BoolCol']

¡El resultado es en realidad Falso!

¿Cuál sería la forma correcta de pandas para hacer esto?

avatar de usuario de unutbu
unutbu

df.iloc[i] devuelve el ith fila de df. i no se refiere a la etiqueta de índice, i es un índice basado en 0.

A diferencia de, el atributo index devuelve etiquetas de índice realesno índices de fila numéricos:

df.index[df['BoolCol'] == True].tolist()

o equivalente,

df.index[df['BoolCol']].tolist()

Puede ver la diferencia bastante claramente al jugar con un DataFrame con un índice no predeterminado que no es igual a la posición numérica de la fila:

df = pd.DataFrame({'BoolCol': [True, False, False, True, True]},
       index=[10,20,30,40,50])

In [53]: df
Out[53]: 
   BoolCol
10    True
20   False
30   False
40    True
50    True

[5 rows x 1 columns]

In [54]: df.index[df['BoolCol']].tolist()
Out[54]: [10, 40, 50]

Si desea utilizar el índice,

In [56]: idx = df.index[df['BoolCol']]

In [57]: idx
Out[57]: Int64Index([10, 40, 50], dtype="int64")

entonces puede seleccionar las filas usando loc en vez de iloc:

In [58]: df.loc[idx]
Out[58]: 
   BoolCol
10    True
40    True
50    True

[3 rows x 1 columns]

Tenga en cuenta que loc también puede aceptar matrices booleanas:

In [55]: df.loc[df['BoolCol']]
Out[55]: 
   BoolCol
10    True
40    True
50    True

[3 rows x 1 columns]

Si tiene una matriz booleana, masky necesita valores de índice ordinal, puede calcularlos usando np.flatnonzero:

In [110]: np.flatnonzero(df['BoolCol'])
Out[112]: array([0, 3, 4])

Usar df.iloc para seleccionar filas por índice ordinal:

In [113]: df.iloc[np.flatnonzero(df['BoolCol'])]
Out[113]: 
   BoolCol
10    True
40    True
50    True

Avatar de usuario de Surya
Surya

Se puede hacer usando la función numpy where():

import pandas as pd
import numpy as np

In [716]: df = pd.DataFrame({"gene_name": ['SLC45A1', 'NECAP2', 'CLIC4', 'ADC', 'AGBL4'] , "BoolCol": [False, True, False, True, True] },
       index=list("abcde"))

In [717]: df
Out[717]: 
  BoolCol gene_name
a   False   SLC45A1
b    True    NECAP2
c   False     CLIC4
d    True       ADC
e    True     AGBL4

In [718]: np.where(df["BoolCol"] == True)
Out[718]: (array([1, 3, 4]),)

In [719]: select_indices = list(np.where(df["BoolCol"] == True)[0])

In [720]: df.iloc[select_indices]
Out[720]: 
  BoolCol gene_name
b    True    NECAP2
d    True       ADC
e    True     AGBL4

Aunque no siempre necesita el índice para una coincidencia, pero en caso de que necesite:

In [796]: df.iloc[select_indices].index
Out[796]: Index([u'b', u'd', u'e'], dtype="object")

In [797]: df.iloc[select_indices].index.tolist()
Out[797]: ['b', 'd', 'e']

Si desea usar su objeto de marco de datos solo una vez, use:

df['BoolCol'].loc[lambda x: x==True].index

La forma simple es restablecer el índice del DataFrame antes del filtrado:

df_reset = df.reset_index()
df_reset[df_reset['BoolCol']].index.tolist()

¡Un poco raro, pero es rápido!

Avatar de usuario de BENY
beny

Primero puede verificar query cuando la columna de destino es tipo bool (PD: sobre cómo usarlo, consulte el enlace)

df.query('BoolCol')
Out[123]: 
    BoolCol
10     True
40     True
50     True

Después de filtrar el df original por la columna booleana, podemos elegir el índice.

df=df.query('BoolCol')
df.index
Out[125]: Int64Index([10, 40, 50], dtype="int64")

También los pandas tienen nonzerosolo seleccionamos el posición de True fila y usándolo cortar el DataFrame o index

df.index[df.BoolCol.values.nonzero()[0]]
Out[128]: Int64Index([10, 40, 50], dtype="int64")

Avatar de usuario de Carson
Carson

Extendí esta pregunta que es cómo obtener el row, columny value del valor de todos los partidos?

aquí está la solución:

import pandas as pd
import numpy as np


def search_coordinate(df_data: pd.DataFrame, search_set: set) -> list:
    nda_values = df_data.values
    tuple_index = np.where(np.isin(nda_values, [e for e in search_set]))
    return [(row, col, nda_values[row][col]) for row, col in zip(tuple_index[0], tuple_index[1])]


if __name__ == '__main__':
    test_datas = [['cat', 'dog', ''],
                  ['goldfish', '', 'kitten'],
                  ['Puppy', 'hamster', 'mouse']
                  ]
    df_data = pd.DataFrame(test_datas)
    print(df_data)
    result_list = search_coordinate(df_data, {'dog', 'Puppy'})
    print(f"\n\n{'row':<4} {'col':<4} {'name':>10}")
    [print(f"{row:<4} {col:<4} {name:>10}") for row, col, name in result_list]

Producción:

          0        1       2
0       cat      dog        
1  goldfish           kitten
2     Puppy  hamster   mouse


row  col        name
0    1           dog
2    0         Puppy

Avatar de usuario de Muhammad Yasirroni
Muhammad Yasirroni

Para el candidato de índice conocido que nos interesa, una forma más rápida de no verificar toda la columna se puede hacer de esta manera:

np.array(index_slice)[np.where(df.loc[index_slice]['column_name'] >= threshold)[0]]

Comparación completa:

import pandas as pd
import numpy as np

index_slice = list(range(50,150)) # know index location for our inteterest
data = np.zeros(10000)
data[(index_slice)] = np.random.random(len(index_slice))

df = pd.DataFrame(
    {'column_name': data},
)

threshold = 0.5

%%timeit
np.array(index_slice)[np.where(df.loc[index_slice]['column_name'] >= threshold)[0]]
# 600 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%%timeit
[i for i in index_slice if i in df.index[df['column_name'] >= threshold].tolist()]
# 22.5 ms ± 29.1 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

La forma en que funciona es así:

# generate Boolean satisfy condition only in sliced column
df.loc[index_slice]['column_name'] >= threshold

# convert Boolean to index, but start from 0 and increment by 1
np.where(...)[0]

# list of index to be sliced
np.array(index_slice)[...]

Nota: Es necesario tener en cuenta que np.array(index_slice) no puede ser sustituido por df.index debido a np.where(...)[0] indexación start from 0 and increment by 1pero puedes hacer algo como df.index[index_slice]. Y creo que esto no vale la pena si solo lo hace una vez con una pequeña cantidad de filas.

¿Ha sido útil esta solución?