【rust】《Rust深度学习[1]-科学计算库(Ndarray)》
什么是Ndarray?
ndarray是Rust生态中用于处理数组的库。它包含了所有常用的数组操作。简单地说ndarray相当于Rust版本的numpy。
ndarray生态系统中crate的文档:
ndarray 基础库
ndarray-rand 随机数生成库
ndarray-stats 统计方法
顺序统计(最小、最大、中值、分位数等);
汇总统计(平均值、偏斜度、峰度、中心矩等)
分区;
相关分析(协方差、皮尔逊相关);
信息论的度量(熵、KL散度等);
偏差函数(距离、计数、误差等);
直方图计算。
ndarray-npy 多维数组的存储与加载
ndarray-linalg 线性代数相关函数
为什么需要Ndarray?
ndarray是专门为处理n维数组(矩阵)而设计的,里面包含了很多数学运算,比如矩阵相乘、矩阵求逆等。
其次,ndarray支持SIMD(Single Instruction Multiple Data),可以进一步提升计算性能。
SIMD 的全称是 Single Instruction Multiple Data,中文名“单指令多数据”。顾名思义,一条指令处理多个数据。
如上图所示
(a)是普通的标量计算,加法指令一次只能对两个数执行一个加法操作。
(b)是SIMD向量计算,SIMD加法指令一次可以对两个数组(向量)执行加法操作。
导入依赖库
[dependencies] ndarray = "0.15.6"
数组创建
创建数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建数组 let arr1 = array![1.,2.,3.,4.,5.]; println!("{}", arr1); }
创建阵列值
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建阵列值 // range有三个参数分别是:起始值, 范围结束值(不包含), 步长 let arr1 = Array::range(0., 10., 0.5); println!("{}", arr1); }
创建指定个数的范围均值
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建指定个数的范围值 // linspace有三个参数分别是:起始值, 范围结束值(包含), 个数 // 重点:个数超出范围的总个数时,数值就会生成错误 let arr1 = Array::linspace(0., 10., 6); // [0, 2, 4, 6, 8, 10] let arr2 = Array::linspace(0., 10., 3); // [0, 5, 10] println!("{}", arr1); println!("{}", arr2); }
创建用1填充的多维数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一维或多维数组,填充数值为 1 // ones的主要参数可以是一个整数,表示创建一维数组,也可以是一个元组,表示创建多维数组 let arr1: ArrayBase<OwnedRepr<i32>, _> = Array::ones((3, 4, 5)); println!("{}", arr1); } // 输出结果 // 一维数组包含3组数组 // 二维数组包含4组数组 // 三维数组包含5个元素 /* [[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]] */
创建用0填充的多维数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一维或多维数组,填充数值为 0 // zeros的主要参数可以是一个整数,表示创建一维数组,也可以是一个元组,表示创建多维数组 let arr1: ArrayBase<OwnedRepr<i32>, _> = Array::zeros((3, 4, 5)); println!("{}", arr1); } // 输出结果 // 一维数组包含3组数组 // 二维数组包含4组数组 // 三维数组包含5个元素 /* [[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]] */
创建用0填充的多维数组(设置内存排列方式)
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一维或多维数组,填充数值为 0 // zeros的主要参数可以是一个整数,表示创建一维数组,也可以是一个元组,表示创建多维数组 // 设置数组元素在内存中的排列顺序,可选参数,可以是 .c()(按行排列)或 .f()(按列排列) let arr1: ArrayBase<OwnedRepr<i32>, _> = Array::zeros((3, 4, 5).f()); println!("{}", arr1); } // 输出结果 // 一维数组包含3组数组 // 二维数组包含4组数组 // 三维数组包含5个元素 /* [[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]] */
数组置0操作
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建多维数组 let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; println!("arr7 : \n{}", arr1); // 将多维数组内的元素重置为0 let arr2:ArrayBase<OwnedRepr<i32>, _> = Array::zeros(arr1.raw_dim()); println!("置零后 : \n{}", arr2); }
创建指定元素的多维数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建指定元素 7 的多维数组 let arr1 = Array::from_elem((3,4), 7.); println!("{}", arr1); } /* [[7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7]] */
创建单位矩阵
单位矩阵是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1;除此以外全都为0。
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建 4 * 4 的单位矩阵 let arr1:ArrayBase<OwnedRepr<i32>, _> = Array::eye(4); println!("{}", arr1); } /* [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] */
列表转多维数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 通过Vec的数据 创建 2 * 2 * 2 的矩阵 let arr1:ArrayBase<OwnedRepr<i32>, _> = Array::from_shape_vec( // 设置数组元素在内存中的排列顺序,可选参数,可以是 .c()(按行排列)或 .f()(按列排列) (2,2,2).f(), vec![1,2,3,4,5,6,7,8] ).unwrap(); println!("{}", arr1); } /* [[[1, 5], [3, 7]], [[2, 6], [4, 8]]] */
索引与切片
访问一维数组最后一位元素
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![1,2,3,4,5,6]; // 长度减一 println!("{}", arr1[arr1.len() - 1]); }
取多维数组某行某列的值元素
重点:如果取值的位置是个数组,则取值报错。
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![ [1,2,5], [8,14,23], [37,45,53] ]; println!("{}", arr1[[1, 2]]); // 输出为:23 }
取多维数组某行某列的数组或值元素
重点:如果取值的位置是个数组,也可以取到数据。
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 这里是不可变数组 let arr1 = array![ [[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]] ]; println!("列表值:\n{}", arr1); // 这里表示 取第一列下的所有数组下的所有数组(获取的是不可变数组) // .. 表示所有,可以指定下标位置获取指定行列值 println!("取值:\n{}", arr1.slice(s![1, .., ..])); // 重点:如果想获取可变数据需要数组本身为 let mut 然后通过 .slice_mut 方法获取 } /* 列表值: [[[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]]] 取值: [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]] */
取二维数组的某几行或某几个元素
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![ [1,2,3,11,22,33], [4,5,6,44,55,66], [7,8,9,77,88,99] ]; // 取二维数组的前两行 // 从0下标开始取,范围到2下标(但是不包含结束的2下标数据) // .. 取全部,也可以指定下标位组成新数组 // .. 前可以指定下标开始位,如:0..2 1..3 println!("{}", arr1.slice(s![..2, ..])); // 取二维数组的后两行 println!("{}", arr1.slice(s![-2.., ..])); // 取二维数组的前两行的2,3,4列 println!("{}", arr1.slice(s![..2, 2..5])); // 取二维数组的前两行并将数值反序 // -1就是反序排列 println!("{}", arr1.slice(s![..2, ..;-1])); }
数组维度与步长
获取数组维度
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个3维数组(3D数组) let arr1 = array![ [[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]] ]; // 获取数组的维度 println!("{}", arr1.ndim()); // 输出为:3 }
获取数组的总元素个数
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个3维数组(3D数组) let arr1 = array![ [[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]] ]; // 获取数组的总元素个数 println!("{}", arr1.len()); // 输出为:60 }
获取数组的维度结构
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个3维数组 let arr1 = array![ [[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]] ]; // 获取数组维度的结构 println!("{:?}", arr1.shape()); // 输出为数组:[3, 4, 5] // 表示:一维数组为3行,二维数组为4列,三维数组为5行 // 另一种方式:获取数组维度的结构 println!("{:?}", arr1.dim()); // 输出为元组Vec:(3, 4, 5) }
获取数组某层维度内的维度结构
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个3维数组 维度结构 [3, 4, 5] let arr1 = array![ [[17, 8, 7, 7, 7], [27, 8, 7, 7, 7], [37, 8, 7, 7, 7], [47, 8, 7, 7, 7]], [[57, 8, 7, 7, 7], [67, 8, 7, 7, 7], [77, 8, 7, 7, 7], [87, 8, 7, 7, 7]], [[97, 8, 7, 7, 7], [107, 8, 7, 7, 7], [117, 8, 7, 7, 7], [127, 8, 7, 7, 7]] ]; // 获取数组第0层维度的结构数 println!("{}", arr1.len_of(Axis(0))); // 输出为 3 // 获取数组第1层维度的结构数 println!("{}", arr1.len_of(Axis(1))); // 输出为 4 // 获取数组第2层维度的结构数 println!("{}", arr1.len_of(Axis(0))); // 输出为 5 }
计算维度之间的步长(计算跨度字节数)
步长就是告诉我们必须在内存中跳过多少字节才能沿着某个轴移动到下一个位置,同一维度就可以看作为一个轴,如下示例:
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个三维数组 let arr1 = array![ [ [1,2], [3,4], [5,6] ], [ [7,8], [9,10], [11,12] ], [ [13,14], [15,16], [17,18] ] ]; // 同维度下跨越同下标元素需要的步长(步幅) println!("{:?}", arr1.strides()); // 输出为 [6, 2, 1] // 从一维轴计算跨度(跨越的元素个数) // 即从下标为0的内部下标0的元素1 跨到 下标为1的内部下标0的元素7 需要跨越6个元素 // 从二维轴计算跨度(跨越的元素个数) // 即从下标为0的内部下标0的元素1 跨到 下标为1的内部下标0的元素3 需要跨越2个元素 // 从三维轴计算跨度(跨越的元素个数) // 即从下标为0的元素1 跨到 下标为1元素2 需要跨越1个元素 }
判断数组长度是否为零
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个三维数组 let arr1 = array![ [ [1,2], [3,4], [5,6] ], [ [7,8], [9,10], [11,12] ], [ [13,14], [15,16], [17,18] ] ]; // 判断数组长度是否为0 println!("{}", arr1.is_empty()); // 输出:false 表示数组长度不为0 }
多维数组的数学计算
基础运算
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr16 = array![[1,2], [3,4]]; let arr17 = array![[5,6], [6,7]]; // 将同下标位的数进行相乘 println!("{}", &arr16 * &arr17); /* [[5, 12], [18, 28]] */ // 将同下标位的数进行相加 println!("{}", &arr16 + &arr17); /* [[6, 8], [9, 11]] */ }
数组内所有元素的和
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个三维数组 let arr1 = array![ [ [1,2], [3,4], [5,6] ], [ [7,8], [9,10], [11,12] ], [ [13,14], [15,16], [17,18] ] ]; // 数组内所有元素的总和 println!("{}", arr1.sum()); // 171 }
沿数组轴求和
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个三维数组 let arr1 = array![ [ [1,2], [3,4], [5,6] ], [ [7,8], [9,10], [11,12] ], [ [13,14], [15,16], [17,18] ] ]; // 数组某个维度(轴)所有元素的总和 println!("{}", arr1.sum_axis(Axis(2))); /* [[3, 7, 11], [15, 19, 23], [27, 31, 35]] */ }
沿数组轴求平均值
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 创建一个三维数组 let arr1 = array![ [ [1.,2.], [3.,4.], [5.,6.] ], [ [7.,8.], [9.,10.], [11.,12.] ], [ [13.,14.], [15.,16.], [17.,18.] ] ]; // 数组某个维度(轴)的平均值 println!("{}", arr1.mean_axis(Axis(2)).unwrap()); /* [[1.5, 3.5, 5.5], [7.5, 9.5, 11.5], [13.5, 15.5, 17.5]] */ // 求 f64类型数组 整体的平均值 // 平均值结果为 f64 类型 println!("{}", arr1.sum() / a.len() as f64); // 9.5 }
数组操作
数组转置
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![ [ [1,2,12], [3,4,34], [5,6,56] ], [ [7,8,78], [9,10, 90], [11,12, 23] ], [ [13,14, 27], [15,16, 31], [17,18, 35] ] ]; // 数组转置 println!("{}", arr1.t()); } /* [[[1, 7, 13], [3, 9, 15], [5, 11, 17]], [[2, 8, 14], [4, 10, 16], [6, 12, 18]], [[12, 78, 27], [34, 90, 31], [56, 23, 35]]] */
查看对角线
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![ [ [1,2,12], [3,4,34], [5,6,56] ], [ [7,8,78], [9,10, 90], [11,12, 23] ], [ [13,14, 27], [15,16, 31], [17,18, 35] ] ]; // 查看对角线 // 默认为:左上角 到 右下角 println!("{}", arr1.diag()); // [1, 10, 35] }
修改数组全部元素为指定值
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 可变数组 let mut arr1 = array![ [ [1,2,12], [3,4,34], [5,6,56] ], [ [7,8,78], [9,10, 90], [11,12, 23] ], [ [13,14, 27], [15,16, 31], [17,18, 35] ] ]; // 所有元素都修改为 3 arr1.fill(3); println!("{}", arr1); } /* [[[3, 3, 3], [3, 3, 3], [3, 3, 3]], [[3, 3, 3], [3, 3, 3], [3, 3, 3]], [[3, 3, 3], [3, 3, 3], [3, 3, 3]]] */
同维度数组覆盖
// 导入ndarray库 use ndarray::prelude::*; fn main(){ // 3*3 矩阵数组 let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; // 创建0填充的二维数组 3*3 let mut arr2 = Array::zeros((3, 3)); println!("复制前:\n{}", arr2); arr2.assign(&arr1); println!("复制后:\n{}", arr2); } /* 复制前: [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 复制后: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] */
展开多维数组成一维数组
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; println!("{}", Array::from_iter(arr1.iter())); // [1, 2, 3, 4, 5, 6, 7, 8, 9] }
迭代器
逻辑顺序迭代全部元素
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; for item in arr1.iter() { println!("iter: {}", item); } } /* iter: 1 iter: 2 iter: 3 iter: 4 iter: 5 iter: 6 iter: 7 iter: 8 iter: 9 */
维度迭代展示子视图
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; for item in arr1.outer_iter() { println!("outer_iter: {}", item); } } /* outer_iter: [1, 2, 3] outer_iter: [4, 5, 6] outer_iter: [7, 8, 9] */
平面迭代并展示索引维度
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; for (index, item) in arr1.indexed_iter() { println!("indexed_iter: {:?}, {}", index, item); } } /* indexed_iter: (0, 0), 1 indexed_iter: (0, 1), 2 indexed_iter: (0, 2), 3 indexed_iter: (1, 0), 4 indexed_iter: (1, 1), 5 indexed_iter: (1, 2), 6 indexed_iter: (2, 0), 7 indexed_iter: (2, 1), 8 indexed_iter: (2, 2), 9 */
二维阵列常用方法
判断是否为正方形
// 导入ndarray库 use ndarray::prelude::*; fn main(){ let arr1 = array![[1,2,3], [4,5,6], [7,8,9]]; println!("{}", arr1.is_square()); // true }
部分内容来源:https://jarod.blog.csdn.net/article/details/127807622
本文作者:芋白🥕
本文链接:https://www.cnblogs.com/-CO-/p/18152666
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本