Pandas DataFrame 窗口函数

3

我将尝试使用类似于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 | highest | highest_fruit_per_ta |
+--------+------+----------+------------+-------------+-------+-------+---------+----------------------+
| 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   | grape                |
| orange |    1 | full     | 19.1       | 20.1        |     2 | g     | true    | apple, orange        |
| orange |    2 | full     | 17.1       | 18.5        |     1 | g     | false   | orange               |
| orange |    1 | partial  | 23.4       | 22.7        |     3 | g     | true    | orange               |
| grape  |    1 | full     | 23.1       | 14.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     | true    | grape                |
+--------+------+----------+------------+-------------+-------+-------+---------+----------------------+
+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+
| 水果   | 测试 | 分析     | 第一遍    | 第二遍      | 排序  | 单位  | 最高分  | 最高水果            |
+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+
| 苹果   |    1 | 全部检测 | 12.1       | 20.1        |     2 | g     | true    | ["苹果", "橙子"]    |
| 苹果   |    2 | 全部检测 | 7.1        | 12.0        |     1 | g     | false   | ["橙子"]             |
| 苹果   |    1 | 部分检测 | 14.3       | 13.1        |     3 | g     | false   | ["橙子"]             |
| 橙子   |    1 | 全部检测 | 19.1       | 20.1        |     2 | g     | true    | ["苹果", "橙子"]    |
| 橙子   |    2 | 全部检测 | 17.1       | 18.5        |     1 | g     | true    | ["橙子"]             |
| 橙子   |    1 | 部分检测 | 23.4       | 22.7        |     3 | g     | true    | ["橙子"]             |
| 葡萄   |    1 | 全部检测 | 23.1       | 22.1        |     3 | g     | false   | ["橙子"]             |
| 葡萄   |    2 | 全部检测 | 17.2       | 17.1        |     2 | g     | false   | ["橙子"]             |
| 葡萄   |    1 | 部分检测 | 19.1       | 19.4        |     1 | g     | false   | ["橙子"]             |
+--------+------+----------+------------+-------------+-------+-------+---------+---------------------+

我刚接触pandas,所以肯定有些简单的东西我不知道。


很抱歉,我现在手头忙不过来,帮不了更多的忙。但是,就我目前所知,g = df.groupby(['test','analysis'])['second_pass'].agg('idxmax') 可以给你分组后 testanalysissecond_pass 最大值所在行的索引。不过我现在不确定它是否能检测到并列的情况。 - WGS
2个回答

1

second_pass等于group max时,您可以返回boolean值,因为idxmax仅返回max的第一个出现:

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

然后使用 np.where 捕获所有具有 group maxfruit 值,并将结果与您的 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]} 

作为后续问题,是否有一种方法可以根据最高水果将first_pass值提取到新列中。例如,葡萄的完整分析和测试1(idx:6)得出[苹果,橙子]作为最高水果。如果我想使用该测试/分析组合将苹果和橙子的first_pass值提取到新列中(结果为[12.1,19.1]),是否有pandas的方法? - Jammy
这是一个非常强大的模块。感谢 Stefan! - Jammy

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

接下来,找出每个测试中“highest”设置为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()

定义一个函数来查看测试号码,然后返回我们刚生成的相应最大水果量:
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

创建一个列并将此函数应用于您的“test”列:
df['highest_fruits'] = df['test'].apply(max_fruits)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接