numpy总结
最常用的python库,却经常混淆,故对网页上的内容和书籍上内容汇总总结下,未完待续。。。
1 数组基础
1.1数组创建
创建数组有5种常规机制:
- 从其他Python结构(例如,列表,元组)转换
- numpy原生数组的创建(例如,arange、ones、zeros等)
- 从磁盘读取数组,无论是标准格式还是自定义格式
- 通过使用字符串或缓冲区从原始字节创建数组
- 使用特殊库函数(例如,random)
1.1.1将Python array_like对象转换为Numpy数组
常规是从Python列表或元组中创建数组
例如,你可以使用array函数从常规Python列表或元组中创建数组。得到的数组的类型是从Python列表中元素的类型推导出来的。
>>> import numpy as np
>>> a = np.array([2,3,4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')
一个常见的错误,就是调用array
的时候传入多个数字参数,而不是提供单个数字的列表类型作为参数。
>>> a = np.array(1,2,3,4) # WRONG
>>> a = np.array([1,2,3,4]) # RIGHT
array
还可以将序列的序列转换成二维数组,将序列的序列的序列转换成三维数组,等等。
>>> b = np.array([(1.5,2,3), (4,5,6)])
>>> b
array([[ 1.5, 2. , 3. ],
[ 4. , 5. , 6. ]])
也可以在创建时显式指定数组的类型:
>>> c = np.array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[ 1.+0.j, 2.+0.j],
[ 3.+0.j, 4.+0.j]])
1.1.2numpy原生数组的创建
用 NumPy 内置的方法从头创建数组
通常,数组的元素最初是未知的,但它的大小是已知的。因此,NumPy提供了几个函数来创建具有初始占位符内容的数组。这就减少了数组增长的必要,因为数组增长的操作花费很大。
函数zeros
创建一个由0组成的数组,函数 ones
创建一个完整的数组,函数empty
创建一个数组,其初始内容是随机的,取决于内存的状态。默认情况下,创建的数组的dtype是 float64
类型的。
>>> np.zeros( (3,4) )
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
>>> np.ones( (2,3,4), dtype=np.int16 ) # dtype can also be specified
array([[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]],
[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]]], dtype=int16)
>>> np.empty( (2,3) ) # uninitialized, output may vary
array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
[ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
为了创建数字组成的数组,NumPy提供了一个类似于range
的函数,该函数返回数组而不是列表。
>>> np.arange( 10, 30, 5 )
array([10, 15, 20, 25])
>>> np.arange( 0, 2, 0.3 ) # it accepts float arguments
array([ 0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])
当arange
与浮点参数一起使用时,由于有限的浮点精度,通常不可能预测所获得的元素的数量。出于这个原因,通常最好使用linspace
函数来接收我们想要的元素数量的函数,而不是步长(step):
>>> from numpy import pi
>>> np.linspace( 0, 2, 9 ) # 9 numbers from 0 to 2
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
>>> x = np.linspace( 0, 2*pi, 100 ) # useful to evaluate function at lots of points
>>> f = np.sin(x)
另见这些API
array
, zeros
, zeros_like
, ones
, ones_like
, empty
, empty_like
, arange
, linspace
, numpy.random.mtrand.RandomState.rand
, numpy.random.mtrand.RandomState.randn
, fromfunction
, fromfile
1.1.3 从磁盘读取数组
这大概是大数组创建的最常见情况。当然,细节很大程度上取决于磁盘上的数据格式,所以本节只能给出如何处理各种格式的一般指示。
标准二进制格式 #
各种字段都有数组数据的标准格式。下面列出了那些已知的Python库来读取它们并返回numpy数组(可能有其他可能读取并转换为numpy数组的其他数据,因此请检查最后一节)
HDF5: h5py
FITS: Astropy
无法直接读取但不易转换的格式示例是像PIL这样的库支持的格式(能够读取和写入许多图像格式,如jpg,png等)。
常见ASCII格式 #
逗号分隔值文件(CSV)被广泛使用(以及Excel等程序的导出和导入选项)。有很多方法可以在Python中阅读这些文件。python中有CSV函数和pylab函数(matplotlib的一部分)。
更多通用的ascii文件可以在scipy中使用io软件包读取。
自定义二进制格式 #
有各种各样的方法可以使用。如果文件具有相对简单的格式,那么可以编写一个简单的 I/O 库,并使用 numpy fromfile() 函数和 .tofile() 方法直接读取和写入numpy数组(尽管介意你的字节序)!如果存在一个读取数据的良好 C 或 C++ 库,可以使用各种技术来封装该库,但这肯定要做得更多,并且需要更多的高级知识才能与C或C++ 接口。
1.1.4 使用特殊库 #
有些库可用于生成特殊用途的数组,且无法列出所有的这些库。最常见的用途是随机使用许多数组生成函数,这些函数可以生成随机值数组,以及一些实用函数来生成特殊矩阵(例如对角线)。
1.2 数据类型
NumPy支持比Python更多种类的数字类型。本节显示了哪些可用,以及如何修改数组的数据类型。
支持的原始类型与 C 中的原始类型紧密相关:
Numpy 的类型 | C 的类型 | 描述 |
---|---|---|
np.bool | bool | 存储为字节的布尔值(True或False) |
np.byte | signed char | 平台定义 |
np.ubyte | unsigned char | 平台定义 |
np.short | short | 平台定义 |
np.ushort | unsigned short | 平台定义 |
np.intc | int | 平台定义 |
np.uintc | unsigned int | 平台定义 |
np.int_ | long | 平台定义 |
np.uint | unsigned long | 平台定义 |
np.longlong | long long | 平台定义 |
np.ulonglong | unsigned long long | 平台定义 |
np.half / np.float16 | 半精度浮点数:符号位,5位指数,10位尾数 | |
np.single | float | 平台定义的单精度浮点数:通常为符号位,8位指数,23位尾数 |
np.double | double | 平台定义的双精度浮点数:通常为符号位,11位指数,52位尾数。 |
np.longdouble | long double | 平台定义的扩展精度浮点数 |
np.csingle | float complex | 复数,由两个单精度浮点数(实部和虚部)表示 |
np.cdouble | double complex | 复数,由两个双精度浮点数(实部和虚部)表示。 |
np.clongdouble | long double complex | 复数,由两个扩展精度浮点数(实部和虚部)表示。 |
由于其中许多都具有依赖于平台的定义,因此提供了一组固定大小的别名:
Numpy 的类型 | C 的类型 | 描述 |
---|---|---|
np.int8 | int8_t | 字节(-128到127) |
np.int16 | int16_t | 整数(-32768至32767) |
np.int32 | int32_t | 整数(-2147483648至2147483647) |
np.int64 | int64_t | 整数(-9223372036854775808至9223372036854775807) |
np.uint8 | uint8_t | 无符号整数(0到255) |
np.uint16 | uint16_t | 无符号整数(0到65535) |
np.uint32 | uint32_t | 无符号整数(0到4294967295) |
np.uint64 | uint64_t | 无符号整数(0到18446744073709551615) |
np.intp | intptr_t | 用于索引的整数,通常与索引相同 ssize_t |
np.uintp | uintptr_t | 整数大到足以容纳指针 |
np.float32 | float | |
np.float64 / np.float_ | double | 请注意,这与内置python float的精度相匹配。 |
np.complex64 | float complex | 复数,由两个32位浮点数(实数和虚数组件)表示 |
np.complex128 / np.complex_ | double complex | 请注意,这与内置python 复合体的精度相匹配。 |
NumPy数值类型是dtype
(数据类型)对象的实例,每个对象都具有独特的特征。使用后导入NumPy
1.3 数组属性
NumPy的数组类被调用ndarray
。它也被别名所知 array
。请注意,numpy.array
这与标准Python库类不同array.array
,后者只处理一维数组并提供较少的功能。ndarray
对象更重要的属性是:
- ndarray.ndim - 数组的轴(维度)的个数。在Python世界中,维度的数量被称为rank。
- ndarray.shape - 数组的维度。这是一个整数的元组,表示每个维度中数组的大小。对于有 n 行和 m 列的矩阵,
shape
将是(n,m)
。因此,shape
元组的长度就是rank或维度的个数ndim
。 - ndarray.size - 数组元素的总数。这等于
shape
的元素的乘积。 - ndarray.dtype - 一个描述数组中元素类型的对象。可以使用标准的Python类型创建或指定dtype。另外NumPy提供它自己的类型。例如numpy.int32、numpy.int16和numpy.float64。
- ndarray.itemsize - 数组中每个元素的字节大小。例如,元素为
float64
类型的数组的itemsize
为8(=64/8),而complex32
类型的数组的itemsize
为4(=32/8)。它等于ndarray.dtype.itemsize
。 - ndarray.data - 该缓冲区包含数组的实际元素。通常,我们不需要使用此属性,因为我们将使用索引访问数组中的元素。
1.4 数组打印
当您打印数组时,NumPy以与嵌套列表类似的方式显示它,但具有以下布局:
- 最后一个轴从左到右打印,
- 倒数第二个从上到下打印,
- 其余部分也从上到下打印,每个切片用空行分隔。
然后将一维数组打印为行,将二维数据打印为矩阵,将三维数据打印为矩数组表。
>>> a = np.arange(6) # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4,3) # 2d array
>>> print(b)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2,3,4) # 3d array
>>> print(c)
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
有关 reshape
的详情,请参阅下文。
如果数组太大而无法打印,NumPy会自动跳过数组的中心部分并仅打印角点:
>>> print(np.arange(10000))
[ 0 1 2 ..., 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100,100))
[[ 0 1 2 ..., 97 98 99]
[ 100 101 102 ..., 197 198 199]
[ 200 201 202 ..., 297 298 299]
...,
[9700 9701 9702 ..., 9797 9798 9799]
[9800 9801 9802 ..., 9897 9898 9899]
[9900 9901 9902 ..., 9997 9998 9999]]
要禁用此行为并强制NumPy打印整个数组,可以使用更改打印选项set_printoptions
。
>>> np.set_printoptions(threshold=sys.maxsize) # sys module should be imported
2 索引
索引得到的是副本,而不是另一个视图。
2.1单个元素索引
和 Python 列表一样,在一维数组中,你也可以通过中括号指定 索引获取第 i 个值(从 0 开始计数):
请注意,和 Python 列表不同,NumPy 数组是固定类型的。这意味着当你试图将一个浮点值插入一个整型数组时,浮点值会被截短成整型。并 且这种截短是自动完成的,不会给你提示或警告,所以需要特别注意这一点!
In[5]: x1
Out[5]: array([5, 0, 3, 3, 7, 9])
In[6]: x1[0] Out[6]: 5 In[7]: x1[4] Out[7]: 7
为了获取数组的末尾索引,可以用负值索引:
In[8]: x1[-1] Out[8]: 9 In[9]: x1[-2]
Out[9]: 7
在多维数组中,可以用逗号分隔的索引元组获取元素:
In[10]: x2
Out[10]: array([[3, 5, 2, 4], [7, 6, 8, 8], [1, 6, 7, 7]])
In[11]: x2[0, 0]
Out[11]: 3
In[12]: x2[2, 0]
Out[12]: 1
In[13]: x2[2, -1]
Out[13]: 7
也可以用以上索引方式修改元素值:
In[14]: x2[0, 0] = 12 x2
Out[14]: array([[12, [ 7, [ 1,
5, 6, 6,
2, 8, 7,
4], 8], 7]])
请注意,和 Python 列表不同,NumPy 数组是固定类型的。这意味着当你试图将一个浮点值插入一个整型数组时,浮点值会被截短成整型。并 且这种截短是自动完成的,不会给你提示或警告,所以需要特别注意这一点!
In[15]: x1[0] = 3.14159 #这将被截短
x1
Out[15]: array([3, 0, 3, 3, 7, 9])
2.2 索引数组
索引数组返回的是原始数据的副本,而不是切片获取的视图。
索引数组必须是整数类型。数组中的每个值指示要使用的数组中的哪个值代替索引。为了显示:
>>> x = np.arange(10,1,-1)
>>> x
array([10, 9, 8, 7, 6, 5, 4, 3, 2])
>>> x[np.array([3, 3, 1, 8])]
array([7, 7, 9, 2])
由值3,3,1和8组成的索引数组相应地创建一个长度为4的数组(与索引数组相同),其中每个索引由索引数组在被索引的数组中具有的值替换。
允许使用负值,并且与单个索引或切片一样工作:
>>> x[np.array([3,3,-3,8])]
array([7, 7, 4, 2])
索引值超出范围是错误的:
>>> x[np.array([3, 3, 20, 8])]
<type 'exceptions.IndexError'>: index 20 out of bounds 0<=index<9
一般来说,使用索引数组时返回的是与索引数组具有相同形状的数组,但索引的数组的类型和值。作为示例,我们可以使用多维索引数组:
>>> x[np.array([[1,1],[2,3]])]
array([[9, 9],
[8, 7]])
3 切片
正如此前用中括号获取单个数组元素,我们也可以用切片(slice)符号获取子数组,切片符号用冒号(:)表示。NumPy 切片语法和 Python 列 表的标准切片语法相同。为了获取数组 x 的一个切片,可以用以下方式:
x[start:stop:step]
如果以上 3 个参数都未指定,那么它们会被分别设置默认值 start=0、stop= 维度的大小(size of dimension)和 step=1。我们将详细介 绍如何在一维和多维数组中获取子数组。
- 一维子数组
In[16]: x = np.arange(10)
x
Out[16]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In[17]: x[:5] # 前五个元素 Out[17]: array([0, 1, 2, 3, 4])
In[18]: x[5:] # 索引五之后的元素 Out[18]: array([5, 6, 7, 8, 9])
In[19]: x[4:7] # 中间的子数组 Out[19]: array([4, 5, 6])
In[20]: x[::2] # 每隔一个元素
Out[20]: array([0, 2, 4, 6, 8])
In[21]: x[1::2] # 每隔一个元素,从索引1开始 Out[21]: array([1, 3, 5, 7, 9])
你可能会在步长值为负时感到困惑。在这个例子中,start 参数和 stop 参数默认是被交换的。 因此这是一种非常方便的逆序数组的方式:
In[22]: x[::-1] # 所有元素,逆序的
Out[22]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
In[23]: x[5::-2] # 从索引5开始每隔一个元素逆序
Out[23]: array([5, 3, 1])
- 多维子数组
多维切片也采用同样的方式处理,用冒号分隔。例如:
In[24]: x2
Out[24]: array([[12, 5, 2, 4],
[7, 6, 8, 8],
[1 ,6, 7, 7]])
In[25]: x2[:2, :3] # 两行,三列
Out[25]: array([[12, 5, 2],
[ 7, 6, 8]])
In[26]: x2[:3, ::2] # 所有行,每隔一列
Out[26]: array([[12,2],
[ 7,8],
[ 1,7]])
最后,子数组维度也可以同时被逆序:
In[27]: x2[::-1, ::-1]
Out[27]: array([[ 7, 7, 6, 1],
[8, 8 ,6, 7],
[4, 2, 5, 12]])
- 获取数组的行和列
一种常见的需求是获取数组的单行和单列。你可以将索引与切片组合起来实现这个功能,用一个冒号(:)表示空切片:
In[28]: print(x2[:, 0]) # x2的第一列
[12 7 1]
In[29]: print(x2[0, :]) # x2的第一行
[12 5 2 4]
在获取行时,出于语法的简介考虑,可以省略空的切片:
In[30]: print(x2[0]) #等于x2[0, :]
[12 5 2 4]
需要注意的是,二位数组切片获取某一列,得到是数组,而不是二维单列,只有一个纬度。
04. 非副本视图的子数组
关于数组切片有一点很重要也非常有用,那就是数组切片返回的是数组数据的视图,而不是数值数据的副本。这一点也是 NumPy 数组切 片和 Python 列表切片的不同之处:在 Python 列表中,切片是值的副本。
例如此前示例中的那个二维数组:
In[31]: print(x2)
[[12 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
从中抽取一个 2×2 的子数组:
In[32]: x2_sub = x2[:2, :2]
print(x2_sub)
[[12 5]
[ 7 6]]
现在如果修改这个子数组,将会看到原始数组也被修改了!结果如下所示:
In[33]: x2_sub[0, 0] = 99
print(x2_sub)
[[99 5]
[ 7 6]]
In[34]: print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[1 6 7 7]]
这种默认的处理方式实际上非常有用:它意味着在处理非常大的数据集时,可以获取或处理这些数据集的片段,而不用复制底层的数据缓 存。
- 创建数组的副本
尽管数组视图有一些非常好的特性,但是在有些时候明确地复制数组里的数据或子数组也是非常有用的。可以很简单地通过 copy() 方法 实现:
In[35]: x2_sub_copy = x2[:2, :2].copy()
print(x2_sub_copy)
[[99 5]
[ 7 6]]
如果修改这个子数组,原始的数组不会被改变:
In[36]: x2_sub_copy[0, 0] = 42
print(x2_sub_copy)
[[42 5]
[ 7 6]]
In[37]: print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[1 6 7 7]]
4 数组变形和拼接、分裂
4.1数组变形
可以使用各种命令更改数组的形状。请注意,以下三个命令都返回一个修改后的数组,但不会更改原始数组:
ravel
reshape
nT
>>> a.ravel() # returns the array, flattened
array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.])
>>> a.reshape(6,2) # returns the array with a modified shape
array([[ 2., 8.],
[ 0., 6.],
[ 4., 5.],
[ 1., 1.],
[ 8., 9.],
[ 3., 6.]])
>>> a.T # returns the array, transposed
array([[ 2., 4., 8.],
[ 8., 5., 9.],
[ 0., 1., 3.],
[ 6., 1., 6.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)
ndarray.resize
方法会修改数组本身:
>>> a
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.resize((2,6))
>>> a
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])
4.2 数组拼接
拼接或连接 NumPy 中的两个数组主要由 np.concatenate、np.vstack 和 np.hstack 例程实现。np.concatenate 将数组元组或数组列 表作为第一个参数,如下所示:
In[43]: x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
np.concatenate([x, y])
Out[43]: array([1, 2, 3, 3, 2, 1])
你也可以一次性拼接两个以上数组:
In[44]: z = [99, 99, 99]
print(np.concatenate([x, y, z]))
[ 1 2 3 3 2 1 99 99 99]
np.concatenate 也可以用于二维数组的拼接:
In[45]: grid = np.array([[1, 2, 3], [4, 5, 6]])
In[46]: # 沿着第一个轴拼接 np.concatenate([grid, grid])
Out[46]: array([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6]])
In[47]: # 沿着第二个轴拼接(从0开始索引) np.concatenate([grid, grid], axis=1)
Out[47]: array([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
沿着固定维度处理数组时,使用 np.vstack(垂直栈)和 np.hstack(水平栈)函数会更简洁:
In[48]: x = np.array([1, 2, 3])
grid = np.array([[9, 8, 7],
[6, 5, 4]])
垂直栈数组 np.vstack([x, grid])
Out[48]: array([[1, 2, 3],
[9, 8, 7],
[6, 5, 4]])
In[49]: # 水平栈数组
y = np.array([[99], [99]])
np.hstack([grid, y])
Out[49]: array([[ 9, 8, 7, 99],
[ 6, 5, 4, 99]])
与之类似,np.dstack 将沿着第三个维度拼接数组。
4.3 数组分裂
与拼接相反的过程是分裂。分裂可以通过 np.split、np.hsplit 和 np.vsplit 函数来实现。可以向以上函数传递一个索引列表作为参 数,索引列表记录的是分裂点位置:
In[50]: x = [1, 2, 3, 99, 99, 3, 2, 1]
x1, x2, x3 = np.split(x, [3, 5])
print(x1, x2, x3)
[1 2 3] [99 99] [3 2 1]
值得注意的是,N 分裂点会得到 N + 1 个子数组。相关的 np.hsplit 和 np.vsplit 的用法也类似:
In[51]: grid = np.arange(16).reshape((4, 4)) grid
Out[51]: array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
In[52]: upper, lower = np.vsplit(grid, [2])
print(upper)
print(lower)
[[0 1 2 3]
[4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]
In[53]: left, right = np.hsplit(grid, [2])
print(left)
print(right)
[[ 0 1]
[ 4 5]
[ 8 9]
[12 13]]
[[ 2 3]
[ 6 7]
[10 11]
[14 15]]
同样,np.dsplit 将数组沿着第三个维度分裂。
5 通用函数
5.1 数组的运算
NumPy 通用函数的使用方式非常自然,因为它用到了 Python 原生的算术运算符,标准的加、减、乘、除都可以使用:
In[7]: x = np.arange(4)
print("x =", x)
print("x + 5 =", x + 5)
print("x - 5 =", x - 5)
print("x * 2 =", x * 2)
print("x / 2 =", x / 2)
print("x // 2 =", x // 2) #地板除法运算
x = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [ 0. 0.5 1. 1.5]
x // 2 = [0 0 1 1]
还有逻辑非、** 表示的指数运算符和 % 表示的模运算符的一元通用函数:
In[8]: print("-x = ", -x)
print("x ** 2 = ", x ** 2)
print("x % 2 = ", x % 2)
-x = [ 0 -1 -2 -3]
x ** 2 = [0 1 4 9]
x % 2 = [0 1 0 1]
你可以任意将这些算术运算符组合使用。当然,你得考虑这些运算符的优先级:
In[9]: -(0.5*x + 1) ** 2
Out[9]: array([-1. , -2.25, -4. , -6.25])
所有这些算术运算符都是 NumPy 内置函数的简单封装器,例如 + 运算符就是一个 add 函数的封装器:
In[10]: np.add(x, 2)
Out[10]: array([2, 3, 4, 5])
表 2-2 列出了所有 NumPy 实现的算术运算符。 表2-2:NumPy实现的算术运算符
运算符 | 对应的通用函数 | 描述 |
---|---|---|
+ | np.add | 加法运算(即 1 + 1 = 2) |
- | np.subtract | 减法运算(即 3 - 2 = 1) |
- | np.negative | 负数运算( 即 -2) |
* | np.multiply | 乘法运算(即 2 * 3 = 6) |
/ | np.divide | 除法运算(即 3 / 2 = 1.5) |
// | np.floor_divide | 地板除法运算(floor division,即 3 // 2 = 1) |
** | np.power | 指数运算(即 2 ** 3 = 8) |
% | np.mod | 模 / 余数( 即 9 % 4 = 1) |
另外,NumPy 中还有布尔 / 位运算符.
需要注意的是与许多矩阵语言不同,乘积运算符*
在NumPy数组中按元素进行运算。矩阵乘积可以使用@
运算符(在python> = 3.5中)或dot
(不推荐)函数或方法执行.
@也能表示两个数组的点积。
>>> A = np.array( [[1,1],
... [0,1]] )
>>> B = np.array( [[2,0],
... [3,4]] )
>>> A * B # elementwise product
array([[2, 0],
[0, 4]])
>>> A @ B # matrix product
array([[5, 4],
[3, 4]])
>>> A.dot(B) # another matrix product
array([[5, 4],
[3, 4]])
5.2 其他数学函数
5.2.1 绝对值
正如 NumPy 能理解 Python 内置的运算操作,NumPy 也可以理解 Python 内置的绝对值函数:
In[11]: x = np.array([-2, -1, 0, 1, 2])
abs(x)
Out[11]: array([2, 1, 0, 1, 2])
对应的 NumPy 通用函数是 np.absolute,该函数也可以用别名 np.abs 来访问:
In[12]: np.absolute(x)
Out[12]: array([2, 1, 0, 1, 2])
In[13]: np.abs(x)
Out[13]: array([2, 1, 0, 1, 2])
这个通用函数也可以处理复数。当处理复数时,绝对值返回的是该复数的幅度:
In[14]: x = np.array([3 - 4j, 4 - 3j, 2 + 0j, 0 + 1j])
np.abs(x)
Out[14]: array([ 5., 5., 2., 1.])
5.2.2 三角函数
NumPy 提供了大量好用的通用函数,其中对于数据科学家最有用的就是三角函数。首先定义一个角度数组:
In[15]: theta = np.linspace(0, np.pi, 3)
现在可以对这些值进行一些三角函数计算:
In[16]: print("theta = ", theta)
print("sin(theta) = ", np.sin(theta))
print("cos(theta) = ", np.cos(theta))
print("tan(theta) = ", np.tan(theta))
theta = [ 0. 1.57079633 3.14159265]
sin(theta) = [ 0.00000000e+00 1.00000000e+00 1.22464680e-16]
cos(theta) = [ 1.00000000e+00 6.12323400e-17 -1.00000000e+00]
tan(theta) = [ 0.00000000e+00 1.63312394e+16 -1.22464680e-16]
这些值是在机器精度内计算的,所以有些应该是 0 的值并没有精确到 0 。逆三角函数同样可以使用:
In[17]: x = [-1, 0, 1]
print("x = ", x)
print("arcsin(x) = ", np.arcsin(x))
print("arccos(x) = ", np.arccos(x))
print("arctan(x) = ", np.arctan(x))
x = [-1, 0, 1]
arcsin(x) = [-1.57079633 0. 1.57079633]
arccos(x) = [3.14159265 1.57079633 0. ]
arctan(x) = [-0.78539816 0. 0.78539816]
5.2.3 指数和对数
NumPy 中另一个常用的运算通用函数是指数运算:
In[18]: x = [1, 2, 3]
print("x=", x)
print("e^x =", np.exp(x))
print("2^x =", np.exp2(x))
print("3^x=", np.power(3, x))
x = [1, 2, 3]
e^x = [ 2.71828183 7.3890561 20.08553692]
2^x = [ 2. 4. 8.]
3^x = [ 3 9 27]
指数运算的逆运算,即对数运算也是可用的。最基本的 np.log 给出的是以自然数为底数的对数。如果你希望计算以 2 为底数或者以 10 为 底数的对数,可以按照如下示例处理:
In[19]: x = [1, 2, 4, 10]
print("x =", x)
print("ln(x) =", np.log(x))
print("log2(x) =", np.log2(x))
print("log10(x) =", np.log10(x))
x = [1, 2, 4, 10]
ln(x) = [ 0. 0.69314718 1.38629436 2.30258509]
log2(x) = [ 0. 1. 2. 3.32192809]
log10(x) = [ 0. 0.30103 0.60205999 1. ]
还有一些特殊的版本,对于非常小的输入值可以保持较好的精度:
In[20]: x = [0, 0.001, 0.01, 0.1]
print("exp(x) - 1 =", np.expm1(x))
print("log(1 + x) =", np.log1p(x))
exp(x) - 1 = [ 0. 0.0010005 0.01005017 0.10517092]
log(1 + x) = [ 0. 0.0009995 0.00995033 0.09531018]
当 x 的值很小时,以上函数给出的值比 np.log 和 np.exp 的计算更精确。
5.2.4 舍入函数
numpy.around()
around()
函数返回四舍五入到指定精度的值。
numpy.around(num, decimals)
复制
参数:
- num 输入数值。
- decimals 四舍五入的精度。默认值为0,如果是负数,对小数点之前进行四舍五入。
示例
import numpy as np
a = np.array([1.0,5.55, 123, 0.567, 25.532])
print ('数组原始值:')
print (a)
print ('\n')
print ('四舍五入后:')
print (np.around(a))
print (np.around(a, decimals = 1))
print (np.around(a, decimals = -1))
复制
输出
数组原始值:
[ 1. 5.55 123. 0.567 25.532]
四舍五入后:
[ 1. 6. 123. 1. 26. ]
[ 1. 5.6 123. 0.6 25.5]
[ 0. 10. 120. 0. 30. ]
numpy.floor()
floor()
函数用于对数值往小取整。
示例
import numpy as np
a = np.array([-1.7, 1.5, -0.2, 0.6, 10])
print ('数组原始值:')
print (a)
print ('\n')
print ('修改后:')
print (np.floor(a))
复制
输出
数组原始值:
[-1.7 1.5 -0.2 0.6 10. ]
修改后:
[-2. 1. -1. 0. 10.]
复制
numpy.ceil()
ceil()
函数用于对数值往大取整。
示例
import numpy as np
a = np.array([-1.7, 1.5, -0.2, 0.6, 10])
print ('数组原始值:')
print (a)
print ('\n')
print ('修改后:')
print (np.ceil(a))
复制
输出
数组原始值:
[-1.7 1.5 -0.2 0.6 10. ]
修改后:
[-1. 2. -0. 1. 10.]
5.3 统计函数
5.3.1 Order statistics
method | description |
---|---|
amin(a[, axis, out, keepdims, initial, where]) | Return the minimum of an array or minimum along an axis. |
amax(a[, axis, out, keepdims, initial, where]) | Return the maximum of an array or maximum along an axis. |
nanmin(a[, axis, out, keepdims]) | Return minimum of an array or minimum along an axis, ignoring any NaNs. |
nanmax(a[, axis, out, keepdims]) | Return the maximum of an array or maximum along an axis, ignoring any NaNs. |
ptp(a[, axis, out, keepdims]) | Range of values (maximum - minimum) along an axis. |
percentile(a, q[, axis, out, …]) | Compute the q-th percentile of the data along the specified axis. |
nanpercentile(a, q[, axis, out, …]) | Compute the qth percentile of the data along the specified axis, while ignoring nan values. |
quantile(a, q[, axis, out, overwrite_input, …]) | Compute the q-th quantile of the data along the specified axis. |
nanquantile(a, q[, axis, out, …]) | Compute the qth quantile of the data along the specified axis, while ignoring nan values. |
5.3.2#Averages and variances
method | description |
---|---|
median(a[, axis, out, overwrite_input, keepdims]) | Compute the median along the specified axis. |
average(a[, axis, weights, returned]) | Compute the weighted average along the specified axis. |
mean(a[, axis, dtype, out, keepdims]) | Compute the arithmetic mean along the specified axis. |
std(a[, axis, dtype, out, ddof, keepdims]) | Compute the standard deviation along the specified axis. |
var(a[, axis, dtype, out, ddof, keepdims]) | Compute the variance along the specified axis. |
nanmedian(a[, axis, out, overwrite_input, …]) | Compute the median along the specified axis, while ignoring NaNs. |
nanmean(a[, axis, dtype, out, keepdims]) | Compute the arithmetic mean along the specified axis, ignoring NaNs. |
nanstd(a[, axis, dtype, out, ddof, keepdims]) | Compute the standard deviation along the specified axis, while ignoring NaNs. |
nanvar(a[, axis, dtype, out, ddof, keepdims]) | Compute the variance along the specified axis, while ignoring NaNs. |
5.3.3 #Correlating
method | description |
---|---|
corrcoef(x[, y, rowvar, bias, ddof]) | Return Pearson product-moment correlation coefficients. |
correlate(a, v[, mode]) | Cross-correlation of two 1-dimensional sequences. |
cov(m[, y, rowvar, bias, ddof, fweights, …]) | Estimate a covariance matrix, given data and weights. |
5.3.4#Histograms
method | description |
---|---|
histogram(a[, bins, range, normed, weights, …]) | Compute the histogram of a set of data. |
histogram2d(x, y[, bins, range, normed, …]) | Compute the bi-dimensional histogram of two data samples. |
histogramdd(sample[, bins, range, normed, …]) | Compute the multidimensional histogram of some data. |
bincount(x[, weights, minlength]) | Count number of occurrences of each value in array of non-negative ints. |
histogram_bin_edges(a[, bins, range, weights]) | Function to calculate only the edges of the bins used by the histogram function. |
digitize(x, bins[, right]) | Return the indices of the bins to which each value in input array belongs. |
5.4 排序、查找和计数
Sorting, searching, and counting
#Sorting
method | description |
---|---|
sort(a[, axis, kind, order]) | Return a sorted copy of an array. |
lexsort(keys[, axis]) | Perform an indirect stable sort using a sequence of keys. |
argsort(a[, axis, kind, order]) | Returns the indices that would sort an array. |
ndarray.sort([axis, kind, order]) | Sort an array in-place. |
msort(a) | Return a copy of an array sorted along the first axis. |
sort_complex(a) | Sort a complex array using the real part first, then the imaginary part. |
partition(a, kth[, axis, kind, order]) | Return a partitioned copy of an array. |
argpartition(a, kth[, axis, kind, order]) | Perform an indirect partition along the given axis using the algorithm specified by the kind keyword. |
#Searching
method | description |
---|---|
argmax(a[, axis, out]) | Returns the indices of the maximum values along an axis. |
nanargmax(a[, axis]) | Return the indices of the maximum values in the specified axis ignoring NaNs. |
argmin(a[, axis, out]) | Returns the indices of the minimum values along an axis. |
nanargmin(a[, axis]) | Return the indices of the minimum values in the specified axis ignoring NaNs. |
argwhere(a) | Find the indices of array elements that are non-zero, grouped by element. |
nonzero(a) | Return the indices of the elements that are non-zero. |
flatnonzero(a) | Return indices that are non-zero in the flattened version of a. |
where(condition, [x, y]) | Return elements chosen from x or y depending on condition. |
searchsorted(a, v[, side, sorter]) | Find indices where elements should be inserted to maintain order. |
extract(condition, arr) | Return the elements of an array that satisfy some condition. |
#Counting
method | description |
---|---|
count_nonzero(a[, axis]) | Counts the number of non-zero values in the array a. |
5.5 专用的通用函数
sqrt(x, /[, out, where, cast, order, …]) | 以元素方式返回数组的非负平方根。 |
---|---|
square(x, /[, out, where, cast, order, …]) | 返回输入的元素方块。 |
cbrt(x, /[, out, where, cast, order, …]) | 以元素方式返回数组的立方根。 |
reciprocal(x, /[, out, where, cast, …]) | 以元素方式返回参数的倒数。 |
6 聚合
当你面对大量的数据时,第一个步骤通常都是计算相关数据的概括统计值。最常用的概括统计值可能是均值和标准差,这两个值能让你分别概 括出数据集中的“经典”值,但是其他一些形式的聚合也是非常有用的(如求和、乘积、中位数、最小值和最大值、分位数,等等)。
NumPy 有非常快速的内置聚合函数可用于数组,我们将介绍其中的一些。
### 6.1 数组值求和
先来看一个小例子,设想计算一个数组中所有元素的和。Python 本身可用内置的 sum 函数来实现:
In[1]: import numpy as np
In[2]: L = np.random.random(100) sum(L)
Out[2]: 55.61209116604941
它的语法和 NumPy 的 sum 函数非常相似,并且在这个简单的例子中的结果也是一样的:
In[3]: np.sum(L)
Out[3]: 55.612091166049424
但是,因为 NumPy 的 sum 函数在编译码中执行操作,所以 NumPy 的操作计算得更快一些:
In[4]: big_array = np.random.rand(1000000)
%timeit sum(big_array)
%timeit np.sum(big_array)
10 loops, best of 3: 104 ms per loop
1000 loops, best of 3: 442 μs per loop
但是需要注意,sum 函数和 np.sum 函数并不等同,这有时会导致混淆。尤其是它们各自的可选参数都有不同的含义,np.sum 函数是知道数组 的维度的,这一点将在接下来的部分讲解。
6.2 最小值和最大值
同样,Python 也有内置的 min 函数和 max 函数,分别被用于获取给定数组的最小值和最大值:``
In[5]: min(big_array), max(big_array)
Out[5]: (1.1717128136634614e-06, 0.9999976784968716)
NumPy 对应的函数也有类似的语法,并且也执行得更快:
In[6]: np.min(big_array), np.max(big_array)
Out[6]: (1.1717128136634614e-06, 0.9999976784968716)
In[7]: %timeit min(big_array)
%timeit np.min(big_array)
10 loops, best of 3: 82.3 ms per loop
1000 loops, best of 3: 497 μs per loop
对于 min、max、sum 和其他 NumPy 聚合,一种更简洁的语法形式是数组对象直接调用这些方法:
In[8]: print(big_array.min(), big_array.max(), big_array.sum())
1.17171281366e-06 0.999997678497 499911.628197
当你操作 NumPy 数组时,确保你执行的是 NumPy 版本的聚合。
6.3 多维度聚合
一种常用的聚合操作是沿着一行或一列聚合。例如,假设你有一些数据存储在二维数组中:
In[9]: M = np.random.random((3, 4)) print(M)
[[ 0.8967576 0.03783739 0.75952519 0.06682827]
[ 0.8354065 0.99196818 0.19544769 0.43447084]
[ 0.66859307 0.15038721 0.37911423 0.6687194]]
默认情况下,每一个 NumPy 聚合函数将会返回对整个数组的聚合结果:
In[10]: M.sum()
Out[10]: 6.0850555667307118
聚合函数还有一个参数,用于指定沿着哪个轴的方向进行聚合。例如,可以通过指定 axis=0 找到每一列的最小值:
In[11]: M.min(axis=0)
Out[11]: array([ 0.66859307, 0.03783739, 0.19544769, 0.06682827])
这个函数返回四个值,对应四列数字的计算值。同样,也可以找到每一行的最大值:
In[12]: M.max(axis=1)
Out[12]: array([ 0.8967576 , 0.99196818, 0.6687194])
其他语言的用户会对轴的指定方式比较困惑。axis 关键字指定的是数组将会被折叠的维度,而不是将要返回的维度。因此指定 axis=0 意味着第一个轴将要被折叠——对于二维数组,这意味着每一列的值都将被聚合。
6.4 其他聚合函数
NumPy 提供了很多其他聚合函数,但是这里不会详细地介绍它们。另外,大多数的聚合都有对 NaN 值的安全处理策略(NaN-safe),即计 算时忽略所有的缺失值,这些缺失值即特殊的 IEEE 浮点型 NaN 值(关于缺失值更全面的介绍请参见 3.5 节)。有些 NaN-safe 的函数直到 NumPy 1.8 版本才加进去,所以更早版本的 NumPy 并不支持此功能。 表 2-3 提供了一个 NumPy 中可用的聚合函数的清单。 表2-3:NumPy中可用的聚合函数
函数名称 | NaN安全版本 | 描述 |
---|---|---|
np.sum | np.nansum | 计算元素的和 |
np.prod | np.nanprod | 计算元素的积 |
np.mean | np.nanmean | 计算元素的平均值 |
np.std | np.nanstd | 计算元素的标准差 |
np.var | np.nanvar | 计算元素的方差 |
np.min | np.nanmin | 找出最小值 |
np.max | np.nanmax | 找出最大值 |
np.argmin | np.nanargmin | 找出最小值的索引 |
np.argmax | np.nanargmax | 找出最大值的索引 |
np.median | np.nanmedian | 计算元素的中位数 |
np.percentile | np.nanpercentile | 计算基于元素排序的统计值 |
np.any | N/A | 验证任何一个元素是否为真 |
np.all | N/A | 验证所有元素是否为真 |
7 广播
广播可以简单理解为用于不同大小数组的二进制通用函数(加、减、乘等)的一组规则。
2.5.1 广播的介绍
前面曾提到,对于同样大小的数组,二进制操作是对相应元素逐个计算:
In[1]: import numpy as np
In[2]: a = np.array([0, 1, 2])
b = np.array([5, 5, 5])
a + b
Out[2]: array([5, 6, 7])
广播允许这些二进制操作可以用于不同大小的数组。例如,可以简单地将一个标量(可以认为是一个零维的数组)和一个数组相加:
In[3]: a + 5
Out[3]: array([5, 6, 7])
我们可以认为这个操作是将数值 5 扩展或重复至数组 [5, 5, 5],然后执行加法。NumPy 广播功能的好处是,这种对值的重复实际上并没有 发生,但是这是一种很好用的理解广播的模型。 我们同样也可以将这个原理扩展到更高维度的数组。观察以下将一个一维数组和一个二维数组相加的结果:
In[4]: M = np.ones((3, 3))
M
Out[4]: array([[ 1., [ 1., [ 1.,
In[5]: M + a
Out[5]: array([[ 1., [ 1., [ 1.,
1., 1.],
1., 1.],
1., 1.]])
2., 3.],
2., 3.],
2., 3.]])
这里这个一维数组就被扩展或者广播了。它沿着第二个维度扩展,扩展到匹配 M 数组的形状。 以上的这些例子理解起来都相对容易,更复杂的情况会涉及对两个数组的同时广播,例如以下示例:
In[6]: a = np.arange(3) b = np.arange(3)[:, np.newaxis]
print(a) print(b)
[0 1 2] [[0]
[1]
[2]]
In[7]: a + b
Out[7]: array([[0, 1, 2],
[1, 2, 3], [2, 3, 4]])
正如此前将一个值扩展或广播以匹配另外一个数组的形状,这里将 a 和 b 都进行了扩展来匹配一个公共的形状,最终的结果是一个二维数组。 以上这些例子的几何可视化如图 所示。
图 2-4:NumPy 广播的可视化 浅色的盒子表示广播的值。同样需要注意的是,这个额外的内存并没有在实际操作中进行分配,但是这样的想象方式更方便我们从概念上理 解。
广播的规则
NumPy 的广播遵循一组严格的规则,设定这组规则是为了决定两个数组间的操作。
规则 1:如果两个数组的维度数不相同,那么小维度数组的形状将会在最左边补 1。
规则 2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度为 1 的维度扩展以匹配另外一个数组的形状。
规则 3:如果两个数组的形状在任何一个维度上都不匹配并且没有任何一个维度等于 1,那么会引发异常。 为了更清楚地理解这些规则,来看几个具体示例。
- 广播示例1 将一个二维数组与一个一维数组相加:
In[8]: M = np.ones((2, 3))
a = np.arange(3)
来看这两个数组的加法操作。两个数组的形状如下:
M.shape = (2, 3)
a.shape = (3,)
可以看到,根据规则 1,数组 a 的维度数更小,所以在其左边补 1:
M.shape -> (2, 3)
a.shape -> (1, 3)
根据规则 2,第一个维度不匹配,因此扩展这个维度以匹配数组:
M.shape -> (2, 3)
a.shape -> (2, 3)
现在两个数组的形状匹配了,可以看到它们的最终形状都为 (2, 3):
In[9]: M + a
Out[9]: array([[ 1.,2., 3.],
[ 1.,,2., 3.]])
- 广播示例2
来看两个数组均需要广播的示例:
In[10]: a = np.arange(3).reshape((3, 1))
b = np.arange(3)
同样,首先写出两个数组的形状:
a.shape = (3, 1)
b.shape = (3,)
规则 1 告诉我们,需要用 1 将 b 的形状补全:
a.shape -> (3, 1)
b.shape -> (1, 3)
规则 2 告诉我们,需要更新这两个数组的维度来相互匹配:
a.shape -> (3, 3) b.shape -> (3, 3)
因为结果匹配,所以这两个形状是兼容的,可以看到以下结果:
In[11]: a + b
Out[11]: array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4]])
- 广播示例3
现在来看一个两个数组不兼容的示例:
In[12]: M = np.ones((3, 2))
a = np.arange(3)
和第一个示例相比,这里有个微小的不同之处:矩阵 M 是转置的。那么这将如何影响计算呢?两个数组的形状如下:
M.shape = (3, 2)
a.shape = (3,)
同样,规则 1 告诉我们,a 数组的形状必须用 1 进行补全:
M.shape -> (3, 2)
a.shape -> (1, 3)
根据规则 2,a 数组的第一个维度进行扩展以匹配 M 的维度:
M.shape -> (3, 2) a.shape -> (3, 3)
现在需要用到规则 3——最终的形状还是不匹配,因此这两个数组是不兼容的。当我们执行运算时会看到以下结果:
In[13]: M + a
-------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-13-9e16e9f98da6> in <module>()
----> 1 M + a ValueError: operands could not be broadcast together with shapes (3,2) (3,)
请注意,这里可能发生的混淆在于:你可能想通过在 a 数组的右边补 1,而不是左边补 1,让 a 和 M 的维度变得兼容。但是这不被广播的 规则所允许。这种灵活性在有些情景中可能会有用,但是它可能会导致结果模糊。如果你希望实现右边补全,可以通过变形数组来实现 (将会用到 np.newaxis 关键字,详情请参见 2.2 节):
In[14]: a[:, np.newaxis].shape
Out[14]: (3, 1)
In[15]: M + a[:, np.newaxis]
Out[15]: array([[ 1.,1.],
[ 2.,2.],
[ 3.,3.]])
另外也需要注意,这里仅用到了 + 运算符,而这些广播规则对于任意二进制通用函数都是适用的。例如这里的 logaddexp(a, b) 函数, 比起简单的方法,该函数计算 log(exp(a) + exp(b)) 更准确:
In[16]: np.logaddexp(M, a[:, np.newaxis])
Out[16]: array([[ 1.31326169, 1.31326169],
[ 1.69314718, 1.69314718],
[ 2.31326169,2.31326169]])
8 比较、掩码和布尔逻辑
9 花哨的索引
10 数组的排序
尽管 Python 有内置的 sort 和 sorted 函数可以对列表进行排序,但是这里不会介绍这两个函数,因为 NumPy 的 np.sort 函数实际上效率更 高。默认情况下,np.sort 的排序算法是 快速排序,其算法复杂度为 [N log N],另外也可以选择归并排序和堆排序。对于大多数应用场景, 默认的快速排序已经足够高效了。
如果想在不修改原始输入数组的基础上返回一个排好序的数组,可以使用 np.sort:
In[5]: x = np.array([2, 1, 4, 3, 5]) np.sort(x)
Out[5]: array([1, 2, 3, 4, 5])
如果希望用排好序的数组替代原始数组,可以使用数组的 sort 方法:
In[6]: x.sort()
print(x)
[1 2 3 4 5]
另外一个相关的函数是 argsort,该函数返回的是原始数组排好序的索引值:
In[7]: x = np.array([2, 1, 4, 3, 5])
i = np.argsort(x)
print(i)
[1 0 3 2 4]
以上结果的第一个元素是数组中最小元素的索引值,第二个值给出的是次小元素的索引值,以此类推。这些索引值可以被用于(通过花哨的索 引)创建有序的数组:
In[8]: x[i]
Out[8]: array([1, 2, 3, 4, 5])
沿着行或列排序 NumPy 排序算法的一个有用的功能是通过 axis 参数,沿着多维数组的行或列进行排序,例如:
In[9]: rand = np.random.RandomState(42)
X = rand.randint(0, 10, (4, 6))
print(X)
[[6 3 7 4 6 9]
[2 6 7 4 3 7]
[7 2 5 4 1 7]
[5 1 4 0 9 5]]
In[10]: # 对X的每一列排序
np.sort(X, axis=0)
Out[10]: array([[2, 1, 4, 0, 1, 5],
[5, 2, 5, 4, 3, 7],
[6, 3, 7, 4, 6, 7],
[7, 6, 7, 4, 9, 9]])
In[11]: # 对X每一行排序 np.sort(X, axis=1)
Out[11]: array([[3, 4, 6, 6, 7, 9],
[2, 3, 4, 6, 7, 7],
[1, 2, 4, 5, 7, 7],
[0, 1, 4, 5, 5, 9]])
需要记住的是,这种处理方式是将行或列当作独立的数组,任何行或列的值之间的关系将会丢失!
11 结构化数据
12 副本和视图
数组副本是内容与原数组相同,存储在另一个内存位置的数组。
数组视图是由原数组生成的另一个数组,但是与原数组共享数组元素内存,是对同一个内存位置所存储数组元素的不同呈现。
数组引用是原数组的别名,与原数组是同一个数组。
数组赋值
NumPy中,把一个数组赋值给另一个数组,不会拷贝数组,赋值只是对原始数组的引用。对被赋值数组做的更改也会反映在原始数组中。
id()
函数返回数组的通用标识符,类似于C语言中的指针。
示例
import numpy as np
a = np.array([[1,2,3,4],[9,0,2,3],[1,2,3,19]])
print("原始数组:")
print(a)
print('\n')
print("数组a的ID:", id(a))
b = a
print("\n赋值操作 b = a:")
print("\nb的ID:",id(b))
b.shape = 4,3;
print("\nb上的修改也反映到a上:")
print(a)
复制
输出
原始数组:
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
数组a的ID: 140377691416656
赋值操作 b = a:
b的ID: 140377691416656
b上的修改也反映到a上:
[[ 1 2 3]
[ 4 9 0]
[ 2 3 1]
[ 2 3 19]]
复制
ndarray.view()
view()
方法返回新的数组对象,但数组元素数据与原始数组共享,因此是浅拷贝。与前面的情况不同,新数组的维数更改不会影响原数组的维数。
示例
import numpy as np
a = np.array([[1,2,3,4],[9,0,2,3],[1,2,3,19]])
print("原始数组:\n",a)
print("\na的ID:",id(a))
b = a.view()
print("\nb的ID:",id(b))
print("\n打印b的view")
print(b)
b.shape = 4,3;
print("\nb的维数更改不影响a")
print("\n原始数组 \n",a)
print("\nview\n",b)
b[0, 0] = 100
print("\nb的元素更改会影响a")
print("\n原始数组 \n",a)
print("\nview\n",b)
复制
输出
原始数组:
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
a的ID: 140249104167360
b的ID: 140249103376752
打印b的view
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
b的维数更改不影响a
原始数组
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
view
[[ 1 2 3]
[ 4 9 0]
[ 2 3 1]
[ 2 3 19]]
b的元素更改会影响a
原始数组
[[100 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
view
[[100 2 3]
[ 4 9 0]
[ 2 3 1]
[ 2 3 19]]
复制
ndarray.copy()
copy()
返回原始数组的深层副本,该副本不与原始数组共享任何内存,是深拷贝。对数组副本所做的修改不会影响原始数组。
示例
import numpy as np
a = np.array([[1,2,3,4],[9,0,2,3],[1,2,3,19]])
print("原始数组:\n", a)
print("\na的ID:", id(a))
b = a.copy()
print("\nb的ID:", id(b))
print("\n打印b")
print(b)
b.shape = 4,3;
print("\nb的维数更改不影响a")
print("\n原始数组a \n", a)
print("\n数组副本b \n", b)
b[0, 0] = 100
print("\nb的元素更改不影响a")
print("\n原始数组a \n", a)
print("\n数组副本b \n", b)
复制
输出
原始数组:
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
a的ID: 140312719819200
b的ID: 140312509357872
打印b
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
b的维数更改不影响a
原始数组a
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
数组副本b
[[ 1 2 3]
[ 4 9 0]
[ 2 3 1]
[ 2 3 19]]
b的元素更改不影响a
原始数组a
[[ 1 2 3 4]
[ 9 0 2 3]
[ 1 2 3 19]]
数组副本b
[[100 2 3]
[ 4 9 0]
[ 2 3 1]
[ 2 3 19]]
numpy 切片
numpy切片和python切片不同的是,numpy切片是视图,而python切片是副本
矩阵库和线性代数
numpy保护两种数据对象,数组和矩阵
矩阵库
numpy.matlib
不推荐使用
线性代数函数
reference
numpy 中文网 www.numpy.org.cn
<<python 数据科学手册>>
https://www.qikegu.com/docs/3466