【问题标题】:stratified sample on ranges范围内的分层样本
【发布时间】:2019-12-27 11:43:11
【问题描述】:

我有 table_1,它的数据如下:

Range Start   Range End   Frequency   
10                   20          90   
20                   30          68   
30                   40         314   
40                   40         191 (here, it means we have just 40 as data point repeating 191 times) 

table_2:

group     value   
10        56.1   
10        88.3   
20        53   
20        20   
30        55   

我需要根据 table_1 的范围获取分层样本,table_2 可以有数百万行但结果应该限制在 10k 点。

尝试以下查询:

SELECT   
    d.*   
FROM   
    (   
        SELECT   
            ROW_NUMBER() OVER(   
                                PARTITION BY group   
                                ORDER BY group   
                            ) AS seqnum,   
            COUNT(*) OVER() AS ct,   
            COUNT(*) OVER(PARTITION BY group) AS cpt,   
            group, value   
        FROM   
            table_2 d   
    ) d   
WHERE   
    seqnum < 10000 * ( cpt * 1.0 / ct )   

但对这里的分析功能使用有点困惑。

期望 10k 记录作为来自 table_2 的分层样本:

结果表:

group     value   
10       56.1   
20       53   
20        20   
30       55

【问题讨论】:

  • 为什么结果表中第10组只有一条记录?
  • 结果表只是一个例子。基本上,结果表应该包含 table_2 中记录的子集(分层样本)
  • 10000 个结果是否应该与频率成比例,因此 30-40 范围内的值大约是 20-30 范围内的值的 5 倍(即 314/68 = 4.617...时间一样多)?还有第 20 组的行是否计入 10-20 范围内,或 20-30 范围内,或两者兼而有之? (有人可能会假设您包含值 >= start 和
  • @SurendraSingh 。 . .你的问题没有意义。您的第一个表的总频率为 663,但您要求 10,000 行。剩下的 9337 行是从哪里来的?
  • 你为什么不使用sample clause:docs.oracle.com/cd/E11882_01/server.112/e41084/…

标签: sql oracle


【解决方案1】:

这意味着您需要每个组至少有一条记录,并且随机需要更多记录,然后试试这个:

SELECT GROUP, VALUE FROM
(SELECT T2.GROUP, T2.VALUE, 
ROW_NUMBER() 
OVER (PARTITION BY T2.GROUP ORDER BY NULL) AS RN
FROM TABLE_1 T1
JOIN TABLE_2 T2
ON(T1.RANGE = T2.GROUP))
WHERE RN = 1 OR
CASE WHEN RN > 1 
AND RN = CEIL(DBMS_RANDOM.VALUE(1,RN))
THEN 1 END = 1
FETCH FIRST 10000 ROWS ONLY;

这里,Rownum 是在每个组的随机基础上获取的,然后结果是 rownum 1 和其他 rownum 如果它们满足随机条件。

干杯!!

【讨论】:

    【解决方案2】:

    如果我了解您想要什么 - 这绝不是肯定的 - 那么我认为您希望获得最多 10000 行,组值的数量与频率成正比。因此,您可以通过以下方式从每个范围中获取所需的行数:

    select range_start, range_end, frequency,
      frequency/sum(frequency) over () as proportion,
      floor(10000 * frequency/sum(frequency) over ()) as limit
    from table_1;
    
    RANGE_START  RANGE_END  FREQUENCY PROPORTION      LIMIT
    ----------- ---------- ---------- ---------- ----------
             10         20         90 .135746606       1357
             20         30         68 .102564103       1025
             30         40        314 .473604827       4736
             40         40        191 .288084465       2880
    

    这些限制加起来还不到 10000;您可以使用ceil 而不是floor 略高于。

    然后,您可以根据 table_2 中的每个条目所在的范围为其分配一个名义行号,然后通过该限制限制该范围内的行数:

    with cte1 (range_start, range_end, limit) as (
      select range_start, range_end, floor(10000 * frequency/sum(frequency) over ())
      from table_1
    ),
    cte2 (grp, value, limit, rn) as (
      select t2.grp, t2.value, cte1.limit,
        row_number() over (partition by cte1.range_start order by t2.value) as rn
      from cte1
      join table_2 t2
      on (cte1.range_end > cte1.range_start and t2.grp >= cte1.range_start and t2.grp < cte1.range_end)
      or (cte1.range_end = cte1.range_start and t2.grp = cte1.range_start)
    )
    select grp, value
    from cte2
    where rn <= limit;
    
    ...
    
    9998 rows selected.
    

    我在row_number() 调用中使用了order by t2.value,因为不清楚你想如何选择你真正想要的范围内的哪些行;您可能想通过dbms_random.value 或其他方式订购。

    db<>fiddle 带有一些人工数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 2020-05-07
      • 2016-11-10
      • 1970-01-01
      • 1970-01-01
      • 2017-04-23
      相关资源
      最近更新 更多