计算组间重叠

Calculating overlap between groups

我有一个 table,其中包含两个感兴趣的列,item_idbucket_idbucket_id 有固定数量的值,如果需要,我可以列出它们。

每个 item_id 可以出现多次,但每次出现都会有一个单独的 bucket_id 值。例如,123item_id可以在table中出现两次,一次在[=20=的bucket_id下,一次在B下。

我的目标是确定每对 bucket_id 值之间存在多少重叠并将其显示为 N×N 矩阵。

例如,考虑下面的小例子table:

item_id     bucket_id
=========   ===========
111         A
111         B
111         C

222         B
222         D

333         A
333         C

444         C

所以对于这个数据集,桶 AB 有一个共同点 item_id,桶 CD 没有共同点,等等

我想将上面的 table 格式化成如下格式:

        A       B       C       D
===================================
A       2       1       2       0
B       1       2       1       1
C       2       1       3       0
D       0       1       0       1

在上面的 table 中,行和列的交集告诉您两个 bucket_id 值中存在多少条记录。例如,在 A 行与 C 列相交的地方,我们有一个 2,因为在 bucket_id A 和 C 中都存在 2 条记录。因为交集X 和 Y 与 Y 和 X 的交集相同,上面的 table 在对角线上镜像。

我想该查询涉及一个 PIVOT,但我终究无法弄清楚如何让它工作。

我相信这应该能为您提供所需的数据。然后可以通过编程方式(或在 Excel 等中)对 table 进行旋转。

-- This gets the distinct pairs of buckets
select distinct
    a.name,
    b.name
from
    bucket a
    join bucket b
where
    a.name < b.name
order by
    a.name,
    b.name

+ --------- + --------- +
| name      | name      |
+ --------- + --------- +
| A         | B         |
| A         | C         |
| A         | D         |
| B         | C         |
| B         | D         |
| C         | D         |
+ --------- + --------- +
6 rows

-- This gets the distinct pairs of buckets with the counts you are looking for
select distinct
    a.name,
    b.name,
    count(distinct bi.item_id)
from
    bucket a
    join bucket b
    left outer join bucket_item ai on ai.bucket_name = a.name
    left outer join bucket_item bi on bi.bucket_name = b.name and ai.item_id = bi.item_id
where
    a.name < b.name
group by
    a.name,
    b.name
order by
    a.name,
    b.name

+ --------- + --------- + ------------------------------- +
| name      | name      | count(distinct bi.item_id)      |
+ --------- + --------- + ------------------------------- +
| A         | B         | 2                               |
| A         | C         | 1                               |
| A         | D         | 0                               |
| B         | C         | 2                               |
| B         | D         | 0                               |
| C         | D         | 0                               |
+ --------- + --------- + ------------------------------- +
6 rows

这是包含 DDL 的完整示例并插入内容以进行设置(这在 mysql 中,但同样的想法也适用于其他地方):

use example;

drop table if exists bucket;

drop table if exists item;

drop table bucket_item;

create table bucket (
    name varchar(1)
);

create table item(
    id int
);

create table bucket_item(
    bucket_name varchar(1) references bucket(name),
    item_id int references item(id)
);

insert into bucket values ('A');
insert into bucket values ('B');
insert into bucket values ('C');
insert into bucket values ('D');

insert into item values (111);
insert into item values (222);
insert into item values (333);
insert into item values (444);
insert into item values (555);

insert into bucket_item values ('A',111);
insert into bucket_item values ('A',222);
insert into bucket_item values ('A',333);
insert into bucket_item values ('B',222);
insert into bucket_item values ('B',333);
insert into bucket_item values ('B',444);
insert into bucket_item values ('C',333);
insert into bucket_item values ('C',444);
insert into bucket_item values ('D',555);


-- query to get distinct pairs of buckets
select distinct
    a.name,
    b.name
from
    bucket a
    join bucket b
where
    a.name < b.name
order by
    a.name,
    b.name
;

select distinct
    a.name,
    b.name,
    count(distinct bi.item_id)
from
    bucket a
    join bucket b
    left outer join bucket_item ai on ai.bucket_name = a.name
    left outer join bucket_item bi on bi.bucket_name = b.name and ai.item_id = bi.item_id
where
    a.name < b.name
group by
    a.name,
    b.name
order by
    a.name,
    b.name
;

您可以使用简单的 PIVOT:

SELECT t1.bucket_id,
       SUM( CASE WHEN t2.bucket_id = 'A' THEN 1 ELSE 0 END ) AS A,
       SUM( CASE WHEN t2.bucket_id = 'B' THEN 1 ELSE 0 END ) AS B,
       SUM( CASE WHEN t2.bucket_id = 'C' THEN 1 ELSE 0 END ) AS C,
       SUM( CASE WHEN t2.bucket_id = 'D' THEN 1 ELSE 0 END ) AS D
FROM table1 t1
JOIN table1 t2 ON t1.item_id = t2.item_id
GROUP BY t1.bucket_id
ORDER BY 1
;

或者您可以使用 Oracle PIVOT 子句(适用于 11.2 及更高版本):

SELECT * FROM (
   SELECT t1.bucket_id AS Y_bid,
          t2.bucket_id AS x_bid
   FROM table1 t1
   JOIN table1 t2 ON t1.item_id = t2.item_id
)
PIVOT (
  count(*) FOR x_bid in ('A','B','C','D')
)
ORDER BY 1
;

示例:http://sqlfiddle.com/#!4/39d30/7