【问题标题】:How to get the slope for every n days per group with respect to a conditioned row using Pandas?对于使用 Pandas 的条件行,如何获得每组每 n 天的斜率?
【发布时间】:2022-10-26 01:12:35
【问题描述】:

我有以下数据框(示例):

import pandas as pd

n = 3

data = [['A', '2022-09-01', False, 2, -3], ['A', '2022-09-02', False, 1, -2], ['A', '2022-09-03', False, 1, -1], ['A', '2022-09-04', True, 3, 0], 
        ['A', '2022-09-05', False, 3, 1], ['A', '2022-09-06', False, 2, 2], ['A', '2022-09-07', False, 1, 3], ['A', '2022-09-07', False, 2, 3], 
        ['A', '2022-09-08', False, 4, 4], ['A', '2022-09-09', False, 2, 5],
        ['B', '2022-09-01', False, 2, -4], ['B', '2022-09-02', False, 2, -3], ['B', '2022-09-03', False, 4, -2], ['B', '2022-09-04', False, 2, -1], 
        ['B', '2022-09-05', True, 2, 0], ['B', '2022-09-06', False, 2, 1], ['B', '2022-09-07', False, 1, 2], ['B', '2022-09-08', False, 3, 3], 
        ['B', '2022-09-09', False, 3, 4], ['B', '2022-09-10', False, 2, 5]]
df = pd.DataFrame(data = data, columns = ['group', 'date', 'indicator', 'value', 'diff_days'])

   group        date  indicator  value  diff_days
0      A  2022-09-01      False      2         -3
1      A  2022-09-02      False      1         -2
2      A  2022-09-03      False      1         -1
3      A  2022-09-04       True      3          0
4      A  2022-09-05      False      3          1
5      A  2022-09-06      False      2          2
6      A  2022-09-07      False      1          3
7      A  2022-09-07      False      2          3
8      A  2022-09-08      False      4          4
9      A  2022-09-09      False      2          5
10     B  2022-09-01      False      2         -4
11     B  2022-09-02      False      2         -3
12     B  2022-09-03      False      4         -2
13     B  2022-09-04      False      2         -1
14     B  2022-09-05       True      2          0
15     B  2022-09-06      False      2          1
16     B  2022-09-07      False      1          2
17     B  2022-09-08      False      3          3
18     B  2022-09-09      False      3          4
19     B  2022-09-10      False      2          5

我想计算斜率n相对于条件行的每组行(指标 == True)。所以这意味着它应该返回一个列“斜率”,该列在该条件行之前和之后的斜率应该为 0。除此之外,我想返回一个名为“id”的列,它实际上是一个组 id表示该条件行之前(负)或之后(正)的斜率的值。这是所需的输出:

data = [['A', '2022-09-01', False, 2, -3, -1, -0.5], ['A', '2022-09-02', False, 1, -2, -1, -0.5], ['A', '2022-09-03', False, 1, -1, -1, -0.5], ['A', '2022-09-04', True, 3, 0, 0, 0], 
        ['A', '2022-09-05', False, 3, 1, 1, -1], ['A', '2022-09-06', False, 2, 2, 1, -1], ['A', '2022-09-07', False, 1, 3, 1, -1], ['A', '2022-09-07', False, 2, 3, 2, 0], 
        ['A', '2022-09-08', False, 4, 4, 2, 0], ['A', '2022-09-09', False, 2, 5, 2, 0],
        ['B', '2022-09-01', False, 2, -4, -2], ['B', '2022-09-02', False, 2, -3, -1, 0], ['B', '2022-09-03', False, 4, -2, -1, 0], ['B', '2022-09-04', False, 2, -1, -1, 0], 
        ['B', '2022-09-05', True, 2, 0, 0, 0], ['B', '2022-09-06', False, 2, 1, 1, 0.5], ['B', '2022-09-07', False, 1, 2, 1, 0.5], ['B', '2022-09-08', False, 3, 3, 1, 0.5], 
        ['B', '2022-09-09', False, 3, 4, 2, -1], ['B', '2022-09-10', False, 2, 5, 2, -1]]
df_desired = pd.DataFrame(data = data, columns = ['group', 'date', 'indicator', 'value', 'diff_days', 'id', 'slope'])

   group        date  indicator  value  diff_days  id  slope
0      A  2022-09-01      False      2         -3  -1   -0.5
1      A  2022-09-02      False      1         -2  -1   -0.5
2      A  2022-09-03      False      1         -1  -1   -0.5
3      A  2022-09-04       True      3          0   0    0.0
4      A  2022-09-05      False      3          1   1   -1.0
5      A  2022-09-06      False      2          2   1   -1.0
6      A  2022-09-07      False      1          3   1   -1.0
7      A  2022-09-07      False      2          3   2    0.0
8      A  2022-09-08      False      4          4   2    0.0
9      A  2022-09-09      False      2          5   2    0.0
10     B  2022-09-01      False      2         -4  -2    NaN
11     B  2022-09-02      False      2         -3  -1    0.0
12     B  2022-09-03      False      4         -2  -1    0.0
13     B  2022-09-04      False      2         -1  -1    0.0
14     B  2022-09-05       True      2          0   0    0.0
15     B  2022-09-06      False      2          1   1    0.5
16     B  2022-09-07      False      1          2   1    0.5
17     B  2022-09-08      False      3          3   1    0.5
18     B  2022-09-09      False      3          4   2   -1.0
19     B  2022-09-10      False      2          5   2   -1.0

以下是A组的一些解释:

  • 第 0,1 行和第 2 行是斜率 (x=[-3,-2,-1],y=[2,1, 1])=-0.5
  • 第 4,5 行和第 6 行是 (id=1) 条件行(第 3 行)之后的第一个值,斜率 (x=[1,2,3],y=[3,2,1])= -1
  • 第 7,8 和 9 行是在 (id=2) 条件行(第 3 行)之后的第二个值,斜率 (x=[3,4,5],y=[2,4,2])= 0

所以我想知道是否有人知道是否可以使用Pandas 计算每 n 天相对于条件行的斜率?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    这可以完成工作,但我不知道是否有任何更好的熊猫做事方式。

    groups=['A','B']
    indexs=[]
    for i in groups:
        indexs.append(df.loc[(df['group'] == i )& (df['indicator']== True)].index[0])
    id2=[]
    id3=[]
    for i in groups:
        id2=df.loc[(df['group'] == i )].index[:]-indexs[groups.index(i)]
        for j in id2:
            if j < 0:
             id3.append(math.floor(j/n))
            elif j>=0:
             id3.append(math.ceil(j/n))
    
    df['id']=id3
    
    grady=[]
    gradx=[]
    SlopeList=[]
    for i in groups:
        idum=[]
        for number in df['id'].loc[(df['group']==i)]:
            #unique values in list.
            if number not in idum:
                idum.append(number)
        for k in idum:
            grady=df['value'].loc[( df['group'] == i ) &(df['id'] == k ) ]
            gradx=df['diff_days'].loc[ (df['group'] == i )&(df['id'] == k ) ]
            
            Xm=slope(grady.tolist(),gradx.tolist()) #average slope
            for m in range(0,len(gradx)): #create a suitabily sized list with the average slope value.
                SlopeList.append(Xm)
            
    df['slope']=SlopeList   
               
    

    p.s.我没有对此代码进行任何单元测试,所以请在使用它之前检查。

    【讨论】:

    • 非常感谢您的回答!这正是我想要的。与Pandas 一起使用将是完美的。
    【解决方案2】:

    主要思想可以是:

    • 为每个组创建单独的索引;
    • 标记行的零;
    • 将索引转换为除以 n 的楼层;
    • 将 positiv 索引向前移动一步并将它们递增 1 以将它们与零点区分开来

    之后,我们可以使用获得的索引作为附加的 grouper 来计算斜率:

    # create individual indexing for eash group
    id = df.groupby('group')['indicator'].cumcount()
    
    # find positions of the condition rows in the group indexes
    offset = id.where(df.indicator).groupby(df.group).first()
    
    # shift the groups indexes so that condition rows are indexed by zero
    id = id.groupby(df.group).transform(lambda x: x - offset[x.name])
    
    # transform the group indexes to their floor division by n
    # shift those which ware positive by one position forward
    # and increment their values by 1
    n = 3 
    id = (id//n).mask(id>0,(id//n).shift().add(1))
    
    # assign obtained id to a new column
    df['id'] = id
    
    # calculate slopes for each `group,id` pair:
    grouped_slopes =  df.groupby(['group','id']).apply(lambda g: slope(g.diff_days, g.value))
    
    # add slopes to the data
    df = df.join(grouped_slopes , on=['group','id'])
    

    至于斜率计算,我们可以使用准备好的公式或自己制作。但无论如何,我们也应该区分组中只有一项的情况,并为零点(条件行)返回 0,为单个元素尾部返回 nan

    from typing import Literal
    
    def slope(x, y, engine: Literal['numpy', 'scipy']='numpy'):
        from numpy import polyfit
        from scipy.stats import linregress
    
        match engine:
            case 'numpy':
                func = lambda x, y: polyfit(x, y, 1)[0]
            case 'scipy':
                func = lambda x, y: linregress(x, y).slope
            case other:
                raise ValueError(f'Wrong {engine=}')
    
        if len(x) > 1:
            return func(x, y)
        if len(x) == 1 and x.iloc[0] == 0:
            return 0
        return float('nan')
    

    【讨论】:

      猜你喜欢
      • 2022-10-13
      • 2021-06-24
      • 2016-08-12
      • 1970-01-01
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      相关资源
      最近更新 更多