如何在保持数据分布的同时从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】:

如果您的列表不是很庞大并且内存不是问题,您可以使用这个简单的方法。

要从abc 中获取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中的列表中随机采样的主要内容,如果未能解决你的问题,请参考以下文章

在保持项目排序的同时从列表中获取随机样本?

OrderedDict如何在Python中保持秩序

在 python 中保持格式的同时将字符串列表转换为整数

如何在 javascript 中删除图像数组中的图像,同时在浏览器屏幕上保持当前图像列表?

如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]

如何在保持格式的同时从数据库中检索和回显文本[重复]