代码之家  ›  专栏  ›  技术社区  ›  mykhal

python柱状图一行

  •  47
  • mykhal  · 技术社区  · 14 年前

    编写计算柱状图的python程序有很多种方法。

    通过柱状图,我指的是一个计算对象在 iterable 并在字典中输出计数。例如:

    >>> L = 'abracadabra'
    >>> histogram(L)
    {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
    

    编写此函数的一种方法是:

    def histogram(L):
        d = {}
        for x in L:
            if x in d:
                d[x] += 1
            else:
                d[x] = 1
        return d
    

    写这个函数有更简洁的方法吗?

    如果我们有关于python的字典理解,我们可以写:

    >>> { x: L.count(x) for x in set(L) }
    

    但是由于python 2.6没有它们,我们必须写:

    >>> dict([(x, L.count(x)) for x in set(L)])
    

    虽然这种方法可能是可读的,但它是不有效的:l被遍历多次。此外,对于单寿命生成器,这不起作用;对于迭代器生成器,该函数应该同样有效,例如:

    def gen(L):
        for x in L:
            yield x
    

    我们可以尝试使用 reduce 功能(R.I.P.):

    >>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong!
    

    哎呀,这不管用:密钥名是 'x' 不是 x . :(

    我的结局是:

    >>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {})
    

    (在python 3中,我们必须 list(d.items()) 而不是 d.items() 但这是假设,因为没有 减少 在那里)

    请用更好、更易读的一行字打败我!;)

    9 回复  |  直到 9 年前
        1
  •  76
  •   Eli Courtwright    14 年前

    python 3.x确实有 reduce 你只需要做一个 from functools import reduce . 它还具有“dict-understructions”,这与示例中的语法完全相同。

    python 2.7和3.x还具有 Counter 类,它完全满足您的需要:

    from collections import Counter
    cnt = Counter("abracadabra")
    

    在Python2.6或更早版本中,我个人会使用 defaultdict 分两行进行:

    d = defaultdict(int)
    for x in xs: d[x] += 1
    

    这是干净、高效的,而且对大多数人来说比任何涉及到的事情都容易理解。 减少 .

        2
  •  7
  •   John La Rooy    14 年前

    为OneLines导入模块有点欺骗,所以这里有一个OneLiner,它是O(N),至少可以追溯到python2.4。

    >>> f=lambda s,d={}:([d.__setitem__(i,d.get(i,0)+1) for i in s],d)[-1]
    >>> f("ABRACADABRA")
    {'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
    

    如果你认为 __ 方法很简单,你可以一直这样做

    >>> f=lambda s,d=lambda:0:vars(([setattr(d,i,getattr(d,i,0)+1) for i in s],d)[-1])
    >>> f("ABRACADABRA")
    {'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
    

    :)

        3
  •  6
  •   Sean Vieira    11 年前
    $d{$_} += 1 for split //, 'abracadabra';
    
        4
  •  6
  •   mirandes    9 年前
    import pandas as pd
    
    pd.Series(list(L)).value_counts()
    
        5
  •  5
  •   Walter Cacau    11 年前

    对于Python2.7,您可以使用下面的小列表理解:

    v = list('abracadabra')
    print {x: v.count(x) for x in set(v)}
    
        6
  •  4
  •   dgulino    12 年前

    一个可以追溯到2.3(比Timmerman的稍短,我认为可读性更强):

    L = 'abracadabra'
    hist = {}
    for x in L: hist[x] = hist.pop(x,0) + 1
    print hist
    {'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
    
        7
  •  1
  •   PaulMcG    14 年前

    有一段时间,任何使用 itertools 从定义上说是蟒蛇。不过,这有点不透明:

    >>> from itertools import groupby
    >>> grouplen = lambda grp : sum(1 for i in grp)
    >>> hist = dict((a[0], grouplen(a[1])) for a in groupby(sorted("ABRACADABRA")))
    >>> print hist
    {'A': 5, 'R': 2, 'C': 1, 'B': 2, 'D': 1}
    

    我目前正在运行python 2.5.4。

        8
  •  1
  •   tokland    13 年前

    您的一行程序使用 reduce 几乎没问题,你只需要稍微调整一下:

    >>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {})
    {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
    

    当然,这并不能击败现有的解决方案(也不能在速度上,也不能在脓毒症上),但作为交换,你已经得到了一个很好的、纯功能的代码片段。顺便说一句,如果Python有一个 dict.merge() .

        9
  •  1
  •   Jens Timmerman    12 年前

    我需要一个柱状图实现来在Python2.2到2.7版本中工作,并得出了以下结论:

    >>> L = 'abracadabra'
    >>> hist = {}
    >>> for x in L: hist[x] = hist.setdefault(x,0)+1
    >>> print hist
    {'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
    

    我被伊莱·考特赖特的一篇不守规矩的口述所启发。这些是在Python2.5中引入的,因此不能使用。但是可以使用dict.setdefault(key,default)来模拟它们。

    这基本上是相同的事情,gnibble正在做,但我必须先写这之前,我可以完全理解他的lambda函数。