0

我编写了以下函数用于使用 2 个参数进行线性回归(其背后的实际数学与这个问题无关)。它需要两个函数 ,f1f2两个列表xs, ys

def lr2par(f1, f2, xs, ys):
    c11 = sum(map(lambda x: (f1(x))**2, xs))
    c12 = sum(map(lambda x: f1(x) * f2(x), xs))
    c22 = sum(map(lambda x: (f2(x))**2, xs))
    d1 = sum(map(lambda x, y: y*f1(x), xs,ys))
    d2 = sum(map(lambda x, y: y*f2(x), xs,ys))

    a1 = -(c22*d1 - c12*d2)/(c12*c12 - c11*c22)
    a2 = (c12*d1 - c11*d2)/(c12*c12 - c11*c22)

    return (c11, c12, c22, d1, d2, a1, a2)

它按预期工作,xs并且ys是列表。但是,正如您所看到的,它是以一种非常实用的风格编写的,所以我当然希望能够在函数式代码中优雅地使用这个函数。这包括map在将函数输入到函数之前调用列表中的函数,例如ys本例中的参数:

lr2par(lambda x: x, lambda x: 1, [1, 3, 5, 7], map(math.log, [130, 150, 175, 210]))

这对我来说看起来很自然,我希望它能够工作(虽然我是一个 python 菜鸟)。事实证明它没有。我很确定问题是ys参数现在不再是一个列表,而是一个迭代器(这似乎是使用 python 中的功能工具时的主要类型)只能迭代一次,所以当它涉及线

d2 = sum(map(lambda x, y: y*f2(x), xs,ys))

ys只是空的。我想以一种惯用的函数式 Python 方式解决这个问题。我目前的解决方案是添加行

xs = list(xs)
ys = list(ys)

到函数体的开头。这行得通,但这是解决问题的好方法吗?我是否必须将这些行添加到几乎所有使用对象集合的函数中,并且可以很好地与 和 之类的函数map一起filter使用zip

4

1 回答 1

2

通常,您不能对可迭代对象进行两次迭代。如果需要,您必须先将其转换为序列,就像您所做的那样list——这实际上是一个非常标准的解决方案。

这样做的主要问题是它可能效率低下,因为它会复制任何进入的列表或元组,因此您可能需要检查这些并list用这个辅助函数替换:

from collections import Sequence  # ABC for lists and tuples

def tosequence(it):
    """Convert iterable to sequence, avoiding unnecessary copies."""
    return it if isinstance(it, Sequence) else list(it)

(作为一个站点说明,如果您替换maplambda使用生成器理解,您的代码会变得更具可读性;如果您将其重写为使用 NumPy,它也可能会变得更快,我建议在 Python 中处理任何数字。)

于 2013-05-28T14:18:06.493 回答