【问题标题】:How to add count for each unique val in list如何为列表中的每个唯一值添加计数
【发布时间】:2019-04-05 12:41:09
【问题描述】:

假设我有一个列表。

temp = ['A', 'B', 'A', 'B', 'A', 'B']

我正在寻找一种方法来加入里面的字符串的计数。

预期输出:

['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']

我能够通过使用列表理解来解决它,但我正在寻找一种无需指定列表[1, 1, 2, 2, 3, 3] 的方法。有可能吗?

[j + "_" + str(i) for i, j in zip([1, 1, 2, 2, 3, 3], temp)]

【问题讨论】:

    标签: python string list counter defaultdict


    【解决方案1】:

    您可以将collections.defaultdictfor 循环一起使用:

    from collections import defaultdict
    
    L = ['A', 'B', 'A', 'B', 'A', 'B']
    
    dd = defaultdict(int)
    
    res = []
    for item in L:
        dd[item] += 1
        res.append(f'{item}_{dd[item]}')
    
    print(res)
    
    ['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']
    

    【讨论】:

    • 谢谢你:)。是否可以将其写在列表理解中?
    • @Rakesh 是的,但只使用了非常难看的副作用。
    • 您必须在理解中使用布尔表达式,其中对其部分真实性的评估也调用像__setitem__ 这样的方法。相信我,你不想要一个列表。
    • 在这里同意@timgeb,如果您预先计算索引值(这将过于昂贵),列表理解将起作用。但当您需要根据先验值计算它们时,则不需要。
    • @timgeb:事实上,你可以只用一个列表理解来做到这一点,而不会使用像你提到的那样讨厌的副作用 相当 - 请参阅my answer 了解一种方法这样做。不过,我仍然不能 100% 确定我会推荐这样做 :)
    【解决方案2】:

    您可以使用Counterdefaultdict(int) 来记录您遇到某个角色时被看到的次数。

    >>> from collections import Counter
    >>> 
    >>> temp = ['A', 'B', 'A', 'B', 'A', 'B']
    >>> seen = Counter()
    >>> 
    >>> result = []
    >>> for c in temp:
    ...:    seen.update(c)
    ...:    result.append('{}_{}'.format(c, seen[c]))
    ...:    
    >>> result
    >>> ['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']
    

    请注意,如果您希望在temp 中包含多个字符的字符串,seen.update(c) 可能会产生意外结果。演示:

    >>> seen = Counter()
    >>> seen.update('ABC')
    >>> seen
    >>> Counter({'A': 1, 'B': 1, 'C': 1})
    

    根据您想要的计数方式和期望的数据类型,您可能需要使用该行

    seen[c] += 1
    

    而不是

    seen.update(c)
    

    或者,没有任何导入:

    >>> seen = {}
    >>> result = []
    >>> 
    >>> for c in temp:
    ...:    seen[c] = seen.get(c, 0) + 1
    ...:    result.append('{}_{}'.format(c, seen[c]))
    ...:    
    >>> result
    >>> ['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']
    

    【讨论】:

      【解决方案3】:

      您可以使用字典(或者更好的是,collections.defaultdict)来维护每个项目的计数:

      from collections import defaultdict
      
      lst = ['A', 'B', 'A', 'B', 'A', 'B']
      lst2 = []
      d = defaultdict(int)
      
      for item in lst:
          d[item] += 1
          lst2.append('{}_{}'.format(item, d[item]))
      
      print(lst2)  # ['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']
      

      要在此处使用列表推导式,您需要某种方式在迭代原始列表时更新每个项目的状态(即计数器)。为此,您可以使用带有默认参数的函数,例如:

      def get_count(item, d=defaultdict(int)):
          d[item] += 1
          return '{}_{}'.format(item, d[item])
      
      lst2 = [get_count(item) for item in lst]
      print(lst2)  # ['A_1', 'B_1', 'A_2', 'B_2', 'A_3', 'B_3']
      

      【讨论】:

        【解决方案4】:

        实际上(正如 OP 在某些 cmets 中要求的那样)仅通过列表理解就可以做到这一点,而不会产生太多不良副作用。我不确定这是否一定是个好主意 - 有些人可能觉得它不是最容易理解的代码:

        from collections import defaultdict
        import itertools
        
        temp = ['A', 'B', 'A', 'B', 'A', 'B']
        
        result = [j + "_" + str(next(c[j]))
                  for c in [defaultdict(itertools.count)]
                  for j in temp]
        

        【讨论】:

        • 很好,但我同意,这里的循环更具可读性。并且可能会更高效。
        • @Baldrickk:是的,我不提倡使用它,至少在没有认真考虑的情况下!如果需要高性能,请始终衡量:)
        • 对于 Python 3,您应该将 next() 方法调用替换为 next() 函数。这也适用于 Python 2,所以它更好,因为更便携的解决方案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 2021-02-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多