【R语言数据科学】:(九)数据清洗技巧之数据表连接大全-灵析社区

秋叶无缘

💮9.数据清洗之表连接

有时候我们要分析的数据可能不是来自一个数据集,此时需要我们对不同表按照某一关键字进行连接 假设我们想探索美国各州的人口规模与选举人票之间的关系。我们的人口规模如下表所示:

library(tidyverse)
library(dslabs)
head(murders)
stateabbregionpopulationtotal
<chr><chr><fct><dbl><dbl>
1AlabamaALSouth4779736135
2AlaskaAKWest71023119
3ArizonaAZWest6392017232
4ArkansasARSouth291591893
5CaliforniaCAWest372539561257
6ColoradoCOWest502919665

选票数据如下

head(results_us_election_2016)
stateelectoral_votesclintontrumpothers
<chr><int><dbl><dbl><dbl>
1California5561.731.66.7
2Texas3843.252.24.5
3Florida2947.849.03.2
4New York2959.036.54.5
5Illinois2055.838.85.4
6Pennsylvania2047.948.63.6

直接将这两个表拼接在一起是不对的,因为他们的顺序不一致,因此我们需要考虑按照state连接两个表

🏵️9.1 joins()函数

left()函数是根据某个关键字列来将多个表进行合并。和sql中的语法基本类似,大家如果想了解sql语法可以看我的专栏。 一般的做法是,首先确定一个或多个用于匹配两个表的列,然后返回一个包含组合数据的新表。

下面,我们使用left_join连接两个表,并且去除others的列,并将electoral_votes重命名

tab <- right_join(murders, results_us_election_2016, by = "state") %>%
    select(-others) %>% rename(ev=electoral_votes)
head(tab)
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
1AlabamaALSouth4779736135934.462.1
2AlaskaAKWest71023119336.651.3
3ArizonaAZWest63920172321145.148.7
4ArkansasARSouth291591893633.760.6
5CaliforniaCAWest3725395612575561.731.6
6ColoradoCOWest502919665948.243.3

现在我们成功的将两个表连接在一起了,下面我们简单绘制一下人口和选票之间的关系

tab %>% ggplot(aes(population/10^6, ev, label = abb)) +
  geom_point() +
  geom_text_repel() + 
  scale_x_continuous(trans = "log2") +
  scale_y_continuous(trans = "log2") +
  geom_smooth(method = "lm", se = FALSE)
`geom_smooth()` using formula 'y ~ x'

Warning message:
"ggrepel: 15 unlabeled data points (too many overlaps). Consider increasing max.overlaps"

从结果来看,可以看出人口越多,选票越多,平均而言每十万人有2选票,但是在人口较少的州,这个比例稍高

实际上,并不总是一个表中的每一行在另一个表中都有一个匹配的行。因此,我们有不同的连接方法,为例举例说明, 下面我们先创建tab1和tab2

tab_1 <- slice(murders, 1:6) %>% select(state, population)
tab_1
statepopulation
<chr><dbl>
Alabama4779736
Alaska710231
Arizona6392017
Arkansas2915918
California37253956
Colorado5029196
tab_2 <- results_us_election_2016 %>% 
  filter(state%in%c("Alabama", "Alaska", "Arizona", 
                    "California", "Connecticut", "Delaware")) %>% 
  select(state, electoral_votes) %>% rename(ev = electoral_votes)
tab_2
stateev
<chr><int>
California55
Arizona11
Alabama9
Connecticut7
Alaska3
Delaware3

🌹9.1.1 左连接(left join)

假设我们想要一张像tab_1这样的表格,但将选举人票添加到我们现有州。此时tab_1作为第一个参数。我们指定要使用哪个列与by参数匹配。没能匹配到的会自动返回NA

left_join(tab_1,tab_2,by = 'state') 
statepopulationev
<chr><dbl><int>
Alabama47797369
Alaska7102313
Arizona639201711
Arkansas2915918NA
California3725395655
Colorado5029196NA

可以看出ArkansasColorado的ev没有匹配到,所以返回NA,下面我们将tab_2传入第一个参数

left_join(tab_2,tab_1,by = 'state') 
stateevpopulation
<chr><int><dbl>
California5537253956
Arizona116392017
Alabama94779736
Connecticut7NA
Alaska3710231
Delaware3NA

可以看出结果返回了包含了tab_2的所有信息,其中Connecticut和Delaware的population返回NV

🥀9.1.2 右连接(right join)

右连接与左连接相反,返回第二个表的全部信息,匹配连接第一个表,下面我们来看一个案例

tab_2 %>% right_join(tab_1, by = "state")
stateevpopulation
<chr><int><dbl>
California5537253956
Arizona116392017
Alabama94779736
Alaska3710231
ArkansasNA2915918
ColoradoNA5029196

可以看出结果交换参数位置后使用右连接和直接使用左连接的结果一致。

可以这样理解:

    • 左连接是将第一个表的所有信息保存下来了,同时按照某一列匹配第二个表的信息并整合起来,没有匹配到的返回NA
    • 右连接是将第二个表的所有信息保存下来了,相当于是左连接交换参数

🌺9.1.3 内连接(inner join)

如果我们只希望得到两个表中都包含的信息,我们可以使用内连接,没有匹配到的行将不显示

inner_join(tab_1, tab_2, by = "state")
statepopulationev
<chr><dbl><int>
Alabama47797369
Alaska7102313
Arizona639201711
California3725395655

🌻9.1.4 全连接(full join)

如果我们希望保留两个表的所有行,并且用NA来填充未匹配到的值,如下所示

full_join(tab_1, tab_2, by = "state")
statepopulationev
<chr><dbl><int>
Alabama47797369
Alaska7102313
Arizona639201711
Arkansas2915918NA
California3725395655
Colorado5029196NA
ConnecticutNA7
DelawareNA3

🌼9.1.5 半连接(semi join)

这个方法在mysql中没有涉及到,它具体工作原理是匹配两个表都有的行,然后只显示第一个表的信息。 相当于先做内连接,再把第二个表的信息去除,具体如下:

semi_join(tab_1, tab_2, by = "state")
statepopulation
<chr><dbl>
Alabama4779736
Alaska710231
Arizona6392017
California37253956

🌷9.1.6 反连接(anti join)

函数anti_join与semi_join相反。它保留第一个表中没有匹配到第二个表中信息的元素,具体如下

anti_join(tab_1, tab_2, by = "state")
statepopulation
<chr><dbl>
Arkansas2915918
Colorado5029196

🌱9.2 binding

另一个连接表的方法是使用binding,和join函数不同,binding函数不通过某个变量匹配,而是直接连接数据框,如果两个数据框纬度不同,则会报错

🌲9.2.1 按列连接

使用bind_cols,可以方便的连接不同列,例如

bind_cols (a=1:3,b=4:6)
ab
<int><int>
14
25
36

注意,有一个R函数cbind具有完全相同的功能。 主要区别是cbind可以创建不同类型的对象,而bind_cols总是生成一个数据框。

bind_cols也可以连接不同的数据框,如下所示

tab_1 <- tab[, 1:3]
tab_2 <- tab[, 4:6]
tab_3 <- tab[, 7:8]
new_tab <- bind_cols(tab_1, tab_2, tab_3)
head(new_tab)
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
1AlabamaALSouth4779736135934.462.1
2AlaskaAKWest71023119336.651.3
3ArizonaAZWest63920172321145.148.7
4ArkansasARSouth291591893633.760.6
5CaliforniaCAWest3725395612575561.731.6
6ColoradoCOWest502919665948.243.3

🌳9.2.2 按行连接

bind_rows()和bind_cols()函数类似,但是是按行连接

tab_1 <- tab[1:2,]
tab_2 <- tab[3:4,]
bind_rows(tab_1, tab_2)
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
AlabamaALSouth4779736135934.462.1
AlaskaAKWest71023119336.651.3
ArizonaAZWest63920172321145.148.7
ArkansasARSouth291591893633.760.6

同样的,R语言基础函数rbin()可以实现类似的功能

🌴9.3 集合运算符

另一组用于连接数据集方法是集合运算符。当运用在向量上面,则实现和它们字面意思一样,例如intersect、union、setdiff和setequal。 则这些函数可以用于数据帧,而不仅仅是向量。

🌵9.3.1 交集(intersect)

intersect(1:10, 6:15)

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 6
  2. 7
  3. 8
  4. 9
  5. 10
intersect(c("a","b","c"), c("b","c","d"))

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 'b'
  2. 'c'

当我们导入了dyply包,我们可以使用intersect应用在数据框上,其功能也是求交集

tab_1 <- tab[1:5,]
tab_2 <- tab[3:7,]
intersect(tab_1, tab_2)
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
ArizonaAZWest63920172321145.148.7
ArkansasARSouth291591893633.760.6
CaliforniaCAWest3725395612575561.731.6

🌾9.3.2 并集(Union)

union(1:10, 6:15)

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
union(c("a","b","c"), c("b","c","d"))

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 'a'
  2. 'b'
  3. 'c'
  4. 'd'
tab_1 <- tab[1:5,]
tab_2 <- tab[3:7,]
dplyr::union(tab_1, tab_2) 
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
AlabamaALSouth4779736135934.462.1
AlaskaAKWest71023119336.651.3
ArizonaAZWest63920172321145.148.7
ArkansasARSouth291591893633.760.6
CaliforniaCAWest3725395612575561.731.6
ColoradoCOWest502919665948.243.3
ConnecticutCTNortheast357409797754.640.9

🌿9.3.3 差集(setdiff)

返回第一个参数对第二个参数的差集,因此和上面两个方法不同,setdiff不是对称的,具体案例如下

setdiff(1:10, 6:15)


setdiff(6:15, 1:10)

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5

.list-inline {list-style: none; margin:0; padding: 0} .list-inline>li {display: inline-block} .list-inline>li:not(:last-child)::after {content: "\00b7"; padding: 0 .5ex}

  1. 11
  2. 12
  3. 13
  4. 14
  5. 15
tab_1 <- tab[1:5,]
tab_2 <- tab[3:7,]
dplyr::setdiff(tab_1, tab_2)
stateabbregionpopulationtotalevclintontrump
<chr><chr><fct><dbl><dbl><int><dbl><dbl>
AlabamaALSouth4779736135934.462.1
AlaskaAKWest71023119336.651.3

☘️9.3.4 setequal

setequal判断两个集合是否相等,而不管顺序。例如

setequal(1:5, 1:6)

FALSE

setequal(1:5, 5:1)

TRUE

setequal(tab_1, tab_2)

FALSE

阅读量:1978

点赞量:0

收藏量:0