如何更快地找到从第一个位置开始的组的最后一个真实位置为真?

How to find the last true position of the group starting from the first position to be true faster?

提问人:jaried 提问时间:12/10/2021 最后编辑:jaried 更新时间:12/14/2021 访问量:189

问:

我有一个 dataframe,演示由 生成。generate_data()

  1. 如果数据列中的第一个值为 false,则返回 0。
  2. 如果数据列的第一个值为 true,则返回连续 true 的最后一个位置的顺序。

我写了两种方法:和sort_data()sort_data2()

%timeit sort_order(df.copy())
1.12 ms ± 14.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit sort_order2(df.copy())
715 µs ± 10.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

有没有更快的方法?

我的代码如下:

import pandas as pd
import numpy as np


def generate_data():
    order = range(1,7)
    data = [True, True, False, False, True, False]
    c = {'order': order,
         'data': data}
    df = pd.DataFrame(c)
    return df


def sort_order(df):
    order_first_false = df.loc[~df.data, 'order']
    if len(order_first_false) == 0:
        order_last_true = df.order.values[-1]
    else:
        order_first_false = order_first_false.values[0]
        df = df[df.order < order_first_false]
        if len(df):
            order_last_true = df.order.values[-1]
        else:
            order_last_true = 0
    return order_last_true


def sort_order2(df):
    groups = df[f'data'].ne(True).cumsum()
    len_true = len(groups[groups == 0])
    if len_true:
        order_last_true = df.at[df.index[len_true - 1], 'order'].max()
    else:
        order_last_true = 0
    return order_last_true


def main():
    df = generate_data()
    print(df)

    order_last_true = sort_order(df.copy())
    print(order_last_true)

    order_last_true = sort_order2(df.copy())
    print(order_last_true)


if __name__ == '__main__':
    main()

我尊重的结果是:

   order   data
0      1   True
1      2   True
2      3  False
3      4  False
4      5   True
5      6  False

2

2

Python Pandas 数据帧

评论

0赞 Wilian 12/10/2021
您想要的输出是什么?
0赞 jaried 12/10/2021
我写了一个错误,现在正在纠正它。
0赞 jezrael 12/10/2021
一个问题,order only helper 列吗?可能的使用范围是什么?
0赞 jaried 12/11/2021
@jezrael顺序是一个示例,在我的代码中还有其他列。

答:

4赞 jezrael 12/10/2021 #1

使用 numba 处理第一个 s 块的值,灵感来自解决方案:True

from numba import njit

@njit
def sort_order3(a, b):
    if not a[0]:
        return 0
    else:
        for i in range(1, len(a)):
            if not a[i]:
                return b[i - 1]
        return b[-1]


  
df = generate_data()
print (sort_order3(df['data'].to_numpy(), df['order'].to_numpy()))

评论

0赞 jaried 12/11/2021
谢谢你的回答。你的回答对我来说非常有价值。%timeit sort_order3(df.copy()['data'].to_numpy(), df.copy()['order'].to_numpy()) 300 μs ± 2.23 μs (平均±标准开发 7 次,每次 1000 次循环)
1赞 Andre 12/10/2021 #2

也许我遗漏了一些东西,但你为什么不直接获取第一个索引,然后使用该索引来获取列中的值?Falsedf.datadf.order

例如:

def sort_order3(df):
    try:
        idx = df.data.to_list().index(False)
    except ValueError: # meaning there is no False in df.data
        idx = df.data.size - 1
    return df.order[idx]

或者对于非常大的数据,numpy 可能会更快:

def sort_order4(df):
    try:
        idx = np.argwhere(~df.data.values)[0, 0]
    except IndexError: # meaning there is no False in df.data
        idx = df.data.size - 1
    return df.order[idx]

我设备上的计时:

%timeit sort_order(df.copy())
565 µs ± 6.29 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit sort_order2(df.copy())
443 µs ± 10.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit sort_order3(df.copy())
96.5 µs ± 2.16 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit sort_order4(df.copy())
112 µs ± 5.06 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

评论

0赞 jaried 12/11/2021
谢谢。%timeit sort_order3(df.copy()) 226 μs ± 3.08 μs (±平均 7 次运行,每次 1000 次循环)%timeit sort_order4(df.copy()) 250 μs ± 2.14 μs (平均±标准开发 7 次运行,每次 1000 次循环)
0赞 jaried 12/14/2021
当第一个值为 false 时,会出现问题。
0赞 Andre 12/14/2021
问题是什么?
0赞 jaried 12/14/2021
如果 change data = [False, True, False, False, True, False], sort_order3 并且sort_order4得到 1,则期望值为 0。
0赞 Andre 12/14/2021
该函数从列中第一个索引处的列中返回值。因此,如果索引为 0,则函数会重新运行,即 1 ()。df.orderFalsedf.datadf.order[0]as df.order == range(1,7)