荐 NumPy 快速入门:数组对象,数组属性,花式索引等基础介绍
NumPy 快速入门:数组对象,数组属性,花式索引等基础介绍
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,
结尾:
以上就是本篇博客全部内容,感谢阅读。
本文地址:https://blog.csdn.net/zhouz92/article/details/107250364