Numpy
什么是NumPy:Python普通列表list是为通用数据设计,循环很慢。NumPy专门做数字计算,底层是C语言,运算速度快几十上百倍,是数据分析、机器学习的基础库。
1. NumPy 内置了并行运算功能,当系统有多个核心时,做某种计算时,NumPy 会自动做并行计算。
2. NumPy 底层使用 C 语言编写,内部解释器(全局解释器锁),其对数组的操作速度不受 Python 解释器的限制,效率远高于纯 Python 代码。
3. 有一个强大的 N 维数组对象 Array(一种类似于列表的东西)。
安装:pip install numpy
固定导入写法:import numpy as np
安装 Jupyter 学习 NumPy 相对方便,通过 pip install jupyter 安装
pip install numpy pip install jupyterNumpy数组和Python列表性能对比:
import time import numpy as np start_time = time.time() a = [] for x in range(100000): a.append(x**2) end_time = time.time() print("%.6f"%float(end_time-start_time))# 0.011967897415161133 start_time = time.time() a = np.arange(100000)**2 end_time = time.time() print("%.6f"%float(end_time-start_time))1. ndarray 数组
ndarray是NumPy的核心多维数组对象,全称 n-dimensional array,n 维数组。
普通 Python 列表:可以放不同类型数据[1,"abc",3.5]
NumPy 数组 ndarray:全部元素必须是同一个类型(全是整数 / 全是小数)
创建数组(np.array 对象):np.array()是 NumPy 最核心函数,作用:把 Python 列表 / 元组等序列,转换成 NumPy 数组对象(ndarray 对象),返回的对象类型叫 ndarray
基础语法:
np.array(object, dtype=None, copy=True, order=None, ndmin=0)import numpy as np 把python列表转numpy数组 arr = np.array([[1,2,3], [4,5,6]]) print(arr) #[[1 2 3] [4 5 6]] print("维度ndim:", arr.ndim) # 2 print("形状shape:", arr.shape) # (2,3) 2行,3列 print("元素总数size:", arr.size) # 6 2*3=6 print("数据类型dtype:",arr.dtype) # int64 整数 print("单个元素占字节itemsize:",arr.itemsize)解析:
- object:必传参数,输入数据,可以是列表、元组或嵌套列表。
- ndim:维度。一维就是向量,二维就是表格矩阵。
- shape:格式为(行数,列数),通过它就能看出数组的结构。
- size:数组中元素的总个数。
- dtype:数组中元素的数据类型,如 int 整数、float 小数。
- itemsize:单个元素占用字节数。
指定数据类型
# 强制设置为32位浮点数 a = np.array([1,2,3], dtype=np.float32) print(a.dtype) 转换类型,astype会生成新数组,不会改原来的 b = a.astype(np.int32) print(b.dtype)⚠️小白坑:astype()不会修改原数组,要接收返回值。
2. 创建数组
① 创建全0、全1数组
# 参数传元组(行数,列数) zero_arr = np.zeros((2,3)) print(zero_arr) ''' [[0. 0. 0.] [0. 0. 0.]] ''' one_arr = np.ones((3,2)) print(one_arr) 全部填充同一个数字 full_arr = np.full((2,2), 8) print(full_arr) ''' [[8 8] [8 8]] '''解析:np.zeros((行,列)),括号里面要传元组,不能写np.zeros(2,3)会报错!
② 生成数字序列
# np.arange(起点,终点,步长),左闭右开,取不到终点数字 a1 = np.arange(0,10,2) print(a1) # [0 2 4 6 8] linspace:起点到终点,一共生成多少个点,包含首尾 a2 = np.linspace(0, 10, 5) print(a2) # [ 0. 2.5 5. 7.5 10. ]对比记忆:
arange:按步长取数字,不一定包含终点linspace:指定一共多少个数字,首尾一定包含- linspace = linear space,线性等间距采样作用:在
[start, stop]区间,生成指定个数的均匀间隔的一维 ndarray 数组。 - 重点:你告诉它要多少个点,numpy 自动计算两点之间步长。
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)参数 说明 start序列起点,包含 stop序列终点,默认包含(左闭右闭 [start, stop])num生成多少个样本点,默认 50,必须≥0endpoint=TrueTrue:把 stop 作为最后一个元素;False:不包含终点 retstep=FalseTrue 时,返回元组 (数组,步长step),同时返回算出的间隔大小dtype指定输出数组的数据类型
③ 随机数数组
# 设置随机种子,写一样的种子,每次运行随机数都一样,方便调试 np.random.seed(10) rand(行,列) 生成0~1之间的小数 r1 = np.random.rand(2,3) print(r1) randint(最小值,最大值,(行,列)),整数,取不到最大值 r2 = np.random.randint(0,10,(3,3)) print(r2)1️⃣
np.random.seed(种子数字)随机种子- 核心作用:固定随即结果
- 只要种子数字相同,每次运行代码生成的随机数组完全一模一样,方便调试、复现实验。
- 只对后面的随机函数生效,写一次即可,不需要每次生成随机数都写。
- 如果不写 seed,每次运行得到的随机数都会不一样。
- 示例:
np.random.seed(10) print(np.random.rand(2)) # 不管运行多少次,输出永远是 [0.77132064 0.02075195] 2️⃣
np.random.rand(行,列)- 生成
[0,1)区间的浮点数(包含 0,不包含 1) - 参数直接写维度:
rand(2,3)→2 行 3 列数组 - 注意:
rand()参数是分开写,不是传元组,rand(2,3)✔;rand((2,3))也可以,但习惯分开写。 3️⃣
np.random.randint(low, high, size)随机整数语法:
np.random.randint(最小值, 最大值, (行数,列数))- 取值范围:
[low , high),能取到最小值,取不到最大值! - 例子:
randint(0,10,(3,3))→ 整数范围:0,1,2,...,9,永远不会出现 10 - 第三个参数
size传入元组,指定数组形状。
3. 数组形状操作(reshape)
视图view:不复制新内存,和原数组共用一份数据,改视图,原数组也变!
reshape:只改变数组的形状,不改变元素数量、不改变内存里真实的数据。
总元素数量必须严格匹配,否则报错!
原始arr = np.arange(6)一共 6 个元素。
reshape(2,3)→ 2×3=6 ✔合法reshape(3,2)→3×2=6 ✔合法reshape(2,2)→2×2=4≠6 ❌直接报错
-1自动推导维度
-1代表:这个维度交给 numpy 帮我自动算出来,reshape 里面最多只能写一个 - 1。
想要独立副本(不影响原数组)
调用.copy(),强制复制一份新内存:
arr2 = arr.reshape(2,3).copy() # 此时arr2是拷贝,修改arr2不会改动arrarr = np.arange(6) print(arr) # [0 1 2 3 4 5] reshape修改形状,总数元素必须匹配,6个元素只能改成23,不能改成22 arr2 = arr.reshape(2,3) print(arr2) ''' [[0 1 2] [3 4 5]] ''' -1:交给numpy自动计算维度 arr3 = arr.reshape(-1,2) print(arr3) 一共6个元素,每一行2个,自动算出有3行 → (3,2)ravel() vs flatten()多维→一维
arr = np.array([[1,2],[3,4]]) a = arr.ravel() ravel展平,优先返回【视图】,共用内存 b = arr.flatten() flatten展平,永远返回【拷贝】,全新内存,互不干扰ravel能不拷贝就不拷贝;flatten一定拷贝一份新的。
reshape() vs resize()
核心区别:reshape:不改变原数组,返回新视图;元素总数必须完全匹配resize:直接修改原数组,可以改变元素总个数
import numpy as np # reshape:返回新数组,元素总数必须匹配,支持-1自动算维度 a = np.arange(12) b = a.reshape(3, 4) print("reshape结果:\n", b) print("reshape原数组不变:", a) c = a.reshape(-1, 6) print("reshape(-1,6):\n", c) # resize:原地修改原数组,可改总元素,多出补0 arr = np.arange(12) arr.resize(4, 3) print("\nresize(4,3):\n", arr) arr.resize(5, 5) print("resize扩大补0:\n", arr)对比表
| 方法 | reshape() | resize() |
|---|---|---|
| 是否修改原数组 | ❌不修改,返回新数组 | ✅直接修改原数组 |
| 元素总数 | 总数必须相等,否则报错 | 可以变大/变小;多出补0,不够直接截断 |
| 自动维度 | 支持‑1自动推算 | 不支持‑1 |
易错
reshape一定要接收返回值,原数组不变!
a.reshape(3,4) #错误,结果丢弃,a没变 a2 = a.reshape(3,4) #正确- resize直接改自己,不需要赋值接收
arr.resize(4,3) #直接生效,不用 arr = arr.resize(4,3)复制,区分视图和真拷贝
ori = np.array([1,2,3]) view = ori[:] #切片,得到视图,共享内存 copy = ori.copy()#真正复制,两块独立内存 view[0] = 99 print(ori) # [99 2 3] 原数组被修改! copy[0] = 100 print(ori) # [99 2 3] copy修改不会影响原数组易错:切片得到视图,修改切片会把原始数组改掉!不想改原数组,就用.copy()
转置
和 reshape 一样,不复制新内存,共用底层数据修改转置后的数组,原数组也会变!
import numpy as np arr = np.array([[1,2,3], [4,5,6]]) print(arr.shape) # (2, 3) 2行3列 写法1:.T 最简洁,二维最常用 arr_T = arr.T print(arr_T) ''' [[1 4] [2 5] [3 6]] ''' #行变列,列变行 print(arr_T.shape) # (3, 2) 3行2列 #写法2:.transpose() arr_T2 = arr.transpose() #想要独立副本: t=arr.T.copy()拼接、分割数组
a = np.array([[1,2],[3,4]]) b = np.array([[5,6],[7,8]]) axis=0:上下拼接,行变多(纵向) c1 = np.concatenate([a,b],axis=0) print(c1) axis=1:左右拼接,列变多(横向) c2 = np.concatenate([a,b],axis=1) print(c2) c1 = concatenate([a,b],axis=0) [[1 2] [3 4] [5 6] [7 8]] shape (4,2),行数变多 c2 = concatenate([a,b],axis=1) [[1 2 5 6] [3 4 7 8]] shape (2,4),列数变多axis简单理解:axis=0→ 沿着行方向操作(上下)axis=1→ 沿着列方向操作(左右)
axis=0上下拼接:列数必须相同,行数可以不一样axis=1左右拼接:行数必须相同,列数可以不一样a = np.array([[1,2],[3,4]]) # shape(2,2) b = np.array([[5,6]]) # shape(1,2) np.concatenate([a,b],axis=0) # ✔列都是2,可以上下拼 np.concatenate([a,b],axis=1) ❌行数不一致,报错
vstack等价axis=0;hstack等价axis=1
np.vstack([a,b])⇔np.concatenate([a,b], axis=0)上下堆np.hstack([a,b])⇔np.concatenate([a,b], axis=1)左右拼
np.split(数组, 分割点列表, axis=?)
axis=0按行切割(横向切开,切成好几块)axis=1按列切割(纵向切开)arr = np.arange(12).reshape(4,3) # 在行索引 2 的位置切开,切成两块 parts = np.split(arr,[2],axis=0) print(parts[0]) #前2行 print(parts[1]) #后2行np.vsplit等价axis=0;np.hsplit等价axis=1。- concatenate 第一个参数是数组列表
[a,b],不要写成a,b。 - axis=0 上下,要求列数相等;axis=1 左右,要求行数相等。
- vstack/hstack 只是 concatenate 的简写。
- 拼接出来是新拷贝数组,不是视图,修改拼接结果不会影响原数组。
4. 索引切片(取数组里的数据)
一维数组
a = np.array([10,20,30,40,50]) print(a[0]) #取第0个元素 10 print(a[1:3]) #切片,下标1到2,左闭右开 [20 30] print(a[::2]) #步长2,隔一个取一个 [10 30 50]二维数组(表格)
arr = np.array([[1,2,3], [4,5,6], [7,8,9]]) arr[行下标 , 列下标] 逗号分隔!! print(arr[0,1]) #第0行,第1列 → 2 print(arr[1:3, 0:2]) 行:取1,2两行;列:取0,1两列 print(arr[2,:]) #第2行,所有列 print(arr[:,1]) #所有行,第1列 import numpy as np a1 = np.arange(0,29) 1. 获取某⾏的数据 数组名[索引] a1[1] a1 = np.arange(0,24).reshape((4,6) ''' [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17] [18 19 20 21 22 23]] ''' a[2] #获取下标为2的整行(第3行) a[2,:]#标准写法 #- 逗号前面:行下标 #- 逗号后面::代表所有列 a[2][:] # Python列表风格,numpy不推荐,性能差 2. 连续获取某几行数据 a[1:3, :]# 获取下标1、2两行(第2、3行) 1:3:行从1取到3 之前,取 1、2 : 代表所有列,不能省略 a[0:2, :] # 取第0、1行(前2行) a[1:, :] # 从下标1一直到最后所有行 a[:3, :] # 从开头到下标3之前,取0,1,2行 a[-2:, :] # 取最后2行 a[:, 1:4] # 所有行,第1,2,3列 #注意:a[1:3]也可以运行,省略后面的,:,但考试写完整 a[1:3, :]更规范 3. 获取某⾏某列的数据 a[0,0] #第0行第0列,左上角第一个元素 →0 a[1,2] #第1行第2列 →8 a[-1,-1] #最后一行最后一列 →23 4. 获取某列的数据 a[:, 0] # 第0列(第一列) a[:, -1] # 最后一列 #连续取多列 a[:, 1:4] # 所有行,列下标1、2、3(左闭右开)小白注意:推荐逗号写法arr[i,j],不要写arr[i][j]
总结:
- 如果数组是一维的,那么索引和切片就和 Python 中的列表是一样的。
- 如果是多维的,那么在中括号中给两个值,两个值是通过逗号分隔的,逗号前面是行,后面是列。如果中括号中只有一个值,那么就表示行。
- 如果是多维数组,那么行的部分和列的部分都是遵循一维数组的方式,可以使用整形、切片,还可以使用中括号的形式来代表不连续的。
布尔索引(条件筛选)
arr = np.array([1,2,3,4,5]) mask = arr > 2 print(mask) # [False False True True True] 用布尔数组当掩码,取出条件成立的元素 res = arr[mask] print(res) # [3 4 5]多条件:必须用& | ~,每个条件加括号,不能用and or
res = arr[(arr>1) & (arr<4)] print(res)&并且|或者~非,取反
花式索引(整数数组索引)
arr = np.arange(10) res = arr[[0,2,4]] print(res) #取下标0、2、4的元素 [0 2 4]⚠️重点:普通切片是视图;布尔索引、花式索引返回拷贝,修改结果不会影响原数组。
值的替换
import numpy as np # ========== 1.切片/索引替换 ========== arr = np.arange(12).reshape(3, 4) print("=====1.切片索引替换=====") print("原始数组:\n", arr) arr[1, :] = 99 # 修改第1整行 print("修改第1行后:\n", arr) arr[:, 2] = 88 # 修改第2整列 print("修改第2列后:\n", arr) arr[0:2, 0:2] = 0 # 修改局部一块区域 print("局部切片替换后:\n", arr) # ========== 2.条件布尔索引替换(原地修改原数组) ========== arr = np.arange(12).reshape(3, 4) print("\n=====2.条件布尔替换=====") print("原始数组:\n", arr) arr[arr > 5] = -1 # 大于5替换为-1 print("大于5替换成-1:\n", arr) # ========== 3.np.where()替换,返回新数组,原数组不变 ========== arr = np.arange(12).reshape(3, 4) print("\n=====3.np.where替换=====") print("原始数组:\n", arr) new_arr = np.where(arr > 5, 1, 0) print("where处理后的新数组:\n", new_arr) print("原数组不变:\n", arr) # 考试高频示例:奇数替换-1 print("\n=====高频例题:奇数替换为‑1=====") arr1 = np.arange(10) arr1[arr1 % 2 == 1] = -1 print("布尔原地替换结果:", arr1) arr2 = np.arange(10) res_where = np.where(arr2 % 2 == 1, -1, arr2) print("where新数组结果:", res_where) print("where原数组:", arr2)where
import numpy as np #模式1:三参数where(条件,x,y),返回新数组,原数组不变 arr = np.array([1, 6, 3, 8, 2]) res = np.where(arr > 4, 99, 0) print("模式1原数组:", arr) print("where结果:", res) arr2 = np.arange(10) res2 = np.where(arr2 % 2 == 1, -1, arr2) print("奇数替换-1:", res2) print("原数组不变:", arr2) #模式2:仅传条件,获取满足条件的下标 arr3 = np.array([1, 6, 3, 8, 2]) idx = np.where(arr3 > 4) print("\n满足条件下标:", idx) print("取出元素:", arr3[idx]) #二维where a2d = np.arange(12).reshape(3, 4) pos = np.where(a2d > 8) print("二维行索引:", pos[0]) print("二维列索引:", pos[1]) #对比布尔索引原地修改 arr_test = np.array([1, 6, 3, 8, 2]) arr_test[arr_test > 4] = 99 print("\n布尔索引原地修改:", arr_test)numpy索引和切片的练习
1.将np,arange(10)数组中的奇数全部都替换成-1.
2.有⼀个4⾏4列的数组(⽐如:np.random.randint(0,10,size=(4,4)),请将其中对⻆线的数取出来形成⼀
个⼀维数组,提示(使⽤np.eye)·
3.有⼀个4⾏4列的数组,请取出其中(0,0),(1,2),(3,2)的点。
12
4.有⼀个4⾏4列的数组,请取出其中2:3⾏(包括第3⾏)的所有数据。
5.有⼀个8⾏9列的数组,请将其中1-6⾏(包含第5⾏)的第8列⼤于3的数全部都取出来。
6.替换数组中所有⼩于平均值的元素
7.将⼀个数组的边框元素设置为0
8.反转⼆维数组的⾏
9. 将3D数组的最后⼀个维度进⾏求和
10.选择数组中的⾮对⻆线元素import numpy as np ========== 1. 将np.arange(10)数组中的奇数全部都替换成-1 ========== arr = np.arange(10) arr[arr % 2 == 1] = -1 print("1.奇数替换-1:\n", arr) ========== 2. 4×4数组,取出对角线元素形成一维数组 ========== np.random.seed(0) a2 = np.random.randint(0, 10, size=(4, 4)) diag = a2[np.eye(4, dtype=bool)] print("\n2.对角线元素:\n", diag) ========== 3. 4×4数组,取出(0,0),(1,2),(3,2)的点 ========== np.random.seed(1) a3 = np.random.randint(0, 10, (4, 4)) res3 = a3[[0, 1, 3], [0, 2, 2]] print("\n3.指定坐标取值:\n", res3) ========== 4. 4×4数组,取出2:3行(包括第3行)所有数据 ========== a4 = np.random.randint(0, 10, (4, 4)) res4 = a4[1:3, :] print("\n4.第2、3行:\n", res4) ========== 5. 8行9列数组,取出1‑6行(包含第5行)的第8列大于3的全部数 ========== a5 = np.random.randint(0, 10, (8, 9)) sub5 = a5[0:5, 7] res5 = sub5[sub5 > 3] print("\n5.筛选结果:\n", res5) ========== 6. 替换数组中所有小于平均值的元素 ========== a6 = np.random.randint(0, 10, (5, 5)) mean_val = a6.mean() a6[a6 < mean_val] = 0 print("\n6.小于均值置0:\n", a6) ========== 7. 将数组的边框元素设置为0 ========== a7 = np.ones((5, 5)) a7[0, :] = 0 a7[-1, :] = 0 a7[:, 0] = 0 a7[:, -1] = 0 print("\n7.边框置0:\n", a7) ========== 8. 反转二维数组的行 ========== a8 = np.arange(12).reshape(4, 3) res8 = a8[::-1, :] print("\n8.反转行:\n", res8) ========== 9. 将3D数组的最后一个维度进行求和 ========== a9 = np.arange(24).reshape(2, 3, 4) res9 = a9.sum(axis=2) print("\n9.3D最后一维求和 shape:", res9.shape) print(res9) ========== 10. 选择数组中的非对角线元素 ========== a10 = np.random.randint(0, 10, (4, 4)) mask = ~np.eye(4, dtype=bool) res10 = a10[mask] print("\n10.非对角线元素:\n", res10)
5. 向量化运算 + 广播
普通python列表做加法需要循环;numpy数组可以直接加减乘除,不用写for循环!底层C跑,速度飞快
a = np.array([1,2,3]) b = np.array([4,5,6]) print(a + b) # [5 7 9] print(a * b) # [ 4 10 18] print(a **2) # [1 4 9] print(a>2) # [False False True]广播机制:形状不一样的数组也能计算
广播规则:从最后一维开始对齐,维度相等,或者其中一个维度是1,就可以自动复制扩展进行运算。
例子1:数组和单个数字运算
a = np.array([1,2,3]) res = a + 10 print(res) # [11 12 13] # 数字10相当于 [10,10,10],自动广播例子2:二维+一维
# (3,1) 3行1列 x = np.ones((3,1))#np.ones()生成全部元素为1的数组 # (1,4) 1行4列 y = np.ones((1,4)) z = x + y print(z.shape) # (3,4)np.newaxis用来增加维度
x = np.array([1,2,3]) print(x.shape) # (3,) x2 = x[:, np.newaxis] print(x2.shape) # (3,1)6. ufunc通用函数(对数组每一个元素计算)
数学函数
arr = np.array([1,4,9]) print(np.sqrt(arr)) #开根号 [1. 2. 3.] print(np.exp(arr)) #e的次方 print(np.abs([-1,-2])) #绝对值统计函数 sum mean max min
axis=0:沿着列运算,把每一列算成一个值axis=1:沿着行运算,把每一行算成一个值
arr = np.array([[1,2], [3,4]]) print(arr.sum()) #全部求和 10 print(arr.sum(axis=0)) #按列求和 [4 6] print(arr.sum(axis=1)) #按行求和 [3 7] print(arr.max()) #最大值 print(arr.argmax()) #最大值所在下标 print(arr.mean()) #平均值 print(arr.std()) #标准差np.where 条件选择
arr = np.array([1,-2,3,-4]) # np.where(条件,条件满足取这个,不满足取这个) res = np.where(arr>0, arr, 0) print(res) # [1 0 3 0]7. 矩阵运算 linalg模块
⚠️超级大坑:*代表对应位置数字相乘,不是矩阵乘法!
✅矩阵乘法用@或者np.dot()
A = np.array([[1,2],[3,4]]) B = np.array([[1,0],[0,1]]) element_mul = A * B #逐元素相乘 mat_mul = A @ B #数学矩阵乘法 print(element_mul) print(mat_mul) #求逆矩阵 inv_A = np.linalg.inv(A) #行列式 det_A = np.linalg.det(A)8. 文件读写
arr = np.arange(10) #保存numpy专用二进制文件,速度快 np.save("test.npy", arr) data = np.load("test.npy") #读写csv文本 np.savetxt("out.csv",arr,delimiter=",") read_data = np.loadtxt("out.csv",delimiter=",")