Tengo un marco de datos del siguiente formato:

Location |  Y   | X
--------------------
A1       | -10  | 1
A1       | -20  | 2
A1       | -10  | 3
A1       | -25  | 4
A2       | -10  | 1
A2       | -20  | 2
A2       | -10  | 3
A2       | -25  | 4

Por simplicidad, supongamos que para A1 y A2, -20 y -25 son los mínimos locales. Supongamos que hay muchas ubicaciones (digamos alrededor de 1000), y supongamos que para cada ubicación hay muchos puntos de datos (digamos también alrededor de 1000).

Quiero obtener esta tabla:

Location | min for Y
--------------------
A1       | -20
A1       | -25
A2       | -20
A2       | -25

Mi algoritmo actual lleva demasiado tiempo. Brevemente, en pseudo código de python:

minima_list = []
loc_list = []
for location in locations:
    # Find all minima, transform to temp_list, and append to long list
    temp_list = find_minima
    minima_list += temp_list
    loc_list += [location for item in temp_list]
res = pd.DataFrame({'min': Series(minima_list), 'loc': Series(loc_list)

Este enfoque es largo debido a la multiplicidad, aún es un poco más rápido que enviar las salidas individuales a un marco de datos y luego concatenarlos a todos; mi conjetura es debido a la sobrecarga asociada con la configuración de marcos de datos y luego concatenarlos.

Pero este enfoque todavía lleva demasiado tiempo. Con solo 500,000 puntos de datos totales, toma de 5 a 6 segundos. Por lo tanto, me pregunto si hay una forma aún más elegante que también sea más eficiente.

El método de transformación parece funcionar cuando la longitud de entrada = longitud de salida. Pero en este caso, el DF de salida será un subconjunto de la tabla de entrada. La salida también es diferente a la entrada.

Estoy pensando en probar np.arrays. Mi intuición es que si puedo vectorizar la operación, será más rápido. Pero tampoco estoy seguro de cómo. Así que pensé en pedirle a la comunidad un enfoque mucho más elegante y eficaz que mi enfoque de loopy-loo antes de emprender un viaje de 2 horas de regreso a casa.

0
Thornhale 5 mar. 2017 a las 20:38

2 respuestas

La mejor respuesta

Usaría scipy.signal. Método argrelextrema:

In [198]: from scipy.signal import argrelextrema

In [199]: df
Out[199]:
  Location   Y  X
0       A1 -10  1
1       A1 -20  2
2       A1 -10  3
3       A1 -25  4
4       A2 -10  1
5       A2 -20  2
6       A2 -10  3
7       A2 -25  4

In [200]: df.loc[argrelextrema(df.Y.values, np.less)]
Out[200]:
  Location   Y  X
1       A1 -20  2
3       A1 -25  4
5       A2 -20  2

PS IMO no puede decir si el último punto es un mínimo local a menos que sepamos el siguiente punto, por ejemplo, si en la siguiente fila (virtual) tendría -30 ...

2
MaxU 5 mar. 2017 a las 18:57

Solución usando pandas, por ejemplo:

# Group df by 'Location'
location_groups = df.groupby(['Location'])

# Loop location_groups
for location, location_group in location_groups:

    # Sort this location group ascending by 'Y', get the first 2 rows on top
    minimas = location_group.sort_values(by='Y').head(2)

    # Append minimas to df2
    df2.append(minimas, ignore_index=True)
0
stovfl 6 mar. 2017 a las 16:13