在numpy矩阵行/列上应用函数
我正在使用Numpy来存储数据到矩阵中。因为我之前用的是R语言,所以在R中有一种非常简单的方法可以对矩阵的行或列(或者两者)应用一个函数。
在Python和Numpy的组合中,有没有类似的功能呢?我可以自己写一个小的实现,但我觉得我想到的大多数版本在效率上会明显低于现有的实现,或者会占用更多的内存。
我希望能避免从Numpy矩阵复制到本地变量之类的操作,这样可以吗?
我想实现的函数主要是一些简单的比较,比如某一列有多少个元素小于数字x,或者有多少个元素的绝对值大于y。
4 个回答
我也是从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的那一行里。
希望你觉得这个有用!
从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, :])。
几乎所有的numpy函数都是在整个数组上操作的,或者可以指定在某一特定的轴(行或列)上操作。
只要你能用numpy函数来定义你的函数,并且这些函数作用于numpy数组或数组的切片,你的函数就会自动在整个数组、行或列上运行。
如果你想得到更具体的建议,问一下如何实现某个特定的函数可能会更有帮助。
Numpy提供了np.vectorize和np.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.vectorize
和np.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