文章目录
- 引言:numpy的常见注意事项
- 一、numpy数组创建
- 二、numpy数组的常用属性与方法
-
- 1 numpy数组常用属性
- 2 改变numpy数组形状的方法
- 3 改变数组的数据类型
- 4 numpy数组的扁平化(展开)
- 5 numpy数组合并
- 6 numpy数组的插入、删除、尾部增加
- 7 numpy数组random模块
-
- (1)np.random.random((a,b))---- 生成在区间 [0.0, 1.0) 内均匀分布的 随机数
- (2) np.random.randint(a,b,(2,3)) ---生成shape=(2.3)数组,各元素为[a,b)间的 整数
- (3) np.random.rand(a,b) ---生成在 [0.0, 1.0) 区间上均匀分布的随机样本.括号里面的就是形状
- (4) np.random.randn(a,b) --- 生成服从标准正态分布(均值为0,标准差为1)的随机样本.括号里面的就是形状
- (5)np.random.normal(均值,标准差,(a,b)) ----- 生成服从正态分布(高斯分布)的随机样本
- 8 numpy获取数组最大最小索引
- 9 numpy数组排序
- 10 numpy数组的去重和重复
- 三 numpy数组的矩阵运算与统计函数
-
- 1 逐点相乘(点乘)---- * 和np.multiply()
- 2 矩阵乘法
- 3 转置
- 4 np.linalg模块
- 5 np.corrcoef(arr) ---- 计算变量间相关系数矩阵
- 6 numpy中各种统计函数
- 四 numpy数组的操作 --- 索引、访问、切片、布尔布尔数组进行索引、整数数组进行索引
- 五 numpy数组的保存与读取
- 六 numpy统计分析实际案例
- 总结
引言:numpy的常见注意事项
1 numpy数组是可变数据类型
NumPy 数组(numpy.ndarray
)是 可变数据类型。
以下是一些关键点来解释 NumPy 数组的可变性:
(1). 元素的修改:
- NumPy 数组中的元素是可以被修改的。你可以直接通过索引来改变数组中的某个值。例如,给定一个数组
arr
,你可以通过arr[0] = 10
来修改数组的第一个元素。
import numpy as np
arr = np.array([1, 2, 3])
arr[0] = 10 # 修改第一个元素为 10
print(arr) # 输出: [10 2 3]
(2). 数据的视图与切片:
- NumPy 提供了一种称为视图(view)的机制,通过该机制可以从一个数组创建一个新的数组对象,而不复制数据本身。这意味着通过一个视图修改数组中的元素也会影响到原始数组。
arr = np.array([1, 2, 3, 4])
view = arr[1:3] # 创建一个视图
view[0] = 10 # 修改视图中的元素
print(arr) # 输出: [ 1 10 3 4],原始数组也被修改
(3). 内存共享:
- 多个数组可以共享相同的数据内存。例如,通过
np.copy
创建的数组和通过view
创建的数组都可以共享同一块内存。这种机制增强了数组的可变性,因为一个数组的修改可能会影响到共享同一内存的其他数组。
综上所述,NumPy 数组允许对其内容进行修改,并且这种修改会反映在使用该数组的所有视图中,这表明它是可变的。
2 numpy数组的view视图机制以及与列表的区别
2.1 numpy数组的view机制
NumPy 的视图(view)机制是一种允许多个数组对象共享相同数据内存的方法。通过这种机制,创建一个数组的视图(view)不会复制数据本身,而是创建一个新的数组对象,该对象使用与原始数组相同的内存。因此,对视图的修改会影响原始数组,反之亦然。(简单来说就是numpy数组的赋值操作是共享原内存的。)
2.1.1视图机制的工作原理
视图机制的关键在于 NumPy 数组的设计。每个 numpy.ndarray
对象包括以下几个部分:
- 数据缓冲区:实际存储数组数据的内存区域。
- 形状信息:定义数组的维度和每个维度的大小。
- 步长信息:定义在数组的每个维度上前进一个元素所需跳过的字节数。
视图与原数组共享相同的数据缓冲区,但它们可以拥有不同的形状和步长信息,这使得视图可以表现为不同的数组结构。
2.1.2视图的创建方式
(1). 数组切片(Slicing):
使用切片操作可以创建一个数组的视图。切片操作不会创建数据的副本,而是生成一个新的数组对象,该对象仍然引用原始数据。
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4] # 创建视图
view[0] = 10 # 修改视图中的元素
print(arr) # 输出: [ 1 10 3 4 5],原始数组也被修改
print(view) # 输出: [10 3 4]
(2). 数组的变形(Reshape):— 特别注意这个也是内存共享的在numpy里面没有想到吧
使用 reshape
方法重新定义数组的形状,也会创建一个视图,只要新形状与原数组兼容且没有引入新的内存布局(如展平操作)。
arr = np.array([[1, 2, 3], [4, 5, 6]])
view = arr.reshape(3, 2) # 创建视图
view[0, 0] = 10
print(arr) # 输出: [[10 2 3]
# [ 4 5 6]],原始数组被修改
print(view) # 输出: [[10 2]
# [ 3 4]
# [ 5 6]]
(3). 数组的转换操作(Transpose):
使用 transpose
或 T
属性可以创建一个转置视图。
arr = np.array([[1, 2], [3, 4]])
view = arr.T # 创建转置视图
view[0, 1] = 10
print(arr) # 输出: [[ 1 2]
# [ 10 4]],原始数组被修改
print(view) # 输出: [[ 1 10]
# [2 4]]
总结一下就是,numpy的view机制就是让将原本的赋值给新变量应该是创建一个新内存,但numpy的view视图机制 ---- 就是让赋值操作不改变内存,还是共享原来的内存。
arr = np.array([1, 2, 3, 4])
arr2 = arr
arr2[0] = 100
print(arr) # [100 2 3 4]
print(arr2) # [100 2 3 4]
arr 和arr2的内存是共享的,这就是numpy数组的view视图机制的神奇之处,也是我们需要注意的地方。
【注】:numpy数组变成就是进行的是赋值操作也要想想是不是内存共享的。
2.2 numpy view机制与列表(没有view机制的数据结构)的区别
看下面前先了解列表的浅复制与深复制python列表
NumPy 的视图(view)机制和 Python 列表之间有着显著的差异,尤其在内存管理和数据操作方面。下面我们详细对比 NumPy 数组的视图机制与 Python 列表的特性。
1. 内存管理
NumPy 数组的视图
-
共享内存:
- NumPy 数组的视图和原数组共享相同的内存空间。任何对视图的修改都会直接反映在原数组上,反之亦然。
- 视图(view)是通过操作原数组的元数据(如形状、步长等)而产生的,不涉及数据的复制。
import numpy as np arr = np.array([1, 2, 3, 4]) view = arr[1:3] # 创建一个视图 view[0] = 10 # 修改视图中的值 print(arr) # 输出: [ 1 10 3 4],原始数组被修改 print(view) # 输出: [10 3],视图反映出修改
-
内存连续性:
- NumPy 数组通常是内存连续的,这意味着数据在内存中是按顺序排列的,这对计算效率有很大的提升。
- 视图可以通过步长(strides)和形状信息创建,从而允许非常高效地操作数组的子集或重塑数组。
Python 列表
-
独立内存:
- Python 列表没有视图机制。每个列表元素的引用是独立的,创建子列表时会复制引用(浅拷贝),但不会共享相同的内存空间。即使是子列表,对它的修改不会影响原始列表。
- 深拷贝和浅拷贝的概念在列表中尤为重要。浅拷贝会创建一个新列表,其中的元素是原列表中对象的引用,而深拷贝则会递归地复制整个对象及其子对象。
lst = [1, 2, 3, 4] sub_lst = lst[1:3] # 创建一个子列表(浅拷贝) sub_lst[0] = 10 # 修改子列表中的值 print(lst) # 输出: [1, 2, 3, 4],原始列表未被修改 print(sub_lst) # 输出: [10, 3],子列表显示修改
-
内存分散:
- 列表中的元素是独立存储的引用,存储地址可能不连续。这种非连续性使得某些操作的效率较低,特别是与数值运算相关的操作。
2. 数据操作
NumPy 数组的视图
-
高效的切片与变形:
- NumPy 允许通过视图对数组进行非常高效的切片、重塑和操作,而无需实际的数据复制。可以通过视图进行快速的子数组操作。
arr = np.array([1, 2, 3, 4, 5, 6]) reshaped = arr.reshape((2, 3)) # 重塑数组,不复制数据 print(reshaped) # 输出: [[1 2 3] # [4 5 6]]
-
内存高效性:
- 由于视图共享内存,操作大数据集时无需担心内存的过度使用。许多数值操作可以在原地进行,减少了内存的开销。
Python 列表
- 切片创建新列表:
- 切片操作创建一个新的列表(浅拷贝),这意味着元素的引用被复制了,但实际的数据没有复制。如果原始列表包含的不是简单数据类型,而是对象,修改对象会影响到切片和原列表。
- 但如果是顶层不可变数据,浅拷贝是修改是不会影响原列表的
ls = [1,[2,3],4,5]
a = ls[1:3] # [[2, 3], 4] 将列表切片进行赋值给变量a。本质上就是浅拷贝
a[1] = 100
print(a) # [[2, 3], 100]
print(ls) # [1, [2, 3], 4, 5] 浅拷贝修改顶层不影响原列表
a[0][0] = 200
print(ls) # [1, [200, 3], 4, 5] 浅拷贝修改内层可变数据类型会影响原列表
-
深拷贝:
- 如果需要真正独立的副本,必须使用深拷贝(
copy.deepcopy
),这会递归地复制列表及其包含的所有元素。深拷贝在需要隔离修改时非常重要,但它比视图操作的效率低得多。
import copy lst = [[1, 2], [3, 4]] deep_copy_lst = copy.deepcopy(lst) deep_copy_lst[0][0] = 10 print(lst) # 输出: [[1, 2], [3, 4]],原始列表未受影响 print(deep_copy_lst) # 输出: [[10, 2], [3, 4]],深拷贝反映出修改
- 如果需要真正独立的副本,必须使用深拷贝(
2.3 视图与副本的区别
看下面前先了解列表的浅复制与深复制python列表
视图(view)和副本(copy)的主要区别在于内存共享:
- 视图:多个数组对象共享相同的数据内存,对其中任何一个的修改都会反映在所有视图中。
- 副本:副本是原数据的完全独立的克隆,修改副本不会影响原始数组,反之亦然。把副本理解成深拷贝就行了。
- 在numpy数组里面数据
- 注意,numpy自带的 .copy方法是深复制,和copy模块的copy方法是浅复制不一样
arr = np.array([1, 2, 3, 4])
view = arr[1:3] # 创建视图
copy = arr[1:3].copy() # 创建副本
view[0] = 10
copy[0] = 20
print(arr) # 输出: [ 1 10 3 4],视图的修改影响原数组
print(view) # 输出: [10 3]
print(copy) # 输出: [20 3],副本的修改不影响原数组
二维数组
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
copy = arr[:,0].copy() # 创建副本,numpy自带的是深复制
print(copy) # 输出: [1 4 7]
copy[0] = 20
print(arr) # 输出: [[1 2 3] [4 5 6] [7 8 9]],副本的修改不影响原数组
print(copy) # 输出: [20 4 7],副本的修改不影响原数组
【注】:在numpy数组中由于有自带的 .copy(深复制)就不要和列表一样用copy模块了。另外由于numpy数组特殊的视图机制,如果用copy模块里面浅复制,两者结合在一起会导致浅复制效果和深复制效果一样。之前列表那套规则完全不适用,就不用管了。
2.4 视图的优势
- 效率:视图避免了数据复制,因此可以显著节省内存和时间,尤其是处理大型数据集时。
- 灵活性:可以通过视图创建不同的数组视图,进行不同的操作,而无需改变原始数组的数据。
NumPy 的视图机制是高效数据操作的核心特性,理解和正确使用它有助于优化数组操作的性能。
一、numpy数组创建
1. np.array(object,dtype)
np.array()
是 NumPy 中用于创建数组的函数,它将输入的数据转换为 NumPy 数组。以下是 np.array()
函数的两个常用参数及其详细说明,其他参数默认值即可:
(1). object
:输入的数据对象,可以是以下类型之一:
- Python 列表或元组:
[1, 2, 3]
,(1, 2, 3)
- 嵌套列表或元组:
[[1, 2], [3, 4]]
,((1, 2), (3, 4))
- 可迭代对象:如 Python 迭代器、生成器等
(2). dtype
:(可选参数)所需的输出数据类型。如果未提供,NumPy 将自动推断数据类型。常见的数据类型包括:
int
,float
,complex
: 整数、浮点数、复数bool
: 布尔值str
: 字符串- 其他数据类型:如
np.int32
,np.float64
等
arr1 = np.array([1,2,3])
print(arr1) # 输出 [1,2,3]
arr2 = np.array([[1,2,3],
[4,5,6]], dtype=np.float64)
print(arr2) # 输出 [[1. 2. 3.]
# [4. 5. 6.]]
2. np.arange(start,stop,step,dtype)
左闭右开
np.arange()
是 NumPy 库中的一个函数,用于创建一个按指定范围和间隔的等差数组。
参数说明:
start
:可选,表示起始值,默认为0。stop
:结束值(不包含在数组内)。step
:可选,表示步长(增量),默认为1。如果未指定步长,会自动使用1。dtype
:可选,返回数组的数据类型。如果未提供,则会根据其他输入推断数据类型。
np.arange()
返回的是一个 NumPy 数组,其中包含从 start
到 stop
(不包含 stop
)之间,以 step
为步长的一系列数值。
arr1 = np.arange(1,10,2,dtype=np.float64)
print(arr1) # [1. 3. 5. 7. 9.]
arr2 = np.arange(10)
print(arr2) # [0 1 2 3 4 5 6 7 8 9]
3 np.linspace(start,stop,num)
默认是左闭右闭
np.linspace()
函数是 NumPy 库中的一个函数,用于创建一个指定范围内的等间隔数组。它的语法如下:
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
参数说明:
start
:表示数组的起始值。stop
:表示数组的结束值(包含在数组内)。num
:可选,表示生成的样本数,默认为50。endpoint
:可选,如果为 True,则在 stop 值包含在数组内,如果为 False,则不包含在数组内。默认为 True。retstep
:可选,如果为 True,则返回 (array, step) 的元组,其中 step 为数组的步长。默认为 False。dtype
:可选,返回数组的数据类型。如果未提供,则会根据其他输入推断数据类型。
np.linspace()
返回的是一个 NumPy 数组,其中包含从 start
到 stop
(包含 stop
)之间,总共 num
个等间隔的数值。
下面是一些示例:
import numpy as np
# 生成一个从0到10,共11个元素的等间隔数组
array1 = np.linspace(0, 10, 11)
print(array1) # 输出: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
# 生成一个从0到2π,共100个元素的等间隔数组
array2 = np.linspace(0, 2*np.pi, 100)
print(array2) # 输出: [0. 0.06346652 0.12693304 ... 6.15625227 6.21971879 6.28318531]
# 生成一个从1到5,共20个元素的等间隔数组,并返回步长
array3, step = np.linspace(1, 5, 20, retstep=True)
print(array3) # 输出: [1. 1.21052632 1.42105263 ... 4.78947368 5. ]
print(step) # 输出: 0.21052631578947367
总之,np.linspace()
函数非常方便,可以快速生成指定范围内等间隔的数组,并且可以选择是否包含结束值以及是否返回步长。
4 np.logspace(start,stop,num)
np.logspace()
函数是 NumPy 库中的一个函数,用于创建一个对数刻度的等比数组。它的语法如下:
numpy.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
参数说明:
start
:表示序列的起始值为 base ** start。stop
:表示序列的结束值为 base ** stop。num
:可选,表示生成的样本数,默认为50。endpoint
:可选,如果为 True,则在 stop 值包含在数组内,如果为 False,则不包含在数组内。默认为 True。base
:可选,对数空间的底数,默认为10。dtype
:可选,返回数组的数据类型。如果未提供,则会根据其他输入推断数据类型。
np.logspace()
返回的是一个 NumPy 数组,其中包含从 base ** start
到 base ** stop
之间,总共 num
个对数刻度上等分(等比均分)(的数值。
下面是一个示例:
import numpy as np
# 生成一个在 10**0=1 到 10**2=100 之间,在对数刻度上均匀分布(等比均分)的 5 个点
array = np.logspace(0, 2, 5)
print(array) # 输出: [ 1. 3.16227766 10. 31.6227766 100. ]
# 10/3.16227766=100/31.6227766=3.16227766
在上述示例中,np.logspace(0, 2, 5)
生成了一个包含 5 个数值的数组,这些数值在对数刻度上均匀分布在 1 到 100 之间。
np.logspace()
函数在需要在对数刻度下生成均匀分布的数值时非常有用,例如在绘制对数坐标图时。
5 创建特殊数组
np.zeros((a,b))—生成0矩阵,a行,b列(a行,b列全0二维数组)
np.ones((a,b))—生成1矩阵,a行,b列(a行,b列全1二维数组)
np.eye(m)—生成m阶单位阵
np.diag([a,b,c,…])—生成对角阵
np.full((a,b),num)—生成用指定num填充的数组
arr = np.full((3, 3), 9)
print(arr) # [[9 9 9]
# [9 9 9]
# [9 9 9]]
np.empty((a,b))----生成二维空数组,只申请空间,不初始化
6 创建与给定数组相同形状的数组
参数:
arr:数组,创建与这个数组相同形状的数组
dtype:数据类型
np.zeros_like(arr,dtype)
np.ones_like(arr,dtype)
np.empty_like(arr.dtype) ---- 函数会返回一个与给定数组形状相同,并且元素为随机值的数组。
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6]])
zero = np.zeros_like(arr)
print(zero) # [[0 0 0]
# [0 0 0]]
one = np.ones_like(arr)
print(one) # [[1 1 1]
# [1 1 1]]
empty = np.empty_like(arr)
print(empty)
np.full_like(arr,num) — 创建与arr形状相同用num填充的数组
a = np.arange(12).reshape(3,4)
full = np.full_like(a, 10)
print(full) # [[10 10 10 10]
# [10 10 10 10]
# [10 10 10 10]]
二、numpy数组的常用属性与方法
1 numpy数组常用属性
NumPy 数组(也称为 ndarray)是 NumPy 库的核心数据结构,它具有许多属性和方法,用于对数组进行各种操作和计算。以下是一些常用的 NumPy 数组属性:
ndarray.shape:表示数组的维度,返回一个元组,元组的长度就是数组的维数,每个元素的值对应于数组在相应维度上的大小。
ndarray.ndim:表示数组的维数(即数组轴的数量)。
ndarray.size:表示数组中元素的总数。
ndarray.dtype:表示数组中元素的数据类型。
下面是一个示例,演示了如何访问和使用这些属性:
import numpy as np
# 创建一个 2x3 的二维数组
arr = np.array([[1, 2, 3], [4, 5