JS宏案例:在wps编辑器中玩numpy

发布于:2025-03-01 ⋅ 阅读:(9) ⋅ 点赞:(0)

NumPy 是 Python 中用于科学计算的一个基础库,它提供了大量的数学函数工具,尤其是用于高效处理大型多维数组和矩阵。NumPy 是 Python 数据分析、机器学习、科学计算等领域中不可或缺的一部分。

然,在wps的js宏编辑器中,并没有这样一个模块或是全局对象,但是,问题不大,我们可以手搓一个。不过,要使用JS完全模拟python中的numpy是比较困难的,工作量也非常的大,我们可以适当简化一下,如只对常用方法和属性进行模拟。

一、自定义错误

class DimensionError extends Error {
	constructor(message) {
		super(message);
		this.name = "维度错误";
	}
}

class ValueError extends Error {
	constructor(message) {
		super(message);
		this.name = "值错误";
	}
}

上述代码中,定义了一个维度错误和值错误,是py中numpy模块中常见的两种错误。

二、构造Numpy全局对象

1、示例代码:

class Numpy {
	
	constructor(array) {
		if (array !== undefined && !Array.isArray(array)) {
			throw new TypeError("the parm array must be array.");
		} 
		this.#_array = array;
	}
	
	// 获取数组维度
	get ndim() {
		let n = 0;
		let array = this.#_array;
		while (Array.isArray(array)) {
			n++;
			array = array[0];
		}
		return n;
	}
	
	// 返回数组的维度,是一个整数数组,表示每个维度中数组的大小。对于具有 n 行和 m 列的矩阵,shape 将是 [n, m]。
	get shape() {
		let result = [];
		let n = this.ndim;
		let array = this.#_array; 
		while (n > 0) {
			result.push(array.length);
			array = array[0];
			n--;
		}
		return result;
	}
	
	// 返回数组中所有元素的个数
	get size() {
		let arr = this.shape;
		let product = 1;
		for (let i = 0; i < arr.length; i++) {
			product *= arr[i];
		}
		return product;
	}
	
	// 转置
	get T() {
		return this.#_transpose(this.#_array);
	}
	
	get random() {
		// random对象
		let self = this;
		return {
			rand: function(shape) {
				// 生成shape形状的随机数组,数值用随机数填充
				if (shape === undefined || shape === null) return Math.random();
				if (!Array.isArray(shape)) throw new TypeError("param shape must be array.");
				if (!shape.every(i => Number.isInteger(i)))  throw new TypeError("shape must be array of integer.");
				return self.full(shape, () => Math.random()); 
			},
			randn: function(shape) {
				// 随机正态分布
			    if (typeof shape === "number") shape = [shape]; // 如果 shape 是数字,转换为数组
			    if (!Array.isArray(shape)) throw new TypeError("shape must be a number or array");
			    const totalElements = shape.reduce((acc, val) => acc * val, 1); // 计算总元素个数
			    const result = Array.from({ length: totalElements }, () => self.#_randn()); // 生成随机数
			    // 将一维数组转换为多维数组
			    function reshape(arr, shape) {
			        if (shape.length === 1) return arr;
			        const [currentDim, ...remainingDims] = shape;
			        const result = [];
			        const chunkSize = arr.length / currentDim;
			        for (let i = 0; i < currentDim; i++) {
			            result.push(reshape(arr.slice(i * chunkSize, (i + 1) * chunkSize), remainingDims));
			        }
			        return result;
			    }
			    return new Numpy(reshape(result, shape));
			},
			randint: function(low, high = null, size = null) {
			    if (high === null) {
			        high = low;
			        low = 0;
			    }
			    if (low >= high) throw new ValueError("low must be less than high");
			    // 单个随机整数
			    function getRandomInt(low, high) {
			    	 return Math.floor(Math.random() * (high - low)) + low;
			    }
			    // 如果 size 未提供,返回单个随机数
			    if (size === null) return getRandomInt(low, high);
			    // 如果 size 是数字,转换为数组
			    if (typeof size === "number") size = [size];
			    if (!Array.isArray(size)) throw new TypeError("size must be a number or array");
			    const totalElements = size.reduce((acc, val) => acc * val, 1);
			    const result = Array.from({ length: totalElements }, () => getRandomInt(low, high));
			    function reshape(arr, shape) {
			        if (shape.length === 1) return arr;
			        const [currentDim, ...remainingDims] = shape;
			        const result = [];
			        const chunkSize = arr.length / currentDim;
			        for (let i = 0; i < currentDim; i++) {
			            result.push(reshape(arr.slice(i * chunkSize, (i + 1) * chunkSize), remainingDims));
			        }
			        return result;
			    }
			    return new Numpy(reshape(result, size));
			},
			choice: function(a, size = null, replace = true, p = null) {
				    if (typeof a === "number") {
				        a = Array.from({ length: a }, (_, i) => i); // 如果 a 是数字,生成 range(a)
				    }
				    if (!Array.isArray(a)) throw new TypeError("a must be a number or array");
				    if (p !== null) {
				        if (!Array.isArray(p)) throw new TypeError("p must be an array");
				        if (p.length !== a.length) throw new ValueError("a and p must have the same length");
				        if (p.some(prob => prob < 0)) throw new ValueError("probabilities must be non-negative");
				        const totalProb = p.reduce((acc, val) => acc + val, 0);
				        if (Math.abs(totalProb - 1) > 1e-6) throw new ValueError("probabilities must sum to 1");
				    }
				    // 如果 size 未提供,返回单个随机元素
				    if (size === null) {
				        if (p === null) {
				            return a[Math.floor(Math.random() * a.length)];
				        } else {
				            const rand = Math.random();
				            let cumulativeProb = 0;
				            for (let i = 0; i < a.length; i++) {
				                cumulativeProb += p[i];
				                if (rand < cumulativeProb) return a[i];
				            }
				        }
				    }
				    // 如果 size 是数字,转换为数组
				    if (typeof size === "number") size = [size];
				    if (!Array.isArray(size)) throw new TypeError("size must be a number or array");
				    const totalElements = size.reduce((acc, val) => acc * val, 1);
				    const result = [];
				    for (let i = 0; i < totalElements; i++) {
				        if (p === null) {
				            const randomIndex = Math.floor(Math.random() * a.length);
				            result.push(a[randomIndex]);
				            if (!replace) a.splice(randomIndex, 1); // 如果不允许重复,移除已选元素
				        } else {
				            const rand = Math.random();
				            let cumulativeProb