Python Pandas

12-10第四章2 NumPy基础:数组和矢量计算

2018-12-10  本文已影响0人  渔家傲_俞

4.2 Universal Functions: Fast Element-Wise Array Functions(通用函数:快速点对点数组函数)

universal function, 或 ufunc, 是用来在ndarray中实现element-wise操作的。
可以认为这个ufunc可以把一些简单的函数做快速的向量化封装,输入是一个以上的标量,输出也是一个以上的标量。
很多ufuncs都是点对点的变换,像sqrt或exp:

In [137]: arr = np.arange(10)

In [138]: arr
Out[138]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [139]: np.sqrt(arr) #平方根
Out[139]: 
array([ 0.    ,  1.    ,  1.4142,  1.7321,  2.    ,  2.2361,  2.4495,
        2.6458,  2.8284,  3.    ])

In [140]: np.exp(arr) #以自然常数e为底的指数函数
Out[140]: 
array([    1.    ,     2.7183,     7.3891,    20.0855,    54.5982,
         148.4132,   403.4288,  1096.6332,  2980.958 ,  8103.0839])

这些函数叫做一元通用函数(unary ufuncs)。其他一些函数,比如add或maximum,需要两个数组(binary ufuncs),并返回一个数组作为结果:

In [141]: x = np.random.randn(8)

In [142]: y = np.random.randn(8)

In [143]: x
Out[143]: 
array([-0.0119,  1.0048,  1.3272, -0.9193, -1.5491,  0.0222,  0.7584,
       -0.6605])

In [144]: y
Out[144]: 
array([ 0.8626, -0.01  ,  0.05  ,  0.6702,  0.853 , -0.9559, -0.0235,
       -2.3042])

In [145]: np.maximum(x, y)
Out[145]: 
array([ 0.8626,  1.0048,  1.3272,  0.6702,  0.853 ,  0.0222,  0.7584,   
       -0.6605])

这里mamimum点对点的比较x和y中的元素。

尽管不常见,但ufunc也能返回多个数组。例如modf,这是一个向量版的divmod(python内建函数),modf会返回小数部分和整数部分:

In [146]: arr = np.random.randn(7) * 5

In [147]: arr
Out[147]: array([-3.2623, -6.0915, -6.663 ,  5.3731,  3.6182,  3.45  ,  5.0077])

In [148]: remainder, whole_part = np.modf(arr)

In [149]: remainder
Out[149]: array([-0.2623, -0.0915, -0.663 ,  0.3731,
0.6182,  0.45  ,  0.0077])

In [150]: whole_part
Out[150]: array([-3., -6., -6.,  5.,  3.,  3.,  5.])

Ufuncs可以接受一个out可选参数,这样就能在数组原地进行操作:

In [151]: arr
Out[151]: array([-3.2623, -6.0915, -6.663 ,  5.3731,  3.6182,  3.45  ,  5.0077])

In [152]: np.sqrt(arr)
Out[152]: array([    nan,     nan,     nan,  2.318 ,  1.9022,  1.8574,  2.2378])

In [153]: np.sqrt(arr, arr)
Out[153]: array([    nan,     nan,     nan,  2.318 ,  1.9022,  1.8574,  2.2378])

In [154]: arr
Out[154]: array([    nan,     nan,     nan,  2.318 ,  1.9022,  1.8574,  2.2378])

下面分别列出了一些一元和二元ufunc。


一元
二元

4.3 利用数组进行数据处理

向量化的数组运算比纯python同等程度的运算要快很多。
一个简单的例子,假设我们想要评价函数sqrt(x^2 + y^2)。

在进行书中的内容之前,先举个例子说明meshgrid的效果。meshgrid函数用两个坐标轴上的点在平面上画网格。用法:
[X,Y]=meshgrid(x,y)
[X,Y]=meshgrid(x)与[X,Y]=meshgrid(x,x)是等同的
[X,Y,Z]=meshgrid(x,y,z)生成三维数组,可用来计算三变量的函数和绘制三维立体图
这里,主要以[X,Y]=meshgrid(x,y)为例,来对该函数进行介绍。
[X,Y] = meshgrid(x,y) 将向量x和y定义的区域转换成矩阵X和Y,其中矩阵X的行向量是向量x的简单复制,而矩阵Y的列向量是向量y的简单复制(注:下面代码中X和Y均是数组,在文中统一称为矩阵了)。
假设x是长度为m的向量,y是长度为n的向量,则最终生成的矩阵X和Y的维度都是 nm (注意不是mn)。

In [5]: m, n = (5, 3)
   ...: x = np.linspace(0, 1, m) #linspace(x1,x2,n)是生成从x1到x2的n个向量
   ...: y = np.linspace(0, 1, n)
   ...: X, Y = np.meshgrid(x, y)
   ...: x
   ...:
   ...:
Out[5]: array([0.  , 0.25, 0.5 , 0.75, 1.  ])

In [6]: y
Out[6]: array([0. , 0.5, 1. ])

In [7]: X
Out[7]:
array([[0.  , 0.25, 0.5 , 0.75, 1.  ],
       [0.  , 0.25, 0.5 , 0.75, 1.  ],
       [0.  , 0.25, 0.5 , 0.75, 1.  ]])

In [8]: Y
Out[8]:
array([[0. , 0. , 0. , 0. , 0. ],
       [0.5, 0.5, 0.5, 0.5, 0.5],
       [1. , 1. , 1. , 1. , 1. ]])  

可以看到X和Y的shape都是3x5,用图的话更好理解:
把X和Y画出来后,就可以看到网格了:

import matplotlib.pyplot as plt 
# matplotlib.pyplot是一些命令行风格函数的集合,使matplotlib以类似于MATLAB的方式工作
%matplotlib inline
plt.style.use('ggplot')

plt.plot(X, Y, marker='.', color='blue', linestyle='none')
image.png

可以用zip得到网格平面上坐标点的数据:

z = [i for i in zip(X.flat, Y.flat)]
z

np.meshgrid函数取两个1维的数组,产生一个2维的矩阵,对应于所有两个数组中(x, y)的组合:

In [155]: points = np.arange(-5, 5, 0.01)  # 1000 equally spaced points

In [156]: xs, ys = np.meshgrid(points, points) # xs和ys是一样的
In [157]: ys
Out[157]: 
array([[-5.  , -5.  , -5.  , ..., -5.  , -5.  , -5.  ],
       [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
       [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
       ..., 
       [ 4.97,  4.97,  4.97, ...,  4.97,  4.97,  4.97],
       [ 4.98,  4.98,  4.98, ...,  4.98,  4.98,  4.98],
       [ 4.99,  4.99,  4.99, ...,  4.99,  4.99,  4.99]])
In [158]: z = np.sqrt(xs ** 2 + ys ** 2)

In [159]: z
Out[159]: 
array([[ 7.0711,  7.064 ,  7.0569, ...,  7.0499,  7.0569,  7.064 ],
       [ 7.064 ,  7.0569,  7.0499, ...,  7.0428,  7.0499,  7.0569],
       [ 7.0569,  7.0499,  7.0428, ...,  7.0357,  7.0428, 7.0499],
       ..., 
       [ 7.0499,  7.0428,  7.0357, ...,  7.0286,  7.0357,  7.0428],
       [ 7.0569,  7.0499,  7.0428, ...,  7.0357,  7.0428,  7.0499],
       [ 7.064 ,  7.0569,  7.0499, ...,  7.0428,  7.0499,  7.0569]])

作为第9章的先导,我用matplotlib创建了这个二维数组的可视化:

In [160]: import matplotlib.pyplot as plt

In [161]: plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
#Python在Matplotlib库中,调用imshow()函数实现热图绘制
Out[161]: <matplotlib.colorbar.Colorbar at 0x7f715e3fa630>
In [162]: plt.title("yujiaw")
Out[162]: <matplotlib.text.Text at 0x7f715d2de748>
image.png

1 Expressing Conditional Logic as Array Operations (像数组操作一样表示逻辑条件)

numpy.where函数是一个向量版的三相表达式,x if condition else y。假设我们有一个布尔数组和两个数组:

In [165]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])

In [166]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])

In [167]: cond = np.array([True, False, True, True, False])

假设我们想要根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。列表推导式的写法应该如下所示:

In [168]: result = [(x if c else y)
   .....:           for x, y, c in zip(xarr, yarr, cond)]

In [169]: result
Out[169]: [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]

这么做的话会有很多问题。首先,对于很大的数组,会比较慢。第二,对于多维数组不起作用。但np.where能让我们写得更简洁:

In [170]: result = np.where(cond, xarr, yarr)

In [171]: result
Out[171]: array([ 1.1,  2.2,  1.3,  1.4,  2.5])

np.where中第二个和第三个参数不用必须是数组。where在数据分析中一个典型的用法是基于一个数组,产生一个新的数组值。假设我们有一个随机数字生成的矩阵,我们想要把所有的正数变为2,所有的负数变为-2。用where的话会非常简单:

In [172]: arr = np.random.randn(4, 4)

In [173]: arr
Out[173]: 
array([[-0.5031, -0.6223, -0.9212, -0.7262],
       [ 0.2229,  0.0513, -1.1577,  0.8167],
       [ 0.4336,  1.0107,  1.8249, -0.9975],
       [ 0.8506, -0.1316,  0.9124,  0.1882]])

In [174]: arr > 0
Out[174]: 
array([[False, False, False, False],
       [ True,  True, False,  True],
       [ True,  True,  True, False],
       [ True, False,  True,  True]], dtype=bool)

In [175]: np.where(arr > 0, 2, -2)
Out[175]: 
array([[-2, -2, -2, -2],
       [ 2,  2, -2,  2],
       [ 2,  2,  2, -2],
       [ 2, -2,  2,  2]])

使用np.where,可以将标量和数组结合起来。例如,我可用常数2替换arr中所有正的值:

In [176]: np.where(arr > 0, 2, arr) # set only positive values to 2
Out[176]: 
array([[-0.5031, -0.6223, -0.9212, -0.7262],
       [ 2.    ,  2.    , -1.1577,  2.    ],
       [ 2.    ,  2.    ,  2.    , -0.9975],
       [ 2.    , -0.1316,  2.    ,  2.    ]])

传递给where的数组大小可以不相等,甚至可以是标量值。

2 Mathematical and Statistical Methods (数学和统计方法)

一些能计算统计值的数学函数能基于整个数组,或者沿着一个axis(轴)。可以使用aggregations(often called reductions,汇总,或被叫做降维),比如sum, mean(平均值), and std(标准差).
下面是一些aggregate statistics(汇总统计):

In [177]: arr = np.random.randn(5, 4)

In [178]: arr
Out[178]: 
array([[ 2.1695, -0.1149,  2.0037,  0.0296],
       [ 0.7953,  0.1181, -0.7485,  0.585 ],
       [ 0.1527, -1.5657, -0.5625, -0.0327],
       [-0.929 , -0.4826, -0.0363,  1.0954],
       [ 0.9809, -0.5895,  1.5817, -0.5287]])

In [179]: arr.mean()
Out[179]: 0.19607051119998253

In [180]: np.mean(arr)
Out[180]: 0.19607051119998253

In [181]: arr.sum()
Out[181]: 3.9214102239996507

mean和sum这类的函数可以接受一个axis选项参数,用于计算该轴向上的统计值,最终结果是一个少一维的数组:

In [182]: arr.mean(axis=1)
Out[182]: array([ 1.022 ,  0.1875, -0.502 , -0.0881,  0.3611])

In [183]: arr.sum(axis=0)
Out[183]: array([ 3.1693, -2.6345,  2.2381,  1.1486])

mean, sum这样的函数能接受axis作为参数来计算统计数字,返回的结果维度更少:

In [182]: arr.mean(axis=1)
Out[182]: array([ 1.022 ,  0.1875, -0.502 , -0.0881,  0.3611])

In [183]: arr.sum(axis=0)
Out[183]: array([ 3.1693, -2.6345,  2.2381,  1.1486])

这里,arr.mean(1)是“计算行的平均值”,arr.sum(0)是“计算每列的和”。
其他如cumsum和cumprod之类的方法则不聚合,而是产生一个由中间结果组成的数组:

In [184]: arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])

In [185]: arr.cumsum() #cumsum是matlab中一个函数,通常用于计算一个数组各行的累加
Out[185]: array([ 0,  1,  3,  6, 10, 15, 21, 28])#上面的计算是一个累加的结果0+1=1,1+2=3,3+3=6以类推。

在多维数组中,累加函数(如cumsum)返回的是同样大小的数组,但是会根据每个低维的切片沿着标记轴计算部分聚类:

In [186]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])

In [187]: arr
Out[187]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [188]: arr.cumsum(axis=0)
Out[188]: 
array([[ 0,  1,  2],
       [ 3,  5,  7],
       [ 9, 12, 15]])

In [189]: arr.cumprod(axis=1)
Out[189]: 
array([[  0,   0,   0],
       [  3,  12,  60],
       [  6,  42, 336]])

表4-5列出了全部的基本数组统计方法。后续章节中有很多例子都会用到这些方法。


基本数组统计方法

3.用于布尔型数组的方法

在上面这些方法中,布尔值会被强制转换为1(True)和0(False)。因此,sum经常被用来对布尔型数组中的True值计数:

In [190]: arr = np.random.randn(100)

In [191]: (arr > 0).sum() # Number of positive values
Out[191]: 42

有两个其他方法,any和all,对于布尔数组特别有用。any检测数组中只要有一个ture返回就是true,而all检测数组中都是true才会返回true。

In [192]: bools = np.array([False, False, True, False])

In [193]: bools.any()
Out[193]: True

In [194]: bools.all()
Out[194]: False

这两个方法也能用于非布尔型数组,所有非0元素将会被当做True。

4 Sorting(排序)

跟Python内置的列表类型一样,NumPy数组也可以通过sort方法就地排序:

In [195]: arr = np.random.randn(6)

In [196]: arr
Out[196]: array([ 0.6095, -0.4938,  1.24  , -0.1357,  1.43  , -0.8469])

In [197]: arr.sort()

In [198]: arr
Out[198]: array([-0.8469, -0.4938, -0.1357,  0.6095,  1.24  ,  1.43  ])

多维数组可以在任何一个轴向上进行排序,只需将轴编号传给sort即可:

In [199]: arr = np.random.randn(5, 3)

In [200]: arr
Out[200]: 
array([[ 0.6033,  1.2636, -0.2555],
       [-0.4457,  0.4684, -0.9616],
       [-1.8245,  0.6254,  1.0229],
       [ 1.1074,  0.0909, -0.3501],
       [ 0.218 , -0.8948, -1.7415]])

In [201]: arr.sort(1)

In [202]: arr
Out[202]: 
array([[-0.2555,  0.6033,  1.2636],
       [-0.9616, -0.4457,  0.4684],
       [-1.8245,  0.6254,  1.0229],
       [-0.3501,  0.0909,  1.1074],
       [-1.7415, -0.8948,  0.218 ]])

上面是直接调用数组的sort方法,会改变原有数组的顺序。但如果使用np.sort()函数的话,会生成一个新的排序后的结果。

一个计算分位数的快捷方法是先给数组排序,然后选择某个排名的值:

In [203]: large_arr = np.random.randn(1000)

In [204]: large_arr.sort()

In [205]: large_arr[int(0.05 * len(large_arr))] # 5% quantile,第5%位置的数
Out[205]: -1.5311513550102103

5 Unique and Other Set Logic (单一性和其他集合逻辑)

Numpy也有一些基本的集合操作用于一维数组。np.unique,能返回排好序且不重复的值:

In [206]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])

In [207]: np.unique(names)
Out[207]: 
array(['Bob', 'Joe', 'Will'],
      dtype='<U4')

In [208]: ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])

In [209]: np.unique(ints)
Out[209]: array([1, 2, 3, 4])

拿跟np.unique等价的纯Python代码来对比一下:

In [210]: sorted(set(names))
Out[210]: ['Bob', 'Joe', 'Will']

另一个函数np.in1d用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组:

In [211]: values = np.array([6, 0, 0, 3, 2, 5, 6])

In [212]: np.in1d(values, [2, 3, 6])
Out[212]: array([ True, False, False,  True,  True, False,  True], dtype=bool)
image.png
上一篇下一篇

猜你喜欢

热点阅读