【问题标题】:How to get rows in pandas data frame, with maximal values in a column and keep the original index?如何获取熊猫数据框中的行,列中具有最大值并保留原始索引?
【发布时间】:2014-01-09 07:32:43
【问题描述】:

我有一个熊猫数据框。在第一列中,它可以多次具有相同的值(换句话说,第一列中的值不是唯一的)。

每当我有几行在第一列中包含相同的值时,我只想在第三列中保留那些具有最大值的行。我几乎找到了解决方案:

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

【问题讨论】:

    标签: python group-by indexing pandas argmax


    【解决方案1】:

    试试这个:

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

    【讨论】:

    • 我不能强迫自己投票支持 pep8 违规代码;尚未产生 OP 所需的结果,您可能需要添加 .reset_index(level=0, drop=True)
    【解决方案2】:

    在调用df.groupby(...).apply(foo) 时,foo 返回的对象类型会影响结果融合在一起的方式。

    如果返回一个 Series,则 Series 的索引成为最终结果的列,而 groupby 键成为索引(有点绕口令)。

    如果改为返回一个DataFrame,最终结果使用DataFrame的索引作为索引值,DataFrame的列作为列(非常明智)。

    因此,您可以通过将 Series 转换为 DataFrame 来安排所需的输出类型。

    在 Pandas 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
    

    在 Pandas 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 对于小型 DataFrame 来说比我的要快。 sort 将时间复杂度从 O(n) 提升到 O(n log n),因此当应用于更大的 DataFrame 时,它​​比上面显示的 to_frame 解决方案要慢。

    这是我对其进行基准测试的方法:

    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 是我尝试过的另一种可能性。)

    【讨论】:

    • 它将从pandas 0.13 开始工作,在旧版本系列中没有to_frame 功能。
    • @alko:感谢您的提醒。我添加了适用于 0.12 或更早版本的等效代码。
    猜你喜欢
    • 1970-01-01
    • 2022-07-25
    • 2018-08-04
    • 2022-01-01
    • 2013-02-03
    • 2019-11-12
    • 1970-01-01
    • 2016-08-06
    • 2019-05-13
    相关资源
    最近更新 更多