代码之家  ›  专栏  ›  技术社区  ›  Binyamin Even

获取前一个较小值的索引

  •  3
  • Binyamin Even  · 技术社区  · 6 年前

    我有一个这样的数据框架:

    index value
    0     1
    1     1
    2     2
    3     3
    4     2
    5     1
    6     1
    

    我希望每个值返回前一个较小值的索引,以及前一个“1”值的索引。如果值为1,我不需要它们(两个值都可以 -1 或某事)。

    所以我想要的是:

    index value  previous_smaller_index  previous_1_index
    0     1            -1                      -1
    1     1            -1                      -1
    2     2             1                       1
    3     3             2                       1
    4     2             1                       1
    5     1            -1                      -1
    6     1            -1                      -1
    

    我试过使用滚动函数、累积函数等,但没能搞清楚。 任何帮助都将不胜感激!

    编辑: SPGHTTCD已经为“前1个”问题提供了一个很好的解决方案。我在找一只漂亮的熊猫,一条线,来解决“以前的小问题”。(当然,尽管对这两个问题都欢迎有更好、更有效的解决方案)

    4 回复  |  直到 6 年前
        1
  •  2
  •   SpghttCd    6 年前

    你可以试试

    df = pd.DataFrame({'value': [1,  1,  2,  3,  2,  1,  1, 2, 3, 4, 5]})
    
    df['prev_smaller_idx'] = df.apply(lambda x: df.index[:x.name][(x.value>df.value)[:x.name]].max(), axis=1)
    
    df['prev_1_idx'] = pd.Series(df.index.where(df.value==1)).shift()[df.value!=1].ffill()
    
    #    value  prev_smaller_idx  prev_1_idx
    #0       1               NaN         NaN
    #1       1               NaN         NaN
    #2       2               1.0         1.0
    #3       3               2.0         1.0
    #4       2               1.0         1.0
    #5       1               NaN         NaN
    #6       1               NaN         NaN
    #7       2               6.0         6.0
    #8       3               7.0         6.0
    #9       4               8.0         6.0
    #10      5               9.0         6.0
    
        2
  •  6
  •   cs95 abhishek58g    6 年前
    • “上一个较小的索引”可以通过与 argmax .

    • “上一个”索引“可以用 groupby idxmax 在一 cumsum MED掩模。

    m = df.value.eq(1)
    u = np.triu(df.value.values < df.value[:,None]).argmax(1)
    v = m.cumsum()
    
    df['previous_smaller_index'] = np.where(m, -1, len(df) - u - 1)
    df['previous_1_index'] = v.groupby(v).transform('idxmax').mask(m, -1)
    

    df
       index  value  previous_smaller_index  previous_1_index
    0      0      1                      -1                -1
    1      1      1                      -1                -1
    2      2      2                       1                 1
    3      3      3                       2                 1
    4      4      2                       1                 1
    5      5      1                      -1                -1
    6      6      1                      -1                -1
    

    如果您希望这些行作为一个行,您可以将一些行合并为一个行:

    m = df.value.eq(1)
    df['previous_smaller_index'] = np.where(
        m, -1, len(df) - np.triu(df.value.values < df.value[:,None]).argmax(1) - 1
    )[::-1]
    
    # Optimizing @SpghttCd's `previous_1_index` calculation a bit
    df['previous_1_index'] = (np.where(
        m, -1, df.index.where(m).to_series(index=df.index).ffill(downcast='infer'))
    )
    
    df
    
       index  value  previous_1_index  previous_smaller_index
    0      0      1                -1                      -1
    1      1      1                -1                      -1
    2      2      2                 1                       1
    3      3      3                 1                       2
    4      4      2                 1                       1
    5      5      1                -1                      -1
    6      6      1                -1                      -1
    

    总体性能

    设置和性能基准测试是使用 perfplot . 代码可以在 this gist .

    enter image description here

    计时是相对的(Y比例是对数)。


    previous_1_index 性能

    Gist with relevant code.

    enter image description here

        3
  •  1
  •   busybear Danny Boy150    6 年前

    此功能应工作:

    def func(values, null_val=-1):
        # Initialize with arbitrary value
        prev_small = values * -2
        prev_1 = values * -2
    
        # Loop through values and find previous values
        for n, x in enumerate(values):
            prev_vals = values.iloc[:n]
            prev_small[n] = prev_vals[prev_vals < x].index[-1] if (prev_vals < x).any() else null_val
            prev_1[n] = prev_vals[prev_vals == 1].index[-1] if x != 1 and (prev_vals == 1).any() else null_val
    
        return prev_small, prev_1
    
    df = pd.DataFrame({'value': [1,  1,  2,  3,  2,  1,  1,]})
    df['previous_small'], df['previous_1'] = func(df['value'])
    

    输出:

       value  previous_small  previous_1
    0      1              -1          -1
    1      1              -1          -1
    2      2               1           1
    3      3               2           1
    4      2               1           1
    5      1              -1          -1
    6      1              -1          -1
    
        4
  •  1
  •   BENY    6 年前

    这是要做的 previous_smaller_index

    l=list(zip(df['index'],df.value))[::-1]
    
    t=[]
    n=len(l)
    for x in l:
        if x[1]==1:
            t.append(-1)
        else:
            t.append(next(y for y in l[n-x[0]:] if y[1]<x[1])[0])
    df['previous_smaller_index']=t[::-1]
    df
    Out[71]: 
       index  value  previous_smaller_index
    0      0      1                      -1
    1      1      1                      -1
    2      2      2                       1
    3      3      3                       2
    4      4      2                       1
    5      5      1                      -1
    6      6      1                      -1
    

    获取前一个1

    df['index'].where(df.value==1).ffill().where(df.value!=1,-1)
    Out[77]: 
    0   -1.0
    1   -1.0
    2    1.0
    3    1.0
    4    1.0
    5   -1.0
    6   -1.0
    Name: index, dtype: float64
    

    把它分配回去

    df['previous_1_index']=df['index'].where(df.value==1).ffill().where(df.value!=1,-1)
    
    
    
    df
    Out[79]: 
       index  value  previous_smaller_index  previous_1_index
    0      0      1                      -1              -1.0
    1      1      1                      -1              -1.0
    2      2      2                       1               1.0
    3      3      3                       2               1.0
    4      4      2                       1               1.0
    5      5      1                      -1              -1.0
    6      6      1                      -1              -1.0