Python 将列表拆分为具有相等值的较小列表
我希望将一个列表转换为更小的相等值列表。我举的一个例子是:Python 将列表拆分为具有相等值的较小列表,python,Python,我希望将一个列表转换为更小的相等值列表。我举的一个例子是: ["a", "a", "a", "b", "b", "c", "c", "c", "c"] 到 您认为最有效的方法是什么?您可以使用以下方法解决此问题: >>> from itertools import groupby >>> [list(grp) for k, grp in groupby(["a", "a", "a", "b", "b", "c", "c", "c", "c"])] [['a
["a", "a", "a", "b", "b", "c", "c", "c", "c"]
到
您认为最有效的方法是什么?您可以使用以下方法解决此问题:
>>> from itertools import groupby
>>> [list(grp) for k, grp in groupby(["a", "a", "a", "b", "b", "c", "c", "c", "c"])]
[['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
它只对连续相等的元素进行分组,但在您的情况下这似乎足够了。您可以使用以下方法来解决此问题:
>>> from itertools import groupby
>>> [list(grp) for k, grp in groupby(["a", "a", "a", "b", "b", "c", "c", "c", "c"])]
[['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
它只对连续相等的元素进行分组,但这在您的情况下似乎已经足够了。您可以使用
集合。Counter
>>> lst = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
>>> import collections
>>> collections.Counter(lst).most_common()
[('c', 4), ('a', 3), ('b', 2)]
即使在值没有排序的情况下也可以这样做,它提供了一个非常紧凑的表示形式,您可以根据需要将其扩展到列表中:
>>> [[i]*n for i,n in collections.Counter(lst).most_common()]
[['c', 'c', 'c', 'c'], ['a', 'a', 'a'], ['b', 'b']]
您可以使用
collections.Counter
>>> lst = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
>>> import collections
>>> collections.Counter(lst).most_common()
[('c', 4), ('a', 3), ('b', 2)]
即使在值没有排序的情况下也可以这样做,它提供了一个非常紧凑的表示形式,您可以根据需要将其扩展到列表中:
>>> [[i]*n for i,n in collections.Counter(lst).most_common()]
[['c', 'c', 'c', 'c'], ['a', 'a', 'a'], ['b', 'b']]
虽然我个人认为,
itertools.groupby
是最方便的方式,但您要求提高效率,这应该比任何itertools
选项都要快得多:
data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
lookup = {} # lookup map
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
print(result)
# [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
如果数据总是按顺序排列(即元素不混合),则可以在不使用查找表和使用列表理解的情况下进一步优化,以获得最佳性能
更新-一些关于效率和操作的澄清。如果您将测试设置为:
from itertools import groupby
def itools_func(data):
return [list(grp) for k, grp in groupby(data)]
def manual_func(data):
lookup = {}
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
return result
问题在于,这两个函数不会返回相同的值:
test_data = ["a", "a", "b", "c", "c", "b", "a"]
itools_func(test_data) # [['a', 'a'], ['b'], ['c', 'c'], ['b'], ['a']]
manual_func(test_data) # [['a', 'a', 'a'], ['b', 'b'], ['c', 'c']]
从OP的问题中,我了解到他想要后一个(基于他的评论“我对列表进行排序以使值连续”),因为使用排序的列表可以轻松得多。因此,如果我们为这些函数提供一个非常长的列表:
test_data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"] * 10000 # 10000 x the original
在我的系统上,它的时钟如下所示:
itools_func - 100 loops: 2.668s, per loop: 26.68ms
manual_func - 100 loops: 1.005s, per loop: 10.05ms
但是,这对于itertools.groopby
来说是一个不利的设置。如果要对数据进行如下排序:
test_data = ["a"] * 3000 + ["b"] * 2000 + ["c"] * 40000
随着C后端的出现,情况有了很大的不同:
itools_func - 1000 loops: 656.3ms, per loop: 656.3µs
manual_func - 1000 loops: 4.816s, per loop: 4.816ms
当对数据进行排序时,手动功能可以进一步优化,但它很难打败
itertools
在引擎盖下的功能。而我个人选择itertools.groupby
作为最方便的方式,您要求提高效率,这应该比任何itertools
选项都要快得多:
data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
lookup = {} # lookup map
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
print(result)
# [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
如果数据总是按顺序排列(即元素不混合),则可以在不使用查找表和使用列表理解的情况下进一步优化,以获得最佳性能
更新-一些关于效率和操作的澄清。如果您将测试设置为:
from itertools import groupby
def itools_func(data):
return [list(grp) for k, grp in groupby(data)]
def manual_func(data):
lookup = {}
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
return result
问题在于,这两个函数不会返回相同的值:
test_data = ["a", "a", "b", "c", "c", "b", "a"]
itools_func(test_data) # [['a', 'a'], ['b'], ['c', 'c'], ['b'], ['a']]
manual_func(test_data) # [['a', 'a', 'a'], ['b', 'b'], ['c', 'c']]
从OP的问题中,我了解到他想要后一个(基于他的评论“我对列表进行排序以使值连续”),因为使用排序的列表可以轻松得多。因此,如果我们为这些函数提供一个非常长的列表:
test_data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"] * 10000 # 10000 x the original
在我的系统上,它的时钟如下所示:
itools_func - 100 loops: 2.668s, per loop: 26.68ms
manual_func - 100 loops: 1.005s, per loop: 10.05ms
但是,这对于itertools.groopby
来说是一个不利的设置。如果要对数据进行如下排序:
test_data = ["a"] * 3000 + ["b"] * 2000 + ["c"] * 40000
随着C后端的出现,情况有了很大的不同:
itools_func - 1000 loops: 656.3ms, per loop: 656.3µs
manual_func - 1000 loops: 4.816s, per loop: 4.816ms
当对数据进行排序时,手动功能可以进一步优化,但它很难打败
itertools
在引擎盖下的功能。另一种获得所需输出的方式是使用集合
模块中的defaultdict
(使用此方法的最佳时间为:~=0.02s,与使用groupby
相同):
所以,你现在要做的是:
list(b.values())
>>> [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
获得所需输出的另一种方式是使用
collections
模块中的defaultdict
(使用此方法的最佳时间为:~=0.02s,与使用groupby
相同):
所以,你现在要做的是:
list(b.values())
>>> [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
相等值是否必须连续?我对列表进行排序以使值连续?我对列表进行排序以使值连续。在分组之前,可以(应该?)对列表进行排序。这取决于确切的要求。如果它应该将相等的连续元素分组,则为“否”;如果它应该将所有相等的值(总体)分组并保持顺序,则有更好的方法使用
OrderedDict
和计数器
。如果顺序不重要,相等的元素不是连续的,那么排序是一种有效的策略。对于给定的示例,最有效的方法就是使用groupby
(不进行排序):)同意。OP只是说他们对列表进行排序是为了方便。人们可以(应该)在分组之前对列表进行排序。这取决于具体的要求。如果它应该将相等的连续元素分组,则为“否”;如果它应该将所有相等的值(总体)分组并保持顺序,则有更好的方法使用OrderedDict
和计数器
。如果顺序不重要,相等的元素不是连续的,那么排序是一种有效的策略。对于给定的示例,最有效的方法就是使用groupby
(不进行排序):)同意。OP刚才说他们对列表进行排序是为了方便。你知道如何访问每个元素的计数器值吗?在本例中,4、3和2序列只需使用:[n代表collections.Counter(lst)中的i,n.most_common()]
您知道如何访问每个元素的计数器值吗?在这种情况下,4、3和2序列只需使用:[n代表集合中的i,n.Counter(lst).most_common()]
好吧,如果您关心效率,您可能应该使用defaultdict
,或者至少使用普通dict
的.setdefault
方法,而不是检查如果元素不在查找中:
。还有,我很好奇你为什么说这会快得多。你有时间安排吗<毕竟,代码>itertools.groupby是用C编写的。对于非常短的输入来说,这只会“更”有效。如果<代码>数据很大或很大,那么速度会慢得多。@juanpa.arrivillaga@MSeifert-我已经用一些数字更新了我的帖子。至于为什么不使用de