2013-12-20 8 views
3

У меня есть рамка данных панд. В первом столбце он может иметь одно и то же значение несколько раз (другими словами, значения в первом столбце не уникальны).Как получить строки в кадре данных pandas с максимальными значениями в столбце и сохранить исходный индекс?

Всякий раз, когда у меня есть несколько строк, которые содержат одно и то же значение в первом столбце, я хотел бы оставить только те, которые имеют максимальное значение в третьем столбце. Я почти нашел решение:

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())) 

Как результат я получаю:

c1 c2 c3 
0 a a 1 
1 a c 3 
2 a b 2 
3 b b 10 
4 b c 12 
5 b a 7 
-------------------- 
    c1 c2 c3 
c1   
a a c 3 
b b c 12 

Моя проблема заключается в том, что я не хочу иметь c1 как индекс. То, что я хочу иметь следующий:

c1 c2 c3 
1 a c 3 
4 b c 12 

ответ

4

При вызове df.groupby(...).apply(foo), тип объекта, возвращаемый foo влияет на то, что результаты объединялись вместе.

Если вы вернете Серию, индекс Серии станет столбцом конечного результата, а ключ groupby станет индексом (немного ума-твистера).

Если вместо этого вы возвращаете DataFrame, конечный результат использует индекс DataFrame в качестве значений индекса, а столбцы DataFrame - как столбцы (очень разумные).

Таким образом, вы можете организовать желаемый тип вывода, преобразуя свою серию в DataFrame.

С Пандой 0.13 вы можете использовать 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) 

дающего

c1 c2 c3 
1 a c 3 
4 b c 12 

В Панде 0.12 или старше, эквивалент будет:

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

Кстати, behzad.nouri's clever and elegant solution быстрее, чем мой, для небольших DataFrames. sort повышает временную сложность от O(n) до O(n log n), поэтому он становится медленнее, чем решение to_frame, показанное выше при применении к более крупным DataFrames.

Вот как я протестированные его:

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 

дающий

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 была еще одна возможность, я попробовал)

+0

Он будет работать с [pandas 0.13] (https://github.com/pydata/pandas/pull/5164), в более старых версиях Series не имело функциональности 'to_frame'. – alko

+0

@alko: Спасибо за подголовник. Я добавил эквивалентный код, который будет работать с версией 0.12 или старше. – unutbu

1

попробовать это:

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

Я не могу заставить себя голосовать до pep8 нарушающих код; но для получения результата, желаемого OP, вам, вероятно, нужно добавить '.reset_index (level = 0, drop = True)' – alko

 Смежные вопросы

  • Нет связанных вопросов^_^