作者:黄天元

集合运算与连接其实有相似之处,但是连接是针对特定列(主键)来对表格进行连接,而集合运算则直接对记录(entry)进行运算。简单来说,就是以行为单位进行运算。假设表格A与表格B有相同的列名称,它们其实数据的来源是相同的,但是记录的东西有重复的部分,又有不一样的地方。如果我们想要知道哪些部分重复了,就需要求两个数据的交集。简单来讲,本章就是要讲数据记录的“交、并、补”运算。具体的运算逻辑如下图所示:

R语言 转为集合 r语言集合运算_集合运算


集合运算基本逻辑

内容比较简单,我们直接上例子。本章用到的数据集主要是mtcars,我们把它转化为tibble然后进行演示。环境准备如下:

library(tidyverse)
mtcars %>% 
 rownames_to_column(var = "type") %>% #把行名称转为其中一列
 as_tibble() -> mtcars1 #转化为tibble格式
mtcars1## # A tibble: 32 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mazda~ 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 Mazda~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 3 Datsu~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 4 Horne~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 5 Horne~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 6 Valia~ 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 7 Duste~ 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 8 Merc ~ 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
## 9 Merc ~ 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
## 10 Merc ~ 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
## # ... with 22 more rows

这样还不能够满足我们距离的需要,现在我们对这个数据集取两个子集,一个是从第1到第6列,第二个是从第3到第8列,分别存在变量a和变量b中。

mtcars1 %>%
 slice(1:6) -> a
mtcars1 %>%
 slice(3:8) -> b
a## # A tibble: 6 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mazda ~ 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 Mazda ~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 3 Datsun~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 4 Hornet~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 5 Hornet~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 6 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1b
## # A tibble: 6 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Datsun~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 2 Hornet~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 3 Hornet~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 4 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 5 Duster~ 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 6 Merc 2~ 24.4 4 147. 62 3.69 3.19 20 1 0 4 2

仔细看两分数据,我们知道他们有重叠的部分,也有不同的部分。下面我们来分别举例。

交(interset)

a %>%
 intersect(b)## # A tibble: 4 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Datsun~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 2 Hornet~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 3 Hornet~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 4 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1

我们发现两个表中,有四行是一样的。
SQL代码为:

<SQL> SELECT *
FROM `a`
INTERSECT
SELECT *
FROM `b`

并(union)

a %>%
 dplyr::union(b)## # A tibble: 8 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Merc 2~ 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
## 2 Duster~ 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 3 Mazda ~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 4 Hornet~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 5 Datsun~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 6 Mazda ~ 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 7 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 8 Hornet~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2

注意在这个步骤中,我们使用了“dplyr::”放在函数union之前,为什么呢?因为很多包(包括基本包)都会有同名函数union尽管有时候加载的时候会互相覆盖掉,这样就会发生歧义。因此如果我们知道一些函数的命名太过常见,就应该在函数之前加上包的名字和两个冒号,来声明我们用的是这个包里面的这个函数。

SQL代码为:

<SQL> SELECT *
FROM `a`
UNION
SELECT *
FROM `b`

在我们进行“并”运算的时候,两个表格重复的部分在新表格中只会出现一次。如果我们想要的是记录的单纯叠加的话,可以这么做:

a %>%
 union_all(b)## # A tibble: 12 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mazda~ 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 Mazda~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 3 Datsu~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 4 Horne~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 5 Horne~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 6 Valia~ 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 7 Datsu~ 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 8 Horne~ 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 9 Horne~ 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 10 Valia~ 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 11 Duste~ 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 12 Merc ~ 24.4 4 147. 62 3.69 3.19 20 1 0 4 2

SQL代码:

<SQL> SELECT *
FROM `a`
UNION ALL
SELECT *
FROM `b`

补(setdiff)

a %>% 
 setdiff(b)## # A tibble: 2 x 12
## type mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mazda ~ 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 Mazda ~ 21 6 160 110 3.9 2.88 17.0 0 1 4 4

这样我们得到的是表格a中有,而表格b中没有的记录。 SQL代码:

<SQL> SELECT *
FROM `a`
EXCEPT
SELECT *
FROM `b`

判断是否相等(setequal)

dplyr中的函数还可以支持判断两个集合是否相等,这里判断标准是记录是否都一样,返回一个逻辑值(T/F)。判断表格是否相等,只跟内容有关,跟内容的排列顺序无关。

a %>%
 setequal(b)## [1] FALSE
mtcars1 %>%
 setequal(mtcars1[32:1,]) #mtcars1[32:1,]中,我们把mtcars1所有记录倒序排列了## [1] TRUE

小结

本章讲述了集合运算,即集合之间的交、并、补运算,当我们把表格记录视为对象的时候,这些操作能够帮助我们找到两个表格中重复的记录、不同的记录,还可以判断两个表格是否相同。