Python随机采样详解:random与numpy的有放回与无放回实现
本文深入解析Python中的随机采样机制,涵盖random模块与numpy库的核心函数。详细对比random.choice、random.choices(支持权重与有放回)及random.sample(无放回)的使用场景,并介绍基于Random实例的采样方法。同时解析numpy.random.choice的参数配置,包括权重、大小及替换选项,助你在机器学习数据预处理中精准控制采样逻辑。
Python及其扩展库Numpy内置了丰富的概率算法,既包含基础的随机采样功能,也涵盖多种经典概率分布的生成方式。本系列文章将重点介绍机器学习中常用的几类概率函数,首先从最基础的随机采样功能开始讲解。
1. random.choice
若仅需从序列中抽取单个样本,且假设所有样本被抽中的概率均等,可直接调用random.choice函数实现。
import random
res1 = random.choice([0, 1, 2, 3, 4])
print(res1) # 3
2. random.choices(有放回)
在实际应用中,往往需要抽取多个样本,并指定序列中各元素被抽中的不同概率。此时应使用random.choices函数,该函数支持有放回采样,即同一数据项可被多次抽取。其函数定义如下:
random.choices(population, weights=None, *, cum_weights=None, k=1)
population:指定待采样的目标序列。
weights:为每个样本设定的权重(相对权重),用于决定各样本被抽中的概率,例如传入[10, 0, 30, 60, 0]。
cum_weights:累积权重,相对权重[10, 0, 30, 60, 0]对应的累积权重为[10, 10, 40, 100, 100]。
以下示例展示从[0, 1, 2, 3, 4]中依据相对权重抽取3个样本的过程:
res2 = random.choices([0, 1, 2, 3, 4], weights=[10, 0, 30, 60, 0], k=3)
# 注意population不是关键字参数,在函数调用时不能写成population=[0,1,2,3,4]来传参
# 关于关键字参数和位置参数,可以参看我的博客《Python技法2:函数参数的进阶用法》https://www.cnblogs.com/orion-orion/p/15647408.html
print(res2) # [3, 3, 2]
以下示例展示从[0, 1, 2, 3, 4]中依据累积权重抽取3个样本的过程:
res3 = random.choices([0, 1, 2, 3, 4], cum_weights=[10, 10, 40, 100, 100], k=3)
print(res3) # [0, 3, 3]
需注意,weights与cum_weights不可同时传入参数,否则系统将抛出TypeError异常'Cannot specify both weights and cumulative weights'。
3. numpy.sample(无放回)
random.sample执行的是无放回采样,即每个元素仅能被抽取一次。若需进行无放回操作,必须使用random.sample。值得注意的是,该函数不支持定义样本权重。其函数原型如下:
random.sample(population, k, *, counts=None)¶
population:指定待采样的序列。
k:指定需要抽取的元素数量。
counts:当population为可重复集合时使用,用于定义集合中各元素的重复次数。例如sample(['red', 'blue'], counts=[4, 2], k=5)等价于sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)。
以下示例展示对序列[0, 1, 2, 3, 4]进行无放回采样,抽取3次:
res3 = random.sample([0, 1, 2, 3, 4], k=3)
print(res3) # [3, 2, 1]
以下示例展示对可重复集合[0, 1, 1, 2, 2, 3, 3, 4]进行无放回采样,抽取3次:
res4 = random.sample([0, 1, 2, 3, 4], k=3, counts=[1, 2, 2, 2, 1])
print(res4) # [3, 2, 2]
若counts的长度与population序列长度不一致,系统将抛出异常ValueError:"The number of counts does not match the population"。
4.rng.choices 和 rng.sample
另一种有放回采样的实现方式源自论文[1]的代码[2],即先定义一个随机数生成器实例,再调用该实例的choices或sample方法。其使用方式与random.choice/random.sample函数一致。
rng_seed = 1234
rng = random.Random(rng_seed)
res5 = rng.choices(
population=[0,1,2,3,4],
weights=[0.1, 0, 0.3, 0.6, 0],
k=3,
)
print(res5) # [3, 3, 0]
res6 = rng.sample(
population=[0, 1, 2, 3, 4],
k=3,
)
print(res6) # [4, 0, 2]
在论文[1]的实现代码[2]中,这两个函数被用于随机选择任务节点client:
def sample_clients(self):
"""
sample a list of clients without repetition
"""
rng_seed = (seed if (seed is not None and seed >= 0) else int(time.time()))
self.rng = random.Random(rng_seed)
if self.sample_with_replacement:
self.sampled_clients = \
self.rng.choices(
population=self.clients,
weights=self.clients_weights,
k=self.n_clients_per_round,
)
else:
self.sampled_clients = self.rng.sample(self.clients, k=self.n_clients_per_round)
5. numpy.random.choices
若需按权重分布从序列中采样,也可采用numpy.random.choice实现。其函数原型如下:
random.choice(a, size=None, replace=True, p=None)
a:1-D array-like或int。若为1-D array-like,则从数组元素中抽取样本;若为int,则从np.arange(a)中抽取。
size:int或tuple of ints,可选。指定输出形状的大小,若指定形状为(m,n,k),则抽取m×n×k个样本。默认为None,即返回单一标量。
replace:boolean,可选。表示采样是否为有放回。若replace=True,则为有放回采样(值可被多次抽取);否则为无放回采样(值仅能抽取一次)。
p:1-D array-like,可选。表示a中各项被抽取的概率。若未指定,则假定a中各项服从均匀分布(即概率相同)。
以下示例展示从[0,1,2,3,4,5]中重复/不重复采样3次的情况:
import numpy as np
res1 = np.random.choice(5, 3, replace=True)
print(res1) # [1 1 4]
res2 = np.random.choice(5, 3, replace=False)
print(res2) # [2 1 4]
同样从[0,1,2,3,4,5]中重复/不重复采样3次,此处展示为每个样本设定不同概率的情况:
res3 = np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0])
print(res3) # [2 3 3]
res4 = np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0])
print(res4) # [3 2 0]
参考文献
https://github.com/omarfoq/FedEM
https://www.python.org/
https://numpy.org/
本文关于Python随机采样与概率分布的解析到此结束,更多Python相关内容可查阅本站往期文章或继续浏览后续文章,感谢支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













