Pandas DataFrame窗口函数

时间:2016-01-12 04:59:45

标签: python pandas

我试图操纵我的数据框,类似于使用SQL窗口函数的方式。请考虑以下示例集:

import pandas as pd

df = pd.DataFrame({'fruit' : ['apple', 'apple', 'apple', 'orange', 'orange', 'orange', 'grape', 'grape', 'grape'],
               'test' : [1, 2, 1, 1, 2, 1, 1, 2, 1],
               'analysis' : ['full', 'full', 'partial', 'full', 'full', 'partial', 'full', 'full', 'partial'],
               'first_pass' : [12.1, 7.1, 14.3, 19.1, 17.1, 23.4, 23.1, 17.2, 19.1],
               'second_pass' : [20.1, 12.0, 13.1, 20.1, 18.5, 22.7, 14.1, 17.1, 19.4],
               'units' : ['g', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g'],
               'order' : [2, 1, 3, 2, 1, 3, 3, 2, 1]})
+--------+------+----------+------------+-------------+-------+-------+
| fruit  | test | analysis | first_pass | second_pass | order | units |
+--------+------+----------+------------+-------------+-------+-------+
| apple  |    1 | full     | 12.1       | 20.1        |     2 | g     |
| apple  |    2 | full     | 7.1        | 12.0        |     1 | g     |
| apple  |    1 | partial  | 14.3       | 13.1        |     3 | g     |
| orange |    1 | full     | 19.1       | 20.1        |     2 | g     |
| orange |    2 | full     | 17.1       | 18.5        |     1 | g     |
| orange |    1 | partial  | 23.4       | 22.7        |     3 | g     |
| grape  |    1 | full     | 23.1       | 14.1        |     3 | g     |
| grape  |    2 | full     | 17.2       | 17.1        |     2 | g     |
| grape  |    1 | partial  | 19.1       | 19.4        |     1 | g     |
+--------+------+----------+------------+-------------+-------+-------+

我想添加几列:

  • 一个布尔列,用于指示该测试和分析的second_pass值是否是所有水果类型中最高的。
  • 另一列,列出了每个测试和分析组合中哪些水果具有最高的second_pass值。

使用这个逻辑,我想得到下表:

+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+
| fruit  | test | analysis | first_pass | second_pass | order | units | highest |   highest_fruits    |
+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+
| apple  |    1 | full     | 12.1       | 20.1        |     2 | g     | true    | ["apple", "orange"] |
| apple  |    2 | full     | 7.1        | 12.0        |     1 | g     | false   | ["orange"]          |
| apple  |    1 | partial  | 14.3       | 13.1        |     3 | g     | false   | ["orange"]          |
| orange |    1 | full     | 19.1       | 20.1        |     2 | g     | true    | ["apple", "orange"] |
| orange |    2 | full     | 17.1       | 18.5        |     1 | g     | true    | ["orange"]          |
| orange |    1 | partial  | 23.4       | 22.7        |     3 | g     | true    | ["orange"]          |
| grape  |    1 | full     | 23.1       | 22.1        |     3 | g     | false   | ["orange"]          |
| grape  |    2 | full     | 17.2       | 17.1        |     2 | g     | false   | ["orange"]          |
| grape  |    1 | partial  | 19.1       | 19.4        |     1 | g     | false   | ["orange"]          |
+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+

我是大熊猫的新手,所以我确定我错过了一些非常简单的事情。

2 个答案:

答案 0 :(得分:1)

您可以在boolean等于second_pass group的情况下返回max个值,因为idxmax仅返回max的第一个匹配项:

df['highest'] = df.groupby(['test', 'analysis'])['second_pass'].transform(lambda x: x == np.amax(x)).astype(bool)

然后使用np.where捕获fruit group的所有max值,并将merge结果记录到DataFrame中这样:

highest_fruits = df.groupby(['test', 'analysis']).apply(lambda x: [f for f in np.where(x.second_pass == np.amax(x.second_pass), x.fruit.tolist(), '').tolist() if f!='']).reset_index()
df =df.merge(highest_fruits, on=['test', 'analysis'], how='left').rename(columns={0: 'highest_fruit'})

最后,为了您的跟进:

first_pass = df.groupby(['test', 'analysis']).apply(lambda x: {fruit: x.loc[x.fruit==fruit, 'first_pass'] for fruit in x.highest_fruit.iloc[0]}).reset_index()
df =df.merge(first_pass, on=['test', 'analysis'], how='left').rename(columns={0: 'first_pass_highest_fruit'})

得到:

  analysis  first_pass   fruit  order  second_pass  test units highest  \
0     full        12.1   apple      2         20.1     1     g    True   
1     full         7.1   apple      1         12.0     2     g   False   
2  partial        14.3   apple      3         13.1     1     g   False   
3     full        19.1  orange      2         20.1     1     g    True   
4     full        17.1  orange      1         18.5     2     g    True   
5  partial        23.4  orange      3         22.7     1     g    True   
6     full        23.1   grape      3         14.1     1     g   False   
7     full        17.2   grape      2         17.1     2     g   False   
8  partial        19.1   grape      1         19.4     1     g   False   

     highest_fruit             first_pass_highest_fruit  
0  [apple, orange]  {'orange': [19.1], 'apple': [12.1]}  
1         [orange]                   {'orange': [17.1]}  
2         [orange]                   {'orange': [23.4]}  
3  [apple, orange]  {'orange': [19.1], 'apple': [12.1]}  
4         [orange]                   {'orange': [17.1]}  
5         [orange]                   {'orange': [23.4]}  
6  [apple, orange]  {'orange': [19.1], 'apple': [12.1]}  
7         [orange]                   {'orange': [17.1]}  
8         [orange]                   {'orange': [23.4]} 

答案 1 :(得分:0)

我会假设你的意思

'test' : [1, 2, 3, 1, 2, 3, 1, 2, 3]

要生成第一列,您可以按测试编号分组,并将每个第二次通过分数与最高分数进行比较:

df['highest'] = df['second_pass'] == df.groupby('test')['second_pass'].transform('max')

对于第二部分,我没有一个干净的解决方案,但这里有点难看,首先将索引设置为水果:

df = df.set_index('fruit')

接下来,找出每个测试中哪些行的“最高”设置为True,并返回这些行所拥有的索引列表(这些是水果的名称):

test1_max_fruits = df[df['test']==1&df['highest']].index.values.tolist()
test2_max_fruits = df[df['test']==2&df['highest']].index.values.tolist()
test3_max_fruits = df[df['test']==3&df['highest']].index.values.tolist()

定义一个函数来查看测试编号,然后返回我们刚生成的相应max_fruits:

def max_fruits(test_num):

    if test_num == 1:
    return test1_max_fruits

    if test_num == 2:
    return test2_max_fruits

    if test_num == 3:
    return test3_max_fruits

创建一个列并在“测试”列上应用此功能:

df['highest_fruits'] = df['test'].apply(max_fruits)