NumPy 快速入门:数组对象,数组属性,花式索引等基础介绍
- 1、NumPy 简介:
- 2、ndarray 数组对象:
- 3、创建数组:
- 4、数组的数据类型:
- 5、数组的结构:
- 6、数组的简单计算:
- 7、数组切片与索引:
- 8、数组的组合与切割:
- 9、数组的属性:
- 结尾:
1、NumPy 简介:
NumPy (Numerical Python的简称)是高性能科学计算和数据分析的基础包,是想利用 Python 进行数据分析人士必须了解的模块之一。
由于各种原因 NumPy 模块并不是 Python 的标准模块,故需安装使用:
pip install numpy
导入 NumPy 模块时通常约定写成:
import numpy as np
numpy 主要功能:
- 提供 ndarray 对象:具有矢量算数运算和复杂广播能力的多维数组;
- 提供了许多可对数组对象直接运算的标准数学函数,而无需编写循环;
- 提供了用于读写磁盘数据的工具及用于操作内存映射文件的工具;
- 提供了线性代数、随机生成及傅里叶变换功能;
- 用于集成由 C 、C++、Fortran 等语言编写的代码的工具。
2、ndarray 数组对象:
ndarray 是指 NumPy 中的N维数组对象,是一个快速而灵活地大数据集容器。
3、创建数组:
array 函数: 可接收一切序列对象(包括其他数组对象),然后产生一个新的 NumPy 数组。
numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
参数 | 含义 |
---|---|
object | 数组或嵌套的数列 |
dtype | 数组元素的数据类型,可选 |
copy | 对象是否需要复制,可选 |
order | 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认) |
subok | 默认返回一个与基类类型一致的数组 |
ndmin | 指定生成数组的最小维度 |
简单示例:
import numpy as np # 导入模块,后面示例不再写
data = [6,7.5,8,0,1]
arr1 = np.array(data)
print(arr1) # [6. 7.5 8. 0. 1. ]
接收一个嵌套序列:
data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
print(arr2)
''' [[1 2 3 4] [5 6 7 8]] '''
解析: 除非设置数据类型(数据类型稍后会详细介绍),或者 array 函数会根据传入的序列选择适合的数据类型。
如,在上面 arr1 中,传入的列表中有 7.5 浮点数,所以创建的 arr1 数组类型为浮点型。
查看创建的数组数据类型:
print(arr1.dtype) # float64
print(arr2.dtype) # int32 或 int64 ,和安装的 Python 版本有关
其他创建数组的方法:
zeros 函数: 创建全 0 数组
# 创建一维数组
print(np.zeros(10)) # [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# 创建多维数组
print(np.zeros((3,6)))
''' [[0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0.]] '''
empty 函数:
print(np.empty((2,3,2)))
''' [[[8.74e-322 0.00e+000] [0.00e+000 0.00e+000] [0.00e+000 0.00e+000]] [[0.00e+000 0.00e+000] [0.00e+000 0.00e+000] [0.00e+000 0.00e+000]]] '''
arange 函数: 是 Python 内置函数 range 的数组版
# arange 函数
print(np.arange(15))
# [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
eye 函数: 创建一个单位矩阵
print(np.eye(3,3))
''' [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] '''
4、数组的数据类型:
NumPy 的一个数组对象里所有的数据都是同质的,即数组内 数据类型一致 。
设置、查看数组对象数据类型:
arr3 = np.array([1,2,3],dtype=np.float)
arr4 = np.array([1,2,3],dtype=np.int)
print(arr3.dtype,arr4.dtype)
# 打印结果
# float64 int32
转换数组数据类型:
arr5 = np.array([1,2,3,4,5])
print(arr5.dtype) # int32
float_arr5 = arr5.astype(np.float)
print(float_arr5.dtype) # float64
注:astype 会创建一个新的数组对象
自定义数据类型:
NumPy 也提供了自定义数组对象的数据类型方法。自定义数据类型是一种异构数据类型,可以当做记录一行数据的结构。
如:创建一个存储商店库存信息的数据类型,该库存信息有:商品名称、库存数量、商品价格。
# 创建数据类型
my_type = np.dtype([('name',np.str,40),('numitems',np.int),('price',np.float)])
print(my_type) # [('name', '<U40'), ('numitems', '<i4'), ('price', '<f8')]
# 创建数组
my_array = np.array([('DVD',42,3.14),('Butter',13,2.72)],dtype= my_type)
# 查看数组数据类型
print(my_array.dtype)
# [('name', '<U40'), ('numitems', '<i4'), ('price', '<f8')]
5、数组的结构:
数组对象的结构指的是数组的维度、轴的概念。
查看数组结构:
b = np.arange(24)
print(b.shape) # (24,)
改变数组结构:
reshape 方法:生成一个新的数组对象。
c = b.reshape(2,3,4) # 改变数组维度
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(c.shape) # (2, 3, 4)
resize 方法: 原位改变数组结构。
g = np.arange(6)
print(g.shape) # (6,)
g.resize(2,3) # 会在原位修改数组结构
print(g)
''' [[0 1 2] [3 4 5]] '''
print(g.shape) # (2, 3)
注:reshape 方法和 resize 都可改变数组维度,但是 reshape 改变数组结构时,会产生一个新的数组对象;而 resize 则是直接修改原数组的维度。
数组的转置:
arr3 = np.arange(6).reshape(2,3)
print(arr3)
''' [[0 1 2] [3 4 5]] '''
arr4 = arr3.transpose()
print(arr4)
''' [[0 3] [1 4] [2 5]] '''
6、数组的简单计算:
NumPy 数组对象与 Python 原生序列最显著的区别之一就是在对序列中的数据执行批量运算,数组对象进行批量运算时,不需要编写循环可直接进行计算,及数组的矢量化。
这样也令其在大量数据计算时,运行效率明显提升。
数组与标量之间的运算:
arr6 = np.array([[1,2,3],[4,5,6]],dtype=float)
print(arr6)
''' [[1. 2. 3.] [4. 5. 6.]] '''
# 加法
print(arr6 + 2)
''' [[3. 4. 5.] [6. 7. 8.]] '''
# 乘法:
print(arr6*0.5)
''' [[0.5 1. 1.5] [2. 2.5 3. ]] '''
数组与数组之间的运算:
大小相同的数组之间的任何算术运算都会讲运算应用到元素级:
# 减法
print(arr6-arr6)
''' [[0. 0. 0.] [0. 0. 0.]] '''
# 乘法
print(arr6*arr6)
''' [[ 1. 4. 9.] [16. 25. 36.]] '''
7、数组切片与索引:
一维数组的切片与索引:
arr7 = np.arange(10)
print(arr7) # [0 1 2 3 4 5 6 7 8 9]
print(arr7[5]) # 5
print(arr7[5:8]) # [5 6 7]
以上操作的结果和 Python 列表切片索引操作现象一致。
切片赋值操作:
对一个切片对象赋值:
arr7[0] = 10
print(arr7)
# [10 1 2 3 4 5 6 7 8 9]
对切片组赋值:
arr7[5:8] = 12
print(arr7)
# [10 1 2 3 4 12 12 12 8 9]
注:当将一个标量值给一个切片时,该值会自动传播到整个选取,这是和Python列表切片最重要的区别 !
arr_slice = arr7[5:8]
arr_slice[1] = 12345
print(arr7)
# [ 0 1 2 3 4 12 12345 12 8 9]
arr_slice[:] = 64
print(arr7) # [ 0 1 2 3 4 64 64 64 8 9]
这是因为NumPy的设计目的是处理大数据,如要将数据多次复制(创建新的对象),会产生非常大的性能和内存问题。
高维数组的索引及切片:
二维数组:
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(arr2d[2]) # [7 8 9]
# 以下两种方式效果一致
print(arr2d[0][2]) # 3
print(arr2d[0,2]) # 3
三维数组:
arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print(arr3d)
print(arr3d.shape) # (2, 2, 3)
''' 一个 2 x 2 x 3 数组 [[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]] '''
print(arr3d[0])
''' [[1 2 3] [4 5 6]] '''
标量值和数组都可被赋值给切片对象,如:
old_value = arr3d[0].copy() # 复制数组对象
arr3d[0] = 42
print(arr3d)
''' [[[42 42 42] [42 42 42]] [[ 7 8 9] [10 11 12]]] '''
arr3d[0] = old_value
print(arr3d)
''' [[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]] '''
print(arr3d[1,0]) # [7 8 9]
布尔值切片:
这也是和 Python 原序列切片很大的不同之处,如:
先建立一个布尔序列:
names = np.array(['bob','joe','will','bob','will','joe','joe'])
# 数组的比较运算也是矢量化的,会产生一个布尔型数组
print(names == 'bob')
# [ True False False True False False False]
用布尔序列对数组进行切片操作:
data = np.random.rand(7,4) # 生成一些正态分布的随机数据
print(data)
''' [[0.64831166 0.53764074 0.05100258 0.86194823] [0.75817438 0.68161727 0.23507438 0.06506362] [0.31393836 0.3548072 0.52740244 0.07263428] [0.03731951 0.3401235 0.25695301 0.08632226] [0.84201383 0.54950122 0.38923988 0.77718169] [0.14664734 0.59178141 0.62562549 0.2584329 ] [0.03764809 0.1270825 0.39032711 0.09212854]] '''
# 布尔型数组可用于数组索引
print(data[names == 'bob'])
''' [[0.64831166 0.53764074 0.05100258 0.86194823] [0.03731951 0.3401235 0.25695301 0.08632226]] '''
注:布尔型数组的长度必须和被索引的轴长度一致。
布尔型数组也可以跟切片、整数混合使用:
print(data[names == 'bob', 2:])
''' [[0.05100258 0.86194823] [0.25695301 0.08632226]] '''
print(data[names == 'bob',3])
''' [0.86194823 0.08632226] '''
注意以下的一种用法:
# 将data中的所有小于0.5的值设置为0
data[data<0.5] = 0
print(data)
''' [[0.64831166 0.53764074 0. 0.86194823] [0.75817438 0.68161727 0. 0. ] [0. 0. 0.52740244 0. ] [0. 0. 0. 0. ] [0.84201383 0.54950122 0. 0.77718169] [0. 0.59178141 0.62562549 0. ] [0. 0. 0. 0. ]] '''
这在实现筛选数据功能上非常方便!
花式索引:
花式索引:NumPy术语,指利用整数数组进行索引,如:
arr9 = np.empty((8,4))
for i in range(8):
arr9[i] = i
print(arr9)
''' [[0. 0. 0. 0.] [1. 1. 1. 1.] [2. 2. 2. 2.] [3. 3. 3. 3.] [4. 4. 4. 4.] [5. 5. 5. 5.] [6. 6. 6. 6.] [7. 7. 7. 7.]] '''
print(arr9[[4,3,0,6]])
''' [[4. 4. 4. 4.] [3. 3. 3. 3.] [0. 0. 0. 0.] [6. 6. 6. 6.]] '''
使用负数会从末尾开始选取行:
print(arr9[[-1,-3,-7]])
[[7. 7. 7. 7.]
[5. 5. 5. 5.]
[1. 1. 1. 1.]]
'''
多个花式索引结合:
arr10 = np.arange(32).reshape((8,4))
print(arr10)
''' [[ 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 30 31]] '''
print(arr10[[1,5,7,2],[0,3,1,2]]) # [ 4 23 29 10]
print(arr10[[1,5,7,2]][:,[0,3,1,2]])
''' [[ 4 7 5 6] [20 23 21 22] [28 31 29 30] [ 8 11 9 10]] '''
注:花式索引和切片不一样,它总是将数据复制到新数组中。
8、数组的组合与切割:
水平组合:
a1 = np.arange(9).reshape(3,3)
a2 = a1 * 2
a3 = np.hstack((a1,a2))
print(a3)
''' [[ 0 1 2 0 2 4] [ 3 4 5 6 8 10] [ 6 7 8 12 14 16]] '''
使用 concatenate 函数进行水平组合
a4 = np.concatenate((a1,a2),axis=1)
print(a4)
''' [[ 0 1 2 0 2 4] [ 3 4 5 6 8 10] [ 6 7 8 12 14 16]] '''
垂直组合:
# 垂直组合
a5 = np.vstack((a1,a2))
print(a5)
''' [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 0 2 4] [ 6 8 10] [12 14 16]] '''
# 使用 concatenate 函数进行垂直组合
a6 = np.concatenate((a1,a2),axis=0)
print(a6)
''' [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 0 2 4] [ 6 8 10] [12 14 16]] '''
深度组合:
a7 = np.dstack((a1,a2))
print(a7)
''' [[[ 0 0] [ 1 2] [ 2 4]] [[ 3 6] [ 4 8] [ 5 10]] [[ 6 12] [ 7 14] [ 8 16]]] '''
注:深度组合,改变了数组的维度。
print(a1.shape,a2.shape,a7.shape)
# (3, 3) (3, 3) (3, 3, 2)
列组合:
# 列组合
b1 = np.arange(2)
b2 = b1 * 2
b3 = np.column_stack((b1,b2))
print(b3)
''' [[0 0] [1 2]] '''
# 注意一维数组的列组合与水平组合的区别
b4 = np.hstack((b1,b2))
print(b4)
''' [0 1 0 2] '''
# 对于二维数组,列组合与水平组合效果相同
b5 = np.column_stack((a1,a2))
print(b5)
''' [[ 0 1 2 0 2 4] [ 3 4 5 6 8 10] [ 6 7 8 12 14 16]] '''
注:对于二维数组,列组合与水平组合效果相同。
行组合:
# 行组合
b6 = np.row_stack((b1,b2))
print(b6)
''' [[0 1] [0 2]] '''
# 同样需注意一维数组的行组合与垂直组合的区别
# 对于二维数组,行组合与垂直组合效果一致
b6 = np.row_stack((a1,a2))
print(b6)
''' [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 0 2 4] [ 6 8 10] [12 14 16]] '''
数组分割:
对应数组的组合,也包括数组的水平分割、垂直分割、深度分割。
c1 = np.arange(9).reshape(3,3)
print(c1)
''' [[0 1 2] [3 4 5] [6 7 8]] '''
# 水平分割
c2 = np.hsplit(c1,3)
print(c2)
''' [array([[0], [3], [6]]), array([[1], [4], [7]]), array([[2], [5], [8]])] '''
print(type(c2)) # 获得一个包含数组的列表
# <class 'list'>
# split 函数进行水平分割
c3 = np.split(c1,3,axis=1)
print(c3)
''' [array([[0], [3], [6]]), array([[1], [4], [7]]), array([[2], [5], [8]])] '''
# 垂直分割
c4 = np.vsplit(c1,3)
print(c4)
''' [array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])] '''
# 同样也可以用 split 函数实现垂直分割
c5 = np.split(c1,3,axis=0)
print(c5) # [array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]
# 深度分割
d1 = np.arange(27).reshape(3,3,3)
print(d1)
''' [[[ 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]]] '''
print(d1.shape) # (3, 3, 3)
d2 = np.dsplit(d1,3)
print(d2)
''' [array([[[ 0], [ 3], [ 6]], [[ 9], [12], [15]], [[18], [21], [24]]]), array([[[ 1], [ 4], [ 7]], [[10], [13], [16]], [[19], [22], [25]]]), array([[[ 2], [ 5], [ 8]], [[11], [14], [17]], [[20], [23], [26]]])] '''
for d_ in d2:
print(d_.shape)
# 深度分割将一个 3x3x3 数组切割为一个包含3个 3x3x1 数组的列表
''' (3, 3, 1) (3, 3, 1) (3, 3, 1) '''
9、数组的属性:
ndim 获取数组维度或轴个数:
e1 = np.arange(24).reshape(2,12)
print(e1)
''' [[ 0 1 2 3 4 5 6 7 8 9 10 11] [12 13 14 15 16 17 18 19 20 21 22 23]] '''
# ndim 获取数组维度或轴个数
print(e1.ndim) # 2
size 获取数组元素总个数:
print(e1.size) # 24
itemsize 获取数组中元素在内存中所占字节数:
print(e1.itemsize) # 4
nbytes 获取数组对象所占储存空间:
print(e1.nbytes) # 96
注:总是等于 itemsize 值与 size 值的乘积。
flat 获取数组对象的扁平迭代器(flatiter):
e2 = np.arange(9).reshape(3,3)
print(e2)
''' [[0 1 2] [3 4 5] [6 7 8]] '''
e_flat = e2.flat # 这是获取 flatiter 对象的唯一方式
print(e_flat) # <numpy.flatiter object at 0x000000000A1306C0>
for item in e_flat:
print(item,end=',')
# 0,1,2,3,4,5,6,7,8,
结尾:
以上就是本篇博客全部内容,感谢阅读。