R로 데이터 다루기

R에서 dplyr을 이용해 데이터를 추출, 가공하는 방법을 알아봅니다


수강중

16. 다른 데이터 합치기(join)

예제 데이터 생성

titlelast rank가 들어간 테이블을 만든다.

df1 <- data.frame(title = c('카페', '한식', '유아용품', '양식', '전자제품'), last_rank = c(1,2,3,4,5))

titlethis rank가 들어간 테이블을 만든다.

df2 <- data.frame(title = c('한식', '양식', '전자제품', '일식', '꽃'), this_rank = c(1,2,3,4,5))

key

어떤 것을 기준으로 데이터를 합칠 것인지를 나타낸다.

left join

left join은 왼쪽을 기준으로 join을 한다. left join 한 결과, 왼쪽에는 데이터는 그대로 복사해 붙여놓고 그 옆에는 올해의 데이터를 넣어준다. 일식은 올해의 데이터에 있지만 작년을 기준으로 left join 하기 때문에 left join 결과에는 없다.

title

join by title 이라고 나오는데, title을 키로 join 했다는 것이다. Left join을 할 때 by=title이라고 수동으로 적어줘도 되고, by를 적어주지 않으면 공통으로 변수이름이 동일한 것을 찾는다. 공통인 키가 하나면 하나로 join하고 여러 개가 있으면 여러 개를 키로 하여 join 한다.

library(dplyr)
left_join(df1, df2)
Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Joining, by = "title"
Warning message:
“Column `title` joining factors with different levels, coercing to character vector”
  title    last_rank this_rank
1 카페     1         NA       
2 한식     2          1       
3 유아용품 3         NA       
4 양식     4          2       
5 전자제품 5          3       

right join

right join은 오른쪽을 기준으로 한다. right join 한 결과, 올해의 데이터를 다 복사해놓고, 올해의 순위는 동일하고 작년 순위는 올해에 따라 달라진다. 한식은 올해는 1등인데 작년에는 2등이다. 일식과 꽃은 작년 순위건에 없기 때문에 NA를 채워넣는다.

right_join(df1, df2)
Joining, by = "title"
Warning message:
“Column `title` joining factors with different levels, coercing to character vector”
  title    last_rank this_rank
1 한식      2        1        
2 양식      4        2        
3 전자제품  5        3        
4 일식     NA        4        
5 꽃       NA        5        

left join과 right join 비교

데이터의 순서를 바꾸고 left join 을 하면 right join과 똑같다.

full join

full은 전체를 다 가지고 join을 하는 것이다. 왼쪽이나 오른쪽이든 하나의 기준이 아니라 전체를 가지고 한다. Full join은 모든 데이터, 서로 다른 모든 데이터에 있는 모든 행을 다 채운다고 생각하면 된다.

full_join(df1, df2)
Joining, by = "title"
Warning message:
“Column `title` joining factors with different levels, coercing to character vector”
  title    last_rank this_rank
1 카페      1        NA       
2 한식      2         1       
3 유아용품  3        NA       
4 양식      4         2       
5 전자제품  5         3       
6 일식     NA         4       
7 꽃       NA         5       

inner join

inner join은 공통만 뽑아 낸다. 예시에서는 한식, 양식, 전자제품만 공통으로 나왔다. 공통 업종만 뽑아서 새로운 데이터를 나타낸다. Left join을 해서 na 값을 제외한 형태가 inner join과 동일한 형태가 된다.

inner_join(df1, df2)
Joining, by = "title"
Warning message:
“Column `title` joining factors with different levels, coercing to character vector”
  title    last_rank this_rank
1 한식     2         1        
2 양식     4         2        
3 전자제품 5         3        

anti join

anti join은 거의 쓸일이 없을 것이다. 왼쪽에서 공통이 없는 부분, 예시에서는 카페, 유아용품은 올해의 데이터에 없다. 카페와 유아용품을 추출하는게 anti join 이다.

anti_join(df1, df2)
Joining, by = "title"
Warning message:
“Column `title` joining factors with different levels, coercing to character vector”
  title    last_rank
1 카페     1        
2 유아용품 3        

실제 데이터 불러오기 및 데이터 변수 처리

판매 데이터 불러오기

sales 데이터를 불러오고 colnames를 이용해 변수명 처리를 한다.

sales <- read.csv('sales.csv', stringsAsFactors = F,
                  fileEncoding = 'UTF-8')
colnames(sales) <- c('city', 'district', 'gender', 'sales.nm', 'sales.cd', 'ymd', 'sales.num')

업종 코드 데이터 불러오기

productcode 데이터를 불러오고 colnames를 이용해 변수명 처리를 한다.

products <- read.csv("productcode.csv", stringsAsFactors = F,
                     fileEncoding = "UTF-8")
colnames(products) <- c('products.cd', 'products.nm')

기후 데이터 불러오기

seoul_climate 데이터를 불러오고 colnames를 이용해 변수명 처리를 한다.

climates <- read.csv('seoul_climate.csv', stringsAsFactors = F,
                     fileEncoding = 'UTF-8')
colnames(climates) <- c('city', 'district', 'ym', 'lon', 'lat', 'rainfall')

데이터 확인

head를 이용해 데이터를 탐색적으로 확인한다.

sales %>% head
  city       district gender sales.nm sales.cd ymd      sales.num
1 서울특별시 강남구   남     스탑버스 12AA02   20140101 103      
2 서울특별시 강남구   여     키즈랜드 100101   20140101 309      
3 서울특별시 강남구   여     키즈랜드 100101   20140101 365      
4 서울특별시 강남구   남     스탑버스 12AA02   20140101 421      
5 서울특별시 강남구   여     스탑버스 12AA02   20140101 413      
6 서울특별시 강남구   남     키즈랜드 100101   20140101 428      

head를 이용해 데이터를 탐색적으로 확인한다.

products %>% head
  products.cd products.nm
1 1001        유아용품   
2 1002        한식       
3 1100        완구       
4 12AA        카페       

판매 데이터에 업종코드 변수 추가하기

판매 데이터에는 업종 코드가 없다. 그래서 점포명 코드에서 업종코드를 줘야한다. mutate를 사용하여 추가한다.

sales <- sales %>% mutate(products.cd = substr(sales.cd, 1, 4))

판매 데이터와 업종코드 데이터 left join

판매 데이터에 생성한 products.cd라는 변수를 키로 left join을 한다. 판매 데이터에는 업종코드가 있지만 업종명이 없다. 결과, products.cd 이외에 업종명(products.nm)이 붙어있는 것을 확인할 수 있다

sales <- left_join(sales, products, by = 'products.cd')

데이터 확인

head를 이용해 데이터를 탐색적으로 확인한다. 기후 데이터 같은 경우에 2014년 1월에 강남구는 하나 밖에 없다. 판매 데이터를 보면 2014년 1월에 강남구에 데이터가 엄청 많다. 그러면 새로운 강수량 변수는 다 동일한 값이 붙는다. 왜냐하면 2014년 강남구는 다 동일한 강수량을 갖고 있기 때문이다.

climates %>% head
  city       district ym     lon      lat     rainfall
1 서울특별시 강남구   201401 37.49666 127.063  NA     
2 서울특별시 강남구   201402 37.49666 127.063 187     
3 서울특별시 강남구   201403 37.49666 127.063 122     
4 서울특별시 강남구   201404 37.49666 127.063   0     
5 서울특별시 강남구   201405 37.49666 127.063  NA     
6 서울특별시 강남구   201406 37.49666 127.063 144     

head를 이용해 데이터를 탐색적으로 확인한다. 연월일이 있는데 연월만 있는 데이터가 없다. 연월이 있는 데이터를 판매 데이터에서 만들어서 다시 판매 데이터에서 집어넣자. 결과 확인 판매 데이터를 확인하면 연월 데이터가 붙은 것을 확인할 수 있다.

sales %>% head
  city       district gender sales.nm sales.cd ymd      sales.num products.cd
1 서울특별시 강남구   남     스탑버스 12AA02   20140101 103       12AA       
2 서울특별시 강남구   여     키즈랜드 100101   20140101 309       1001       
3 서울특별시 강남구   여     키즈랜드 100101   20140101 365       1001       
4 서울특별시 강남구   남     스탑버스 12AA02   20140101 421       12AA       
5 서울특별시 강남구   여     스탑버스 12AA02   20140101 413       12AA       
6 서울특별시 강남구   남     키즈랜드 100101   20140101 428       1001       
  products.nm
1 카페       
2 유아용품   
3 유아용품   
4 카페       
5 카페       
6 유아용품   

판매 데이터에 변수 추가

판매 데이터에는 연월일이 붙어있고 연월에 해당하는 변수가 없다. 따라서 mutate를 이용해 판매 데이터에 연월에 해당하는 변수를 만들어준다.

sales <- sales %>% mutate(ym = substr(ymd, 1, 6))

판매 데이터와 기후 데이터 left join

sales <- left_join(sales, climates)
Joining, by = c("city", "district", "ym")
Error: Can't join on 'ym' x 'ym' because of incompatible types (integer / character)
Traceback:

1. left_join(sales, climates)
2. left_join.data.frame(sales, climates)
3. as.data.frame(left_join(tbl_df(x), y, by = by, copy = copy, ...))
4. left_join(tbl_df(x), y, by = by, copy = copy, ...)
5. left_join.tbl_df(tbl_df(x), y, by = by, copy = copy, ...)
6. left_join_impl(x, y, by_x, by_y, aux_x, aux_y, na_matches, environment())

오류 발생

ym을 기준으로 join을 하는데 districtcity도 들어간다. 키가 3개가 들어간다.

str

str로 데이터 타입을 확인한다. 판매 데이터에서 ym은 캐릭터고 기후 데이터에서 ym은 정수로 되어 타입이 맞지 않아 join시 오류가 생긴것이다.

str(sales)
'data.frame':	262800 obs. of  10 variables:
 $ city       : chr  "서울특별시" "서울특별시" "서울특별시" "서울특별시" ...
 $ district   : chr  "강남구" "강남구" "강남구" "강남구" ...
 $ gender     : chr  "남" "여" "여" "남" ...
 $ sales.nm   : chr  "스탑버스" "키즈랜드" "키즈랜드" "스탑버스" ...
 $ sales.cd   : chr  "12AA02" "100101" "100101" "12AA02" ...
 $ ymd        : int  20140101 20140101 20140101 20140101 20140101 20140101 20140101 20140101 20140101 20140101 ...
 $ sales.num  : int  103 309 365 421 413 428 287 363 88 117 ...
 $ products.cd: chr  "12AA" "1001" "1001" "12AA" ...
 $ products.nm: chr  "카페" "유아용품" "유아용품" "카페" ...
 $ ym         : chr  "201401" "201401" "201401" "201401" ...
str(climates)
'data.frame':	600 obs. of  6 variables:
 $ city    : chr  "서울특별시" "서울특별시" "서울특별시" "서울특별시" ...
 $ district: chr  "강남구" "강남구" "강남구" "강남구" ...
 $ ym      : int  201401 201402 201403 201404 201405 201406 201407 201408 201409 201410 ...
 $ lon     : num  37.5 37.5 37.5 37.5 37.5 ...
 $ lat     : num  127 127 127 127 127 ...
 $ rainfall: int  NA 187 122 0 NA 144 0 0 0 NA ...

문제 해결하기

한 쪽을 숫자로 맞추거나 다른 쪽을 문자로 타입을 맞추면 된다. 기후 데이터가 더 적기 때문에 여기서는 문자로 맞췄다. as.character를 사용하여 ym을 캐릭터로 변환한다.

climates$ym <- as.character(climates$ym)

left join

join 결과 정상적으로 오류없이 잘 붙었다.

sales <- left_join(sales, climates)
Joining, by = c("city", "district", "ym")

NA만 자르고 다시 결과 확인

join 결과, 2014년 2월은 다 187로 강수량이 나와있다.

sales %>% filter(!is.na(rainfall)) %>% head
  city       district gender sales.nm sales.cd ymd      sales.num products.cd
1 서울특별시 강남구   남     베타문구 110002   20140205  17       1100       
2 서울특별시 강남구   여     카페베타 12AA01   20140205 347       12AA       
3 서울특별시 강남구   NA     원조한식 100201   20140205 203       1002       
4 서울특별시 강남구   여     원조백반 100202   20140205 107       1002       
5 서울특별시 강남구   NA     키즈랜드 100101   20140205 451       1001       
6 서울특별시 강남구   여     @        @        20140205 493       @          
  products.nm ym     lon      lat     rainfall
1 완구        201402 37.49666 127.063 187     
2 카페        201402 37.49666 127.063 187     
3 한식        201402 37.49666 127.063 187     
4 한식        201402 37.49666 127.063 187     
5 유아용품    201402 37.49666 127.063 187     
6 NA          201402 37.49666 127.063 187     

기후 데이터를 다시 찍어봐 비교 확인한다. 2014년 2월이 187인 것을 알 수 있다.