You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

让函数支持整数与类数组输入的最Pythonic实现方案(Numpy)

解决Numpy处理单整数输入时返回形状不匹配的问题

问题背景

我正在开发一个用于DnD、Pathfinder等TRPG系统的整数分布计算工具,需要实现支持整数和类数组输入的PMF(概率质量函数)与CDF(累积分布函数)查询功能。

处理输入时,我用np.atleast_1d(np.asarray(N, dtype=int))确保输入至少为1维,满足内部逻辑要求,但这样会导致单个整数输入返回形状为(1,)的float数组,而非预期的0维float数组。目前我通过缓存输入形状、最后reshape的方式解决,但觉得这个方法太繁琐,想找Numpy内置的更高效实现方案。

我的最小可运行代码如下:

import numpy as np

class integerDistribution:

    def __init__(self, min_val, probability_distribution, rtol = 1e-10):
        """Initializes the instance from a minimum value and a finite list of probabilities.

        Args:
            min_val : int
                The minimum value of our distribution. As an example, rolling a d20 
                would have a minimum value of 1, while the sum of 2d6 has a minimum 
                value of 2.
            probability_distribution : array-like of floats
                Each entry at index 'idx' in the array corresponds to the probability 
                of obtaining the value 'idx + min_val' from our distribution.
            rtol: float
                When initializing, we perform a sanity check that the sum of 
                probabilities is 1 to within a tolerance of rtol. Defaults to 1e-10

        Raises:
            ValueError: If probabilities do not sum to 1 within the specified
                tolerance (rtol)      
        """
        if not np.isclose(np.sum(probability_distribution), 1.0, rtol):
            raise ValueError(f"Probabilities summed to {np.sum(probability_distribution)}")

        self.min_val = int(min_val)
        self.max_val = self.min_val + len(probability_distribution) - 1
        self.values = self.min_val + np.arange(len(probability_distribution), dtype = int)
        self.probability_distribution = np.array(probability_distribution)
        

    
    def pmf(self, N):
        """Evaluates the probability mass function (PMF) at given value(s)

        The probability mass function is defined as p(N) = P(X = N), 
        where X is a random variable sampled from 'self', and N is the value 
        for which the probability is being calculated. 

        Args:
            N : int or list of int
                The value(s) for which to compute the probability. 
                Can be a single integer or a list/array of integers.

        Returns:
            p(N) : float or list of float
                The probability of obtaining each value in N when sampled from 'self'. 
                The return type matches the input type: a single float if N is an 
                integer, or a list/array of floats if N is a list/array of integers.
        """
        N_shapecache = np.asarray(N, dtype = int)
        N = np.atleast_1d(N_shapecache)
        p_N = np.zeros(N.shape)
        valid_indices = np.where((self.min_val <= N)*(N <= self.max_val))
        p_N[valid_indices] = self.probability_distribution[N[valid_indices] - self.min_val]
        return p_N.reshape(N_shapecache.shape)

解决方案

方案1:标量与数组分支处理(推荐)

直接判断输入是否为标量,分别处理两种情况,避免统一转成1维再reshape,逻辑清晰且性能最优:

def pmf(self, N):
    N_arr = np.asarray(N, dtype=int)
    # 处理标量输入
    if np.isscalar(N):
        if not (self.min_val <= N <= self.max_val):
            return 0.0
        return float(self.probability_distribution[N - self.min_val])
    # 处理数组输入
    p_N = np.zeros(N_arr.shape, dtype=float)
    valid_mask = (self.min_val <= N_arr) & (N_arr <= self.max_val)
    p_N[valid_mask] = self.probability_distribution[N_arr[valid_mask] - self.min_val]
    return p_N

方案2:简化形状还原逻辑

如果希望保持统一的数组处理逻辑,可以用ravel()替代np.atleast_1d,最后直接还原原始形状,写法更简洁:

def pmf(self, N):
    N_arr = np.asarray(N, dtype=int)
    N_1d = N_arr.ravel()  # 转成1维视图,比atleast_1d更高效
    p_N = np.zeros(N_1d.shape, dtype=float)
    valid_indices = np.where((self.min_val <= N_1d) & (N_1d <= self.max_val))
    p_N[valid_indices] = self.probability_distribution[N_1d[valid_indices] - self.min_val]
    return p_N.reshape(N_arr.shape)

方案3:用np.take简化赋值操作

利用np.take直接提取有效索引对应的概率值,结合zeros_like创建同形状数组,最后处理标量返回:

def pmf(self, N):
    N_arr = np.asarray(N, dtype=int)
    p_N = np.zeros_like(N_arr, dtype=float)
    valid_mask = (self.min_val <= N_arr) & (N_arr <= self.max_val)
    # 计算有效输入对应的分布索引
    indices = N_arr[valid_mask] - self.min_val
    p_N[valid_mask] = np.take(self.probability_distribution, indices)
    # 标量输入返回单个float,否则返回原形状数组
    return float(p_N) if N_arr.ndim == 0 else p_N

总结

  • 方案1最直观,针对标量和数组做分支处理,减少了不必要的数组维度转换,性能最优;
  • 方案2和3适合希望保持代码统一性的场景,写法更简洁,利用Numpy内置函数简化操作。

内容的提问来源于stack exchange,提问作者Minty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:22:34