【问题标题】:Count the frequency of a recurring list -- inside a list of lists计算重复列表的频率——在列表列表中
【发布时间】:2016-05-20 20:42:36
【问题描述】:

我在 python 中有一个列表列表,我需要找出每个子列表出现了多少次。这是一个示例,

from collections import Counter
list1 = [[ 1., 4., 2.5], [ 1., 2.66666667, 1.33333333], 
         [ 1., 2., 2.], [ 1., 2.66666667, 1.33333333], [ 1., 4., 2.5],
         [ 1., 2.66666667, 1.33333333]]   
c = Counter(x for x in iter(list1))
print c

如果列表的元素是可散列的(比如 int),我上面的代码将起作用,但在这种情况下,它们是列表,我得到一个错误

TypeError: unhashable type: 'list'

我如何计算这些列表以便我得到类似的东西

[ 1., 2.66666667, 1.33333333], 3
[ 1., 4., 2.5], 2
[ 1., 2., 2.], 1

【问题讨论】:

  • 将它们转换为元组 - 它们是可散列的。

标签: python list python-2.7 counter


【解决方案1】:

Counter 返回一个像字典一样的对象,它的键必须是可散列的。由于列表不可散列,您可以使用 map 函数将它们转换为 tuple

>>> Counter(map(tuple, list1))
Counter({(1.0, 2.66666667, 1.33333333): 3, (1.0, 4.0, 2.5): 2, (1.0, 2.0, 2.0): 1})

请注意,使用map 将比生成器表达式执行得稍好,因为通过将生成器表达式传递给Counter(),python 将自行从生成器函数中获取值,因为使用内置函数map 具有更高的性能在执行时间方面1.

# Use generator expression
~ $ python -m timeit --setup "list1 = [[ 1., 4., 2.5], [ 1., 2.66666667, 1.33333333],[ 1., 2., 2.], [ 1., 2.66666667, 1.33333333], [ 1., 4., 2.5],[ 1., 2.66666667, 1.33333333]] ;from collections import Counter" "Counter(tuple(x) for x in iter(list1))"
100000 loops, best of 3: 9.86 usec per loop
# Use map
~ $ python -m timeit --setup "list1 = [[ 1., 4., 2.5], [ 1., 2.66666667, 1.33333333],[ 1., 2., 2.], [ 1., 2.66666667, 1.33333333], [ 1., 4., 2.5],[ 1., 2.66666667, 1.33333333]] ;from collections import Counter" "Counter(map(tuple, list1))"
100000 loops, best of 3: 7.92 usec per loop

来自PEP 0289 -- Generator Expressions

生成器表达式的语义等价于创建匿名生成器函数并调用它。例如:

g = (x**2 for x in range(10))
print g.next()

相当于:

def __gen(exp):
    for x in exp:
        yield x**2
g = __gen(iter(range(10)))
print g.next()

请注意,由于 generator expressions 在内存使用方面更好,如果您正在处理大数据,您最好使用 generator expression 而不是 map 功能。

【讨论】:

  • 有没有类似的pythonic查找方式?
  • @WanderingMind 查找是什么意思?
  • 如何知道特定子列表的频率比如list1[5]
  • 只需将map(tuple, list1) 的结果放入一个新列表中,然后执行Counter_object[new_list[5]]
【解决方案2】:

试试这个

list1 = [[ 1., 4., 2.5], [ 1., 2.66666667, 1.33333333], 
         [ 1., 2., 2.], [ 1., 2.66666667, 1.33333333], [ 1., 4., 2.5],
         [ 1., 2.66666667, 1.33333333]]

counter = {}
for el in list1:
    el = str(el)     #This sorts your hashable part or use tuple(el)
    if el in counter:
        counter[el]+=1
    else:
        counter[el]=1

print(counter)

应该输出

{'[1.0, 2.0, 2.0]': 1, '[1.0, 2.66666667, 1.33333333]': 3, '[1.0, 4.0, 2.5]': 2}

【讨论】:

    【解决方案3】:

    只需将列表转换为tuple

    >>> c = Counter(tuple(x) for x in iter(list1))
    >>> c
    Counter({(1.0, 2.66666667, 1.33333333): 3, (1.0, 4.0, 2.5): 2, (1.0, 2.0, 2.0): 1})
    

    记得对查找做同样的事情:

    >>> c[tuple(list1[0])]
    2
    

    【讨论】:

    • 哇,这太简单了。只是为了确保计数器始终按从最高频率到最低频率的顺序返回值?
    • @WanderingMind 不确定你的意思。如果您希望项目按频率降序排列,请使用c.most_common()
    • 您显示的结果的频率从高值到低值。我想知道这是否是预期的结果。看来,您需要使用most_common() 来获得该行为。
    • @WanderingMind Counter 由字典支持,因此当您打印或迭代计数器时它们的打印顺序是随机的。如果您想按特定顺序获取它们,则必须使用most_common
    • @tobias_k, 是否可以打印生成器对象,这样它就不会将 '. at 0x0000027AC384A...>,' 附加到它指向的元组?
    猜你喜欢
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 2017-02-25
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多