Pyhonic 使用 if 语句来处理应用于分块列表的不等式
Pyhonic use of if statements for inequalities applied to chunked list
给定 Python 中的以下列表:
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v1 = [1,2,3,4,5,6,7] #7 values
v2 = [a,b,c,d,e,f,g] #7 letters
我想检查一个数字是否包含在任何区间内,return另一个值基于这是哪个区间。
示例:
1. My test value is 1111
2. It belongs to the second interval: 1000 < 1111 < 5000
3. Hence I need to return b
我会通过以下方式解决问题:
- 正在创建
l1
的块
- 迭代每个块
- 为每个块写一个 if 语句
- Return正确的chunk对应的字母
我可以通过查看每一对连续的数字来创建它的块:
def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))
for group in chunker(l1, 2):
print(group)
这个returns:
[0, 1000]
[1000, 5000]
[5000, 10000]
[10000, 20000]
[20000, 30000]
[30000, 40000]
[40000, 50000]
[50000]
我的问题:
- 是否有一种 Pythonic 的方式来编写这些 if 语句而不是
每个块有一个?如果我有 1,000 个区块怎么办?
- 如果有,如何在不为其创建特定案例的情况下处理最后一个块?
这仅适用于 len 2 的块。
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
# v1 = [1,2,3,4,5,6,7] #7 values
v2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] # 7 letters
def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))
def chunk_with_value(list_of_chunks, value):
"""returns the chunk if the value is inside the range"""
for chunk in list_of_chunks:
if chunk[0] < value < chunk[1]:
return chunk
def chunk_to_letter(value_list, letter_list, chunk):
"""returns the letter based on the index of the first chunk element"""
for i, value in enumerate(l1):
if value == chunk[0]:
return v2[i]
chunks = chunker(l1, 2)
chunk = chunk_with_value(chunks, 1111)
print(chunk) # [1000, 5000]
print(chunk_to_letter(l1, v2, chunk)) # b
您可以 zip
列出 l1
和 l1[1:]
,这将按照您需要的方式制作间隔。对于一些额外的指针,您可以将二进制搜索合并到其中,因为间隔已排序,我将留给您进行优化。该算法的当前运行时间是 O(n)
,您可以将运行时间减少到 O(log(n))
。
from __future__ import print_function
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
interval = []
letter = None
for index,(left,right) in enumerate(zip(l1,l1[1:])):
if left <= element <= right:
interval = [left,right]
letter = index
break
if letter:
print("answer is ",v2[letter])
print("interval is" ,interval)
else:
print("No interval found")
编辑
我在 python 中使用 bisect
模块添加了一个算法
它产生相同的输出但使用二进制搜索因此它应该更快并且在 O(log(n))
中运行
from __future__ import print_function
import bisect
li = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
index = bisect.bisect(li, element)
if index == 0 or index == len(li):
print("No interval found")
else:
print("answer is",v2[index - 1])
print("interval is",li[index - 1], li[index])
输出
answer is b
interval is [1000, 5000]
直接来自 itertools 食谱书:
def get_thing(value):
def pairwise(iterable):
from itertools import tee
a, b = tee(iterable)
next(b, None)
return zip(a, b)
interval_ranges = [
0,
100,
500,
1000
]
# There are four interval ranges, so three intervals.
things = [
"A", # 0-100
"B", # 100-500
"C" # 500-1000
]
for (begin, end), thing in zip(pairwise(interval_ranges), things):
if begin <= value < end: # modify this to suit your needs. Is the range inclusive/exclusive?
return thing
return None
def main():
thing = get_thing(400)
print(thing)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
B
我相信你需要得到相应的区间索引并用它来查询v2
。
应该这样做:
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v2 = ['a','b','c','d','e','f','g'] #7 letters
def intervals(l):
for i in range(len(l)-1):
yield i, l[i:i+2]
def interval_value(val, interval_list, value_list):
for i, interval in intervals(interval_list):
if interval[0] <= val <= interval[1]:
return value_list[i]
print(interval_value(1111, l1, v2))
print(interval_value(0, l1, v2))
print(interval_value(51000, l1, v2))
print(interval_value(40000, l1, v2))
输出:
b
a
None
f
您不需要 v1
值 - 您可以直接使用 v2
上的索引
虽然其他答案很有用,但几乎所有答案都在寻找间隔而不是 v2
的输出
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
def get_interval(l1):
for index, left, right in zip(range(len(l1)), l1, l1[1:]):
if left <= element <= right:
return v2[index]
>>> print("answer is:" ,get_interval(element))
>>> answer is: b
您可以只使用生成器来获得结果:
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
value = 1111
result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)
输出:
>>> result
'b'
通过添加默认值 None
作为 next()
的第二个参数,您也可以处理未找到的情况:
value = -50
result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)
>>> result
# None
如果你只是想要索引,你可以使用 enumerate
代替:
result = next((v for v, (i, j) in enumerate(zip(l1[:-1], l1[1:])) if value in range(i, j)), None)
>>> result
# 1
解释:
结果行由几部分组成:
next(iterator [, default])
这是一个函数,用于检索传入参数的 iterator
中的下一项。如果遇到 StopIteration
,default
用于 return 默认值。有问题的 iterator
是这里的生成器(为清楚起见分解):
(
v # point 4
for v, i, j # point 2
in zip(v2, l1[:-1], l1[1:]) # point 1
if value in range(i, j) # point 3
)
zip
函数整理传递的 lists
,因此 v2[0]
、l1[:-1][0]
和 l1[1:][0]
形成一个元组 ('a', 0, 1000)
,每个索引依此类推。
for v, i, j
用于提取元组中的元素。
if value in range(i, j)
用于检查 1111
是否在 range(0, 1000)
之间的范围内。
如果匹配,v
是 returned。如果没有,继续下一次迭代。
给定 Python 中的以下列表:
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v1 = [1,2,3,4,5,6,7] #7 values
v2 = [a,b,c,d,e,f,g] #7 letters
我想检查一个数字是否包含在任何区间内,return另一个值基于这是哪个区间。
示例:
1. My test value is 1111
2. It belongs to the second interval: 1000 < 1111 < 5000
3. Hence I need to return b
我会通过以下方式解决问题:
- 正在创建
l1
的块
- 迭代每个块
- 为每个块写一个 if 语句
- Return正确的chunk对应的字母
我可以通过查看每一对连续的数字来创建它的块:
def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))
for group in chunker(l1, 2):
print(group)
这个returns:
[0, 1000]
[1000, 5000]
[5000, 10000]
[10000, 20000]
[20000, 30000]
[30000, 40000]
[40000, 50000]
[50000]
我的问题:
- 是否有一种 Pythonic 的方式来编写这些 if 语句而不是 每个块有一个?如果我有 1,000 个区块怎么办?
- 如果有,如何在不为其创建特定案例的情况下处理最后一个块?
这仅适用于 len 2 的块。
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
# v1 = [1,2,3,4,5,6,7] #7 values
v2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] # 7 letters
def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq)))
def chunk_with_value(list_of_chunks, value):
"""returns the chunk if the value is inside the range"""
for chunk in list_of_chunks:
if chunk[0] < value < chunk[1]:
return chunk
def chunk_to_letter(value_list, letter_list, chunk):
"""returns the letter based on the index of the first chunk element"""
for i, value in enumerate(l1):
if value == chunk[0]:
return v2[i]
chunks = chunker(l1, 2)
chunk = chunk_with_value(chunks, 1111)
print(chunk) # [1000, 5000]
print(chunk_to_letter(l1, v2, chunk)) # b
您可以 zip
列出 l1
和 l1[1:]
,这将按照您需要的方式制作间隔。对于一些额外的指针,您可以将二进制搜索合并到其中,因为间隔已排序,我将留给您进行优化。该算法的当前运行时间是 O(n)
,您可以将运行时间减少到 O(log(n))
。
from __future__ import print_function
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
interval = []
letter = None
for index,(left,right) in enumerate(zip(l1,l1[1:])):
if left <= element <= right:
interval = [left,right]
letter = index
break
if letter:
print("answer is ",v2[letter])
print("interval is" ,interval)
else:
print("No interval found")
编辑
我在 python 中使用 bisect
模块添加了一个算法
它产生相同的输出但使用二进制搜索因此它应该更快并且在 O(log(n))
from __future__ import print_function
import bisect
li = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
index = bisect.bisect(li, element)
if index == 0 or index == len(li):
print("No interval found")
else:
print("answer is",v2[index - 1])
print("interval is",li[index - 1], li[index])
输出
answer is b
interval is [1000, 5000]
直接来自 itertools 食谱书:
def get_thing(value):
def pairwise(iterable):
from itertools import tee
a, b = tee(iterable)
next(b, None)
return zip(a, b)
interval_ranges = [
0,
100,
500,
1000
]
# There are four interval ranges, so three intervals.
things = [
"A", # 0-100
"B", # 100-500
"C" # 500-1000
]
for (begin, end), thing in zip(pairwise(interval_ranges), things):
if begin <= value < end: # modify this to suit your needs. Is the range inclusive/exclusive?
return thing
return None
def main():
thing = get_thing(400)
print(thing)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
输出:
B
我相信你需要得到相应的区间索引并用它来查询v2
。
应该这样做:
l1 = [0,1000,5000,10000,20000,30000,40000,50000] #8 values, 7 intervals
v2 = ['a','b','c','d','e','f','g'] #7 letters
def intervals(l):
for i in range(len(l)-1):
yield i, l[i:i+2]
def interval_value(val, interval_list, value_list):
for i, interval in intervals(interval_list):
if interval[0] <= val <= interval[1]:
return value_list[i]
print(interval_value(1111, l1, v2))
print(interval_value(0, l1, v2))
print(interval_value(51000, l1, v2))
print(interval_value(40000, l1, v2))
输出:
b
a
None
f
您不需要 v1
值 - 您可以直接使用 v2
上的索引
虽然其他答案很有用,但几乎所有答案都在寻找间隔而不是 v2
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
element = 1111
def get_interval(l1):
for index, left, right in zip(range(len(l1)), l1, l1[1:]):
if left <= element <= right:
return v2[index]
>>> print("answer is:" ,get_interval(element))
>>> answer is: b
您可以只使用生成器来获得结果:
l1 = [0,1000,5000,10000,20000,30000,40000,50000]
v2 = ['a','b','c','d','e','f','g']
value = 1111
result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)
输出:
>>> result
'b'
通过添加默认值 None
作为 next()
的第二个参数,您也可以处理未找到的情况:
value = -50
result = next((v for v, i, j in zip(v2, l1[:-1], l1[1:]) if value in range(i, j)), None)
>>> result
# None
如果你只是想要索引,你可以使用 enumerate
代替:
result = next((v for v, (i, j) in enumerate(zip(l1[:-1], l1[1:])) if value in range(i, j)), None)
>>> result
# 1
解释:
结果行由几部分组成:
next(iterator [, default])
这是一个函数,用于检索传入参数的 iterator
中的下一项。如果遇到 StopIteration
,default
用于 return 默认值。有问题的 iterator
是这里的生成器(为清楚起见分解):
(
v # point 4
for v, i, j # point 2
in zip(v2, l1[:-1], l1[1:]) # point 1
if value in range(i, j) # point 3
)
zip
函数整理传递的lists
,因此v2[0]
、l1[:-1][0]
和l1[1:][0]
形成一个元组('a', 0, 1000)
,每个索引依此类推。for v, i, j
用于提取元组中的元素。if value in range(i, j)
用于检查1111
是否在range(0, 1000)
之间的范围内。如果匹配,
v
是 returned。如果没有,继续下一次迭代。