¿Cómo obtener filas en el dataframe de pandas, con valores máximos en una columna y mantener el índice original?

Tengo un dataframe de pandas. En la primera columna puede tener el mismo valor varias veces (en otras palabras, los valores de la primera columna no son únicos).

Cada vez que tengo varias filas que contienen el mismo valor en la primera columna, me gustaría dejar solo las que tienen un valor máximo en la tercera columna. Casi encuentro una solución:

import pandas ls = [] ls.append({'c1':'a', 'c2':'a', 'c3':1}) ls.append({'c1':'a', 'c2':'c', 'c3':3}) ls.append({'c1':'a', 'c2':'b', 'c3':2}) ls.append({'c1':'b', 'c2':'b', 'c3':10}) ls.append({'c1':'b', 'c2':'c', 'c3':12}) ls.append({'c1':'b', 'c2':'a', 'c3':7}) df = pandas.DataFrame(ls, columns=['c1','c2','c3']) print df print '--------------------' print df.groupby('c1').apply(lambda df:df.irow(df['c3'].argmax())) 

Como resultado obtengo:

  c1 c2 c3 0 aa 1 1 ac 3 2 ab 2 3 bb 10 4 bc 12 5 ba 7 -------------------- c1 c2 c3 c1 aac 3 bbc 12 

Mi problema es que no quiero tener c1 como índice. Lo que quiero tener es lo siguiente:

  c1 c2 c3 1 ac 3 4 bc 12 

Al llamar a df.groupby(...).apply(foo) , el tipo de objeto devuelto por foo afecta a la forma en que los resultados se combinan.

Si devuelves una serie, el índice de la serie se convierte en columnas del resultado final, y la clave groupby se convierte en el índice (un poco de un error).

Si, en cambio, devuelve un DataFrame, el resultado final utiliza el índice del DataFrame como valores de índice y las columnas del DataFrame como columnas (muy sensible).

Por lo tanto, puede organizar el tipo de salida que desea al convertir su Serie en un DataFrame.

Con Pandas 0.13 puede usar el to_frame().T :

 def maxrow(x, col): return x.loc[x[col].argmax()].to_frame().T result = df.groupby('c1').apply(maxrow, 'c3') result = result.reset_index(level=0, drop=True) print(result) 

rendimientos

  c1 c2 c3 1 ac 3 4 bc 12 

En Pandas 0.12 o mayor, el equivalente sería:

 def maxrow(x, col): ser = x.loc[x[col].idxmax()] df = pd.DataFrame({ser.name: ser}).T return df 

Por cierto, la solución inteligente y elegante de behzad.nouri es más rápida que la mía para los pequeños DataFrames. Sin embargo, la sort levanta la complejidad del tiempo de O(n) a O(n log n) , por lo que se vuelve más lenta que la solución to_frame que se muestra arriba cuando se aplica a DataFrames más grandes.

Aquí es cómo lo comparé:

 import pandas as pd import numpy as np import timeit def reset_df_first(df): df2 = df.reset_index() result = df2.groupby('c1').apply(lambda x: x.loc[x['c3'].idxmax()]) result.set_index(['index'], inplace=True) return result def maxrow(x, col): result = x.loc[x[col].argmax()].to_frame().T return result def using_to_frame(df): result = df.groupby('c1').apply(maxrow, 'c3') result.reset_index(level=0, drop=True, inplace=True) return result def using_sort(df): return df.sort('c3').groupby('c1', as_index=False).tail(1) for N in (100, 1000, 2000): df = pd.DataFrame({'c1': {0: 'a', 1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'b'}, 'c2': {0: 'a', 1: 'c', 2: 'b', 3: 'b', 4: 'c', 5: 'a'}, 'c3': {0: 1, 1: 3, 2: 2, 3: 10, 4: 12, 5: 7}}) df = pd.concat([df]*N) df.reset_index(inplace=True, drop=True) timing = dict() for func in (reset_df_first, using_to_frame, using_sort): timing[func] = timeit.timeit('m.{}(m.df)'.format(func.__name__), 'import __main__ as m ', number=10) print('For N = {}'.format(N)) for func in sorted(timing, key=timing.get): print('{:<20}: {:<0.3g}'.format(func.__name__, timing[func])) print 

rendimientos

 For N = 100 using_sort : 0.018 using_to_frame : 0.0265 reset_df_first : 0.0303 For N = 1000 using_to_frame : 0.0358 \ using_sort : 0.036 / this is roughly where the two methods cross over in terms of performance reset_df_first : 0.0432 For N = 2000 using_to_frame : 0.0457 reset_df_first : 0.0523 using_sort : 0.0569 

( reset_df_first fue otra posibilidad que probé).

prueba esto:

 df.sort('c3').groupby('c1', as_index=False).tail(1)