将分布重新采样为具有最大可能数据记录的新分布

Posted

技术标签:

【中文标题】将分布重新采样为具有最大可能数据记录的新分布【英文标题】:Resample distribution to new distribution with maximum possible data records 【发布时间】:2021-07-07 15:37:43 【问题描述】:

上下文

最近,我问here 如何将具有某种随机分布的数据集重新采样到某些类上的特定分布,这应该检索每个类的最大可能数据点。

问题已解决 - 无论如何,仅在 Python 实现中。对于我的用例,我现在需要在纯 SQL 或与 ImpalaHiveQL 兼容的 SQL 中执行此操作。

为了更好地理解手头的问题,这是一个虚构的数据示例(外观和外观)。它是每个日历周三个班级的分布:

| Week | Class | Count | Distribution | Desired Distribution |
|------|-------|-------|--------------|----------------------|
| 01   | A     | 954   |     0.36     |         0.55         |
| 01   | B     | 554   |     0.21     |         0.29         |
| 01   | C     | 1145  |     0.43     |         0.16         |
| 02   | A     | 454   |     0.21     |         0.55         |
| 02   | B     | 944   |     0.44     |         0.29         |
| 02   | C     | 748   |     0.35     |         0.16         |

可以看出,分布是随机的,与基本事实不匹配(Desired Distribution)。此外,所需的分布与现状有很大不同。

问题

需要在每个班级和每个日历周获取尽可能多的数据记录。因此,不可能只将期望分布乘以每周计数的总和 (sum(count_per_week) * desired_distribution)。更具挑战性的是,具有最高期望数据点的类有时在实际数据中最少 (Class="A""),这就是为什么这个先决条件至关重要的原因。 最终,必须使用这些数字将数据记录限制在所需的数量内。

因此,需要找到一个解决方案,将每个日历周分组,计算最大可能的数据点并在 SQL 中执行此操作时相应地选择数据记录。

问题

如何将每个日历周的班级计数分布重新采样为 SQL 中所需的其他分布,同时为每个班级维护尽可能多的数据?

【问题讨论】:

【参考方案1】:

基于 Quang Hoang 对 Python here 的完美答案,我能够解决这个问题。首先看一下必要的计算(命名取自问题):

new_count  = minimum( (Count / Desired_Distribution) per week ) * Desired_Distribution

请注意,这需要每周进行计算,因此必须使用联接。因此,我将拆分查询以便更好地理解这些步骤。


第 1 步:提供代表所需结果的基础数据

with step_1 as (
    SELECT 
        , week  -- Looks e.g. like '202105' -> 5th calendar week in 2021
        , class
        , case 
            when class = "A" THEN 0.55
            when class = "B" THEN 0.29
            when class = "C" THEN 0.16
        end as Desired_Distribution
    FROM source_table
    GROUP BY 1,2,3
)

第 2 步:提供每个班级和每周可能的最少数据点

,  step_2 as (
    SELECT 
        week
        , min(Diverging_Distribution) as Minimal_Population
    FROM (
        SELECT
            week
            , class
            , case 
                when class = "A" THEN count(*) / 0.55
                when class = "B" THEN count(*) / 0.29
                when class = "C" THEN count(*) / 0.16
            end as Diverging_Distribution
        FROM source_table
    ) status_quo
    GROUP BY 1
)

第 3 步:为每个类可能的最大数据记录提供参考

, step_3 as (
    SELECT
        step_1.week
        , step_1.class
        , ceil(step_1.Current_Distribution * step_2.Minimal_Population) as New_Distribution
    FROM step_1
    LEFT JOIN step_2 using(week) -- Provide maximum per week, not per week and class!
)

结果: 放在一起 -> 限制每个类的数据记录数

SELECT 
    sample.week
    , sample.class
    , sample.data_record_id
FROM (
    SELECT
        data_record_id
        , week
        , class
        , row_number() over (
            partition by 
                week
                , class
            order by data_record_id
        ) as rnk -- This provides a unique number per record and week/class
    FROM source_table
) sample
INNER JOIN step_3 on sample.week = step_3.week
        and sample.class = step_3.class
WHERE sample.rnk <= step_3.New_Distribution -- This selects the quantity of records to match the new distribution

【讨论】:

以上是关于将分布重新采样为具有最大可能数据记录的新分布的主要内容,如果未能解决你的问题,请参考以下文章

将数据帧重新采样为具有任意期末月份的 n 个月期间

在执行一些额外操作的同时将数据帧重新采样为新数据帧

Python - Pandas,重新采样数据集以具有平衡的类

Spark重新分区不均匀分布记录

如何对具有多列的df重新采样

『科学计算_理论』最大似然估计