Obtenga la primera fila del marco de datos en Python Pandas según los criterios

4 minutos de lectura

Avatar de usuario de Tasos
Tasos

Digamos que tengo un dataframe como este

import pandas as pd
df = pd.DataFrame([[1, 2, 1], [1, 3, 2], [4, 6, 3], [4, 3, 4], [5, 4, 5]], columns=['A', 'B', 'C'])

>> df
   A  B  C
0  1  2  1
1  1  3  2
2  4  6  3
3  4  3  4
4  5  4  5

La tabla original es más complicada con más columnas y filas.

Quiero obtener la primera fila que cumpla con algunos criterios. Ejemplos:

  1. Obtener la primera fila donde A > 3 (devuelve la fila 2)
  2. Obtenga la primera fila donde A > 4 Y B > 3 (devuelve la fila 4)
  3. Obtener la primera fila donde A > 3 Y (B > 3 O C > 2) (devuelve la fila 2)

Pero, si no hay ninguna fila que cumpla con los criterios específicos, entonces quiero obtener la primera después de ordenarla descendiendo por A (u otros casos por B, C, etc.)

  1. Obtenga la primera fila donde A > 6 (devuelve la fila 4 ordenándola por A desc y obtiene la primera)

Pude hacerlo iterando en el marco de datos (sé que es una mierda: P). Entonces, prefiero una forma más pitónica de resolverlo.

  • La primera fila A> 4 es 4, ¿puede verificar las pruebas que está buscando?

    – Zeugma

    17/11/2016 a las 16:31

  • indexación booleana?

    – Kartik

    17/11/2016 a las 16:31

  • @Boud Lo siento por eso. Error de tecleado 🙁

    – Tasas

    17/11/2016 a las 16:33

  • La indexación booleana probablemente sea más de lo que desea, ya que debe ejecutarse en todo el DataFrame. Si la primera fila es la que desea, debería poder averiguarlo de inmediato, independientemente del tamaño del DataFrame.

    – BolígrafoBen

    17 de noviembre de 2016 a las 16:36

  • ¿Hay alguna forma específica en que los criterios se relacionen con la columna que desea ordenar? ¿O es diferente? Si tiene una lista de criterios, puede usar la respuesta de @ Boud para seleccionar las filas, y si el resultado no tiene filas, puede usar argmax para devolver la fila más grande.

    – Kartik

    17 de noviembre de 2016 a las 16:41

este tutorial es muy bueno para rebanar pandas. Asegúrate de comprobarlo. En algunos fragmentos… Para dividir un marco de datos con una condición, utilice este formato:

>>> df[condition]

Esto devolverá una porción de su marco de datos que puede indexar usando iloc. Aquí están sus ejemplos:

  1. Obtener la primera fila donde A > 3 (devuelve la fila 2)

    >>> df[df.A > 3].iloc[0]
    A    4
    B    6
    C    3
    Name: 2, dtype: int64
    

Si lo que realmente quiere es el número de fila, en lugar de usar ilocpodría ser df[df.A > 3].index[0].

  1. Obtener la primera fila donde A > 4 Y B > 3:

    >>> df[(df.A > 4) & (df.B > 3)].iloc[0]
    A    5
    B    4
    C    5
    Name: 4, dtype: int64
    
  2. Obtener la primera fila donde A > 3 Y (B > 3 O C > 2) (devuelve la fila 2)

    >>> df[(df.A > 3) & ((df.B > 3) | (df.C > 2))].iloc[0]
    A    4
    B    6
    C    3
    Name: 2, dtype: int64
    

Ahora, con su último caso, podemos escribir una función que maneje el caso predeterminado de devolver el marco ordenado descendente:

>>> def series_or_default(X, condition, default_col, ascending=False):
...     sliced = X[condition]
...     if sliced.shape[0] == 0:
...         return X.sort_values(default_col, ascending=ascending).iloc[0]
...     return sliced.iloc[0]
>>> 
>>> series_or_default(df, df.A > 6, 'A')
A    5
B    4
C    5
Name: 4, dtype: int64

Como era de esperar, devuelve la fila 4.

  • En caso de que su marco de datos sea muy grande, ¿no es un desperdicio? Está calculando una serie completa solo para extraer su primer elemento.

    – BolígrafoBen

    19 de febrero de 2019 a las 23:23

Para coincidencias existentes, utilice query:

df.query(' A > 3' ).head(1)
Out[33]: 
   A  B  C
2  4  6  3

df.query(' A > 4 and B > 3' ).head(1)
Out[34]: 
   A  B  C
4  5  4  5

df.query(' A > 3 and (B > 3 or C > 2)' ).head(1)
Out[35]: 
   A  B  C
2  4  6  3

Avatar de usuario de ZeFeng Zhu
ZeFeng Zhu

Para el punto que ‘devuelve el valor tan pronto como encuentra la primera fila/registro que cumple con los requisitos y NO iterando otras filas’, el siguiente código funcionaría:

def pd_iter_func(df):
    for row in df.itertuples():
        # Define your criteria here
        if row.A > 4 and row.B > 3:
            return row

Es más eficiente que Boolean Indexing cuando se trata de un marco de datos grande.

Para hacer que la función anterior sea más aplicable, se pueden implementar funciones lambda:

def pd_iter_func(df: DataFrame, criteria: Callable[[NamedTuple], bool]) -> Optional[NamedTuple]:
    for row in df.itertuples():
        if criteria(row):
            return row

pd_iter_func(df, lambda row: row.A > 4 and row.B > 3)

Como se menciona en la respuesta a la pregunta del ‘espejo’, pandas.Series.idxmax también sería una buena elección.

def pd_idxmax_func(df, mask):
    return df.loc[mask.idxmax()]

pd_idxmax_func(df, (df.A > 4) & (df.B > 3))

  • Me gusta itertuples(). Pero con idxmax(), ¿no se evalúa el máximo en cada fila antes de ejecutar idxmax()?

    – Zachary Ryan Smith

    23 de diciembre de 2020 a las 12:15

Avatar de usuario de PabTorre
pabtorre

puedes encargarte de los primeros 3 artículos con rebanado y cabeza:

  1. df[df.A>=4].head(1)
  2. df[(df.A>=4)&(df.B>=3)].head(1)
  3. df[(df.A>=4)&((df.B>=3) * (df.C>=2))].head(1)

La condición en caso de que no vuelva nada se puede manejar con un intento o un si…

try:
    output = df[df.A>=6].head(1)
    assert len(output) == 1
except: 
    output = df.sort_values('A',ascending=False).head(1)

¿Ha sido útil esta solución?