我想将列表合并到范围中,但保留原始顺序。同时提供定制差距支持。
例如,当输入列表[0, 1, 3, 7, 4, 2, 8, 9, 11, 11]
时,它应该返回范围列表["0-4", "0-4", "7-9", "0-4", "0-4", "0-4", "7-9", "7-9", "11-11", "11-11"]
。
def fun(a_list, gap_length=0):
return a_list_of_range
# from
# [0, 1, 3, 7, 4, 2, 8, 9, 11, 11]
# to
# ["0-4", "0-4", "7-9", "0-4", "0-4", "0-4", "7-9", "7-9", "11-11", "11-11"]
# or to
# {0:"0-4", 1:"0-4", 2:"0-4", 3:"0-4", 4:"0-4", 7:"7-9", 8:"7-9", 9:"7-9", 10:"11-11"}
stackoverflow上有similar question,但所有答案都不能以相应的顺序返回范围。
你的解决方案是什么?
我写了一个丑陋的函数来解决问题,但速度很糟糕。 以下函数支持自定义间隙长度,以便将列表合并到范围内。
def to_ranges_with_gap(input_list, gap_len=20):
"""list into range with gap"""
loc2range = {}
input_list = sorted(set(input_list))
start_loc = input_list[0]
stop_loc = input_list[0]
range_loc_list = []
for element in input_list:
if element < stop_loc + gap_len:
range_loc_list.append(element)
stop_loc = element
else:
for loc in range_loc_list:
loc2range[loc] = "{}-{}".format(start_loc, stop_loc)
start_loc = element
stop_loc = element
range_loc_list = [element]
for loc in range_loc_list:
loc2range[loc] = "{}-{}".format(start_loc, stop_loc)
return loc2range
你能告诉我一个更好的方法吗?
列表的剂量是多少?
列表是:
在结果列表中重复范围的目的是什么?你可以写一个更优雅的解决方案而不需要那个怪癖。 - timgeb
例如,如果我想处理下面的数据框,并尝试对年龄范围进行分组以计算中位数高度。
Age Gender Height
2 M 30
4 M 60
2 M 33
3 F 50
20 M 180
22 F 166
40 F 150
33 M 172
...
我希望得到这样的结果。而年龄列是上面提到的list
。
2-5 M 40.5
2-6 F 50.9
10-25 M 150.8
...
因此,如果我可以直接合并数据帧,而不生成映射器并再次将其重新映射到数据帧,那将会更好。
答案 0 :(得分:1)
我修改了您提供的similar question中已接受的答案代码,并且它对我有用
import itertools
def ranges(i):
for a, b in itertools.groupby(enumerate(i), lambda i: i[1] - i[0]):
b = list(b)
if(b[0][1] - b[-1][1] == 0):
yield "%d-%d"%(b[0][1], b[-1][1])
for ele in range(b[0][1], b[-1][1]):
yield "%d-%d"%(b[0][1], b[-1][1])
print ([ele for ele in ranges([0, 1, 2, 3, 4, 7, 8, 9, 11])])
['0-4','0-4','0-4','0-4','7-9','7-9','11 -11']
注意:请告诉我如果这是错误的回答方式,将从下次开始处理。我的意图只是给予适当的答案并帮助他人,而不是采取别人的回答等等。
请在下面发表评论,如果是,请删除我的答案。
我知道,这是一个不好的补丁。
答案 1 :(得分:0)
这将返回您似乎正在寻找的结果。它并不比你拥有的更漂亮,但它确实有效:
#!/usr/bin/python
arr = []
l = [1,2,3,5,6,7,8,9,11,12,13,14,20]
start,counter,i = (0,0,0)
while i < len(l):
start = i
counter = 0
while (i < len(l) - 1) and (l[i+1] == l[i] +1):
counter += 1
i += 1
for x in range(counter+1):
arr.append("{}-{}".format(l[start], l[start+counter]))
i += 1
print(arr)
输出:
['1-3', '1-3', '1-3', '5-9', '5-9', '5-9', '5-9', '5-9', '11-14', '11-14', '11-14', '11-14', '20-20']
答案 2 :(得分:0)
<强>代码强>
import itertools as it
import collections as ct
# Given
a = [0, 1, 2, 3, 4, 7, 8, 9, 11]
b = [0, 1, 3, 7, 4, 2, 8, 9, 11] # unsorted
c = [0, 15, 2, 3, 4, 7, 8, 9, 11, 14] # unsorted
d = [0, 15, 2, 3, 4, 7, 8, 9, 11, 14, 2, 4] # duplicates
def find_ranges(iterable):
"""Return a defaultdict of ranges."""
# Find ranges
sorted_it = sorted(set(iterable))
keyfunc = lambda i: sorted_it[i[0]] - i[0]
ranges = [[item[1] for item in g]
for k, g in it.groupby(enumerate(sorted_it), keyfunc)]
# Out: [[0, 1, 2, 3, 4], [7, 8, 9], [11]]
# Build dictionary
dd = ct.defaultdict(int)
for r in ranges:
s = "{}-{}".format(min(r), max(r))
for i in r:
if i in r:
dd[i] = s
return dd
find_ranges(a)
输出
defaultdict(int,
{0: '0-4',
1: '0-4',
2: '0-4',
3: '0-4',
4: '0-4',
7: '7-9',
8: '7-9',
9: '7-9',
11: '11-11'})
获得此查找表后,创建范围列表很简单:
[find_ranges(b)[i] for i in b]
# ['0-4', '0-4', '0-4', '7-9', '0-4', '0-4', '7-9', '7-9', '11-11']
<强>详情
此外,此函数查找未排序的iterables(b
和c
)的范围,并处理重复项(d
)。
assert find_ranges(a) == find_ranges(b)
assert find_ranges(c) == find_ranges(d)
这里我们将确认结果对于排序和未排序的输入是等效的。接下来,我们将确认未排序输入的等效性和重复元素的输入。最后,我们演示了一个示例输出:
find_ranges(d)
输出
defaultdict(int,
{0: '0-0',
2: '2-4',
3: '2-4',
4: '2-4',
7: '7-9',
8: '7-9',
9: '7-9',
11: '11-11',
14: '14-15',
15: '14-15'})
注意:“查找范围”部分的灵感来自@ Nirmi的帖子,这是一个很好的贡献。
答案 3 :(得分:0)
受this post和@pylang的启发,我找到了O(n)解决方案。
import itertools
import collections
# an example list
l = [100, 107, 0, 1, 2, 3, 5, 6, 10, 11, 65, 66, 68, 68]
class Gap(object):
def __init__(self, diff):
self.diff, self.flag, self.prev = diff, [0,1], None
def __call__(self, elem):
if self.prev and abs(self.prev - elem) > self.diff:
self.flag = self.flag[::-1]
self.prev= elem
return self.flag[0]
def list_to_ranges_with_gap(raw_list, gap_length = 1):
"""Return a defaultdict of ranges."""
# Find ranges
sorted_list = sorted(set(raw_list))
merged_ranges = [list(g) for k, g in
itertools.groupby(sorted_list, key = Gap(gap_length))]
# Build dictionary
list2range = collections.defaultdict(int)
for r in merged_ranges:
for i in r:
list2range[i] = "{}-{}".format(r[0], r[-1])
return list2range
list_to_ranges_with_gap(l, 10)
defaultdict(int,
{0: '0-11',
1: '0-11',
2: '0-11',
3: '0-11',
5: '0-11',
6: '0-11',
10: '0-11',
11: '0-11',
65: '65-68',
66: '65-68',
68: '65-68',
100: '100-107',
107: '100-107'})