python3 多维数组 NumPy ndarray 简介

目录

基础

重要属性

创建

Converting Python array_like Objects to NumPy Arrays

多维数组

一维

通用数学函数


 

基础

NumPy 的主要对象是齐次多维数组。它是一个元素表(通常是元素是数字),其中所有元素类型都相同,元素以正整数元组索引。在 NumPy 维度(dimension)被称为轴(axis)。
ps. 有几个轴就是几维数组,符合平时生活中有 x, y 两个坐标轴就是二维空间,再加上 z 轴就是三维空间的概念

例如三维空间空一个点的坐标 [1, 2, 1] 有一个轴。这个轴有 3 个元素,即该轴的长度是 3。下面代码区中的数组有两个轴。第一个轴长度是 2,第二个长度是 3.

[[ 1., 0., 0.],
 [ 0., 1., 2.]]

Numpy 的数组类称做 ndarry,别名是 array。注意 numpy.array 和 Python 标准库的类 array.array 不同,标准库的类只处理一维数组(one-dimensional arrays)。

重要属性

  • ndarray.ndim
    the number of axes (dimensions) of the array.
  • ndarray.shape
    数组的维度(the dimensions of the array)。 以一个整型元组的方式表示数组中每个维度的大小。比如对一个有 n 行 m 列的矩阵来说,其 shape 属性为 (n, m)。The length of the shape tuple is therefore the number of axes, ndim.
  • ndarray.size
    数组元素总数。相当于 shape 中每个元素的乘积。
  • ndarray.dtype
    一个用来描述数组中元素类型的对象。我们可以使用 Python 标准类型来创建指定该对象,NumPy 也提供了自己的类型,如 numpy.int32, numpy.int16, and numpy.float641 等
  • ndarray.itemsize
    数组中每个元素的字节大小。 For example, an array of elements of type float64 has itemsize 8 (=64/8), while one of type complex32 has itemsize 4 (=32/8). It is equivalent to ndarray.dtype.itemsize.

创建

对于创建 numpy.ndarray,官网上给出了五种创建方式2,这里介绍更为常见的两种:

  1. 从 python 其他数据结构中转化而来,比如 list, tuple 等
  2. 固有的 NumPy ndarray 创建方式,比如 np.arange(), np.ones(), np.zeros() 等

这里还会补充一种从文件中读入的方式。

Converting Python array_like Objects to NumPy Arrays

整体来说,我们可以使用 numpy.array() 函数将 Python 中任何以类似数组方式组织的数值数据转化成 numpy.ndarray。最显而易见的例子是 list 和 tuple3

有一些对象支持 array-protocol,因此我们也可以使用 numpy.array() 函数将这些对象转换成 numpy.array。最简单判断对象是否支持这种转换方式的方法是自己动手转换试试。

>>> import numpy as np
>>> x = np.array([2,3,1,0]) # 列表方式
>>> x
array([2, 3, 1, 0])
>>> type(x)
<class 'numpy.ndarray'>
>>> x.dtype
dtype('int32')

>>> x = np.array((1, 2, 3)) # 元组方式
>>> x
array([1, 2, 3])

>>> x = np.array([[ 1.+0.j, 2.+0.j], [ 0.+0.j, 0.+0.j], [ 1.+1.j, 3.+0.j]])  # 不同类型的数据
>>> x
array([[1.+0.j, 2.+0.j],
       [0.+0.j, 0.+0.j],
       [1.+1.j, 3.+0.j]])
>>> x.dtype
dtype('complex128')

>>> x = np.array([[1,2.0],[0,0],(1+1j,3.)]) # note mix of tuple and lists, and types
>>> x
array([[1.+0.j, 2.+0.j],
       [0.+0.j, 0.+0.j],
       [1.+1.j, 3.+0.j]])
>>> x.dtype
dtype('complex128')

Intrinsic NumPy Array Creation

一般来说 array 的元素本身是不知道的,但是如果我们知道 array 的大小(size),我们就可以使用 NumPy 提供的一些方法来创建具有初始值的 array。

下面我列举了一些用于创建 numpy.ndarray 的内建函数,更多可以参考 Array creation routines

  1. numpy.zeros(shape, dtype=float, order=‘C’)
  2. numpy.ones(shape, dtype=None, order=‘C’)
  3. numpy.arange([start, ]stop, [step, ]dtype=None)
  4. numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
  5. numpy.indices(dimensions, dtype=<class ‘int’>)

其中的 shape 参数指定你想创建数组的维度以及每维的大小。比如 (2, 3, 4) 就是创建 234 的三维数组。

其中函数 zero() 创建一个全为 0 的 array,函数 ones() 创建一个全为 1 的 array,函数 empty() 创建一个根据内存状态随机初始化值的 array。

numpy.zeros(shape, dtype=float, order=‘C’)

从函数本身我们就可以知道这个是创建一个全为 0 的 ndarray。其中 shape 指定创建 ndarray 的形状,如是 2行3列的,还是 4行5列的。

>>> np.zeros((2, 3))
array([[ 0., 0., 0.], [ 0., 0., 0.]])

numpy.arange([start, ]stop, [step, ]dtype=None)

arange() 会创建有规律递增的数值的 ndarray,个人感觉类似 range() 函数

>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> np.arange(2, 10, dtype=float)
array([ 2., 3., 4., 5., 6., 7., 8., 9.])

>>> np.arange(2, 3, 0.1)
array([ 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9])

numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)

当我们给 arange() 函数传入附点类型参数的时候,我们通常不能预测我们创建 array 里元素的个数,这时候我们可以使用 linspace() 函数。

linspace() 会根据 num 参数创建指定数量等差数据,数据的范围在 start 和 stop 之间,默认包含 stop。

>>> np.linspace(1., 4., 6)
array([ 1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

从文件中读入

使用 numpy.genfromtxt,这里以读入 CSV 文件为例

import numpy as np
myFile = np.genfromtxt('filepath', delimiter=',')

如果想读入以 TAB 为分隔符的文件,将 delimiter 换成 \t

多维数组

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]])

注意事项

NumPy 的 ndarray 基本和 C++/Java 创建时要注意的差不多,创建的时候我们最好对我们需求的大小有个估计,然后再创建一个略多一点的就可以了。 ndarry 并没有 Python 的 list 那么灵活,可以随时更改数组大小(不过你要想增加一行或一列的话,也有一些方法),要更改大小的话一般得重新创建数组,效率不是很高4

增加行

https://stackoverflow.com/questions/3881453/numpy-add-row-to-array
使用函数 vstack

>>> a = np.zeros((3, 4))
>>> a
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

>>> row = np.ones((1, a.shape[1]))
>>> row

array([[1., 1., 1., 1.]])
>>> np.vstack((a, row))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [1., 1., 1., 1.]])

增加列

https://stackoverflow.com/questions/15815854/how-to-add-column-to-numpy-array
用 hstack

>>> col = np.ones((a.shape[0], 2))
>>> col
array([[1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.hstack((a, col))
array([[0., 0., 0., 0., 1., 1.],
       [0., 0., 0., 0., 1., 1.],
       [0., 0., 0., 0., 1., 1.]])

索引,切片,迭代(Indexing, Slicing and Iterating)

一维

一维数组像 Python list 一样被索引、切片和迭代。

>>> a = np.arange(10)**3  # 创建数组
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]  # 索引
8
>>> a[2:5]  # 切片
array([ 8, 27, 64])
>>> a[:6:2] = -1000    # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
>>> a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729])
>>> a[ : :-1]   # reversed a
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000])
>>> for i in a:  # 迭代
...     print(i**(1/3.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

多维

多维数组每个轴可以有一个索引,这些索引以逗号分隔的元组给出:

索引

参考 arrays indexing

索引单个元素

>>> b = np.arange(12).reshape(4,3)  # 将一维数组改成 4*3 的二维数组
>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> b[0, 2]  # 注意这里不能越界了。其实和 C++/Java 二维数组访问差不多。
2

索引行

索引第 1 行

>>> b[1, :]
array([3, 4, 5])

索引 0 3 行

>>> b[[0,3], :]
array([[ 0,  1,  2],
       [ 9, 10, 11]])

索引列

索引第 0 列

>>> b[:, 0]
array([0, 3, 6, 9])

切片

>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> b[0:4, 1]              # each row in the second column of b
array([ 1,  4,  7, 10])
>>> b[0:5, 1]              # 这里可以越界
array([ 1,  4,  7, 10])
>>> b[ : ,1]               # equivalent to the previous example
array([ 1,  4,  7, 10])
>>> b[1:3, : ]             # each column in the second and third row of b
array([[3, 4, 5],
       [6, 7, 8]])

当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片

>>> b[-1]       # the last row. Equivalent to b[-1,:]
array([ 9, 10, 11])

迭代

对多维数组进行迭代是针对第一个轴完成的

>>> b
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
>>> for row in b:
	print(row)

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

如果你想对数组中的每个元素进行操作的话,你可以使用 flat 属性完成对每个元素的迭代。

>>> for element in b.flat:
	print(element)

0
... # 为了节省地方,我这里认为删去了很多行。把 0~11 竖起来就是结果
11

基本运算

当涉及到算术运算的时候,数组中的每个元素都会参与,运算最后会创建一个新数组并填充结果。

>>> a = np.array( [20,30,40,50] )
>>> b = np.arange( 4 )
>>> b
array([0, 1, 2, 3])
>>> c = a-b  # 减
>>> c
array([20, 29, 38, 47])
>>> a + 10  # 加
array([30, 40, 50, 60])
>>> b**2  # 幂运算
array([0, 1, 4, 9])
>>> a<35 # 比较运算
array([ True, True, False, False])

在 NumPy 数组中 * 是按对应元素进行计算的。想要进行 矩阵运算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]])

一些运算如,*= 和 += 并不会新创建数组,而是在原有数组上进行修改。

通用数学函数

NumPy 提供一系列数学函数,例如sin,cos和exp。在 NumPy 中,这些被称为“通用函数”(ufunc)。在 NumPy 中,这些函数都产生一个数组作为输出
由于比较多,我把它放到了脚注6
因为其中的很多函数都涉及到 axis 这个参数,这里就来简单介绍一下这个参数怎么指定,我们不妨以 np.sum() 来探究。

numpy.sum(a, axis=None, dtype=None, out=None, keepdims=, initial=)
给指定轴上的元素求和:
参数说明(这里只介绍这里要谈的参数):

  • a: array_like
  • axis: None or int or tuple of ints, 可选
    指定要求和的轴。默认 axis=None 会对输入数组的所有元素求和,指定负数的话是从最后一个轴开始往前统计(其实和数组负数索引是一个道理)。

对一个轴上的元素求和是一个减少操作,指定的轴会在运算完后消失。比如对一个一维数组上的元素求和,最后我们会得到是一个数。7

想要理解这个东西,我们首先要对每个轴上有哪些元素有个清楚的认识。
比如下图: axis=0 轴的大小是 8,其上的元素是 [100]\begin{bmatrix} 1 &amp; 0 &amp; 0 \end{bmatrix}[1​0​0​]、 [010]\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix}[0​1​0​]、 [001]\begin{bmatrix} 0 &amp; 0 &amp; 1 \end{bmatrix}[0​0​1​] … [010]\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix}[0​1​0​].

而 axis=1 轴的大小是 3,其上的元素是 ⎡⎣⎢⎢⎢⎢⎢⎢1001...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡​1001...​⎦⎥⎥⎥⎥⎤​ 、⎡⎣⎢⎢⎢⎢⎢⎢0100...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡​0100...​⎦⎥⎥⎥⎥⎤​ 和 ⎡⎣⎢⎢⎢⎢⎢⎢0010...⎤⎦⎥⎥⎥⎥⎥⎥\begin{bmatrix} 0 \\ 0 \\ 1 \\ 0 \\ ... \end{bmatrix}⎣⎢⎢⎢⎢⎡​0010...​⎦⎥⎥⎥⎥⎤​。

对 axis=0 求和就是把其上的所有元素相加 [100]+[010]+[001]+...+[010]\begin{bmatrix} 1 &amp; 0 &amp; 0 \end{bmatrix}+\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix}+\begin{bmatrix} 0 &amp; 0 &amp; 1 \end{bmatrix}+ ... +\begin{bmatrix} 0 &amp; 1 &amp; 0 \end{bmatrix}[1​0​0​]+[0​1​0​]+[0​0​1​]+...+[0​1​0​],因此这个这从图上我们可以直观的看出来 axis=0 这个轴被压缩了,也就是消失了。
在这里插入图片描述
注:上图中的 sum of each row 这些文字我都是参考 universal-function 上面一小节程序里的注释的,因此应该不会出错。

要是没搞懂的话,可以再来看看下面这个三维的例子。

>>> a = np.ones((2,3,4))  # 创建一个三维的数组
>>> a
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.]]])
>>> a.shape # 第一个轴大小为2,第二个轴大小为3,第三个轴大小为4
(2, 3, 4)
>>> a.ndim  # 一共有三个轴
3
>>> a.sum() # 计算所有元素的总和
24

>>> a.sum(axis=0)
array([[2., 2., 2., 2.],
       [2., 2., 2., 2.],
       [2., 2., 2., 2.]])
>>> a.sum(axis=0).shape
(3, 4)

>>> a.sum(axis=1)
array([[3., 3., 3., 3.],
       [3., 3., 3., 3.]])
>>> a.sum(axis=1).shape
(2, 4)

>>> a.sum(axis=2)
array([[4., 4., 4.],
       [4., 4., 4.]])
>>> a.sum(axis=2).shape
(2, 3)

参数指定为负数

>>> a.sum(axis=-1)  # 等价于 a.sum(axis=2)
array([[4., 4., 4.],
       [4., 4., 4.]])
>>> a.sum(axis=-2)  # 等价于 a.sum(axis=1)
array([[3., 3., 3., 3.],
       [3., 3., 3., 3.]])
>>> a.sum(axis=-3)  # 等价于 a.sum(axis=0)
array([[2., 2., 2., 2.],
       [2., 2., 2., 2.],
       [2., 2., 2., 2.]])

参数指定为元组

>>> a.shape
(2, 3, 4)
>>> a.sum(axis=(0,1))
array([6., 6., 6., 6.])
>>> a.sum(axis=(0,1)).shape
(4,)

输出

当我们输出数组时,NumPy 以与嵌套列表类似的方式显示它,但具有以下布局:

  • the last axis is printed from left to right,
  • the second-to-last is printed from top to bottom,
  • the rest are also printed from top to bottom, with each slice separated from the next by an empty line.

然后将一维数组打印为行,将二维数组打印为矩阵,将三维数组打印为矩阵列表。

>>> 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]]]

当数组太大的时候,会省略显示一部分内容

>>> print(np.arange(10000))
[   0    1    2 ..., 9997 9998 9999]

我们可以通过以下方式强制显示全:

>>> np.set_printoptions(threshold=np.nan)

 

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值