【问题标题】:Counting occurrences using filter and reduce in Python在 Python 中使用 filter 和 reduce 计算出现次数
【发布时间】:2013-01-22 09:32:34
【问题描述】:
c=["pink", "purple", "black", "yellow", "purple", "indego", "white", "peach"]
import random
import collections
def apply(T):
    i = random.randint(0, 7)
    return c[i]
for x in range(1, 50):
    for ch in map(apply, c):
        print(ch)

我试图弄清楚如何仅使用 filter 和 reduce 函数来计算每个数字的出现次数。

谁能指出我正确的方向?

编辑:为了澄清,我想计算生成某种类型的随机数的次数,而不是列表中的那个!

【问题讨论】:

  • 你能给我们预期的输出吗?
  • 使用collections.Counter。你会用螺丝刀打钉子吗?
  • 不幸的是,我需要使用 Filter 和 Reduce,这就是我苦苦挣扎的原因。预期的输出将是每种颜色的计数,声明它出现了多少次。
  • 我很困惑。你在这里数什么?它只是列表c吗? apply这个函数和random的导入有什么关系...
  • @wim -- 可能是因为某个地方的某个教授正试图教他们函数式编程......

标签: python filter reduce


【解决方案1】:

首先,您的apply 函数会忽略它的参数,这对于您提供给map 的东西来说绝不是一个好兆头。我假设您正在尝试生成一个名为colors 的随机颜色列表,然后计算它;您同样可以用生成器替换它。

Filter、reduce 等是函数式编程概念,通常通过 Python 中的列表推导或生成器更好地处理。这是我的做法:

c = ["pink", "purple", "black", "yellow", "purple", "indego", "white", "peach"]

import random
colors = [c[random.randrange(len(c))] for _ in range(50)]

# now, to count

# (a) the way you'd actually do it in practice:
from collections import Counter
counts = Counter(colors)

# (b) the way you'd actually do it without the collections module
counts = {}
for x in colors:
    if x not in counts:
        counts[x] = 0
    counts[x] += 1

# (c) doing it with reduce...technically.
def add_to_counter(counter, el):
    counter[el] += 1  # can't actually do this in a lambda...
counts = reduce(add_to_counter, colors, Counter())

您也可以执行与 (c) 类似的操作,但不只是使用 Counter 类,方法是维护一个元素列表及其计数,并在 reduce 函数中添加它们,但这只是效率较低且更麻烦的版本同样的事情。

既然你说你使用过滤器和减少,我假设这是一个家庭作业。这很愚蠢,因为这些绝对是解决这个问题的错误工具。但是这里有一种非常低效且难以理解的方法来使用过滤器和减少(以及映射)来解决这个问题,这可能就像您的讲师正在寻找的那样:

from functools import partial
import operator
counts = {}
for x in c:
    counts[x] = reduce(operator.add,
                       map(lambda _: 1, filter(partial(operator.eq, x), colors)),
                       0)

这太可怕了,因为:

  • 需要花费大量精力才能弄清楚发生了什么,而不是上面 (a) 和 (b) 的显而易见性。
  • 您应该始终在 Python 中使用 sum 而不是 reduce(operator.add, ...)
  • 所以,counts[x] = sum(1 for el in colors if el == x) 是相同的(糟糕的)算法,但可读性提高了一百万倍,启动时间也短得多。
  • 即便如此,reduce/summap 也可以只替换为对 len(filter(...)) 的调用(假设 Python 2;在三个中,filter 返回一个迭代器,所以你必须做len(list(filter(...))),很浪费)。
  • 它会为每种颜色传递一次完整的colors 列表,而不仅仅是一次。这使得在您不知道可能结果的完整列表的情况下无法使用,并且在所有情况下效率都大大降低。

有人可以声称的唯一可能的“优势”是它对任何未出现的颜色都包含 0 计数。当然,这对于任何其他解决方案也是微不足道的。

【讨论】:

  • @wim 我不是,因为没有必要这样做。但我的编辑现在使用它。
  • 我正在使用 Python 3.3.0,但出于好奇,我将如何使用 len 方法?
  • 在 Python 2 中,filter 返回一个列表,您可以通过 len 来查看有多少元素。在 3 中,它返回一个没有直接长度的迭代器; sum(1 for x in the_iterator),基本上就是我们在这里所做的,或多或少完成了相同的事情。
  • count = {color: len(filter(lambda x: x == color, c)) for color in set(c)} print count 所以在上述代码的情况下,我将如何得到3.3.0 的输出?
  • 该代码或我的答案中的任何代码都会创建一个字典,其中键为颜色名称并输出计数。要将其打印出来,只需执行print(counts) 或类似for color, count in counts.iteritems(): print("{}: {}".format(color, count)) 的操作。
【解决方案2】:

@杜格尔

下面可以很好地使用 reduce 和 lambda 来计算列表:

    c = ["pink", "purple", "black", "yellow", "purple", "indego", "white", "peach", "test"]
    print reduce(lambda x, y: x + 1, c, 0)

注意 reduce() 的最后一个参数是 0。

【讨论】:

    【解决方案3】:

    我认为这将是最简单的解决方案。它不使用过滤器左右,但它似乎很聪明的解决方案

    counts = [(colors.count(x), x) for x in set(colors)]
    

    您还可以制作字典,而不是元组列表...取决于您使用的 python 解释器版本。

    【讨论】:

    • 这确实 #colors + 1 传递了列表,就像我的答案末尾带有 reduce/filter 的版本一样。使用 Counter/dict 至少同样易于理解,并且在运行时方面具有显着优势。
    • 它需要的代码少了 2 到 3 倍...而且我猜这种方式更优雅...有点 Python 的方式
    • 我强烈反对说“pythonic 方式”是具有相同数量的代码的方式,这种方式不容易理解并且速度慢很多。此外,您的方式仅适用于列表输入,其中 Counter 或使用字典手动执行它适用于迭代器。您对set(colors) 的调用已经完成所有工作,除了加 1!
    猜你喜欢
    • 2012-02-22
    • 1970-01-01
    • 2019-04-04
    • 2019-01-10
    • 2021-09-06
    • 2019-09-24
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    相关资源
    最近更新 更多