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

Resample distribution to new distribution with maximum possible data records

上下文

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

问题已解决 - 无论如何,仅在 Python 实施中。对于我的用例,我现在需要在与 ImpalaHiveQL.

兼容的纯 SQL 或 SQL 中执行此操作

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

| 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         |

可以看出,分布是随机的,与真实情况不匹配(期望分布)。此外,期望的分配与现状有很大不同。

问题

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

因此,需要在SQL.

中找到一个解决方案,将每个日历周分组,计算最大可能的数据点和select相应的数据记录。

问题

如何将每个日历周的 class 计数分布重新采样到 SQL 中所需的其他分布,同时保持每个 [=53= 尽可能多的数据]?

根据 Quang Hoang 对 Python 的完美回答,我解决了这个问题。先看一下必要的计算(命名取自题目):

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: 提供每 class 和每周的最小可能数据点

,  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 步: 根据 class

提供可能的最大数据记录参考
, 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!
)

结果: 放在一起 -> 限制每个 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