【问题标题】:MySql: Evenly distributed sample size on unevenly distributed dataMySql:在不均匀分布的数据上均匀分布的样本量
【发布时间】:2016-03-18 20:10:55
【问题描述】:

假设我有 10.000 个预订客户资料。 这些配置文件具有以下变量:

  • 持续时间(放假天数)
  • 目的地(可能是巴西)
  • People_amount(有多少人)
  • 起飞(他们想离开的日期)

我想通过预订引擎将其中的 1.000 个(10%)传递给定价,但为了开发独立的洞察分析,我必须(尽可能)平均分配配置文件的特征。例如。如果所有配置文件都有 3 种 People_amount(1、2 和 3),最终我希望在 People_amount = 1 的 33.33% 的 10% 内进行选择,在 People_amount = 2 和 33 的 33.33% 中进行选择,33%,People_amount = 3。

但是……

因为配置文件集不是均匀分布的(例如,所有配置文件的 70% 由 People_amount = 1 组成)我无法弄清楚如何找到/创建一种填满的循环(或其他东西)该特征内的所有品种的 SELECT 直到 1 用完并与其余部分一起进一步。

也许是一个示例,说明我想如何填写我的 10k 个人资料中的 10% 样本:

Profile_id  People_amount                                     Profile_id  People_amount
1           1                                                           1           1
2           1                                                           5           2
3           1                                                           8           3
4           1       --> Filling the sample by even distribution         2           1
5           2       of available profile characteristics                6           2
6           2                                                           9           3   
7           2                                                           3           1
8           3                                                           7           2
9           3                                                           4           1

希望你能帮忙!

【问题讨论】:

    标签: mysql select distribution sample


    【解决方案1】:

    您可以使用union 来限制每个子选择:

    (SELECT * FROM profiles WHERE People_amount=1 LIMIT 333)
    UNION
    (SELECT * FROM profiles WHERE People_amount=2 LIMIT 333)
    UNION
    (SELECT * FROM profiles WHERE People_amount=3 LIMIT 333)
    

    需要括号将LIMIT 应用于每个子选择。

    更动态的方法

    如果不知道people_amount 的可能值的数量,则上述方法不可行。然后我会提出一个查询,其中ORDER BY 子句根据出现次数分配people_amount 值。它不会给出完全相等的分布,但不同的值将在结果集中具有可比较的存在:

    select     p.*
    from       (
                select   people_amount,  
                         count(*) as occurrences
                from     profiles
                group by people_amount) as stats
    inner join profiles p
            on p.people_amount = stats.people_amount         
    order by   rand() * stats.occurrences
    limit      1000
    

    SQL fiddle(如果没有过载)。

    如果您想将此扩展到其他列,例如 Destination,您可以按如下方式进行:

    select     p.*
    from       (
                select   people_amount,  
                         destination,
                         count(*) as occurrences
                from     profiles
                group by people_amount,
                         destination) as stats
    inner join profiles p
            on p.people_amount = stats.people_amount         
           and p.destination = stats.destination
    order by   rand() * stats.occurrences
    limit      1000
    

    这个想法是,出现次数少的值将获得较低的 order-by 值,因此会在结果集的开头更频繁地弹出,以补偿它们的低频率。

    【讨论】:

    • 谢谢@trincot。您提供的东西很长,但它确实限制了该值的结果。我需要的是耗尽每列的所有现有值,直到它达到(根据我的示例)总共 1.000 行。最重要的是(因为 Destination 可以包含 20 个不同的值,而 Takeoff 可能包含 50 个)我不想硬编码“People_mount = 1”。如果这可以是动态的,取决于所有可用的值,这将使其更具可扩展性。
    • 耗尽每列的现有值:目的地、人员数量和起飞日期的某些组合不会出现在样本中,因为有两种可能的组合(超过1000)。也许您应该改写您的问题以阐明这一点。此外,我假设此要求不适用于 profile_id。
    • 拼写更正:我的意思是“每个”而不是“每列非常现有的值”。也许最好将其缩小/将其切成碎片以便更好地理解。通过将其保留在我的 People_amount 示例中,有 3 个值(1,2 和 3)。假设我不想自己弄清楚有多少不同的值(因为太多了),我们如何根据列中的不同值动态地使 People_amount=1 中的 '1' 的值?
    • 太棒了!!!!确实,传播不会完全准确,但是如果使用超过 1k 的样本,当使用更多列时,它会比使用 10 更精确。提示:我在第二个脚本中的第一个“目的地”之后发现了一个缺失的“,”。你能为未来的用户说明一下吗?
    • 已更正!谢谢。
    猜你喜欢
    • 1970-01-01
    • 2011-04-04
    • 2020-04-04
    • 2011-09-16
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    • 1970-01-01
    • 2018-02-06
    相关资源
    最近更新 更多