R數(shù)據(jù)科學(xué)(九)使用dplyr處理關(guān)系數(shù)據(jù)

9.1 簡介
三類連接:
? 合并連接:向數(shù)據(jù)框中加入新變量,新變量的值是另一個(gè)數(shù)據(jù)框中的匹配觀測。
? 篩選連接:根據(jù)是否匹配另一個(gè)數(shù)據(jù)框中的觀測,篩選數(shù)據(jù)框中的觀測。
? 集合操作:將觀測作為集合元素來處理。

library(tidyverse)
library(nycflights13) #包括的數(shù)據(jù):
#airlines:可以根據(jù)航空公司的縮寫碼查到公司全名。
#airports:給出了每個(gè)機(jī)場的信息,通過 faa 機(jī)場編碼進(jìn)行標(biāo)識。
#planes:給出了每架飛機(jī)的信息,通過 tailnum 進(jìn)行標(biāo)識。
 #weather:給出了紐約機(jī)場每小時(shí)的天氣狀況。


9.3 鍵
用于連接每對數(shù)據(jù)表的變量稱為鍵。鍵是能唯一標(biāo)識觀測的變量(或變量集合)。
? 主鍵:唯一標(biāo)識其所在數(shù)據(jù)表中的觀測。例如, planestailnum 是一個(gè)主鍵,因?yàn)槠淇?以唯一標(biāo)識 planes 表中的每架飛機(jī)。 ? 外鍵:唯一標(biāo)識另一個(gè)數(shù)據(jù)表中的觀測。例如, flightstailnum 是一個(gè)外鍵,因?yàn)槠?br> 出現(xiàn)在 flights 表中,并可以將每次航班與唯一一架飛機(jī)匹配。

# 對主鍵進(jìn)行 count() 操作,然后查看是否有 n 大于 1 的記錄,即查看是否唯一值
planes %>%
count(tailnum) %>%
filter(n > 1)

weather %>%
count(year, month, day, hour, origin) %>%
filter(n > 1)

#flights 表中的主鍵是什么?
flights %>%
count(year, month, day, flight) %>%
filter(n > 1)

flights %>%
count(year, month, day, tailnum) %>%
filter(n > 1)

如果一張表沒有主鍵,有時(shí)就需要使用 mutate() 函數(shù)和 row_number() 函數(shù)為表加上一個(gè)主鍵。這樣一來,
如果你完成了一些篩選工作,并想要使用原始數(shù)據(jù)檢查的話,就可以更容易地匹配觀測。
這種主鍵稱為代理鍵。

練習(xí)
(1) 向 flights 添加一個(gè)代理鍵

flights %>%
  arrange(year, month, day, sched_dep_time, carrier, flight) %>%
  mutate(flight_id = row_number()) %>%
  glimpse()

9.4 合并連接

# 建立簡化數(shù)據(jù)集
flights2 <- flights %>%
select(year:day, hour, origin, dest, tailnum, carrier)
flights2
head(airlines)
flights2 %>%
select(-origin, -dest) %>% 
  left_join(airlines,by='carrier')

9.4.2 內(nèi)連接
x %>%
inner_join(y, by = "key")

9.4.3 外連接
內(nèi)連接保留同時(shí)存在于兩個(gè)表中的觀測, 外連接則保留至少存在于一個(gè)表中的觀測。外連
接有 3 種類型。
? 左連接:保留 x 中的所有觀測。
? 右連接:保留 y 中的所有觀測
? 全連接:保留 x 和 y 中的所有觀測。

最常用的連接是左連接:只要想從另一張表中添加數(shù)據(jù),就可以使用左連接,因?yàn)樗鼤?br> 留原表中的所有觀測,即使它沒有匹配。左連接應(yīng)該是你的默認(rèn)選擇,除非有足夠充分的
理由選擇其他的連接方式。

9.4.4 重復(fù)鍵

# 當(dāng)連接這樣的重復(fù)鍵時(shí),你會得到所有可能的組合
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
2, "x3",
1, "x4"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2"
)
left_join(x, y, by = "key")

9.4.5 定義鍵列
默認(rèn)值 by = NULL。這會使用存在于兩個(gè)表中的所有變量,即共同的變量,這種方式稱為自然連接。

head(flights2)
head(weather)
flights2 %>%
left_join(weather)

字符向量 by = "x"。這種方式與自然連接很相似,但只使用某些公共變量。

flights2 %>%
left_join(planes, by = "tailnum")

命名字符向量 by = c("a" = "b")。這種方式會匹配 x 表中的 a 變量和 y 表中的 b 變量。輸出結(jié)果中使用的是 x 表中的變量。

head(flights2)
head(airports)
flights2 %>%
left_join(airports, c("dest" = "faa")) %>% glimpse()

flights2 %>%
left_join(airports, c("origin" = "faa")) %>% glimpse()

9.4.6 練習(xí)
(1) 計(jì)算出每個(gè)目的地的平均延誤時(shí)間,然后與 airports 數(shù)據(jù)框連接,從而展示出延誤的
空間分布。以下是畫出美國地圖的一種簡單方法。

airports %>%
semi_join(flights, c("faa" = "dest")) %>%
ggplot(aes(lon, lat)) +
borders("state") +
geom_point() +
coord_quickmap()

avg_dest_delays <-
  flights %>%
  group_by(dest) %>%
  # arrival delay NA's are cancelled flights
  summarise(delay = mean(arr_delay, na.rm = TRUE)) %>%
  inner_join(airports, by = c(dest = "faa"))

avg_dest_delays %>%
  ggplot(aes(lon, lat, colour = delay)) +
    borders("state") +
    geom_point() +
    coord_quickmap()

(2)將起點(diǎn)機(jī)場和終點(diǎn)機(jī)場的位置信息(即 lat 和 lon)添加到 flights 中。

airport_locations <- airports %>% select(faa,lat,lon)
str(flights)

flights %>% select(year:day, hour, origin, dest) %>% left_join(airport_location,by=c("origin" = "faa")) %>% left_join(
    airport_locations,
    by = c("dest" = "faa")
  )

(3)飛機(jī)的機(jī)齡和延誤時(shí)間有關(guān)系嗎?

str(planes)
plane_ages <-
  planes %>%
  mutate(age = 2018 - year) %>%
  select(tailnum, age)

flights %>%
  inner_join(plane_ages, by = "tailnum") %>%
  group_by(age) %>%
  filter(!is.na(dep_delay)) %>%
  summarise(delay = mean(dep_delay)) %>%
  ggplot(aes(x = age, y = delay)) +
  geom_point() +
  geom_line()

(4)什么樣的天氣狀況更容易出現(xiàn)延誤?

str(weather)
flight_weather <-
  flights %>%
  inner_join(weather, by = c("origin" = "origin",
                            "year" = "year",
                            "month" = "month",
                            "day" = "day",
                            "hour" = "hour"))

flight_weather %>%
  group_by(precip) %>%
  summarise(delay = mean(dep_delay, na.rm = TRUE)) %>%
  ggplot(aes(x = precip, y = delay)) +
    geom_line() + geom_point()

(5) 2013 年 6 月 13 日發(fā)生了什么情況?展示出這天延誤時(shí)間的空間模式,并使用 Google 說
明一下這天的天氣狀況。

library(viridis)
flights %>%
  filter(year == 2013, month == 6, day == 13) %>%
  group_by(dest) %>%
  summarise(delay = mean(arr_delay, na.rm = TRUE)) %>%
  inner_join(airports, by = c("dest" = "faa")) %>%
  ggplot(aes(y = lat, x = lon, size = delay, colour = delay)) +
  borders("state") +
  geom_point() +
  coord_quickmap() +
  scale_colour_viridis()

base::merge() 函數(shù)可以實(shí)現(xiàn)所有 4 種合并連接操作。
inner_join(x, y) merge(x, y)
left_join(x, y) merge(x, y, all.x = TRUE)
right_join(x, y) merge(x, y, all.y = TRUE)
full_join(x, y) merge(x, y, all.x = TRUE, all.y = TRUE)

9.5 篩選連接
? semi_join(x, y): 保留 x 表中與 y 表中的觀測相匹配的所有觀測。
? anti_join(x, y): 丟棄 x 表中與 y 表中的觀測相匹配的所有觀測。

top_dest <- flights %>%
count(dest, sort = TRUE) %>%
head(10)
top_dest
# 篩選出前10個(gè)
flights %>%
filter(dest %in% top_dest$dest)
# 半連接,只保留兩個(gè)數(shù)據(jù)框都有的行的數(shù)據(jù)
flights %>%
semi_join(top_dest)

# 反連接保留 x 表中那些沒有匹配 y 表的行。
# 反連接可以用于診斷連接中的不匹配。例如,在連接 flights 和 planes 時(shí),你可能想知道flights 中是否有很多行在 planes 中沒有匹配記錄:
flights %>%
anti_join(planes, by = "tailnum") %>%
count(tailnum, sort = TRUE)

練習(xí)
(1) 如果一條航班信息的 tailnum 是缺失值,這說明什么?如果機(jī)尾編號在 planes 中沒有
匹配的記錄,一般是什么情況?(提示:有一個(gè)變量可以解釋約 90% 的這種情況。)

flights %>%
  anti_join(planes, by = "tailnum") %>%
  count(carrier, sort = TRUE)

(2) 對航班信息進(jìn)行篩選,只保留至少有 100 次飛行記錄的飛機(jī)的航班信息。

planes_gt100 <-
  filter(flights) %>%
  group_by(tailnum) %>%
  count() %>%
  filter(n > 100)

flights %>%
  semi_join(planes_gt100, by = "tailnum")

(3) 使用 fueleconomy::vehicles 和 fueleconomy::common 找出那些用于最常用模型的記錄。

glimpse(fueleconomy::vehicles)
glimpse(fueleconomy::common)
fueleconomy::vehicles %>%
  semi_join(fueleconomy::common, by = c("make", "model"))

(4) 找出這一整年中航班延誤最嚴(yán)重的 48 小時(shí)。與 weather 數(shù)據(jù)互相參照,你能找出某種
模式嗎?

flights %>%
  group_by(year, month, day) %>%
  summarise(total_24 = sum(dep_delay, na.rm = TRUE) + sum(arr_delay, na.rm = TRUE)) %>%
  mutate(total_48 = total_24 + lag(total_24)) %>%
  arrange(desc(total_48))

(5) 你能說出 anti_join(flights, airports, by = c("dest" = "faa")) 這條語句的意義嗎?
anti_join(airports, flights, by = c("faa" = "dest")) 這條語句的意義呢?

(6) 或許你認(rèn)為飛機(jī)和航空公司之間存在著某種隱含關(guān)系,因?yàn)槊考茱w機(jī)都屬于一個(gè)航空公
司。使用你在前面章節(jié)中學(xué)到的工具來確認(rèn)或否定這個(gè)假設(shè)。

str(flights)

multi_carrier_planes <-
  flights %>%
  filter(!is.na(tailnum)) %>%
  count(tailnum, carrier) %>%
  count(tailnum) %>%
  filter(nn > 1)
multi_carrier_planes

multi_carrier_planes <-
  flights %>%
  semi_join(multi_carrier_planes, by = "tailnum") %>%
  select(tailnum, carrier) %>%
  distinct() %>%
  arrange(tailnum)
multi_carrier_planes

carrier_transfer_tbl <-
  multi_carrier_planes %>%
  group_by(tailnum) %>%
  mutate(
    carrier_num = seq_along(tailnum),
    carrier_num = paste0("carrier_", carrier_num)
  ) %>%
  left_join(airlines, by = "carrier") %>%
  select(-carrier) %>%
  spread(carrier_num, name)
carrier_transfer_tbl

9.7 集合操作
所有集合操作都是作用于整行的,比較的是每個(gè)變量的值。集合操作需要 x 和 y 具有相同的變量,并將觀測按照集合來處理。
intersect(x, y)
返回既在 x 表,又在 y 表中的觀測。
union(x, y)
返回 x 表或 y 表中的唯一觀測。
setdiff(x, y)
返回在 x 表,但不在 y 表中的觀測。

df1 <- tribble(
~x, ~y,
1, 1,
2, 1
)
df2 <- tribble(
~x, ~y,
1, 1,
1, 2
)
df1
df2
intersect(df1,df2)
union(df1,df2)
setdiff(df1,df2)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容