numpy中文网:https://www.numpy.org.cn/about/
安装:pip install numpy
/pip3 install pandas(Python3)
NumPy是使用Python进行科学计算的基础包。它包含如下的内容:
- NumPy包的核心是 ndarray 对象。它封装了python原生的同数据类型的 n 维数组,为了保证其性能优良,其中有许多操作都是代码在本地进行编译后执行的。
- 复杂的(广播)功能。/ 矢量化
- 用于集成C / C ++和Fortran代码的工具。
- 有用的线性代数,傅里叶变换和随机数功能。
广播是用于描述操作的隐式逐元素行为的术语; 一般来说,在NumPy中,所有操作,不仅仅是算术运算,而是逻辑,位,功能等,都以这种隐式的逐元素方式表现,即它们进行广播。
NumPy数组 和 原生Python Array(数组)之间有几个重要的区别:
- NumPy 数组在创建时具有固定的大小,与Python的原生数组对象(可以动态增长)不同。更改ndarray的大小将创建一个新数组并删除原来的数组。
- NumPy 数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。 例外情况:Python的原生数组里包含了NumPy的对象的时候,这种情况下就允许不同大小元素的数组。
- NumPy 数组有助于对大量数据进行高级数学和其他类型的操作。通常,这些操作的执 行效率更高,比使用Python原生数组的代码更少。
- 越来越多的基于Python的科学和数学软件包使用NumPy数组; 虽然这些工具通常都支持Python的原生数组作为参数,但它们在处理之前会还是会将输入的数组转换为NumPy的数组,而且也通常输出为NumPy数组。换句话说,为了高效地使用当今科学/数学基于Python的工具(大部分的科学计算工具),你只知道如何使用Python的原生数组类型是不够的 - 还需要知道如何使用 NumPy 数组。
NumPy的数组类被调用ndarray
。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 - 该缓冲区包含数组的实际元素。通常,我们不需要使用此属性,因为我们将使用索引访问数组中的元素。
数组创建
- array函数
使用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
-
zeros/ones/empty
通常,数组的元素最初是未知的,但它的大小是已知的。因此,NumPy提供了几个函数来创建具有初始占位符内容的数组。这就减少了数组增长的必要,因为数组增长的操作花费很大。
np.zeros( (3,4) ) # 3行4列
np.ones( (2,3,4), dtype=np.int16 ) # dtype can also be specified
np.empty( (2,3) )
函数zeros
创建一个由0组成的数组,函数 ones
创建一个完整的数组,函数empty
创建一个数组,其初始内容是随机的,取决于内存的状态。默认情况下,创建的数组的dtype是 float64
类型的。
- arange
NumPy提供了一个类似于Python中range的函数,也和Python一样是左闭右开。
>>> 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])
linspace
当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)
- a = np.random.random((2,3))
2行3列,数值为(0,1)的数字
打印数组
当您打印数组时,NumPy以与嵌套列表类似的方式显示它,但具有以下布局:
- 最后一个轴从左到右打印,
- 倒数第二个从上到下打印,
- 其余部分也从上到下打印,每个切片用空行分隔。
然后将一维数组打印为行,将二维数据打印为矩阵,将三维数据打印为矩数组表。
>>> 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]]]
如果数组太大而无法打印,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]])
幂: **
a<35
array([ True, True, False, False])
某些操作(例如+=
和 *=
)会更直接更改被操作的矩阵数组而不会创建新矩阵数组。
许多一元操作,例如计算数组中所有元素的总和,都是作为ndarray
类的方法实现的。可以通过指定axis
参数,您可以沿数组的指定轴应用操作:axis=0→沿列操作;axis=1→沿行操作
>>> b = np.arange(12).reshape(3,4)
>>> b
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> b.sum(axis=0) # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1) # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1) # cumulative sum along each row 每行的累积总和
array([[ 0, 1, 3, 6],
[ 4, 9, 15, 22],
[ 8, 17, 27, 38]])
>>> np.diff(b) #相邻作差
array([[1, 1, 1],
[1, 1, 1],
[1, 1, 1]])
>>> b.argmin() # 最小值的索引
0
>>> b.mean() # 也可以指定维度
>>> np.median(b)
>>> b.nonzero()
(array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], dtype=int64),
array([1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], dtype=int64)) #行,列坐标
>>> b.transpose() # 转置=b.T,但都不会改变b本身。
>>> b.clip(3,8) # 小于3的置3,大于8的置8,其余的不变。
array([[3, 3, 3, 3],
[4, 5, 6, 7],
[8, 8, 8, 8]])
索引、切片和迭代
索引
索引还是要注意从0开始。
一维的数组可以进行索引、切片和迭代操作的,就像 列表 和其他Python序列类型一样。
>>> a = np.arange(10)**3
>>> a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
>>> a[:6:2] = -1000 # equivalent to a[0:6:2] = -1000;
>>> a
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
>>> a[ : :-1] # 倒序开始
array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
>>> for i in a:
... print(i**(1/3.))
多维的数组每个轴可以有一个索引。这些索引以逗号分隔的元组给出:
>>> b
array([[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43]])
>>> b[2,3] # =b[2][3]
23
>>> b[0:5, 1] # each row in the second column of b
array([ 1, 11, 21, 31, 41])
当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片:
>>> b[-1] # the last row. Equivalent to b[-1,:]
array([40, 41, 42, 43])
也可以用
三个点( ...
)表示产生完整索引元组所需的冒号。
x[1,2,...]
相当于x[1,2,:,:,:]
,x[...,3]
等效于x[:,:,:,:,3]
x[4,...,5,:]
等效于x[4,:,:,5,:]
。
使用索引数组进行索引
>>> a = np.arange(12)**2 # the first 12 square numbers
array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121],
dtype=int32)
>>> i = np.array( [ 1,1,3,8,5 ] ) # an array of indices
>>> a[i] # the elements of a at the positions i
array([ 1, 1, 9, 64, 25])
>>>
# 二维索引数组 a bidimensional array of indices
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] )
>>> a[j] # the same shape as j
array([[ 9, 16],
[81, 49]])
当索引数组a
是多维的时,单个索引数组指的是第一个维度a
。以下示例通过使用调色板将标签图像转换为彩色图像来显示此行为。
>>> palette = np.array( [ [0,0,0], # black
... [255,0,0], # red
... [0,255,0], # green
... [0,0,255], # blue
... [255,255,255] ] ) # white
>>> image = np.array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # the (2,4,3) color image
array([[[ 0, 0, 0],
[255, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 0, 0, 255],
[255, 255, 255],
[ 0, 0, 0]]])
迭代
对多维数组进行 迭代(Iterating) 是相对于第一个轴完成的(行):
>>> for row in b:
... print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]
若要对列迭代:可以用到转置
>>> for column in b.T:
... print(column)
但是,如果想要对数组中的每个元素执行操作,可以使用flat
属性(摊平),该属性是数组的所有元素的迭代器。而 b.flatten() 返回一个被改变的array,b.ravel() 也有相同的效果,但它属于引用操作。
“ravel()和flatten()是将多维数据展平为一维数据,功能相同,区别在于一个是引用操作,一个是复制操作。ravel()展平数据后,修改后面的数据会影响前面的数据,而flatten()展平数据后,不会影响前面的数据。”
>>> for element in b.flat:
... print(element)
...
0
1
2
3
...
形状操纵
shape返回tuple类型,单个元素后面必须写逗号。
>>> a = np.array([2,3,4])
>>> a.shape
(3,)
b.ravel() b.reshape() b.T 可以更改数组的形状,返回一个修改后的数组,但不会更改原始数组。而 ndarray.resize() 方法会修改数组本身。b.T 对于一维数组不能把横向的变成纵向的。
如果在 reshape 操作中将 size 指定为-1,则会自动计算其他的 size 大小。
>>> a = np.floor(10 * np.random.random((3, 4)))
>>> a
array([[2., 1., 7., 1.],
[2., 3., 5., 4.],
[9., 3., 6., 8.]])
>>> a.reshape(2,-1)
array([[2., 1., 7., 1., 2., 3.],
[5., 4., 9., 3., 6., 8.]])
将不同数组堆叠在一起
几个数组可以沿不同的轴堆叠在一起,例如:
>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 8., 8.],
[ 0., 0.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1., 8.],
[ 0., 4.]])
>>> np.vstack((a,b)) # 上下合并
array([[ 8., 8.],
[ 0., 0.],
[ 1., 8.],
[ 0., 4.]])
>>> np.hstack((a,b)) # 左右合并
array([[ 8., 8., 1., 8.],
[ 0., 0., 0., 4.]])
1D数组作为列堆叠到2D数组中:
>>> a = np.array([4.,2.])
>>> b = np.array([3.,8.])
>>> a[:,np.newaxis] # 针对列表的操作[] this allows to have a 2D columns vector
array([[ 4.],
[ 2.]])
>>> a[np.newaxis,:]
array([[4., 2.]])
column_stack 将1D数组作为列堆叠到2D数组中。
>>> a = np.array([4.,2.])
>>> b = np.array([3.,8.])
>>> np.column_stack((a,b)) # returns a 2D array
array([[ 4., 3.],
[ 2., 8.]])
>>> np.column_stack((a[:,np.newaxis],b[:,np.newaxis]))
array([[ 4., 3.],
[ 2., 8.]])
>>> np.hstack((a[:,np.newaxis],b[:,np.newaxis])) # the result is the same
array([[ 4., 3.],
[ 2., 8.]])
row_stack() 等效vstack.
concatenate() 可通过axis自定义合并的维度。
>>> np.concatenate((a,b),axis=0)
array([4., 2., 3., 8.])
>>> np.concatenate((a[:,np.newaxis],b[:,np.newaxis]),axis=1)
array([[4., 3.],
[2., 8.]])
将一个数组拆分成几个较小的数组
使用hsplit,可以沿数组的水平轴拆分数组,方法是指定要返回的形状相等的数组的数量,或者指定应该在其之后进行分割的列:
>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[ 9., 5., 6., 3., 6., 8., 0., 7., 9., 7., 2., 7.],
[ 1., 4., 9., 2., 2., 1., 0., 6., 2., 2., 4., 0.]])
>>> np.hsplit(a,3) # Split a into 3
[array([[ 9., 5., 6., 3.],
[ 1., 4., 9., 2.]]), array([[ 6., 8., 0., 7.],
[ 2., 1., 0., 6.]]), array([[ 9., 7., 2., 7.],
[ 2., 2., 4., 0.]])]
>>> np.hsplit(a,(3,4)) # Split a after the third and the fourth column
[array([[ 9., 5., 6.],
[ 1., 4., 9.]]), array([[ 3.],
[ 2.]]), array([[ 6., 8., 0., 7., 9., 7., 2., 7.],
[ 2., 1., 0., 6., 2., 2., 4., 0.]])]
同样 vsplit() 沿垂直轴分割,np.split(array, x, axis=1) 允许指定要分割的轴;np.array_splist(),可以进行不匹配维度的分隔。
拷贝和视图
“引用”完全不复制:变量赋值
简单分配不会复制数组对象或其数据。“引用”,具有关联性;
常用 ‘b is a‘ 这个判断语句的检查两者是否指向同一个内容。和Python相同。
>>> a = np.arange(12)
>>> b = a # no new object is created
>>> b is a # a and b are two names for the same ndarray object
True
>>> b.shape = 3,4 # changes the shape of a
>>> a.shape
(3, 4)
深拷贝 deep copy
该copy
() 方法生成数组及其数据的完整副本。
>>> d = a.copy() # a new array object with new data is created
>>> d is a
False
>>> d.base is a # d doesn't share anything with a
False
>>> d[0,0] = 9999
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
有时,如果不再需要原始数组,则应在切片后调用 copy
。例如,假设a是一个巨大的中间结果,最终结果b只包含a的一小部分,那么在用切片构造b时应该做一个深拷贝:
>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a # the memory of ``a`` can be released.
如果改为使用 b = a[:100]
,则 a
由 b
引用,并且即使执行 del a
也会在内存中持久存在。
视图或浅拷贝
不同的数组对象可以共享相同的数据。该view
方法创建一个查看相同数据的新数组对象.
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # a's data changes
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])
切片数组会返回一个视图:
>>> s = a[ : , 1:3]
# spaces added for clarity; could also be written "s = a[:,1:3]"
>>> s[:] = 10
# s[:] is a view of s. Note the difference between s=10 and s[:]=10
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])