对数组的每一行应用多个函数

2024-10-02 18:20:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个numpy数组,它只有几个非零项,可以是正的,也可以是负的。E、 g.类似这样的事情:

myArray = np.array([[ 0.        ,  0.        ,  0.        ],
       [ 0.32, -6.79,  0.        ],
       [ 0.        ,  0.        ,  0.        ],
       [ 0.        ,  1.5        ,  0.        ],
       [ 0.        ,  0.        , -1.71]])

最后,我希望收到一个列表,其中该列表的每个条目对应于myArray的一行,并且是依赖于myArray的相应行和另一个list的条目的函数输出的累积乘积(在下面的示例中,它被称为l)。 各个术语取决于myArray条目的符号:当它为正时,我应用“funPos”;当它为负时,我应用“funNeg”,如果条目为0,则该项将为1。因此,在上面的示例数组中,它将是:

^{pr2}$

我实现如下所示,它给了我想要的输出(注意:这只是一个高度简化的玩具示例;实际的矩阵要大得多,函数更复杂)。我遍历数组的每一行,如果行的和是0,我不需要做任何计算,输出就是1。如果它不等于0,我遍历这一行,检查每个值的符号并应用适当的函数。在

import numpy as np
def doCalcOnArray(Array1, myList):

    output = np.ones(Array1.shape[0]) #initialize output

    for indRow,row in enumerate(Array1):

    if sum(row) != 0: #only then calculations are needed
        tempProd = 1. #initialize the product that corresponds to the row
        for indCol, valCol in enumerate(row):

        if valCol > 0:
            tempVal = funPos(valCol, myList[indCol])

        elif valCol < 0:
            tempVal = funNeg(valCol, myList[indCol])

        elif valCol == 0:
            tempVal = 1

        tempProd = tempProd*tempVal

        output[indRow] = tempProd

    return output 

def funPos(val1,val2):
    return val1*val2

def funNeg(val1,val2):
    return val1*(val2+1)

myArray = np.array([[ 0.        ,  0.        ,  0.        ],
       [ 0.32, -6.79,  0.        ],
       [ 0.        ,  0.        ,  0.        ],
       [ 0.        ,  1.5        ,  0.        ],
       [ 0.        ,  0.        , -1.71]])     

l = [1.1, 2., 3.4]

op = doCalcOnArray(myArray,l)
print op

输出是

[ 1.      -7.17024  1.       3.      -7.524  ]

哪一个是理想的。
我的问题是,是否有一种更有效的方法来实现这一点,因为对于大型阵列来说,这是相当“昂贵”的。在

编辑: 我接受了Gabijit的回答,因为他提出的纯numpy解决方案似乎是我正在处理的阵列中最快的解决方案。请注意,RaJa也有一个很好的工作解决方案,需要panda和dave的解决方案很好地工作,可以作为一个很好的例子,如何使用发电机和numpy的“沿_轴应用”。在


Tags: 函数numpy示例outputnp数组解决方案row
3条回答

那么,让我们看看我是否理解你的问题。在

  1. 要将矩阵的元素映射到新矩阵,以便:
    • 0映射到1
    • x>0映射到funPos(x)
    • ^{cd6}映射到
  2. 你要计算这个新矩阵行中所有元素的乘积。在

所以,我要这样做:

1号文件:

def myFun(a):
    if a==0:
        return 1
    if a>0:
        return funPos(a)
    if a<0:
        return funNeg(a)

newFun = np.vectorize(myFun)
newArray = newFun(myArray)

对于2:

^{pr2}$

编辑:要将索引传递给funPos,funNeg,您可以执行以下操作:

# Python 2.7
r,c = myArray.shape
ctr = -1       # I don't understand why this should be -1 instead of 0
def myFun(a):
    global ctr
    global c
    ind = ctr % c
    ctr += 1
    if a==0:
        return 1
    if a>0:
        return funPos(a,l[ind])
    if a<0:
        return funNeg(a,l[ind])

这是我尝试过的-使用reduce,map。我不知道这有多快-但这是你想做的吗?在

编辑4:最简单、最易读的——使l成为一个numpy数组,然后极大地简化where。在

import numpy as np
import time

l = np.array([1.0, 2.0, 3.0])

def posFunc(x,y):
    return x*y

def negFunc(x,y):
    return x*(y+1)

def myFunc(x, y):
    if x > 0:
        return posFunc(x, y)
    if x < 0:
        return negFunc(x, y)
    else:
        return 1.0

myArray = np.array([
        [ 0.,0.,0.],
        [ 0.32, -6.79,  0.],
        [ 0.,0.,0.],
        [ 0.,1.5,0.],
        [ 0.,0., -1.71]])

t1 = time.time()
a = np.array([reduce(lambda x, (y,z): x*myFunc(z,l[y]), enumerate(x), 1) for x in myArray])
t2 = time.time()
print (t2-t1)*1000000
print a

基本上让我们看一下最后一行,它说的是将enumerate(xx)中的东西累加,从1开始(最后一个参数是reduce)。myFunc只接受myArray(row)中的元素和l中的element@index row,并根据需要将它们相乘。在

我的输出和你的不一样,所以我不确定这是否正是你想要的,但也许你可以遵循逻辑。在

我也不太确定这对大型阵列来说有多快。在

编辑:以下是一种“纯粹的裸体方式”来做到这一点。在

^{pr2}$

让我尽力解释zip(*np.where(my != 0))部分。np.where只返回两个numpy数组第一个数组是row的索引,第二个数组是与条件(my != 0)匹配的列的索引。我们获取这些索引的一个元组,然后使用array.itemsetarray.item,谢天谢地,列索引对我们是免费的,所以我们可以在列表l中获取元素@that index。这应该比以前更快(而且可读的数量级!!)。需要timeit来确定它是否真的存在。在

编辑2:不必分别调用,因为一次调用就可以完成正反两次调用np.where(my != 0)。在

我想这个裸体功能对你会有帮助

numpy.apply_along_axis

这里有一个实现。我还警告不要检查数组的和是否为0。由于机器精度限制,将浮点与0进行比较可能会产生意外的行为。如果你有-5和5,总和是零,我不确定这是你想要的。我使用numpy的any()函数查看是否有非零。为了简单起见,我还将您的列表(my_list)纳入全球范围。在

import numpy as np


my_list = 1.1, 2., 3.4

def func_pos(val1, val2):
    return val1 * val2

def func_neg(val1, val2):
    return val1 *(val2 + 1)


def my_generator(row):
    for i, a in enumerate(row):
        if a > 0:
            yield func_pos(a, my_list[i])
        elif a < 0:
            yield func_neg(a, my_list[i])
        else:
            yield 1


def reduce_row(row):
    if not row.any():
        return 1.0
    else:
        return np.prod(np.fromiter(my_generator(row), dtype=float))


def main():
    myArray = np.array([
            [ 0.        ,  0.        ,  0.        ],
            [ 0.32, -6.79,  0.        ],
            [ 0.        ,  0.        ,  0.        ],
            [ 0.        ,  1.5        ,  0.        ],
            [ 0.        ,  0.        , -1.71]])
    return np.apply_along_axis(reduce_row, axis=1, arr=myArray)

有可能有更快的实现,我认为沿_轴应用_实际上只是一个隐藏的循环。在

我没有测试,但我敢打赌这比你刚开始的要快,而且内存效率应该更高。在

相关问题 更多 >