Python以递增的大小对列表中的元素进行分组

Python以递增的大小对列表中的元素进行分组,python,list,python-3.x,Python,List,Python 3.x,有没有一种更简洁的解决方案可以将列表中的元素分组为比这更大的子组 示例: my_list = [my_list[int((i**2 + i)/2):int((i**2 + 3*i + 3)/2)] for i in range(int((-1 + (1 + 8*len(my_list))**0.5)/2))] 编辑 以下是来自timeit的结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] --> [[1], [2, 3], [4, 5, 6], [7, 8,

有没有一种更简洁的解决方案可以将列表中的元素分组为比这更大的子组

示例:

my_list = [my_list[int((i**2 + i)/2):int((i**2 + 3*i + 3)/2)] for i in range(int((-1 + (1 + 8*len(my_list))**0.5)/2))]
编辑

以下是来自
timeit
的结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] --> [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
[1, 2, 3, 4] --> [[1], [2, 3]]
[1, 2, 3, 4, 5, 6] --> [[1], [2, 3], [4, 5, 6]]

老实说,我并不完全清楚你为什么要这么做,我之所以提到这一点,纯粹是因为可能有一种特定于任务的方式来回答你的问题,但我认为以下几点至少更清楚:

from timeit import Timer
from itertools import count

def martijn(it):
    it = iter(it)
    return list([next(it) for _ in range(s)] for s in count(1))

def mathematical(it):
    upper_bound = int(((1 + 8*len(it))**0.5 + 1)//2)
    return [it[i*(i-1)//2:i*(i+1)//2] for i in range(1, upper_bound)]

def time(test, n):
    a = Timer(lambda: martijn(test)).timeit(n)
    b = Timer(lambda: mathematical(test)).timeit(n)
    return round(a, 3), round(b, 3)

>>> for i in range(8):
        loops = 10**max(0, (6-i))
        print(time([n for n in range(10**i)], loops), loops)
(6.753, 4.416) 1000000
(1.166, 0.629) 100000
(0.366, 0.123) 10000
(0.217, 0.036) 1000
(0.164, 0.017) 100
(0.157, 0.017) 10
(0.167, 0.021) 1
(1.749, 0.251) 1
>>> for i in range(8):
        loops = 10**max(0, (6-i))
        print(time(range(10**i), loops), loops)
(6.721, 4.779) 1000000
(1.184, 0.796) 100000
(0.367, 0.173) 10000
(0.218, 0.051) 1000
(0.202, 0.015) 100
(0.178, 0.005) 10
(0.207, 0.002) 1
(1.872, 0.005) 1

此时,您可以通过
列表(增加组(一些列表))
使用生成器表达式获取它:

def increasing_groups(l):
    current_size = 1
    while l:
        yield l[:current_size]
        l = l[current_size:]
        current_size += 1
演示:

这是一个生成器,可用于任何iterable,包括无尽的iterable:

>>> list(incremental_window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]))
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
>>> list(incremental_window([1, 2, 3, 4]))
[[1], [2, 3]]
>>> list(incremental_window([1, 2, 3, 4, 5, 6]))
[[1], [2, 3], [4, 5, 6]]
你可以用一行代码来“内联”列表对象上的
iter()
调用:

>>> from itertools import count
>>> for window in incremental_window(count()):
...     print window
...     if 25 in window:
...         break
... 
[0]
[1, 2]
[3, 4, 5]
[6, 7, 8, 9]
[10, 11, 12, 13, 14]
[15, 16, 17, 18, 19, 20]
[21, 22, 23, 24, 25, 26, 27]

是的,答案很简单

list([next(it) for _ in _range(s)] for it in (iter(my_list),) for s in count(1))

因为每个切片的大小是一个算术序列。计算算术序列总数的公式是已知的。因此,我们可以简单地用该等式直接计算每个切片的开始和结束索引。

您可以使用
itertools.count
跟踪要切片的项目数,并且可以使用
itertools.islice
拾取项目

>>> test = [1, 2, 3, 4, 5, 6, 7]
>>> bound = int((-1 + (1 + 8 * len(test)) ** 0.5) / 2)
>>> res = [test[(i + 1) * i // 2 : (i + 1) * (i + 2) // 2] for i in xrange(bound)]
>>> res
[[1], [2, 3], [4, 5, 6]]
#初始化和声明
数据=[1,2,3,4,5,6,7,8,9,10,11]
来自itertools导入计数,islice
计数器,它=计数(0),iter(数据)
#实际清单结构
结果=[[item]+列表(islice(it,next(counter)),用于其中的项]
#确保列表的最后一项与上一项一致
如果len(结果)>1和len(结果[-1])1和len(结果[-1])
这里的关键是
StopIteration
异常
next(它)
也会在
循环时中断
。这意味着您可能会松开未安装在一组中的最后一个元件

def incr_grouped(iterable):
    it, n = iter(iterable), 1
    while True:
        yield [next(it) for _ in range(n)]
        n += 1
使用
itertools
可以使其更加紧凑。勾选“回答”。

>>> list(incr_grouped('ABCDEF'))
[['A'], ['B', 'C'], ['D', 'E', 'F']]
>>> list(incr_grouped([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]))
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
根据Gauss,为您提供新列表第n个元素的开始和结束索引

所以

(n * (n - 1) / 2, n * (n + 1) / 2)
是列表的第n个元素,带有一点钝过滤:

my_list[n * (n - 1) / 2 : n * (n + 1) / 2]


所提供的
-1
低于列表中的任何项目。(楼层分区用于python 3中的整数输入。)

天哪。为了Python的哲学,我真诚地希望如此。您在这里试图实现的模式是什么?每个子列表是否比上一个子列表长1个元素?@2rs2ts:是。我会添加更多的例子。当然有一种更可读的方法,这是肯定的。@thefourtheye为什么
4
要留下来?组的大小需要以
1
的步骤递增。
4
会让人感觉寡不敌众。是的,解决方案是
int(-1+(1+8*len(我的列表))**0.5)/2
这会产生重叠窗口:
[[1]、[2,3]、[3,4,5]、[4,5,6,7,8,9]
您测试过这个吗?它不起作用,因为切片的开始只是
i
。切片看起来像
(0,1)(1,3)(2,5)
,而不是
(0,1)(1,3)(3,6)
,这是不正确的。@Scorpion\u God更新了答案。赶十分钟去买一台电脑~@MartijnPieters我已经更新了答案。我当时带着一台iPad,无法测试它。如果
限制被折叠到
范围()
,那么
限制不能被折叠到
范围吗?例如,预先计算
n
所需的上限。上限是
int((-1+(1+8*len(my_列表))**0.5)/2
,这是从
s=n*(n+1)/2
导出的二次方程的正解,然后在
范围()中使用
+1
而不是
len(my_列表)
的上限
调用,如果
过滤器可以运行,
我的列表[n*(n-1)//2:n*(n+1)//2]对于范围内的n(1,int((-1+(1+8*len(my\u list))**0.5)/2)+1)]
(加上一些楼层划分)楼层划分没有用,因为
(n*(n-1))
根据定义是成对的。+1用于建议在StackOverflow上编写函数。这是不是因为StopIteration错误导致
[next(it)for u in u range(s)]
表达式未返回?@njzk2:生成器表达式在
next(it)
引发
StopIteration
时退出,是的。异常是传播的,事实上,是
list()
捕获了它。@MartijnPieters:谢谢,我今天学到了一些东西!在生成器上使用列表和生成列表在这方面有不同的行为!这绝对是最简洁的答案。认可的。我在问题中提出了两种主要解决方案的时间安排。虽然这个更整洁,但数学的更快。
>>> list(incr_grouped('ABCDEF'))
[['A'], ['B', 'C'], ['D', 'E', 'F']]
>>> list(incr_grouped([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]))
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
(n * (n - 1) / 2, n * (n + 1) / 2)
my_list[n * (n - 1) / 2 : n * (n + 1) / 2]
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
[my_list[n * (n - 1) / 2: n * (n + 1)/ 2] for n in range(1, len(my_list)) if n * (n + 1)/ 2 <= len(my_list)]
# [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
list(my_list[n * (n - 1) // 2: n * (n + 1) // 2] for n in count(1) if iter(my_list[n * (n + 1)/ 2:]).next() > -1)