NumPy基础知识

本文翻译自NumPy官方文档,只提取了其中部分内容,文档地址NumPy User Guide

1、NumPy介绍

NumPy的核心是ndarray对象,该对象包含相同数据类型的n维数组。

NumPy arrays和python sequences的区别:

  • NumPy arrays在创建时就固定了大小,python lists可以动态增加;改变ndarray的大小会创建一个新的数组,删除原数组;
  • NumPy arrays的所有元素具有相同的数据类型,因此在内存中占用相同大小的空间;
  • Numpy arrays具有更高的运算效率和更少的代码;
  • 由于python内建序列类型效率较低,很多基于python的包转而使用Numpy arrays提高执行效率。

在科学计算时序列的内存大小和运算速度是十分重要的。举一个简单的例子:一个一维序列和另一个相同大小的一维序列对应相乘。
如果数据存储在两个python列表a和b中,我们需要对每个元素迭代:

c = []
for i in range(len(a)):
    c.append(a[i] * b[i])

这确实可以得到正确结果,但是如果a和b每个数组都包含数十亿的数据,我们将花费巨大的代价在python低效的循环上。用下面的C代码可以更快地完成相同的工作(很明显我们忽略了变量的声明、初始化、内存分配等)。

for (i = 0; i < rows; i++): {
    c[i] = a[i] * b[i];
}

这节省了在解释python代码和操作python对象时的所有开销,但是这以牺牲Python代码带来的好处为代价;再者,随着数据维数的增加,编码工作也要增加;
比如:一个二维数组的例子,C代码扩展为:

for (i = 0; i < rows; i++): {
    for (j = 0; j < columns; j++): {
        c[i][j] = a[i][j]*b[i][j];
    }
}

NumPy为我们提供了两全其美:当涉及到ndarray时,逐个元素的操作是“默认模式”,但逐个元素的操作由预编译的C代码快速执行。在NumPy中:

c = a * b

上面的代码确实以接近C的速度执行之前示例所做的事情,并且正如我们期望的和Python代码一样简单。的确,NumPy语法十分简单!最后一个例子说明了NumPy的两个特征,它们是它的大部分功能的基础:向量化广播

向量化使得代码中没有任何显式循环,索引等——当然,这些事情只是在后台进行优化以及预编译C代码。向量化的代码有许多优点(矩阵运算可以分解,并行计算),其中包括:

  • 向量化代码更简洁更易读;
  • 更少的代码行通常意味着更少的bug
  • 代码更接近于标准的数学符号(通常使其更容易正确编码数学公式)
  • 向量化得到更多“Pythonic”代码。 如果没有向量化,我们的代码就会被低效且难以阅读的循环所困扰。

广播是用于描述隐式元素级操作的术语;一般而言,在NumPy中的所有操作,不仅仅是算术运算,包括逻辑运算,逐位运算,函数运算等,都能使用这种隐式的元素级操作,即它们支持广播。此外,在上面的例子中,a和b可以是相同形状的多维数组、或者是标量和一个数组,甚至是具有不同尺寸的两个数组,较小的数组可“扩展”到与较大的数组尺寸一样,以这样的方式广播的结果是明确的。有关广播的详细“规则”可以参阅numpy.doc.broadcasting。

NumPy完全支持面向对象的方法,并且仍旧从ndarray开始。例如,ndarray是一个类,拥有许多方法和属性。它的许多方法对应的函数都在最外层的NumPy命名空间中,使程序员可以完全自由地编写他喜欢的范例或者最适合他手头任务的范例。

2、NumPy基础

ndarray对象的重要属性:

  • ndarray.ndim \quad 数组维度个数
  • ndarray.shape \quad 数组的维度
  • ndarray.size \quad 数组元素总数
  • ndarray.dtype \quad 数组元素类型
  • ndarray.itemsize \quad 数组元素的字节个数
  • ndarray.data \quad 数组元素保存的地址
2.1 一个例子
import numpy as np

a = np.array([[1, 0, 0],
              [0, 1, 0]])

>> a.ndim
2

>> a.shape
(2, 3)

>> a.size
6

>> a.dtype
int32

>> a.itemsize
4

>> a.data
<memory at 0x0000025DC112B708>

>> type(a)
numpy.ndarray
2.2 创建数组
# 通过python list 或 tuple创建
a = np.array([[2, 3, 4], [5, 6, 7]], dtype='float64')
>> a
[[2. 3. 4.]
 [5. 6. 7.]]

>> a.dtype
float64
# 知道数组大小,不知道数组元素,可以初始化占位符创建数组,这种方式可以最小化数组增长带来的昂贵操作
>> np.zeros((3, 6))      # np.zeros_like
[[0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0.]]

>> np.ones((2, 3, 4), dtype=np.int16)      # np.ones_like
[[[1 1 1 1]
  [1 1 1 1]
  [1 1 1 1]]

 [[1 1 1 1]
  [1 1 1 1]
  [1 1 1 1]]]

>> np.empty((2, 3))      # np.empty_like
[[1.39069238e-309 1.39069238e-309 1.39069238e-309]
 [1.39069238e-309 1.39069238e-309 1.39069238e-309]]
# 利用array生成数组
>> np.arange(10, 20, 5)
[10 15]

>> np.arange(0, 2, 0.3)  # 由于float元素的精度问题,元素数量不好预测,最好使用linspace
[0.  0.3 0.6 0.9 1.2 1.5 1.8]

>> np.linspace(0, 2, 10)  # 生成0到2之间10个元素
[0.         0.22222222 0.44444444 0.66666667 0.88888889 1.11111111
 1.33333333 1.55555556 1.77777778 2.        ]
# numpy随机生成数据
>> np.random.rand(10)  # 生成0到1之间的10个数
[0.77451063 0.35239974 0.26213283 0.5354435  0.95467572 0.18997894
 0.35365499 0.06681928 0.49639935 0.70546093]

>> np.random.randn(10)  # 生成服从高斯分布的10个数
[ 0.41544778  1.26711351  0.16725962 -0.17723555  0.99518915 -1.13421186
 -1.34750089  2.08444806  0.09341871  0.85806855]

>> np.fromfunction(lambda i, j: i == j, (3, 3), dtype=int)  # 对索引执行函数操作
array([[ True, False, False],
       [False,  True, False],
       [False, False,  True]])
2.3 基本操作

数组的算术运算是元素级的,创建新的数组保存结果:

a = np.array([20, 30, 40, 50])
b = np.arange(4)
c = a - b
>> c
[20 29 38 47]

>> b**2
array([0, 1, 4, 9], dtype=int32)

在NumPy数组中*的操作是元素级的,矩阵操作可以使用@或者np.dot:

A = np.array([[1, 1],
              [0, 1]])
B = np.array([[2, 0],
              [3, 4]])
>> A * B
[[2 0]
 [0 4]]

>> A @ B
[[5 4]
 [3 4]]

>> A.dot(B)
[[5 4]
 [3 4]]

+= 和 *= 操作是修改原来的数组的值,没有创建新的数组:

a = np.ones((2, 3), dtype=int)
b = np.random.random((2, 3))
a *= 3
>> a
[[3 3 3]
 [3 3 3]]

b += a
>> b
[[3.26092841 3.62240505 3.46978075]
 [3.44889395 3.46666464 3.15168927]]

# a += b 错误,b是浮点型,不能被自动转成整型
b += a  #正确,a是整型,可以自动转换为浮点型
>> b
[[6.26092841 6.62240505 6.46978075]
 [6.44889395 6.46666464 6.15168927]]

c = a + b  # 相加的结果是更一般更准确的数据类型
>> c
[[6.89445233 6.32851139 6.18594597]
 [6.25692685 6.70965361 6.08098335]]

元素级操作与按行/列操作:

a = np.arange(12).reshape(3,4)
>> a
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

# 元素级操作
>> a.sum()
66

>> a.min()
0

>> a.max()
11

# 按行/列操作
>> a.sum(axis=0)
[12 15 18 21]

>> a.min(axis=1)
[0 4 8]

>> a.cumsum(axis=1)  # 按行累积相加
[[ 0  1  3  6]
 [ 4  9 15 22]
 [ 8 17 27 38]]
2.4 通用性函数

以下函数执行元素级操作:

sin, cos, exp, all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, inv, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sort, std, sum, trace, transpose, var, vdot, vectorize, where

2.6 索引、切片和重复

一维数组:

a = np.arange(10) ** 3
>> a
[  0   1   8  27  64 125 216 343 512 729]

>> a[6]
216

>> a[: 6 : 2]
[ 0  8 64]

>> for i in a:
       print(i ** (1 / 3.))
0.0
1.0
2.0
3.0
3.9999999999999996
5.0
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998

多维数组:

每个轴(维)具有一个索引,用逗号分隔的tuple指示:

def f(x, y):
    return 10 * x + y

b = np.fromfunction(f, (5, 4), dtype=int)

>> b
[[ 0  1  2  3]
 [10 11 12 13]
 [20 21 22 23]
 [30 31 32 33]
 [40 41 42 43]]

>> b[2, 2]
22

>> b[1:5, 2]
[12 22 32 42]

>> b[1:3, :]
[[10 11 12 13]
 [20 21 22 23]]

>> b[-1]
[40 41 42 43]

…用于在复杂索引中代替多个冒号:
如果x是5为数组,则x[1, 2, …]等于x[1, 2, :, :, :]

c = np.random.random((2, 2, 3))
>> c
[[[0.12210617 0.7034944  0.62455154]
  [0.25019738 0.90711139 0.75286827]]

 [[0.57574413 0.95889682 0.88900199]
  [0.32624249 0.40613918 0.07059117]]]

>> c.shape
(2, 2, 3)

>> c[..., 2]
[[0.62455154 0.75286827]
 [0.88900199 0.07059117]]

多维数组的迭代使用第一维进行迭代,如果想要获得数组的每一个元素,可以适应flat迭代数组的每一个元素:

for row in b:
    print(row)
    
for elem in b.flat:
    print(elem, end=' ')

print(end="\n")

[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]
0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43 
3、shape操作
3.1 改变数组的形状

有三个命令ravel、reshape、.T返回修改后的数组,但不改变原数组:

a = np.floor(10 * np.random.random((3, 4)))
>> a
[[9. 9. 7. 6.]
 [6. 1. 5. 2.]
 [2. 2. 7. 9.]]

>> a.shape
(3, 4)

>> a.ravel()  # returns the array flattened
[9. 9. 7. 6. 6. 1. 5. 2. 2. 2. 7. 9.]

>> a.reshape(2, 6)
[[9. 9. 7. 6. 6. 1.]
 [5. 2. 2. 2. 7. 9.]]

>> a.reshape(2, -1)  # 如果一个维度设定为-1,则该维度会被自动计算
[[9. 9. 7. 6. 6. 1.]
 [5. 2. 2. 2. 7. 9.]]

>> a.T
[[9. 6. 2.]
 [9. 1. 2.]
 [7. 5. 7.]
 [6. 2. 9.]]

a.resize((2, 6))  # 修改原数组
>> a
[[9. 9. 7. 6. 6. 1.]
 [5. 2. 2. 2. 7. 9.]]
3.2 不同数组堆叠(stacking)
  • numpy.vstack(tup):Stack arrays in sequence vertically(row wise).
  • numpy.hstack(tup):Stack arrays in sequence horizontally (column wise).
    \quad
  • numpy.row_stack(tup)
  • numpy.column_stack(tup):Stack 1-D arrays as columns into a 2-D array.
    \quad
  • numpy.r_:Translates slice objects to concatenation along the first axis.
  • numpy.c_:Translates slice objects to concatenation along the second axis.
    \quad
  • numpy.concatenate((a1, a2, …), axis=0, out=None):Join a sequence of arrays along an existing axis.
a = np.floor(10 * np.random.random((2, 2)))
b = np.floor(10 * np.random.random((2, 2)))
>> np.vstack((a, b))  # 垂直生长
[[8. 0.]
 [4. 1.]
 [4. 7.]
 [8. 0.]]

>> np.hstack((a, b))  # 水平生长
[[8. 0. 4. 7.]
 [4. 1. 8. 0.]]
>> np.row_stack((a, b))   # 按行堆叠,即垂直生长,等于vstack
[[8. 0.]
 [4. 1.]
 [4. 7.]
 [8. 0.]]
>> np.column_stack((a, b))  # 按列堆叠,即水平生长,只有对于二维数组等于hstack
[[8. 0. 4. 7.]
 [4. 1. 8. 0.]]

# 一维
a = np.array([1, 2])
b = np.array([5, 6])
>> np.column_stack((a, b))
[[1 5]
 [2 6]]
>> np.hstack((a, b))
[1 2 5 6]

# 转化为二维
from numpy import newaxis
>> a[:, newaxis]
[[1]
 [2]]

>> np.column_stack((a[:, newaxis], b[:, newaxis]))
[[1 5]
 [2 6]]
>> np.hstack((a[:, newaxis], b[:, newaxis]))
[[1 5]
 [2 6]]

>> np.r_[a, b]  # 等于vstack
[[3. 0.]
 [6. 2.]
 [4. 5.]
 [7. 2.]]

>> np.c_[a, b]  # 等于hstack
[[3. 0. 4. 5.]
 [6. 2. 7. 2.]]

# 一位向量直接拓展
>> np.r_[[1, 2, 3], [4, 5, 6]]
[1 2 3 4 5 6]

>> np.concatenate((a, b), axis=0)  # 按一维堆叠,即垂直生长,等于vstack
[[3. 0.]
 [6. 2.]
 [4. 5.]
 [7. 2.]]

>> np.concatenate((a, b), axis=1)  # 按二维堆叠,即水平生长,等于hstack
[[3. 0. 4. 5.]
 [6. 2. 7. 2.]]
3.2 将数组划分成几个小数组
a = np.floor(10 * np.random.random((2, 12)))
>> a
[[6. 4. 5. 3. 9. 5. 9. 4. 3. 4. 9. 7.]
 [7. 7. 3. 2. 7. 0. 9. 9. 6. 0. 0. 0.]]

>> np.hsplit(a, 3)  # split a into 3
[array([[6., 4., 5., 3.],
       [7., 7., 3., 2.]]), array([[9., 5., 9., 4.],
       [7., 0., 9., 9.]]), array([[3., 4., 9., 7.],
       [6., 0., 0., 0.]])]

>> np.hsplit(a, (3, 4))  # split a after the third and fourth column
[array([[6., 4., 5.],
       [7., 7., 3.]]), array([[3.],
       [2.]]), array([[9., 5., 9., 4., 3., 4., 9., 7.],
       [7., 0., 9., 9., 6., 0., 0., 0.]])]
4、复制和视图
4.1 引用
a = np.arange(12)
b = a
>> b is a
True

b.shape = 3, 4
>> a.shape
(3, 4)

>> a
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

>> id(a)
2159827486960

>> id(b)
2159827486960
4.2 视图和浅拷贝

浅拷贝只是新建了引用,并没有新建数据

c = a.view()
>> c is a
False

>> c.base is a
True

c.shape = 2, 6
>> a.shape
(3, 4)

>> a
[[100   1   2   3]
 [  4   5   6   7]
 [  8   9  10  11]]

c[0, 0] = 100
>> a
[[100   1   2   3]
 [  4   5   6   7]
 [  8   9  10  11]]

>> c
[[100   1   2   3   4   5]
 [  6   7   8   9  10  11]]

>> id(a)
2159827486960

>> id(c)
2159827515472
4.3 深拷贝

copy方法对数组和数组进行完全拷贝:

d = a.copy()
>> d is a
False

>> d.base is a
False

d[0, 0] = 999
>> d, '\n', a
[[999   1   2   3]
 [  4   5   6   7]
 [  8   9  10  11]] 

[[100   1   2   3]
 [  4   5   6   7]
 [  8   9  10  11]]

NumPy函数和方法预览:

  • Array Creation arange, array, copy, empty, empty_like, eye, fromfile, fromfunction,
    identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r, zeros, zeros_like
  • Conversions ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat
  • Manipulations array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit,
    hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes,
    take, transpose, vsplit, vstack
  • Questions all, any, nonzero, where
  • Ordering argmax, argmin, argsort, max, min, ptp, searchsorted, sort
  • Operations choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask,
    real, sum
  • Basic Statistics cov, mean, std, var
  • Basic Linear Algebra cross, dot, outer, linalg.svd, vdot
5、索引技巧

NumPy比常规python序列提供更多的索引方法。包括整数索引和切片索引,正如前面看到的,数组可以被整数数组和布尔数组索引。

5.1 整数数组索引
a = np.arange(12)**2
>> a
[  0   1   4   9  16  25  36  49  64  81 100 121]

i = np.array([1, 1, 3, 8, 5])
>> a[i]  # 输出索引i指定位置的数据
[ 1  1  9 64 25]

j = np.array([[3, 4], [9, 7]])  # 二维索引数组
>> a[j]  # 具有和j相同的形状
[[ 9 16]
 [81 49]]

当数组a是多维的,单个索引数组只会索引a的第一维。下面的例子展示了使用调色板把图片的标签转换为彩色图片:

palette = np.array([[0, 0, 0],
                   [255, 0, 0],
                   [0, 255, 0],
                   [0, 0, 255],
                   [255, 255, 255]])
image = np.array([[0, 1, 2, 0],
                  [0, 3, 4, 0]])

>> palette[image]
[[[  0   0   0]
  [255   0   0]
  [  0 255   0]
  [  0   0   0]]

 [[  0   0   0]
  [  0   0 255]
  [255 255 255]
  [  0   0   0]]]

对于多维的索引,每一维的索引数组都要有相同的形状,生成的数组与索引数组形状相同:

a = np.arange(12).reshape(3, 4)
>> a
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

i = np.array([[0, 1],
              [1, 2]])
j = np.array([[2, 1],
              [3, 3]])
>> a[i, j]
[[ 2  5]
 [ 7 11]]

>> a[i, 2]
[[ 2  6]
 [ 6 10]]

>> a[:, j]
[[[ 2  1]
  [ 3  3]]

 [[ 6  5]
  [ 7  7]]

 [[10  9]
  [11 11]]]

我们可以把i和j放在一个序列中(list),然后对list索引:

l = (i, j)
>> a[l]
array([[ 2,  5],
       [ 7, 11]])

但是我们不能将i和j放到数组中,因为这个数组将会被解释为对a的第一维进行索引(因为组合之后变成了一个索引数组):

s = np.array([i, j])
>> s  # 变成一个索引数组。只对第一维索引
[[[0 1]
  [1 2]]

 [[2 1]
  [3 3]]]

>> a[s]
IndexError: index 3 is out of bounds for axis 0 with size 3

>> tuple(s)
(array([[0, 1],
       [1, 2]]), array([[2, 1],
       [3, 3]]))

a[tuple(s)]
array([[ 2,  5],
       [ 7, 11]])

关于三维的索引:

x = np.array([[[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]],

              [[11, 21, 31],
               [41, 51, 61],
               [71, 81, 91]],])

i = np.array([[[0],
               [1]]])

j = np.array([[[0, 1, 2]]])

z = np.array([[0]])

>> x[i, j, z]
[[[ 1  4  7]
  [11 41 71]]]

利用索引数组对多维数组的每一维进行索引,i, j, k数组分别索引x的第一维、第二维、第三维,其中,i数组的索引使用第三维元素,j数组的索引使用第二维元素,z数组的索引使用第一维元素。

另一个对数组索引的常用功能是搜索时间序列的最大值:

time = np.linspace(20, 145, 5)
data = np.sin(np.arange(20)).reshape(5, 4)
>> time
[ 20.    51.25  82.5  113.75 145.  ]

>> data
[[ 0.          0.84147098  0.90929743  0.14112001]
 [-0.7568025  -0.95892427 -0.2794155   0.6569866 ]
 [ 0.98935825  0.41211849 -0.54402111 -0.99999021]
 [-0.53657292  0.42016704  0.99060736  0.65028784]
 [-0.28790332 -0.96139749 -0.75098725  0.14987721]]

ind = data.argmax(axis=0)
>> ind
[2 0 3 1]

time_max = time[ind]
>> time_max
[ 82.5   20.   113.75  51.25]

data_max = data[ind, np.arange(data.shape[1])]
>> data_max
[0.98935825 0.84147098 0.99060736 0.6569866 ]

>> np.all(data_max == data.max(axis=0))
True

另外,可以利用数组的索引进行赋值:

a = np.arange(5)
a[[1, 3, 4]] = 0
>> a
[0 0 2 0 0]

如果索引数组中存在相同的索引,赋值会执行多次,保留最后一次赋的值:

a = np.arange(5)
a[[0, 0, 2]] = [1, 2, 3]
>> a
[2 1 3 3 4]

这很合理,但是如果你想使用python中的 += ,结果和你预想的不太一样:

a = np.arange(5)
a[[0, 0, 2]] += 1
>> a
[1 1 3 3 4]

这里0在索引数组中出现了两次,但是第0个元素只增加了一次,这是因为python要求“a+=1”与“a=a+1”相等。

5.2 布尔数组索引

我们利用索引数组选择数组中需要的元素,布尔索引是不同的方法,我们明确地在数组中选择需要哪些元素不需要那些元素。

布尔索引最自然的方法就是使用与原数组相同大小的布尔数组来指示。

a = np.arange(12).reshape(3, 4)
b = a > 4
>> b
[[False False False False]
 [False  True  True  True]
 [ True  True  True  True]]

>> a[b]
[ 5  6  7  8  9 10 11]

a[b] = 0
>> a
[[0 1 2 3]
 [4 0 0 0]
 [0 0 0 0]]

利用布尔索引来索引多维数组和整数索引类似,数组的每一维都使用一维布尔数组选择想要的切片。

a = np.arange(12).reshape(3, 4)
b1 = np.array([False, True, True])
b2 = np.array([True, True, False, False])

>> a
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

>> a[b1, :]  # 选择行,等于a[b1]
[[ 4  5  6  7]
 [ 8  9 10 11]]

>> a[:, b2]  # 选择列
[[0 1]
 [4 5]
 [8 9]]

>> a[b1, b2]  # 这个结果比较奇怪,不会输出对应True的行和列,输出的元素与True的个数相
同,这里输出(1,0)和(2,1),与下面的索引对比
[4 9]

>> a[b1][:, b2]  # 这样索引才能得到对应行列的所有元素
[[4 5]
 [8 9]]

注:一维布尔数组要和每一维想要切片的维度长度相同,如上面的例子b1的长度为3(对应a的行的长度),b2的长度为4(对应a的列的索引)

5.3 ix_函数

ix_函数用来组合不同数组获得每一个多元组的结果,比如:如果你想对向量a,b,c的每一个元素获得计算a+b*c的结果:

a = np.array([2, 3, 4, 5])
b = np.array([8, 5, 4])
c = np.array([5, 4, 6, 8, 3])
ax, bx, cx = np.ix_(a, b, c)
>> ax
[[[2]]
 [[3]]
 [[4]]
 [[5]]]

>> bx
[[[8]
  [5]
  [4]]]

>> cx
[[[5 4 6 8 3]]]

>> ax.shape, bx.shape, cx.shape
(4, 1, 1) (1, 3, 1) (1, 1, 5)

result = ax + bx*cx
>> result
[[[42 34 50 66 26]
  [27 22 32 42 17]
  [22 18 26 34 14]]

 [[43 35 51 67 27]
  [28 23 33 43 18]
  [23 19 27 35 15]]

 [[44 36 52 68 28]
  [29 24 34 44 19]
  [24 20 28 36 16]]

 [[45 37 53 69 29]
  [30 25 35 45 20]
  [25 21 29 37 17]]]

>> result[3, 2, 4]
17
>> a[3]+b[2]*c[4]
17
5、线性代数
5.1 简单的数组操作
import numpy as np
a = np.array([[1.0, 2.0], [3.0, 4.0]])
>> a
[[1. 2.]
 [3. 4.]]

>> a.transpose()
[[1. 3.]
 [2. 4.]]

>> np.linalg.inv(a)
[[-2.   1. ]
 [ 1.5 -0.5]]

u = np.eye(2)
>> u
[[1. 0.]
 [0. 1.]]

>> np.trace(u)  # 矩阵的迹
2.0

j = np.array([[0.0, -1.0], [1.0, 0.0]])
>> j @ j
[[-1.  0.]
 [ 0. -1.]]

y = np.array([[5.], [7.]])
>> np.linalg.solve(a, y)
[[-3.]
 [ 4.]]

>> np.linalg.eig(j)  # 计算矩阵的特征值和特征向量
(array([0.+1.j, 0.-1.j]), array([[0.70710678+0.j ,0.70710678-0.j ],
       							 [0.-0.70710678j, 0.+0.70710678j]]))
6、小技巧
6.1 自动重塑

想要改变一个数组的维度,你可以省略一个维度的长度,这个长度会被自动推出:

a = np.arange(30)
a.shape = 2, -1, 3
>> a.shape
(2, 5, 3)

>> a
[[[ 0  1  2]
  [ 3  4  5]
  [ 6  7  8]
  [ 9 10 11]
  [12 13 14]]

 [[15 16 17]
  [18 19 20]
  [21 22 23]
  [24 25 26]
  [27 28 29]]]
6.2 向量堆叠
x = np.arange(0, 10, 2)
y = np.arange(5)
m = np.vstack([x, y])
xy = np.hstack([x, y])
>> m
[[0 2 4 6 8]
 [0 1 2 3 4]]

>> xy
[0 2 4 6 8 0 1 2 3 4]
7、直方图
import numpy as np
import matplotlib.pyplot as plt

mu, sigma = 2, 0.5
v = np.random.normal(mu, sigma, 10000)
plt.hist(v, bins=50, density=1)
plt.show()

在这里插入图片描述

(n, bins) = np.histogram(v, bins=50, density=True)
plt.plot(.5*(bins[1:]+bins[:-1]), n)
plt.show()

在这里插入图片描述

8、广播

numpy.broadcast

广播描述了numpy如何在算术运算时处理具有不同形状的数组。 受某些约束的影响,较小的数组在较大的数组上“广播”,以便它们具有兼容的大小。

广播提供了一种向量化数组操作的方法,以便利用C而不是Python进行循环。 它可以在不用复制不必要的数据副本的情况下实现这一点,并且通常更加高效。然而,有些情况下广播不太好用,因为它会导致内存使用效率低下,从而减慢计算速度。

NumPy操作通常在元素级操作的基础上在数组对上完成。在最简单的情况下,两个数组必须具有完全相同的形状,如下例所示

a = np.array([1.0, 2.0, 3.0])
b = np.array([2.0, 2.0, 2.0])
>> a * b
[2. 4. 6.]

当阵列的形状满足某些约束时,NumPy的广播规则放宽了这种约束。当在操作中组合数组和标量值时,会发生最简单的广播如下:

a = np.array([1.0, 2.0, 3.0])
b = 2.0
>> a * b
[2. 4. 6.]

结果与前面的例子相同。我们可以将在算术运算时被拓展的标量b想象成具有与a相同形状的数组。 b中的新元素只是原始标量的副本,拓展的说法只是概念性的,NumPy十分智能,可以直接使用原始标量值而无需实际复制副本,因此广播操作能尽可能高效地利用内存和计算。

第二个例子中的代码比第一个例子中的代码更有效,因为广播在乘法过程中移动的内存较少(b是标量而不是数组)。

8.1 一般性的广播规则

在两个数组上运行时,NumPy会逐元素地比较它们的大小。它从尾端开始开始,并向前比较。当满足如下规则时,两者维度兼容:

  • 维度是相等的,或者
  • 其中一个是1
    如果不满足这些条件,则会抛出ValueError:frames are not aligned异常,表示数组具有不兼容的大小。 结果返回数组的大小是跟随输入数组的每个维度的最大大小。

数组不需要具有相同数量的维度。 例如,如果您有一个256x256x3的RGB值数组,并且您希望将图像中的每种颜色缩放不同的值,则可以将图像乘以有3个值的一维
数组。根据广播规则排列这些数组尾端的大小,表明它们是兼容的:
Image (3d array): 256 x 256 x 3
Scale (1d array): 3
Result (3d array): 256 x 256 x 3

当比较的任何一维的大小为1时,使用另一个数组该维的大小。换句话说,大小为1的维度被拓展或“复制”以匹配另一个数组该维的大小。
在以下示例中,A和B数组都具有长度为1的轴,在广播操作时将其扩展为更大的大小:
A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
Result (4d array): 8 x 7 x 6 x 5

下面是一些例子:
A (2d array): 5 x 4
B (1d array): 1
Result (2d array): 5 x 4

A (2d array): 5 x 4
B (1d array): 4
Result (2d array): 5 x 4

A (3d array): 15 x 3 x 5
B (3d array): 15 x 1 x 5
Result (3d array): 15 x 3 x 5

A (3d array): 15 x 3 x 5
B (2d array): 3 x 5
Result (3d array): 15 x 3 x 5

A (3d array): 15 x 3 x 5
B (2d array): 3 x 1
Result (3d array): 15 x 3 x 5

下面是一些不能进行广播的例子:
A (1d array): 3
B (1d array): 4 \quad # trailing dimensions do not match
A (2d array): 2 x 1
B (3d array): 8 x 4 x 3 \quad # second from last dimensions mismatched

广播的例子:

x = np.arange(4)
xx = x.reshape(4, 1)
y = np.ones(5)
z = np.ones((3, 4))

>> x.shape
(4,)

>> y.shape
(5,)

>> xx.shape
(4, 1)

# print(x + y)  # ValueError: operands could not be broadcast together with shapes (4,) (5,) 
>> (xx + y).shape
(4, 5) 

>> xx + y
 [[1. 1. 1. 1. 1.]
 [2. 2. 2. 2. 2.]
 [3. 3. 3. 3. 3.]
 [4. 4. 4. 4. 4.]]

>> (x + z).shape
(3, 4) 

>> x + z  # 维度从后向前比较
[[1. 2. 3. 4.]
 [1. 2. 3. 4.]
 [1. 2. 3. 4.]]

广播提供了一种方便的方式来获取两个数组的外部相乘(或任何其他外部操作)。以下示例显示了两个1-d数组的外部添加操作:

a = np.array([0.0, 10.0, 20.0, 30.0])
b = np.array([1.0, 2.0, 3.0])
>> a[:, np.newaxis] + b
[[ 1.  2.  3.]
 [11. 12. 13.]
 [21. 22. 23.]
 [31. 32. 33.]]

这里newaxis索引操作符将一个新轴插入a,使其成为一个二维4x1数组。将4x1数组与具有形状(3,)的b组合,产生4x3阵列。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值