我想合并一个列表到范围,但保持原来的顺序。同时有定制的间隙支持。
例如,当输入列表[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"}
堆栈溢出上有一个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
...
因此,如果我可以直接合并dataframe,而不生成映射程序并再次将其映射到dataframe,则会更好。
发布于 2017-09-08 09:30:27
我已经修改了来自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”
注:请告诉我,如果这是错误的方式回答,将处理它从下一次。我的意图只是给出适当的答案,帮助别人,而不是接受别人的回答等等。
请评论下面,如果是这样,将删除我的答案。
我知道,这是个坏兆头。
发布于 2017-09-08 09:56:15
这将返回您似乎正在寻找的结果。它并不比你所拥有的更漂亮,但它有效:
#!/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']
发布于 2017-09-08 10:02:02
码
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']
详细信息
此外,该函数为未排序的迭代(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的帖子,这是一个伟大的贡献。
https://stackoverflow.com/questions/46112731
复制相似问题