2

所以基本上我的问题与'zip'(或izip)有关,而这个问题之前被问过......

有没有更好的方法来迭代两个列表,每次迭代从每个列表中获取一个元素?

如果我有两个变量 - 它们要么是长度为 n 的一维数组,要么是单个值,我如何循环遍历它们以便返回 n 个值。

'zip' kindof 做我想做的事——除了当我传入一个值和一个数组时它会抱怨。

我在下面有一个我的目标示例 - 基本上我有一个 ac 函数,它比 python 进行更有效的计算。我希望它像一些 numpy 函数一样工作 - 可以处理数组和标量的混合,所以我为它编写了一个 python 包装器。但是 - 就像我说的“zip”失败了。我想原则上我可以对 input 进行一些测试,并为标量和数组的每个变体编写不同的语句 - 但似乎 python 应该有更聪明的东西.... ;) 有什么建议吗?

"""
    Example of zip problems.
"""

import numpy as np
import time

def cfun(a, b) :
    """
        Pretending to be c function which doesn't deal with arrays
    """
    if not np.isscalar(a)   or  not np.isscalar(b)  :

        raise Exception('c is freaking out')
    else :

        return a+b

def pyfun(a, b) :
    """
        Python Wrappper - to deal with arrays input
    """

    if not np.isscalar(a)   or  not np.isscalar(b) :
        return np.array([cfun(a_i,b_i) for a_i, b_i in zip(a,b)])

    else :

        return cfun(a, b)

    return cfun(a,b)


a = np.array([1,2])
b= np.array([1,2])
print pyfun(a, b)

a = [1,2]
b = 1
print pyfun(a, b)

编辑 :

非常感谢大家的建议。认为我必须为 np.braodcast 寻求解决方案 - 因为从我的角度来看,它似乎是最简单的.....

4

3 回答 3

4

由于您使用 numpy,因此您不需要zip()迭代多个数组和标量。您可以使用numpy.broadcast()

In [5]:

list(np.broadcast([1,2,3], 10))

Out[5]:

[(1, 10), (2, 10), (3, 10)]

In [6]:

list(np.broadcast([1,2,3], [10, 20, 30]))

Out[6]:

[(1, 10), (2, 20), (3, 30)]

In [8]:

list(np.broadcast([1,2,3], 100, [10, 20, 30]))

Out[8]:

[(1, 100, 10), (2, 100, 20), (3, 100, 30)]
于 2013-03-01T23:49:07.347 回答
1

如果要强制广播,可以使用numpy.lib.stride_tricks.broadcast_arrays. 重用你的cfun

def pyfun(a, b) :
    if not (np.isscalar(a) and np.isscalar(b)) :
        a_bcast, b_bcast = np.lib.stride_tricks.broadcast_arrays(a, b)
        return np.array([cfun(j, k) for j, k in zip(a_bcast, b_bcast)])
    return cfun(a, b)

现在:

>>> pyfun(5, 6)
11
>>> pyfun(5, [6, 7, 8])
array([11, 12, 13])
>>> pyfun([3, 4, 5], [6, 7, 8])
array([ 9, 11, 13])

对于您的特定应用程序,可能没有比 Rob 的纯 Python 事物更具优势,因为您的函数仍在 Python 循环中运行。

于 2013-03-01T23:03:30.153 回答
0

选择性地将每个参数转换为序列的装饰器可能会有所帮助。这是普通的python(不是numpy)版本:

# TESTED
def listify(f):
  def dolistify(*args):
    from collections import Iterable
    return f(*(a if isinstance(a, Iterable) else (a,) for a in args))
  return dolistify

@listify
def foo(a,b):
  print a, b

foo( (1,2), (3,4) )
foo( 1, [3,4] )
foo( 1, 2 )

因此,在您的示例中,我们需要not np.isscalar用作谓词和np.array修饰符。因为装饰器,pyfun总是接收一个数组。

#UNTESTED
def listify(f):
  def dolistify(*args):
    from collections import Iterable
    return f(*(np.array([a]) if np.isscalar(a) else a for a in args))
  return dolistify

@listify
def pyfun(a, b) :
    """
        Python Wrappper - to deal with arrays input
    """

    return np.array([cfun(a_i,b_i) for a_i, b_i in zip(a,b)])

或者,也许您可​​以将相同的想法应用于zip

#UNTESTED
def MyZip(*args):
  return zip(np.array([a]) if np.isscalar(a) else a for a in args)

def pyfun(a, b) :
    """
        Python Wrappper - to deal with arrays input
    """

    return np.array([cfun(a_i,b_i) for a_i, b_i in MyZip(a,b)])
于 2013-03-01T22:51:49.300 回答