在numpy矩阵行/列上应用函数

43 投票
4 回答
85594 浏览
提问于 2025-04-17 06:02

我正在使用Numpy来存储数据到矩阵中。因为我之前用的是R语言,所以在R中有一种非常简单的方法可以对矩阵的行或列(或者两者)应用一个函数。

在Python和Numpy的组合中,有没有类似的功能呢?我可以自己写一个小的实现,但我觉得我想到的大多数版本在效率上会明显低于现有的实现,或者会占用更多的内存。

我希望能避免从Numpy矩阵复制到本地变量之类的操作,这样可以吗?

我想实现的函数主要是一些简单的比较,比如某一列有多少个元素小于数字x,或者有多少个元素的绝对值大于y。

4 个回答

8

我也是从R语言背景过来的,发现缺少一种更灵活的apply方法,可以使用一些短小的自定义函数。我在论坛上看到有人建议使用基本的numpy函数,因为很多函数都能处理数组。不过,我对“原生”的numpy函数处理数组的方式有点困惑(有时候0表示按行处理,1表示按列处理,有时候又正好相反)。

我个人的解决办法是把apply_along_axis和Python中隐式的lambda函数结合起来。对于习惯用R语言的朋友来说,lambda函数应该很容易理解,因为它们和R中的apply、sapply、lapply等函数有点像,都是更偏向于函数式编程的风格。

举个例子,我想对一个矩阵中的变量进行标准化。在R语言中,通常有一个专门的函数(scale),但你也可以很简单地用apply来实现:

(R代码)

apply(Mat,2,function(x) (x-mean(x))/sd(x) ) 

你可以看到,在apply里面的函数体(x-mean(x))/sd(x是我们不能直接在Python的apply_along_axis中输入的部分。用lambda函数就很容易实现这一点,针对一组值来说,比如:

(Python)

import numpy as np
vec=np.random.randint(1,10,10)  # some random data vector of integers

(lambda x: (x-np.mean(x))/np.std(x)  )(vec)

然后,我们只需要把这个放到Python的apply里面,并通过apply_along_axis传入我们感兴趣的数组。

Mat=np.random.randint(1,10,3*4).reshape((3,4))  # some random data vector
np.apply_along_axis(lambda x: (x-np.mean(x))/np.std(x),0,Mat )

当然,lambda函数也可以单独写成一个函数,但我想主要的目的是使用一些比较小的函数,直接放在apply的那一行里。

希望你觉得这个有用!

14

从NumPy数组中根据一个或多个条件选择元素非常简单,使用NumPy的语法就能做到:

>>> import numpy as NP
>>> # generate a matrix to demo the code
>>> A = NP.random.randint(0, 10, 40).reshape(8, 5)
>>> A
  array([[6, 7, 6, 4, 8],
         [7, 3, 7, 9, 9],
         [4, 2, 5, 9, 8],
         [3, 8, 2, 6, 3],
         [2, 1, 8, 0, 0],
         [8, 3, 9, 4, 8],
         [3, 3, 9, 8, 4],
         [5, 4, 8, 3, 0]])


第二列中有多少个元素大于6?

>>> ndx = A[:,1] > 6
>>> ndx
      array([False,  True, False, False,  True,  True,  True,  True], dtype=bool)
>>> NP.sum(ndx)
      5


数组A的最后一列中,有多少个元素的绝对值大于3?

>>> A = NP.random.randint(-4, 4, 40).reshape(8, 5)
>>> A
  array([[-4, -1,  2,  0,  3],
         [-4, -1, -1, -1,  1],
         [-1, -2,  2, -2,  3],
         [ 1, -4, -1,  0,  0],
         [-4,  3, -3,  3, -1],
         [ 3,  0, -4, -1, -3],
         [ 3, -4,  0, -3, -2],
         [ 3, -4, -4, -4,  1]])

>>> ndx = NP.abs(A[:,-1]) > 3
>>> NP.sum(ndx)
      0


数组A的前两行中,有多少个元素大于或等于2?

>>> ndx = A[:2,:] >= 2
>>> NP.sum(ndx.ravel())    # 'ravel' just flattens ndx, which is originally 2D (2x5)
      2

NumPy的索引语法和R语言很相似;如果你对R很熟悉,这里有一些在这方面NumPy和R的主要区别:

NumPy的索引是从0开始的,而R的索引是从1开始的。

NumPy(和Python一样)允许你使用负数索引从右往左索引,例如:

# to get the last column in A
A[:, -1], 

# to get the penultimate column in A
A[:, -2] 

# this is a big deal, because in R, the equivalent expresson is:
A[, dim(A)[0]-2]

NumPy使用冒号“:”表示“未切片”,例如,在R中,要获取数组A的前三行,你会用A[1:3, ]。而在NumPy中,你会用A[0:2, :](在NumPy中,"0"其实不是必须的,实际上更推荐使用A[:2, :])。

45

几乎所有的numpy函数都是在整个数组上操作的,或者可以指定在某一特定的轴(行或列)上操作。

只要你能用numpy函数来定义你的函数,并且这些函数作用于numpy数组或数组的切片,你的函数就会自动在整个数组、行或列上运行。

如果你想得到更具体的建议,问一下如何实现某个特定的函数可能会更有帮助。


Numpy提供了np.vectorizenp.frompyfunc,可以把那些只对数字操作的Python函数变成可以对numpy数组操作的函数。

举个例子,

def myfunc(a,b):
    if (a>b): return a
    else: return b
vecfunc = np.vectorize(myfunc)
result=vecfunc([[1,2,3],[5,6,9]],[7,4,5])
print(result)
# [[7 4 5]
#  [7 6 9]]

(当第二个数组的元素更大时,第一个数组的元素会被替换为第二个数组对应的元素。)

不过别太兴奋;np.vectorizenp.frompyfunc其实只是一些“语法糖”。它们并不会让你的代码运行得更快。如果你原来的Python函数是一个一个值地处理数据,那么np.vectorize也会一个一个地传递数据,这样整个操作会比较慢(相比于使用一些底层C或Fortran实现的numpy函数)。


如果你想统计列x中有多少个元素小于某个数字y,你可以使用这样的表达式:

(array['x']<y).sum()

例如:

import numpy as np
array=np.arange(6).view([('x',np.int),('y',np.int)])
print(array)
# [(0, 1) (2, 3) (4, 5)]

print(array['x'])
# [0 2 4]

print(array['x']<3)
# [ True  True False]

print((array['x']<3).sum())
# 2

撰写回答