¿Cómo encuentro los valores más cercanos en una serie de Pandas a un número de entrada?

He visto:

  • ¿Cómo encuentro el valor más cercano a un número dado en una matriz?
  • ¿Cómo encuentro el elemento de la matriz más cercana a un número arbitrario (no miembro)? .

Estos se relacionan con la vainilla python y no con los pandas.

Si tengo la serie:

ix num 0 1 1 6 2 4 3 5 4 2 

Y escribo 3, ¿cómo puedo encontrar (eficientemente)?

  1. El índice de 3 si se encuentra en la serie.
  2. El índice del valor por debajo y por encima de 3 si no se encuentra en la serie.

Es decir. Con la serie anterior {1,6,4,5,2} y la entrada 3, debería obtener valores (4,2) con índices (2,4).

    Podrías usar argsort() como

    Diga, input = 3

     In [198]: input = 3 In [199]: df.ix[(df['num']-input).abs().argsort()[:2]] Out[199]: num 2 4 4 2 

    df_sort es el dataframe con 2 valores más cercanos.

     In [200]: df_sort = df.ix[(df['num']-input).abs().argsort()[:2]] 

    Para el índice,

     In [201]: df_sort.index.tolist() Out[201]: [2, 4] 

    Para los valores,

     In [202]: df_sort['num'].tolist() Out[202]: [4, 2] 

    Detalle, para la solución df anterior fue

     In [197]: df Out[197]: num 0 1 1 6 2 4 3 5 4 2 

    Recomiendo usar iloc además de la respuesta de John Galt, ya que esto funcionará incluso con un índice entero sin clasificar, ya que .ix primero observa las tags de índice

     df.iloc[(df['num']-input).abs().argsort()[:2]] 

    Una desventaja de los otros algoritmos discutidos aquí es que tienen que ordenar la lista completa. Esto resulta en una complejidad de ~ N log (N) .

    Sin embargo, es posible lograr los mismos resultados en ~ N. Este enfoque separa la ttwig de datos en dos subconjuntos, uno más pequeño y otro más grande que el valor deseado. El vecino más bajo es el valor más grande en el dataframe más pequeño y viceversa para el vecino superior.

    Esto da el siguiente fragmento de código:

     def find_neighbours(value): exactmatch=df[df.num==value] if !exactmatch.empty: return exactmatch.index[0] else: lowerneighbour_ind = df[df.numtraversed].idxmin() return lowerneighbour_ind, upperneighbour_ind 

    Este enfoque es similar al uso de la partición en pandas , que puede ser realmente útil cuando se trata de grandes conjuntos de datos y la complejidad se convierte en un problema.

    Si tu serie ya está ordenada, podrías usar algo como esto.

     def closest(df, col, val, direction): n = len(df[df[col] <= val]) if(direction < 0): n -= 1 if(n < 0 or n >= len(df)): print('err - value outside range') return None return df.ix[n, col] df = pd.DataFrame(pd.Series(range(0,10,2)), columns=['num']) for find in range(-1, 2): lc = closest(df, 'num', find, -1) hc = closest(df, 'num', find, 1) print('Closest to {} is {}, lower and {}, higher.'.format(find, lc, hc)) df: num 0 0 1 2 2 4 3 6 4 8 err - value outside range Closest to -1 is None, lower and 0, higher. Closest to 0 is 0, lower and 2, higher. Closest to 1 is 0, lower and 2, higher. 

    Si la serie ya está ordenada, un método eficiente para encontrar los índices es utilizando bisect . Un ejemplo:

     idx = bisect_right(df['num'].values, 3) 

    Entonces, para el problema citado en la pregunta, teniendo en cuenta que la columna “col” del dataframe “df” está ordenada:

     from bisect import bisect_right, bisect_left def get_closests(df, col, val): lower_idx = bisect_right(df[col].values, val) higher_idx = bisect_left(df[col].values, val) if higher_idx == lower_idx: return lower_idx else: return lower_idx, higher_idx 

    Es bastante eficiente encontrar el índice del valor específico “val” en la columna de dataframe “col”, o sus vecinos más cercanos, pero requiere que la lista esté ordenada.