2012-03-24 39 views
15

tengo la siguiente trama de datos:pandas - Obtener mayor valor reciente de una columna en particular indexado por otra columna (obtener el valor máximo de una columna en particular indexado por otra columna)

obj_id data_date value 
0 4  2011-11-01 59500  
1 2  2011-10-01 35200 
2 4  2010-07-31 24860 
3 1  2009-07-28 15860 
4 2  2008-10-15 200200 

quiero conseguir un subconjunto de esta información para que solo tenga la más reciente (la más grande 'data_date') 'value' para cada 'obj_id'.

He pirateado una solución, pero se siente sucia. Me preguntaba si alguien tiene una mejor manera. Estoy seguro de que me falta una forma fácil de hacerlo a través de los pandas.

Mi método es esencialmente para agrupar, clasificar, recuperar, y se recombinan como sigue:

row_arr = [] 
for grp, grp_df in df.groupby('obj_id'): 
    row_arr.append(dfg.sort('data_date', ascending = False)[:1].values[0]) 

df_new = DataFrame(row_arr, columns = ('obj_id', 'data_date', 'value')) 

Respuesta

2

El aggregate() method en objetos GroupBy se puede utilizar para crear una nueva trama de datos de un objeto GroupBy en un solo paso. (No estoy al tanto de una forma más limpia para extraer la primera fila/último de una trama de datos sin embargo.)

In [12]: df.groupby('obj_id').agg(lambda df: df.sort('data_date')[-1:].values[0]) 
Out[12]: 
     data_date value 
obj_id     
1  2009-07-28 15860 
2  2011-10-01 35200 
4  2011-11-01 59500 

También puede realizar la agregación de columnas individuales, en cuyo caso la función de agregado funciona en un objeto Series .

In [25]: df.groupby('obj_id')['value'].agg({'diff': lambda s: s.max() - s.min()}) 
Out[25]: 
      diff 
obj_id   
1   0 
2  165000 
4  34640 
4

me gusta la respuesta de crewbum, probablemente esto es más rápido (lo siento, no probado todavía, pero debo evitar clasificar todo):

df.groupby('obj_id').agg(lambda df: df.values[df['data_date'].values.argmax()]) 

se utiliza la función numpys "argmax" para encontrar la rowindex en el que aparece el máximo.

+0

he probado la velocidad de la trama de datos con 24735 filas, agrupados en 16 grupos (BTW: conjunto de datos de planethunter.org) y nos dieron 12,5 ms (argmax) vs 17,5 ms (Sort) como un resultado de% timeit. Así que ambas soluciones son bastante rápidas :-) y mi conjunto de datos parece ser demasiado pequeño ;-) – Maximilian

8

Si el número de "obj_id" es muy alto, querrá ordenar todo el marco de datos y luego soltar los duplicados para obtener el último elemento.

sorted = df.sort_index(by='data_date') 
result = sorted.drop_duplicates('obj_id', take_last=True).values 

Esto debería ser más rápido (lo siento, no probarlo), ya que no tiene que hacer una función agg costumbre, que es lento cuando hay un gran número de claves. Es posible que piense que es peor ordenar todo el marco de datos, pero en la práctica los tipos de pitones son rápidos y los loops nativos son lentos.

+0

Esto funcionó un encanto, el otro responde que todos tuvieron problemas para mí, y esto también fue mucho más rápido. –

+0

Esto fue más que un orden de magnitud más rápido para mí que la respuesta de pdifranc. Esta pregunta existe en varias formas en SO. Los señalaré a todos con esta respuesta. Solo una nota 'FutureWarning: la palabra clave take_last = True está en desuso, use keep = 'last' instead'. –

0

Creo que he encontrado una solución más adecuada basada en las de este hilo. Sin embargo, el mío usa la función aplicar de un marco de datos en lugar del agregado. También devuelve un nuevo dataframe con las mismas columnas que el original.

df = pd.DataFrame({ 
'CARD_NO': ['000', '001', '002', '002', '001', '111'], 
'DATE': ['2006-12-31 20:11:39','2006-12-27 20:11:53','2006-12-28 20:12:11','2006-12-28 20:12:13','2008-12-27 20:11:53','2006-12-30 20:11:39']}) 

print df 
df.groupby('CARD_NO').apply(lambda df:df['DATE'].values[df['DATE'].values.argmax()]) 

original

CARD_NO     DATE 
0  000 2006-12-31 20:11:39 
1  001 2006-12-27 20:11:53 
2  002 2006-12-28 20:12:11 
3  002 2006-12-28 20:12:13 
4  001 2008-12-27 20:11:53 
5  111 2006-12-30 20:11:39 

trama de datos devuelto:

CARD_NO 
000  2006-12-31 20:11:39 
001  2008-12-27 20:11:53 
002  2006-12-28 20:12:13 
111  2006-12-30 20:11:39 
12

Ésta es otra posible solución. Creo que es el más rápido. respuesta

df.loc[df.groupby('obj_id').data_date.idxmax(),:] 
+2

Este es un buen enfoque que me ha funcionado en este y otros contextos. – alexbw

+0

Una buena solución general, pero bastante lenta en comparación con algunos de los otros métodos – josh

0

Actualización de thetainted1 ya que algunas de las funciones tienen advertencias futuras ahora como tommy.carstensen señalado.Esto es lo que funcionó para mí:

sorted = df.sort_values(by='data_date') 

result = sorted.drop_duplicates('obj_id', keep='last') 
Cuestiones relacionadas