pandas 对>=10的前后行计数

xuo3flqw  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(144)

我有一个频谱,想要识别不同的通道。通道可以通过电平(val)与通道之间的边缘区分开。
我已经创建了一个包含列val的简化表,现在我想计算在此之前和之后有多少行包含大于或等于10的值。
示例:

  1. 1.行:val = 10,然后是11, 10, 2, 3-〉next = 2prev = 0
  2. 1.行:val = 11,后面是10, 2, 3,前面是10-〉next = 1prev = 1
  3. 1.行:val = 10,后面是2, 3,前面是11, 10-〉next = 0prev = 2
  4. 1.行:val = 2,后面是3,前面是10, 11, 10-〉next = 0prev = 3
  5. 1.行:val = 2,之前为2, 10, 11, 10-〉prev = 0
    在真实数据中,一个通道包含大约20 - 30个测量点,所以我希望有一个解决方案来解决这个问题,而不会在两个方向上产生30个偏移。而且,并非所有通道都具有相同的宽度,因此它应该是动态的。
    很高兴得到任何帮助或提示,非常感谢。

MWE

import pandas as pd

df_test = pd.DataFrame({"val":[10,11,10,2,3,10,10,10,4,6,11,7,10,10,11,10,11,10]})

df_test["prev"] = [0,1,2,3,0,0,1,2,3,0,0,1,0,1,2,3,4,5]
df_test["next"] = [2,1,0,0,3,2,1,0,0,1,0,6,5,4,3,2,1,0]

+----+-------+--------+--------+
|    |   val |   prev |   next |
|----+-------+--------+--------|
|  0 |    10 |      0 |      2 |
|  1 |    11 |      1 |      1 |
|  2 |    10 |      2 |      0 |
|  3 |     2 |      3 |      0 |
|  4 |     3 |      0 |      3 |
|  5 |    10 |      0 |      2 |
|  6 |    10 |      1 |      1 |
|  7 |    10 |      2 |      0 |
|  8 |     4 |      3 |      0 |
|  9 |     6 |      0 |      1 |
| 10 |    11 |      0 |      0 |
| 11 |     7 |      1 |      6 |
| 12 |    10 |      0 |      5 |
| 13 |    10 |      1 |      4 |
| 14 |    11 |      2 |      3 |
| 15 |    10 |      3 |      2 |
| 16 |    11 |      4 |      1 |
| 17 |    10 |      5 |      0 |
+----+-------+--------+--------+
vshtjzan

vshtjzan1#

矢量化解决方案

def count(vals, direction):
    if direction == 'prev':
        vals = vals[::-1]

    arr = np.tile(vals, (len(vals), 1))

    mask = arr >= 10
    mask[np.tril_indices_from(mask)] = True

    ix = mask.argmin(axis=1) 
    ix[ix == 0] = len(ix)

    result = ix - np.arange(len(ix)) - 1
    return result[::-1] if direction == 'prev' else result

结果

count(df.val, 'prev')
# array([0, 1, 2, 3, 0, 0, 1, 2, 3, 0, 0, 1, 0, 1, 2, 3, 4, 5])

count(df.val, 'next')
# array([2, 1, 0, 0, 3, 2, 1, 0, 0, 1, 0, 6, 5, 4, 3, 2, 1, 0])

解释功能的工作

为简单起见,我们考虑一个包含5行且direction设置为next的示例 Dataframe ,现在我将逐步完成函数中定义的步骤:

df
#     val
# 0   10
# 1   11
# 2   10
# 3    2
# 4    3

将值平铺到2D矩阵中。* 请注意,主对角线表示原始列中的实际值 *

arr = np.tile(vals, (len(vals), 1))
# array([[10, 11, 10,  2,  3],
#        [10, 11, 10,  2,  3],
#        [10, 11, 10,  2,  3],
#        [10, 11, 10,  2,  3],
#        [10, 11, 10,  2,  3]])

创建一个布尔掩码来标识值>= 10,然后使用True屏蔽下三角形中的值,因为我们只对主对角线右侧的值感兴趣

mask
# array([[ True,  True,  True, False, False],
#        [ True,  True,  True, False, False],
#        [ True,  True,  True, False, False],
#        [ True,  True,  True, False, False],
#        [ True,  True,  True, False, False]])

mask[np.tril_indices_from(s)] = True
# array([[ True,  True,  True, False, False],
#        [ True,  True,  True, False, False],
#        [ True,  True,  True, False, False],
#        [ True,  True,  True,  True, False],
#        [ True,  True,  True,  True,  True]])

使用argmin查找每行中第一个false值的索引,如果没有false值(例如,第5行),则将索引设置为数组的长度

ix = mask.argmin(axis=1) 
ix[ix == 0] = len(ix)
# array([3, 3, 3, 4, 5])

从对角线值的索引中减去索引(ix),计算出实际代表满足条件的真值个数的距离

result = ix - np.arange(len(ix)) - 1
# array([2, 1, 0, 0, 0])
ktecyv1j

ktecyv1j2#

我找到了一个有效的解决办法:

def count_prev_next(row, df):
    current_index = row.name
    
    if current_index == 0:
        prev_count = 0
    else:
        try:
            prev_count = (
                pd.DataFrame(df.loc[:current_index-1, "val"] >= 10)[::-1] 
                .reset_index(drop=True)
                .query("val == False")
                .index[0]
            )
        except:
            prev_count = current_index
    
    if current_index == df.shape[0]:
        next_count = 0
    else:
        try:
            next_count = (
                pd.DataFrame(df.loc[current_index+1:, "val"] >= 10) 
                .reset_index(drop=True)
                .query("val == False")
                .index[0]
            )
        except:
            next_count = df.shape[0] - current_index - 1
    
    return pd.Series({"prev": prev_count, "next": next_count})

df_test[["prev", "next"]] = df_test.apply(count_prev_next, axis=1, args=(df_test,))

让它开一会儿,看看有没有人有更好的主意。

相关问题