SQL - 帮助根据比例和唯一 ID 选择随机日期

Posted

技术标签:

【中文标题】SQL - 帮助根据比例和唯一 ID 选择随机日期【英文标题】:SQL - Help select random date based on proportions and unique ids 【发布时间】:2021-09-03 19:13:55 【问题描述】:

我希望如果合适的话,我可以在 T-SQL 中解决这个问题。感谢您提供任何帮助,我也看到过其他类似的问题,但我为同一个人输入了多个条目,这使得它变得困难。

我有一个大型数据集,其中包含 2015-2020 年的 ID(每个人都是唯一的)。这是个人 (ID) 及其填写日期的处方数据,因此每个 ID 通常有多行 - 无论是在一年内还是跨多年。

我想根据以下比例/概率为每个 ID/人随机选择一个日期:5%-2015、10%-2016、10%-2017、15%-2018、20%-2019 和 40% 2020 年。每个人有 120 万个唯一 ID,而 2020 年大约有 300,000 人填满,这似乎是一个限制因素。

【问题讨论】:

如果一个人在某年[s]的表中没有行怎么办? 您是否要纵向跟踪离散的个人,因此需要每个人 5 年的数据?或者您是否希望随机抽取 2015 年所有客户的 5%、2016 年所有客户的 10% 等,而不用担心任何给定的个人是否出现在所有集合中? 这能回答你的问题吗? SQL - 5% random sample by group 每年不同的人的数量是否大致相等,或者您是否需要处理其他可能的限制?也就是说,假设 2020 年有 300,000 不同的人,那么你想要其中的 40%,即 120,000 人。这意味着您希望从 2019 年开始有约 60,000 人。如果 2019 年只有 30,000 人怎么办?您会从 2020 年开始减少人数以保持比例,还是从 2019 年开始尽可能多地增加? 我想另一个问题是:您是否希望总体上最大化样本量,即使这意味着需要在不同年份挑选一些相同的人?例如,假设 Bob 是 2015 年样本总体的成员。如果我们能够再次为 2016 年的人口挑选 Bob,那么我们可以从 2017 年以上的人口中挑选更多的人并保持我们的比例。如果我们在 2016 年不再选择 Bob,那么我们必须减少所有年份的总样本量以保持我们的比例。我们会在 2015 年和 2016 年都选择 Bob 吗? 【参考方案1】:

这是一个非常棘手的问题。基本上,问题是每个人只能选择一个 id。例如,要获得您想要的年份分布,您可以使用:

select t.*
from (select t.*, rand(checksum(newid())) as rnd
      from t
     ) t cross join
     (values (2015, 0, 0.05), (2016, 0.05, 0.15), (2017, 0.15, 0.25), 
             (2018, 0.25, 0.40), (2019, 0.40, 0.60), (2020, 0.60, 1)
     ) v(lo, hi)
where rnd >= lo and rnd < hi;

但是,这并不能保证每人一排。它确实(尽可能)保证了您想要的分发。

最简单的方法是每人随机选择一年,然后进行分层抽样:

select t.*
from (select t.*, rand(checksum(newid())) as rnd,
             row_number() over (partition by person order by newid()) as seqnum
      from t
     ) t cross join
     (values (2015, 0, 0.05), (2016, 0.05, 0.15), (2017, 0.15, 0.25), 
             (2018, 0.25, 0.40), (2019, 0.40, 0.60), (2020, 0.60, 1)
     ) v(lo, hi)
where seqnum = 1 and rnd >= lo and rnd < hi;

这会为每人随机选择一个年份,如果每年有相同数量的行,那么它应该与您想要的分布相匹配。

【讨论】:

【参考方案2】:

这并没有解决问题的 PersonID 部分,但它确实可以生成属于给定年份的随机日期:

首先,我想生成一个包含 ID 和间隔的大型样本数据集。所有这些工作都在FROM 子句中的子查询中完成。这会用 30k 个随机生成的假 ID 填充临时表。 366的模数限制了随机区间。

DROP TABLE IF EXISTS #Table1;

SELECT 1001999 + n AS ID,
       DATEADD(DAY, RandDate, '2015-01-01') AS RandomDate
  INTO #Table1
  FROM (SELECT TOP (30000)
               ROW_NUMBER() OVER (ORDER BY s1.[object_id]) AS n,
               ABS(CHECKSUM(NEWID()) % 366) AS RandDate
          FROM sys.all_objects      AS s1
         CROSS JOIN sys.all_objects AS s2
         ORDER BY s1.[object_id]) AS x;

SELECT TOP 5 PERCENT *
  FROM #Table1 AS t;

在外部查询中,RandDate 间隔值用于DATEADD 函数以获取给定年份内随机选择的日期。我在此示例中使用 2015 年,但可以在任何年份重复。

最后,由于您想要 2015 年所有记录的 5% 的样本大小,我从样本数据中仅选择 TOP 5 PERCENT,得到如下结果集:

ID RandomDate
1014920 2015-01-09 00:00:00.000
1014921 2015-06-22 00:00:00.000
1014922 2015-10-20 00:00:00.000
1014923 2015-01-29 00:00:00.000
1014924 2015-01-03 00:00:00.000
1014925 2015-05-05 00:00:00.000
1014926 2015-10-09 00:00:00.000
1014927 2015-08-29 00:00:00.000
1014928 2015-03-20 00:00:00.000

如果您需要对这些日期作为记录日期存在于您的处方配药记录中进行任何数量的验证,您可以 INNER JOIN 将这组随机日期添加到该表中,让您只获得有效日期。

【讨论】:

以上是关于SQL - 帮助根据比例和唯一 ID 选择随机日期的主要内容,如果未能解决你的问题,请参考以下文章

SQL:根据最近的日期选择一个字段中的值是唯一的记录

为每个唯一 ID 选择最近 30 个日期

如何使用 SQL 选择每个唯一日期的唯一会话?

在PostgreSQL中,如何根据分类列中每个级别的比例从表中随机抽样?

根据日期和请求数创建唯一 ID

如何在 SQL 中计算每个用户的唯一日期的平均收入