如何在保持数据分布的同时从python中的列表中随机采样
Posted
技术标签:
【中文标题】如何在保持数据分布的同时从python中的列表中随机采样【英文标题】:How do I randomly sample from a list in python while maintaining the distribution of data 【发布时间】:2017-08-28 19:27:33 【问题描述】:基本上我要做的是从列表中随机选择项目,同时保持内部分布。请参阅以下示例。
a = 17%
b = 12%
c = 4%
etc.
“a”在列表中有 1700 个项目。 “b”在列表中有 1200 个项目。 "c" 在列表中有 400 项。
我想要一个模拟 a、b、c 等分布的样本,而不是使用所有信息。
所以我们的目标是最终得到,
从“a”中随机选择的 170 个项目 从“b”中随机选择120个项目 从“c”中随机选择40个项目
我知道如何从列表中随机选择信息,但我无法弄清楚如何在强制结果具有相同分布的同时随机选择。
【问题讨论】:
您不能强制样本与总体相似,它是随机的。 你能澄清一下吗?您有三个列表,或者您想将单个样本随机细分为三个列表? 例如numpy.random.choice
允许您传递概率参数(概率列表),但我很难理解您要做什么,所以我不知道是不是合适。
Generating Discrete random variables with specified weights using SciPy or NumPy的可能重复
请参阅hips.seas.harvard.edu/blog/2013/03/03/… 以了解“别名方法”的 python 实现,它需要 O(k) 设置以用于具有 k 个结果的分布,但随后每个值需要 O(1) 才能生成。
【参考方案1】:
pandas 系列/数据框有一个 .sample() 方法,允许包含“权重”系列。
如果是数据框,则该权重可以是与数据相邻的列。
使您的类别总计该权重列,在您的 .sample() 调用中指定该列,然后您就完成了。
https://pandas.pydata.org/docs/reference/api/pandas.Series.sample.html
【讨论】:
【参考方案2】:手动执行此操作非常容易。让我们将您的数据存储在(value, probability)
对象列表中:
data = [(a, 0.17), (b, 0.12), (c, 0.04), ...]
此函数将帮助您选择遵循概率分布的随机值:
import random
def select_random_element(data):
sample_proba = random.uniform(0, 1)
total_proba = 0
for (value, proba) in data:
total_proba += proba
if total_proba >= sample_proba:
return value
最后,这就是我们如何选择 N 个随机项:
random_items = [select_random_element(data) for _ in range(0, N)]
这不需要任何额外的内存。但是,时间复杂度为O(len(data)*N)
。这可以通过预先降低概率对数据列表进行排序来改善:
data = sorted(data, key=lambda i: i[1], reverse=True)
注意,我假设你的数据的总概率是1。如果不是,你应该在上面的代码中写random.uniform(0, total_probability)
而不是random.uniform(0, 1)
,用:
total_probability = sum([i[1] for i in data])
【讨论】:
【参考方案3】:如果您的列表不是很庞大并且内存不是问题,您可以使用这个简单的方法。
要从a
、b
和c
中获取n
元素,您可以将三个列表连接在一起,并使用random.choice
从结果列表中选择随机元素:
import random
n = 50
a = ['a'] * 170
b = ['b'] * 120
c = ['c'] * 40
big_list = a + b + c
random_elements = [random.choice(big_list) for i in range(n)]
# ['a', 'c', 'a', 'a', 'a', 'b', 'a', 'c', 'b', 'a', 'c', 'a',
# 'a', 'a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'a', 'c', 'a',
# 'c', 'a', 'b', 'a', 'a', 'c', 'a', 'b', 'a', 'c', 'b', 'a',
# 'a', 'b', 'a', 'b', 'a', 'a', 'c', 'a', 'c', 'a', 'b', 'c',
# 'b', 'b']
对于每个元素,您将获得从a
获取元素的len(a) / len(a + b + c)
概率。
您可能会多次获得相同的元素。如果您不希望发生这种情况,可以使用random.shuffle
。
【讨论】:
这很简单,在这里可能就足够了。但是,如果性能在某种程度上很重要,那么这种天真的方法并不引人注目。此处进行的时间-内存权衡对于实践中的缓存行为可能很糟糕(并且使用的内存比需要的多得多;大量冗余)。【参考方案4】:只需在列表中使用shuffle
,并取前n 个元素。
【讨论】:
在哪个列表中? OP 至少有 3 个。注意:我没有投反对票。shuffle
是一个有趣的想法,因为它可以避免重复元素。【参考方案5】:
在您的选择中“模仿”这种分布的一种方法是简单地将列表合并为一个,然后从该列表中选择所需的项目总数。如果需要选择的项目总数很大,那么这个近似值会很好。
请注意,它不能保证从每个列表中准确选择这些数量。但是,如果列表很大并且此例程运行多次,则平均值应该不错。
import random
total = a + b + c + ...
samples = []
number = len(total) / 10
for i in range(number):
samples.append(total[random.rand(0, len(total) - 1])
【讨论】:
【参考方案6】:据我了解,您有三个不同的总体,并且您希望从这些总体中随机抽样,但选择某些总体的概率存在偏差。在这种情况下,首先随机生成对应于每个总体的索引列表会更容易(因为我将它们组合成一个名为combined
的单个二维数组)。
然后您可以遍历随机生成的索引列表,它会为您提供要从中选择的总体,然后使用 np.random.choice()
从该数据中随机选择。
import numpy as np
sample_a = np.arange(1, 1000)
sample_b = np.arange(1001, 2000)
sample_c = np.arange(2001, 3000)
combined = np.vstack((sample_a, sample_b, sample_c))
distributions = [0.7, 0.2, 0.1] # The skewed probability distribution for sampling
sample = np.random.choice([0, 1, 2], size=10, p=distributions) # Choose indices with skewed probability
combined_pool = []
for arr in sample:
combined_pool.append(np.random.choice(combined[arr]))
【讨论】:
以上是关于如何在保持数据分布的同时从python中的列表中随机采样的主要内容,如果未能解决你的问题,请参考以下文章
如何在 javascript 中删除图像数组中的图像,同时在浏览器屏幕上保持当前图像列表?