在Python中的列表中创建列表
我有一个名为values的列表,其中包含一系列数字:在Python中的列表中创建列表,python,list,Python,List,我有一个名为values的列表,其中包含一系列数字: values = [0, 1, 2, 3, 4, 5, ... , 351, 0, 1, 2, 3, 4, 5, 6, ... , 750, 0, 1, 2, 3, 4, 5, ... , 559] 我想创建一个新列表,其中包含从0到数字的元素列表 比如: new_values = [[0, 1, 2, ... , 351], [0, 1, 2, ... , 750], [0, 1, 2, ... , 559]] 我所做的代码是: sta
values = [0, 1, 2, 3, 4, 5, ... , 351, 0, 1, 2, 3, 4, 5, 6, ... , 750, 0, 1, 2, 3, 4, 5, ... , 559]
我想创建一个新列表,其中包含从0到数字的元素列表
比如:
new_values = [[0, 1, 2, ... , 351], [0, 1, 2, ... , 750], [0, 1, 2, ... , 559]]
我所做的代码是:
start = 0
new_values = []
for i,val in enumerate(values):
if(val == 0):
new_values.append(values[start:i])
start = i
但是,它返回的是:
new_values = [[], [0, 1, 2, ... , 750], [0, 1, 2, ... , 559]]
如何修复我的代码?这将是一个很大的帮助。您可以使用
itertools对元素进行分组。groupby
基于0
(这是错误的)的存在,并提取0
之间的子列表,同时用列表添加缺少的0
:
[[0]+list(g) for k, g in groupby(values, bool) if k]
例如:
>>> from itertools import groupby
>>> values = [0, 1, 2, 3, 4, 5 , 351, 0, 1, 2, 3, 4, 5, 6, 750, 0, 1, 2, 3, 4, 559]
>>> [[0]+list(g) for k, g in groupby(values, bool) if k]
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 559]]
因此,您编写的代码的问题在于,它在开头包含一个空的
列表
,而忽略了最后一个子列表
。最简单的解决方案是:
列表
(当i
为0时),例如,如果val==0和i!=0:start = 0
new_values = []
for i,val in enumerate(values):
if val == 0 and i != 0: # Avoid adding empty list
new_values.append(values[start:i])
start = i
if values: # Handle edgecase for empty values where nothing to add
new_values.append(values[start:]) # Add final list
我打算添加cleanergroupby
解决方案,它避免了列表开始/结束的特殊情况,但是,我会让您参考他的答案
有点令人惊讶的是,这实际上似乎是最快的解决方案,渐进地,代价是要求输入是一个列表
(其中一些其他解决方案可以接受任意iterables,包括不可能索引的纯迭代器)
用于比较(为了简洁和在现代Python上获得最佳性能,使用Python 3.5额外的解包泛化,并使用int
的隐式布尔值避免与0
进行比较,因为它相当于int
输入,但使用隐式布尔值要快得多):
使用ipython
6.1的%timeit
magic在Python 3.6、Linux x64上计时:
>>> values = [*range(100), *range(50), *range(150)]
>>> %timeit -r5 method1(values)
12.5 μs ± 50.6 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
>>> %timeit -r5 method2(values)
16.9 μs ± 54.9 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
>>> %timeit -r5 method3(values)
13 μs ± 18.9 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
>>> %timeit -r5 method4(values)
16.7 μs ± 9.51 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
>>> %timeit -r5 method5(values)
18.2 μs ± 25.2 ns per loop (mean ± std. dev. of 5 runs, 100000 loops each)
摘要:
批量分割运行的解决方案(method1
,method3
)是最快的,但取决于输入是一个序列(如果返回类型必须是list
,那么输入也必须是list
,或者必须添加转换)
groupby
解决方案(method2
,method5
)稍慢一些,但通常非常简洁(像method5
那样处理所有边缘情况不需要非常详细,也不需要显式的测试和检查LBYL模式)除了使用操作符.truth
而不是bool
之外,它们也不需要太多的黑客来让它们尽可能快地运行。这是必要的,因为由于一些奇怪的实现细节,CPython的bool
构造函数非常慢(bool
必须接受完整的变量,包括关键字,通过对象施工机械进行调度,这比操作符的成本要高很多。truth
使用低开销路径,只接受一个位置参数并绕过对象施工机械);如果bool
被用作键
函数而不是运算符。truth
,则运行时间增加一倍以上(对于方法2
和方法5
,分别为36.8μs和38.8μs)
介于两者之间的是更慢但更灵活的方法(处理任意输入的可重用项,包括迭代器、处理没有特殊大小写的0的运行等)。使用逐项追加s
(方法4
)。问题是,获得最大性能需要更多的详细代码(因为需要避免重复索引和方法绑定);如果method4
的循环更改为更加简洁:
for val in values:
if not val:
new_values.append([])
new_values[-1].append(val)
由于反复索引new_值
和反复绑定append
方法的成本,运行时间增加了一倍以上(达到34.4μs)
在任何情况下,就个人而言,如果性能不是绝对关键,我会使用groupby
解决方案之一,使用bool
作为键,以避免导入和不常见的API。如果性能更重要,我可能仍然使用groupby
,但交换operator.truth>
作为键
函数;当然,它的速度不如详细说明的版本快,但是对于了解groupby
的人来说,它很容易理解,并且对于任何给定级别的边缘案例处理来说,它通常是最简洁的解决方案。您可以通过查找每个val所在的所有组来使用itertools.groupby
ue小于在值中执行它的元素:
import itertools
values = [0, 1, 2, 3, 4, 5, 351, 0, 1, 2, 3, 4, 5, 6, 750, 0, 1, 2, 3, 4, 5, 559]
new_vals = [[i[-1] for i in b] for a, b in itertools.groupby(enumerate(values), key=lambda x:x[-1] <= values[x[0]+1] if x[0]+1 < len(values) else False)]
final_data = [new_vals[i]+new_vals[i+1] for i in range(0, len(new_vals), 2)]
这应该起作用:
values = [0, 1, 2, 3, 4, 5, 351, 0, 1, 2, 3, 4, 5, 6, 750, 0, 1, 2, 3, 4, 5, 559]
new_values = []
split_at = 0 # split the list when this value is reached
idx = -1
for value in values:
if value == split_at:
idx += 1
new_values.append([])
new_values[idx].append(value)
输出:
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
它还可以处理边槽
我的方法比方法快一点,但也比方法慢一点:
您也可以这样做:
values = [0, 1, 2, 3, 4, 5, 351, 0, 1, 2, 3, 4, 5, 6, 750, 0, 1, 2, 3, 4, 5, 559]
# Find all indices whose element is 0.
indices = [index for index, value in enumerate(values) if value==0] + [len(values)]
# Split the list accordingly
values = [values[indices[i]:indices[i+1]] for i in range(len(indices)-1)]
print(values)
输出:
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
[[0, 1, 2, 3, 4, 5, 351], [0, 1, 2, 3, 4, 5, 6, 750], [0, 1, 2, 3, 4, 5, 559]]
次要说明:这假设0
s从不背靠背出现,并且值总是以0
开头。OP的示例遵循此规则,因此如果该规则得到保证,这是迄今为止最简单/最有效的解决方案。更正:因为bool
具有荒谬的开销。将bool
替换为operator.truth
作为键
函数将消除该开销,使该成本与其他优化解决方案类似(稍微慢一点,但微不足道,其中bool
可以使优化的非groupby
解决方案所需的成本增加3倍).就个人而言,如果性能不重要,我会按原样使用此解决方案;如果性能重要,则切换到操作符。真相会让您以最小的复杂度变化获得较大的加速。是的,我知道bool
比它应该的慢,直到我测试时才意识到速度慢了多少