一、简介
numpy 是利用 Python 进行科学计算的最重要的基础包,统计分析、神经网络及机器学习更是离不开 numpy。numpy 主要用于多维数组即矩阵的处理,这样的能力正好符合当下对大数据进行分析和预测的需求,提到多维数组,大家肯定会想到 Matlab 及 R ,基础算法、并行计算及大数据挖掘所面对的数据结构及处理方法,唯有通过矩阵计算才能得以实施,传统的整数及浮点数运算已无法满足大数据背景下的科学计算及机器学习的要求,在这里根据自己的学习和实践,分享相关的知识要点。
本文及随后的所有 Python 代码都基于 Python 3.x,同时假设您已经熟悉 Python 语言,并想充分了解和深入科学计算及机器学习方面的内容,因此关于语言基础,以及安装和配置等都不在本文的涉及范围内。
我们都知道标量和向量,每一个独立的数字,比如 1、2、3.5 等都是标量,我们可以称之为 0 维;向量则是指一组数字,比如 [1,2,3],它是有排列的一组数字,我们称之为 1 维;而 2 维数组其实就是我们所说的矩阵,也最常见,形式为:[[1,2,3],[4,5,6]],以此类推便是多维数组。现实世界中我们的认知只能理解到 4 维,而数学界中却可以表达出 n 维!
二、数组对象
在 numpy 中的最重要的对象或者说类就是 ndarray,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引,在 ndarray 中的每一个元素在内存中都有相同存储大小的区域,同时存储数据和数据类型,如下图:
创建一个ndarray只需调用array函数即可。
numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
具体参数功能如下:
名称 | 描述 |
---|---|
object | 数组或嵌套的数列 |
dtype | 数组元素的数据类型,可选 |
copy | 对象是否需要复制,可选 |
order | 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认) |
subok | 默认返回一个与基类类型一致的数组 |
ndimin | 指定生成数组的最小维度 |
让我们看一些示例:
示例1
import numpy as np
# 一维数组
a = np.array([1,2,3])
print(a)
输出为:
[1, 2, 3]
示例2
# 大于 1 维数组
import numpy as np
a = np.array([[1, 2], [3, 4]])
print(a)
输出为:
[[1, 2]
[3, 4]]
示例3
# 设定最小维度
import numpy as np
a = np.array([1, 2, 3, 4, 5], ndmin = 2)
print(a)
输出为:
[[1, 2, 3, 4, 5]]
示例4
# 设定数据类型
import numpy as np
a = np.array([1, 2, 3], dtype = complex)
print(a)
输出为:
[1.+0.j, 2.+0.j, 3.+0.j]
ndarray 对象由计算机内存的连续一维部分组成,并结合索引模式,将每个元素映射到内存块中的一个位置。内存块以行顺序(C样式)或列顺序(FORTRAN或MatLab风格,即前述的F样式)来保存元素。
三、numpy 数据类型
numpy 支持的数组元素类型比 Python 内置的类型要多很多,下面列出在 numpy 中可以使用的数据类型:
名称 | 描述 |
---|---|
bool_ | 布尔型数据类型(True 或者 False) |
int_ | 默认的整数类型(类似于 C 语言中的 long,int32 或 int64) |
intc | 与 C 的 int 类型一样,一般是 int32 或 int 64 |
intp | 用于索引的整数类型(类似于 C 的 ssize_t,一般情况下仍然是 int32 或 int64) |
int8 | 字节(-128 to 127) |
int16 | 整数(-32768 to 32767) |
int32 | 整数(-2147483648 to 2147483647) |
int64 | 整数(-9223372036854775808 to 9223372036854775807) |
uint8 | 无符号整数(0 to 255) |
uint16 | 无符号整数(0 to 65535) |
uint32 | 无符号整数(0 to 4294967295) |
uint64 | 无符号整数(0 to 18446744073709551615) |
float_ | float64 类型的简写 |
float16 | 半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位 |
float32 | 单精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位 |
float64 | 双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位 |
complex_ | complex128 类型的简写,即 128 位复数 |
complex64 | 复数,表示双 32 位浮点数(实数部分和虚数部分) |
complex128 | 复数,表示双 64 位浮点数(实数部分和虚数部分) |
numpy 的数值类型实际上是 dtype 对象的实例,并对应唯一的字符,包括 np.bool_,np.int32,np.float32,等等。
四、dtype 对象
数据类型对象是用来描述与数组对应的内存区域如何使用,这依赖如下几个方面:
- 数据的类型(整数,浮点数或者 Python 对象)
- 数据的大小
- 字节顺序(小端法或大端法)
- 在结构化类型的情况下,字段的名称、每个字段的数据类型和每个字段所取的内存块的部分
- 如果数据类型是子数组,它的形状和数据类型
字节顺序是通过对数据类型预先设定“<”或“>”来决定的。“<”意味着小端法(最小值存储在最小的地址,即低位组放在最前面)。“>”意味着大端法(最重要的字节存储在最小的地址,即高位组放在最前面)。
dtype 对象是使用以下语法构造的:
numpy.dtype(object, align, copy)
- object – 要转换为的数据类型对象
- align – 如果为 true,填充字段使其类似 C struct
- copy – 复制 dtype 对象 ,如果为 false,则是对内置数据类型对象的引用
让我们看一些示例:
示例1
import numpy as np
# 使用标量类型
dt = np.dtype(np.int32)
print(dt)
输出为:
int32
示例2
import numpy as np
# int8, int16, int32, int64 四种数据类型可以使用字符串 'i1', 'i2','i4','i8' 代替
dt = np.dtype('i4')
print(dt)
输出为:
int32
示例3
import numpy as np
# 字节顺序标注
dt = np.dtype('>i4')
print(dt)
输出为:
int32
下面示例展示结构化数据类型的使用,类型字段和对应的实际类型将被创建
示例4
# 首先创建结构化数据类型
import numpy as np
dt = np.dtype([('age',np.int8)])
print(dt)
输出为:
[('age', 'i1')]
示例5
# 将数据类型应用于 ndarray 对象
import numpy as np
dt = np.dtype([('age',np.int8)])
a = np.array([(10,),(20,),(30,)], dtype = dt)
print(a)
输出为:
[(10,) (20,) (30,)]
示例6
# 类型字段名可以用于存取实际的 age 列
import numpy as np
dt = np.dtype([('age',np.int8)])
a = np.array([(10,),(20,),(30,)], dtype = dt)
print(a['age'])
输出为:
[10 20 30]
示例7
下面的示例定义一个结构化数据类型 student,包含字符串字段 name,整数字段 age,及浮点字段 marks,并将这个dtype应用到 adarray对象。
import numpy as np
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')])
print(student)
输出为:
[('name', 'S20'), ('age', 'i1'), ('marks', '<f4')])
示例8
import numpy as np
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')])
a = np.array([('abc', 21, 50),('xyz', 18, 75)], dtype = student)
print(a)
输出为:
[('abc', 21, 50.0), ('xyz', 18, 75.0)]
每一个内置的数据类型都对应一个字符进行唯一的标识,如下: ‘b’ − boolean ‘i’ − (signed) integer ‘u’ − unsigned integer ‘f’ − floating-point ‘c’ − complex-floating point ‘m’ − timedelta ‘M’ − datetime ‘O’ − (Python) objects ‘S’, ‘a’ − (byte-)string ‘U’ − Unicode ‘V’ − raw data (void)
五、数组属性
ndarray.shape
这个属性返回一个由数组维度组成的元祖,也可以用来改变数组的大小
示例1
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print(a.shape)
输出为:
(2, 3)
示例2
# 重新设定 adarray 大小
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
a.shape = (3,2)
print(a)
输出为:
[[1, 2]
[3, 4]
[5, 6]]
示例3
numpy 另外提供了 reshape 函数用来改变数组大小及维度,即改变数组形状
# 重新设定 adarray 大小
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
b = a.reshape(3,2)
print(b)
输出为:
[[1, 2]
[3, 4]
[5, 6]]
ndarray.ndim
这个属性返回数组维度
示例1
import numpy as np
a = np.arange(24)
print(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]
示例2
import numpy as np
# 创建一个一维数组
a = np.arange(24)
print(a.ndim) # 1
# reshape
b = a.reshape(2,4,3)
print(b) # b 是三维数组
输出为:
[[[ 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.itemsize
这个属性以字节的形式返回数组中每一个元素的大小
示例1
# int8, 1 byte
import numpy as np
x = np.array([1,2,3,4,5], dtype = np.int8)
print(x.itemsize)
输出为:
1
示例2
# float32, 4 byte
import numpy as np
x = np.array([1,2,3,4,5], dtype = np.float32)
print(x.itemsize)
输出为:
4
numpy.flags
通过 flags 返回某些属性的状态标志
属性 | 描述 |
---|---|
C_CONTIGUOUS (C) | 数据是在一个单一的C风格的连续段中 |
F_CONTIGUOUS (F) | 数据是在一个单一的Fortran风格的连续段中 |
OWNDATA (O) | 数组拥有它所使用的内存或从另一个对象中借用它 |
WRITEABLE (W) | 数据区域可以被写入,将该值设置为 False,则数据为只读 |
ALIGNED (A) | 数据和所有元素都适当地对齐到硬件上 |
UPDATEIFCOPY (U) | 这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新 |
示例
# float32, 4 byte
import numpy as np
x = np.array([1,2,3,4,5])
print(x.flags)
输出为:
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
六、创建数组
一个 ndarray 数组可以通过以下几种方式以及底层 ndarray 构造器创建。
numpy.empty
创建一个指定形状、指定 dtype 及未被初始化的数组:
numpy.empty(shape, dtype = float, order = 'C')
构造器接收如下参数:
参数 | 描述 |
---|---|
shape | 空数组的形状 |
dtype | 数据类型,可选 |
order | ‘C’用于C的行数组,或者’F’用于FORTRAN的列数组 |
示例
下面示例展示创建一个空数组
import numpy as np
x = np.empty([3,2], dtype = int)
print(x)
输出为:
[[0 0]
[0 0]
[0 0]]
该函数在Python2.x下会以随机数填充,而在Python3.x下会以 0 填充,效果与下面的 numpy.zeros 类似。
numpy.zeros
创建一个指定形状的数组,用 0 填充。
numpy.zeros(shape, dtype = float, order = 'C')
构造器接收如下参数,与 numpy.empty 一样:
参数 | 描述 |
---|---|
shape | 空数组的形状 |
dtype | 数据类型,可选 |
order | ‘C’用于C的行数组,或者’F’用于FORTRAN的列数组 |
示例1
import numpy as np
# 默认为浮点数
x = np.zeros(5)
print(x)
输出为:
[ 0. 0. 0. 0. 0.]
示例2
import numpy as np
# 设置类型为整数
x = np.zeros((5,), dtype = np.int)
print(x)
输出为:
[0 0 0 0 0]
示例3
# 自定义类型
import numpy as np
x = np.zeros((2,2), dtype = [('x', 'i4'), ('y', 'i4')])
print(x)
输出为:
[[(0,0)(0,0)]
[(0,0)(0,0)]]
numpy.ones
创建一个指定形状的数组,用 1 填充。
numpy.ones(shape, dtype = None, order = 'C')
构造器接收如下参数,同样与 numpy.zeros 一样:
参数 | 描述 |
---|---|
shape | 空数组的形状 |
dtype | 数据类型,可选 |
order | ‘C’用于C的行数组,或者’F’用于FORTRAN的列数组 |
示例1
# 自定义类型
import numpy as np
x = np.ones(5)
print(x)
输出为:
[ 1. 1. 1. 1. 1.]
示例2
# 自定义类型
import numpy as np
x = np.ones([2,2], dtype = int)
print(x)
输出为:
[[1 1]
[1 1]]
七、索引及切片
ndarray 数组对象的内容可以通过索引存取或者切片进行存取,就像 Python 内置的容器对象一样。ndarray 数组可以基于 0 – n 的下标进行索引,切片对象可以通过内置的slice函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组。
示例1
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
s = slice(2,7,2) # 从索引2开始到索引7停止,间隔为2
print(a[s])
输出为:
[2 4 6]
示例2
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
b = a[2:7:2] # 与示例1等效,从索引2开始到索引7停止,间隔为2
print(b)
输出为:
[2 4 6]
冒号”:”的解释:如果只放置一个参数,如[2],将返回与该索引相对应的单个元素。如果为[2:],表示从该索引开始以后的所有项都将被提取。如果使用了两个参数,如[2:7],那么则提取两个索引(不包括停止索引)之间的项。
示例3
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
b = a[5]
print(b)
输出为:
5
示例4
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(a[2:])
输出为:
[2 3 4 5 6 7 8 9]
示例5
import numpy as np
a = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(a[2:5])
输出为:
[2 3 4]
多维数组同样适用上述索引提取方法:
示例6
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a)
# 从某个索引处开始切割
print 'slice the array from the index a[1:]'
print(a[1:])
输出为:
[[1 2 3]
[3 4 5]
[4 5 6]]
slice the array from the index a[1:]
[[3 4 5]
[4 5 6]]
切片还可以包括省略号(…),来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的ndarray。
示例7
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print a[...,1] # 第2列元素
print a[1,...] # 第2行元素
print a[...,1:] # 第2列及剩下的所有元素
输出为:
[2 4 5]
[3 4 5]
[[2 3]
[4 5]
[5 6]]