python - 在Python中检查值是否存在于一系列开始和结束位置中的最有效方法是什么?

标签 python performance python-3.7 memory-efficient

假设我有一个文本文件,其开始和结束位置如下:

Start End
  1     5
 11    14
 15    19
 23    30

我想检查给定的一组值是否存在于这些位置之间并包括这些位置,例如4、14、20 将返回 TRUE、TRUE、FALSE。

最有效的方法是什么?

想法 1)我可以将每个可能的数字生成到一个列表中,并检查值是否在列表中 - 伪代码如下所示:

list = []
values = [4,14,20]
for line in file:
    for position in range(int(line.split()[0]),int(line.split()[1])+1):
        list.append(position) #Populate list with every viable position

for value in values:
    if value in list:
        print("TRUE")
    else:
        print("FALSE")

想法 2)不要将每个可能的位置保存到列表中,而是仅保存开始和结束,然后在检查时遍历每个范围:

list = []
for line in file:
    list.append(line) #Save only start and end into list

for value in values:
    for start_end in list:
        for position in range(int(start_end.split()[0]),int(start_end.split()[1])+1):
            if value == position:
                print("TRUE")

如果我的文件非常大,我怀疑想法 1 会占用大量内存,但另一方面,想法 2 会需要更长的时间才能运行,因为它必须迭代更多内容?

或者有一些完全不同的更好的方法吗? 非常感谢!

最佳答案

验证值是否在一组范围内

无需生成和迭代列表。我怀疑简单地使用比较运算符会更有效。

但是,如果您想确认哪种方法最有效,请使用 profiling tools .

下面是一个分析几种方法的示例,用于查明某个值是否在一组范围内,以验证哪种方法最有效。

import cProfile

# Input data.
ranges = [[1, 5], [11, 14], [15, 19], [23, 30]]
values = [4, 14, 40]

# An implementation using a comparison operator (i.e. "<=").
def is_in_range_comparison_operator(v):
    for r in ranges:
        if r[0] <= v <= r[1]:
            return True
    return False

# An implementation using a range object.
def is_in_range_range_object(v):
    for r in ranges:
        if v in range(r[0], r[1]+1):
            return True
    return False

# An implementation using precomputed range objects.
range_objects = [range(r[0], r[1]+1) for r in ranges]
def is_in_range_precomputed_range_objects(v):
    for r in range_objects:
        if v in r:
            return True
    return False

# A list of the implementations, to make looping through them easier.
implementations = [
        is_in_range_comparison_operator,
        is_in_range_range_object,
        is_in_range_precomputed_range_objects,
        ]

# A function to execute an implementation and print output.
def is_in_range(is_in_range_func):
    print("Using {}:".format(is_in_range_func.func_name))
    for v in values:
        if is_in_range_func(v):
            print ("True")
        else:
            print ("False")
    print

# Run each implementation, printing out the results.
for f in implementations:
    is_in_range(f)

# A function for executing a implementation repeatedly, for profiling purposes.
def test_is_in_range(is_in_range_func, num_iterations):
    for _ in range(num_iterations):
        for v in values:
            if is_in_range_func(v):
               pass

# Profile each implementation by running it num_iterations times.
num_iterations = 100000
for f in implementations:
    command = "test_is_in_range({}, {})".format(
            f.func_name, num_iterations)
    print("Profiling the following command: {}".format(command))
    cProfile.run(command)

这是执行脚本的输出:

$ python in_range.py
Using is_in_range_comparison_operator:
True
True
False

Using is_in_range_range_object:
True
True
False

Using is_in_range_precomputed_range_objects:
True
True
False

Profiling the following command: test_is_in_range(is_in_range_comparison_operator, 100000)
         300004 function calls in 0.388 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.388    0.388 <string>:1(<module>)
        1    0.172    0.172    0.388    0.388 in_range.py:51(test_is_in_range)
   300000    0.212    0.000    0.212    0.000 in_range.py:8(is_in_range_comparison_operator)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.004    0.004    0.004    0.004 {range}


Profiling the following command: test_is_in_range(is_in_range_range_object, 100000)
         1000004 function calls in 1.209 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.209    1.209 <string>:1(<module>)
   300000    0.639    0.000    1.033    0.000 in_range.py:15(is_in_range_range_object)
        1    0.174    0.174    1.209    1.209 in_range.py:51(test_is_in_range)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
   700001    0.396    0.000    0.396    0.000 {range}


Profiling the following command: test_is_in_range(is_in_range_precomputed_range_objects, 100000)
         300004 function calls in 0.391 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.391    0.391 <string>:1(<module>)
   300000    0.220    0.000    0.220    0.000 in_range.py:23(is_in_range_precomputed_range_objects)
        1    0.171    0.171    0.391    0.391 in_range.py:51(test_is_in_range)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.001    0.001    0.001    0.001 {range}

分析结果的一些结论:

  • 使用比较运算符比重复实例化范围对象更有效。
  • 使用预先计算的范围对象与使用比较运算符相同。

但是,如果您有大量范围需要处理,那么放弃使用范围对象可能会更有效。这导致...

<小时/>

有效处理任意大的范围

如果您的输入范围集足够大,以至于您担心内存耗尽,可以使用以下方法一次处理每个范围,并根据该范围测试所有值。

注释:

  • 该方法一次处理每个范围,并根据它测试所有值。这允许处理任意数量的范围,而无需预先摄取所有范围。
  • 这并没有考虑到要搜索的任意数量的值。
  • 有一些基于输入范围和排序值的优化。如果不能依赖排序的输入,则可以相应地修改或删除优化。
ranges = [[4, 5], [11, 14], [15, 19], [23, 30]]
values = [4, 14, 20]

# Create a dictionary to keep track whether or not each value falls within
# a range, with a default value of False.
is_value_in_range = {}

def init_results():
    global is_value_in_range
    is_value_in_range = {v: False for v in values}

def print_values_and_results():
    for v in values:
        print("{}: {}".format(v, is_value_in_range[v]))

def check_for_values_in_range(r, values_index):
    for i, v in enumerate(values[values_index:]):

        # If the value is greater than the upper end of the range, move on
        # to the next range.
        if v > r[1]:
            return (values_index + i, True)

        # If the value is less than the lower end and we're on the last
        # value, stop altogether.
        if v < r[0] and values_index == len(values) - 1:
            return (0, False)

        if r[0] <= v <= r[1]:
            is_value_in_range[v] = True

    return (values_index, True)

def check_for_values_in_ranges(verbose=False):
    init_results()
    if verbose:
        print("Initial results:")
        print_values_and_results()
        print
    i = 0
    for r in ranges:
        i, continue_searching = check_for_values_in_range(r, i)
        if verbose:
            print("After checking range: {}".format(r))
            print_values_and_results()
            print
        if not continue_searching:
            break

    print("Final results:")
    print_values_and_results()
    print

print("*** Check ranges for values (non-verbose) ***")
check_for_values_in_ranges()

print("*** Check ranges for values (verbose) ***")
check_for_values_in_ranges(True)

脚本的输出:

$ python large_input.py
*** Check ranges for values (non-verbose) ***
Final results:
4: True
14: True
20: False

*** Check ranges for values (verbose) ***
Initial results:
4: False
14: False
20: False

After checking range: [4, 5]
4: True
14: False
20: False

After checking range: [11, 14]
4: True
14: True
20: False

After checking range: [15, 19]
4: True
14: True
20: False

After checking range: [23, 30]
4: True
14: True
20: False

Final results:
4: True
14: True
20: False

关于python - 在Python中检查值是否存在于一系列开始和结束位置中的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58776293/

相关文章:

Python3 : os. 系统不重定向标准输出

python - 使用 libpcap 的 TLS 解密

javascript - KineticJS - 使用 Stage.draggable 移动 4000 个图 block

python - 如何清除 Tkinter 上的页面

performance - Lucene (Solr/Zoie/Elasticsearch) 设置的硬件要求

mysql - 对大型表(有两个左连接表)的查询结果非常慢

regex - 从文件中获取列名称列表的精确匹配

opencv - 如何在后台打开网站然后关闭而不中断使用python的程序流程?

python - 如何根据日期列的 1 年滞后创建新的指标列?

python - 比较两个目录及其子目录以发现任何更改?