【问题标题】:Create dynamic ranges and calculate mean创建动态范围并计算平均值
【发布时间】:2021-03-29 12:31:59
【问题描述】:

我想使用动态范围创建一个附加列,其中包含基于 A 列的平均值。

import numpy as np
import pandas as pd
    
test = {'A' : [100, 120, 70, 300, 190, 70, 300, 190, 70],
        'B' : [80, 50, 64, 288, 172, 64, 288, 172, 64],
        'C' : ['NO', 'NO', 'YES', 'NO', 'YES', 'YES', 'NO', 'YES', 'YES'],
        'D' : [0, 1, 0, 3, 2, 2, 3, 1, 4] }

df = pd.DataFrame(data=test)

     A      B      C      D    
0   100    80     NO      0
1   120    50     NO      1
2    70    64    YES      0
3   300   288     NO      3
4   190   172    YES      2
5    70    64    YES      2
6   300   288     NO      3
7   190   172    YES      1
8    70    64    YES      4

C 列中的项目为YES 时,将D 列中的值作为起始行索引,将当前行-1 的行索引作为最高行索引,从A 列中的动态范围中获取平均值.

下面是我想要达到的结果。

     A      B      C      D    Dyn_Ave    
0   100    80     NO      0     NaN
1   120    50     NO      1     NaN
2    70    64    YES      0     110
3   300   288     NO      3     NaN
4   190   172    YES      2     185
5    70    64    YES      2     187
6   300   288     NO      3     NaN
7   190   172    YES      1     175
8    70    64    YES      4     188

我在创建列时尝试使用 np.where 方法,尽管我遇到了以下错误 - TypeError: Cannot index by location index with a non-integer key

df['Dyn_Ave'] = np.where(df['C'] == 'YES', df['A'].iloc[df['D']:df.loc['C'][-1]].mean(), np.nan)

【问题讨论】:

    标签: python pandas dataframe mean


    【解决方案1】:

    我们试试吧:

    s = df['A'].cumsum().shift(fill_value=0)
    
    df['Dyn_Ave'] = np.where(df['C'] == 'YES', 
                             (s - s.reindex(df['D']).values) / (np.arange(len(df)) - df['D']),           
                             np.nan)
    

    输出:

         A    B    C  D     Dyn_Ave
    0  100   80   NO  0         NaN
    1  120   50   NO  1         NaN
    2   70   64  YES  0  110.000000
    3  300  288   NO  3         NaN
    4  190  172  YES  2  185.000000
    5   70   64  YES  2  186.666667
    6  300  288   NO  3         NaN
    7  190  172  YES  1  175.000000
    8   70   64  YES  4  187.500000
    

    解释:让我们先暂时忘记C=='YES',关注动态平均值。从行df['D'] 到行j-1 的平均值可以看作是

    (cumsum[j-1] - cumsum[df['D']-1])/(j-df['D'])
    

    或:

    (cumsum.shift()[j] - cumsum.shift()[df['D']) / (j-df['D'])
    

    这就是为什么我们首先计算 cumsum,然后将其移位:

    s = df['A'].cumsum().shift(fill_value=0)
    

    为了获得df['D'] 处的 cumsum,我们使用 reindex 并传递底层 numpy 数组进行减法:

    (s - s.reindex(df['D']).values)
    

    行数可以很容易地看成:

    (np.arange(len(df)) - df['D'])
    

    最后一部分只是填写C=='YES',就像你试图完成的那样。

    【讨论】:

    • 您对每个组件的详细解释极大地帮助了我理解这些概念并完美地解决了我的问题。谢谢你
    【解决方案2】:

    你可以使用df.apply,但是会比np.where慢。

    df['Dyn_Ave'] = df[df.C == 'YES'].apply(lambda x: np.round(df.A.loc[x.D:x.name-1].mean()) ,axis=1)
    df
    

    输出:

         A    B    C  D  Dyn_Ave
    0  100   80   NO  0      NaN
    1  120   50   NO  1      NaN
    2   70   64  YES  0    110.0
    3  300  288   NO  3      NaN
    4  190  172  YES  2    185.0
    5   70   64  YES  2    187.0
    6  300  288   NO  3      NaN
    7  190  172  YES  1    175.0
    8   70   64  YES  4    188.0
    

    【讨论】:

    • 感谢您。但是,我试图在我的项目中保持尽可能快的速度,这就是为什么np.where 版本更适合
    • 没问题,@QuangHoang 的 answer 不仅(可能要快得多)速度更快,而且对您的问题结构的洞察力非常有趣。
    猜你喜欢
    • 2019-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 2021-12-31
    • 1970-01-01
    相关资源
    最近更新 更多