如何密集排列数据集

Posted

技术标签:

【中文标题】如何密集排列数据集【英文标题】:How to Dense Rank Sets of data 【发布时间】:2015-03-19 07:24:40 【问题描述】:

我试图获得一个密集的排名以将数据集组合在一起。在我的表中,我有 ID、GRP_SET、SUB_SET 和 INTERVAL,它们仅代表一个日期字段。当使用 ID 插入记录时,它们将作为 3 行的 GRP_SET 插入,显示为 SUB_SET。正如您所看到的,当插入发生时,间隔在完成插入集合之前可能会略有变化。

这是一些示例数据,DRANK 列代表我想要获得的排名。

with q as (
select 1 id, 'a' GRP_SET, 1 as SUB_SET, 123 as interval, 1 as DRANK from dual union all
select 1, 'a', 2, 123, 1 from dual union all
select 1, 'a', 3, 124, 1 from dual union all
select 1, 'b', 1, 234, 2 from dual union all
select 1, 'b', 2, 235, 2 from dual union all
select 1, 'b', 3, 235, 2 from dual union all
select 1, 'a', 1, 331, 3 from dual union all
select 1, 'a', 2, 331, 3 from dual union all
select 1, 'a', 3, 331, 3 from dual)

select * from q

示例数据

ID GRP_SET SUBSET INTERVAL DRANK
1  a       1      123      1
1  a       2      123      1
1  a       3      124      1
1  b       1      234      2
1  b       3      235      2
1  b       2      235      2
1  a       1      331      3
1  a       2      331      3
1  a       3      331      3

这是我有接近但我似乎需要类似的查询:

分区依据: ID 分区内的排序方式: ID、间隔 在以下情况下更改排名: ID、GRP_SET(更改)
select
   id, GRP_SET, SUB_SET, interval,
   DENSE_RANK() over (partition by ID order by id, GRP_SET) as DRANK_TEST
from q
Order by
   id, interval

【问题讨论】:

【参考方案1】:

使用MODEL 子句

看哪,您的需求超出了“普通”SQL 易于表达的限制。但幸运的是,您使用的是 Oracle,它具有 MODEL 子句,它的神秘之处仅在于其功能 (excellent whitepaper here)。你应该写:

SELECT
   id, grp_set, sub_set, interval, drank
FROM (
  SELECT id, grp_set, sub_set, interval, 1 drank
  FROM q
)
MODEL PARTITION BY (id)
      DIMENSION BY (row_number() OVER (ORDER BY interval, sub_set) rn)
      MEASURES (grp_set, sub_set, interval, drank)
      RULES (
        drank[any] = NVL(drank[cv(rn) - 1] + 
                         DECODE(grp_set[cv(rn) - 1], grp_set[cv(rn)], 0, 1), 1)
      )

Proof on SQLFiddle

说明:

SELECT
   id, grp_set, sub_set, interval, drank
FROM (
  -- Here, we initialise your "dense rank" to 1
  SELECT id, grp_set, sub_set, interval, 1 drank
  FROM q
)

-- Then we partition the data set by ID (that's your requirement)
MODEL PARTITION BY (id)

-- We generate row numbers for all columns ordered by interval and sub_set,
-- such that we can then access row numbers in that particular order
      DIMENSION BY (row_number() OVER (ORDER BY interval, sub_set) rn)

-- These are the columns that we want to generate from the MODEL clause
      MEASURES (grp_set, sub_set, interval, drank)

-- And the rules are simple: Each "dense rank" value is equal to the
-- previous "dense rank" value + 1, if the grp_set value has changed
      RULES (
        drank[any] = NVL(drank[cv(rn) - 1] + 
                         DECODE(grp_set[cv(rn) - 1], grp_set[cv(rn)], 0, 1), 1)
      )

当然,这只有在没有交错事件时才有效,即在 123 和 124 之间没有除 a 之外的其他 grp_set

【讨论】:

这是解决我的问题的最佳解决方案,因为它不需要对数据集之间的时间延迟进行任何假设。感谢 allot 的帮助 看来我还有几个小时要阅读 Oracle 中的 MODEL 功能。 @CullenBurton:This whitepaper is the best resource I've found so far。但请注意,我的解决方案仍然做了一个假设:假设两个 grp_set 永远不会有交错的 interval 值。如果发生这种情况,你仍然可以使用MODEL,但是写起来会有点复杂…… 在这里找到了更全面的文档,其中包含大量功能:docs.oracle.com/cd/B19306_01/server.102/b14223/sqlmodel.htm @CullenBurton:完美。那应该是1-2天的阅读材料:) @CullenBurton:如果您使用的是 12c,请不要忘记了解新的 MATCH_RECOGNIZE 子句。 Example here。这是一个完全不同的星球(可能更适合您的问题 - 我自己还没来得及学习,所以我还不能在答案中使用它)【参考方案2】:

这可能对你有用。复杂的因素是,对于区间 123124 以及区间 234235,您需要相同的“DENSE RANK”。因此,我们会将它们截断为最接近的 10,以便对 DENSE_RANK() 函数进行排序:

SELECT id, grp_set, sub_set, interval, drank
     , DENSE_RANK() OVER ( PARTITION BY id ORDER BY TRUNC(interval, -1), grp_set ) AS drank_test
  FROM q

Please see SQL Fiddle demo here.

如果您希望间隔更接近以便组合在一起,则可以在截断之前乘以该值。这会将它们按 3 分组(但也许你不需要它们那么细化):

SELECT id, grp_set, sub_set, interval, drank
     , DENSE_RANK() OVER ( PARTITION BY id ORDER BY TRUNC(interval*10/3, -1), grp_set ) AS drank_test
  FROM q

【讨论】:

我认为这种情况下你不需要partition by子句,用order by就够了,但这是个好主意。 我假设 OP 中提到的数据集不是完整集...加上 OP 要求按 id 进行分区。 啊,我明白了。那没关系

以上是关于如何密集排列数据集的主要内容,如果未能解决你的问题,请参考以下文章

如何生成多重集的所有排列?

习题2.8 输出全排列(20 分)浙大版《数据结构(第2版)》题目集

我如何在ML中对评论进行分类?

优化人员安置 - 大型数据集

Scikit 学习系列拆分训练测试

numpy利用下标打乱数据集