R - JSON Files 웹사이트에서 JSON 파일을 다운로드 받아 json_data.json
파일로 저장시킨 후에 이를 데이터프레임으로 변환시킨다.
## json_data.json 데이터셋
{
"ID":["1","2","3","4","5","6","7","8" ],
"Name":["Rick","Dan","Michelle","Ryan","Gary","Nina","Simon","Guru" ],
"Salary":["623.3","515.2","611","729","843.25","578","632.8","722.5" ],
"StartDate":[ "1/1/2012","9/23/2013","11/15/2014","5/11/2014","3/27/2015","5/21/2013",
"7/30/2013","6/17/2014"],
"Dept":[ "IT","Operations","IT","HR","Finance","IT","Operations","Finance"]
}
상기 데이터는 5개 노드에 8개 값이 묶여진 전형적인 데이터프레임 자료구조를 갖추고 있다. listviewer
팩키지를 통해 파일 크기가 그다지 크지 않아 시각화를 해본다.
“r class(json_list)” 명령어를 통해 json_list
객체가 list 리스트임을 알 수 있다. 데이터프레임은 동일한 길이를 갖는 리스트의 특별한 형태임을 알고 있기 때문에 데이터프레임으로 변환시키는 것이 그다지 어렵지 않다. ``
# A tibble: 8 x 5
ID Name Salary StartDate Dept
<chr> <chr> <chr> <chr> <chr>
1 1 Rick 623.3 1/1/2012 IT
2 2 Dan 515.2 9/23/2013 Operations
3 3 Michelle 611 11/15/2014 IT
4 4 Ryan 729 5/11/2014 HR
5 5 Gary 843.25 3/27/2015 Finance
6 6 Nina 578 5/21/2013 IT
7 7 Simon 632.8 7/30/2013 Operations
8 8 Guru 722.5 6/17/2014 Finance
데이터프레임을 JSON으로 바꿀 수 있는 방법도 살펴보자. 이유는 나중에 데이터프레임을 RESTful API에 JSON으로 변환시켜 보내는 경우가 흔히 발생하기 때문이다. jsonlite::toJSON()
함수로 JSON 형식으로 변환시킨다. jsonlight::prettify()
함수로 JSON 파일을 눈에 보기 편한 형태로 변환시킨다.
json_tbl %>%
select(Name, Salary) %>%
filter(Salary > 700) %>%
jsonlite::toJSON() %>%
prettify(., indent = 4)
[
{
"Name": "Ryan",
"Salary": "729"
},
{
"Name": "Gary",
"Salary": "843.25"
},
{
"Name": "Guru",
"Salary": "722.5"
}
]
dplyr
팩키지에 starwars
데이터프레임이 포함되어 있다. JSON 데이터로 저장시킨 후에 이를 불러와서 JSON 파일로 사용한다. 다시 한번 강조하지만 R에서 불러들인 JSON 파일은 list 자료구조를 갖게 된다. 데이터프레임과 동일하지만, films
칼럼에 영화가 최소 하나에서 다수 출연한 영화명이 정리되어 있다.
starwars %>%
select(name, height, sex, films) %>%
jsonlite::write_json("data/starwars.json")
starwars_list <- jsonlite::fromJSON("data/starwars.json")
listviewer::jsonedit(starwars_list)
먼저 JSON 데이터를 본격적으로 들어가기 전에 스타워즈 영화에 가장 많이 출연한 배우가 누구인지 찾아보고 그 배우가 출연한 영화를 출력해보자. 리스트를 데이터프레임으로 변환시킨 후에 purrr
팩키지를 사용해서 films
칼럼에 포함된 값의 길이를 length()
함수로 갯수를 센 다음 내림차순으로 정리하고 “R2-D2”가 최다 출연 배우임을 확인한 후에 “R2-D2”가 출연한 영화를 쭉 출력한다.
starwars_list %>%
as_tibble() %>%
mutate(num_appearance = map_int(films, length)) %>%
arrange(desc(num_appearance))
# A tibble: 87 x 5
name height sex films num_appearance
<chr> <int> <chr> <list> <int>
1 R2-D2 96 none <chr [7]> 7
2 C-3PO 167 none <chr [6]> 6
3 Obi-Wan Kenobi 182 male <chr [6]> 6
4 Luke Skywalker 172 male <chr [5]> 5
5 Leia Organa 150 female <chr [5]> 5
6 Chewbacca 228 male <chr [5]> 5
7 Yoda 66 male <chr [5]> 5
8 Palpatine 170 male <chr [5]> 5
9 Darth Vader 202 male <chr [4]> 4
10 Han Solo 180 male <chr [4]> 4
# … with 77 more rows
[1] "The Empire Strikes Back" "Attack of the Clones"
[3] "The Phantom Menace" "Revenge of the Sith"
[5] "Return of the Jedi" "A New Hope"
[7] "The Force Awakens"
먼저, JSON 파일으로 리스트(starwars_json
)로 변환시킨 후에 unnest()
동사로 깔끔한 데이터 형태로 변환시킨다. 이런 경우 as_tibble()
→ unnest()
두단계를 거치게 된다.
starwars_json <- jsonlite::fromJSON("data/starwars.json")
starwars_json %>%
as_tibble() %>%
unnest(films)
# A tibble: 173 x 4
name height sex films
<chr> <int> <chr> <chr>
1 Luke Skywalker 172 male The Empire Strikes Back
2 Luke Skywalker 172 male Revenge of the Sith
3 Luke Skywalker 172 male Return of the Jedi
4 Luke Skywalker 172 male A New Hope
5 Luke Skywalker 172 male The Force Awakens
6 C-3PO 167 none The Empire Strikes Back
7 C-3PO 167 none Attack of the Clones
8 C-3PO 167 none The Phantom Menace
9 C-3PO 167 none Revenge of the Sith
10 C-3PO 167 none Return of the Jedi
# … with 163 more rows
상기 과정이 번잡하게 느껴진다면 바로 unnest_longer()
혹은 unnest_wider()
동사를 사용해서 깔끔한 데이터로 변환시킨다.
# A tibble: 173 x 4
name height sex films
<chr> <int> <chr> <chr>
1 Luke Skywalker 172 male The Empire Strikes Back
2 Luke Skywalker 172 male Revenge of the Sith
3 Luke Skywalker 172 male Return of the Jedi
4 Luke Skywalker 172 male A New Hope
5 Luke Skywalker 172 male The Force Awakens
6 C-3PO 167 none The Empire Strikes Back
7 C-3PO 167 none Attack of the Clones
8 C-3PO 167 none The Phantom Menace
9 C-3PO 167 none Revenge of the Sith
10 C-3PO 167 none Return of the Jedi
# … with 163 more rows
Rectangling - Geocoding with google에 나온 예제로 주소를 넣어 GeoCoding을 해야 되는데 이를 위해서 Google Maps API를 사용해서 특정 주소 (“서울시 대한민국”)를 넣어 위경도를 뽑아내보자.
readRenviron("~/.Renviron")
geocode <- function(address, api_key = Sys.getenv("GOOGLE_MAPS_API_KEY")) {
url <- "https://maps.googleapis.com/maps/api/geocode/json"
url <- paste0(url, "?address=", URLencode(address), "&key=", api_key)
jsonlite::read_json(url)
}
seoul_list <- geocode("서울시 대한민국")
listviewer::jsonedit(seoul_list)
전통적인 방식으로 한땀 한땀 정성스럽게 위도와 경도를 추출해보자.
location_df <- tibble(
location = "서울시",
json = seoul_list
) %>%
slice(1)
location_df %>%
unnest_longer(json) %>%
unnest_longer(json) %>%
filter(json_id == "geometry") %>%
unnest(json) %>%
unnest_wider(json) %>%
filter(!is.na(lat)) %>%
select(location, lat, lng)
# A tibble: 1 x 3
location lat lng
<chr> <dbl> <dbl>
1 서울시 37.6 127.
Rectangling - Geocoding with google 웹사이트에 나온 사례를 그대로 구현해보자.
city <- c("Houston", "LA", "New York", "Chicago", "Springfield")
city_geo <- purrr::map(city, geocode)
loc_tbl <- tibble(city = city, json = city_geo)
loc_tbl
# A tibble: 5 x 2
city json
<chr> <list>
1 Houston <named list [2]>
2 LA <named list [2]>
3 New York <named list [2]>
4 Chicago <named list [2]>
5 Springfield <named list [2]>
loc_tbl %>%
unnest_wider(json) %>%
unnest_longer(results) %>%
unnest_wider(results) %>%
unnest_wider(geometry) %>%
unnest_wider(location) %>%
select(city, formatted_address, lat, lng)
# A tibble: 14 x 4
city formatted_address lat lng
<chr> <chr> <dbl> <dbl>
1 Houston Houston, TX, USA 29.8 -95.4
2 LA Los Angeles, CA, USA 34.1 -118.
3 New York New York, NY, USA 40.7 -74.0
4 Chicago Chicago, IL, USA 41.9 -87.6
5 Springfi… Springfield, MO, USA 37.2 -93.3
6 Springfi… 12 Burns Ave, Springfield, MN 56087, USA 44.2 -95.0
7 Springfi… Springfield, IL, USA 39.8 -89.7
8 Springfi… Springfield Plain, Oklahoma 74370, USA 36.7 -94.6
9 Springfi… 840 N Boonville Ave, Springfield, MO 65802, USA 37.2 -93.3
10 Springfi… Springfield-Branson National Airport (SGF), 2300 N Ai… 37.2 -93.4
11 Springfi… 955 E Trafficway St, Springfield, MO 65802, USA 37.2 -93.3
12 Springfi… 840 N Boonville Ave, Springfield, MO 65802, USA 37.2 -93.3
13 Springfi… Springfield, OH, USA 39.9 -83.8
14 Springfi… 305 S Market Ave, Springfield, MO 65806, USA 37.2 -93.3
unnest_wider()
, unnest_wider()
을 조합해서 필요한 위경도 정보를 추출하는 것이 그다지 효율적이지 않을 수도 있어 unnest_auto()
명령어를 사용해서 필요하는 위경도 정보를 추출한다.
loc_tbl %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location) %>%
select(city, formatted_address, lat, lng)
# A tibble: 14 x 4
city formatted_address lat lng
<chr> <chr> <dbl> <dbl>
1 Houston Houston, TX, USA 29.8 -95.4
2 LA Los Angeles, CA, USA 34.1 -118.
3 New York New York, NY, USA 40.7 -74.0
4 Chicago Chicago, IL, USA 41.9 -87.6
5 Springfi… Springfield, MO, USA 37.2 -93.3
6 Springfi… 12 Burns Ave, Springfield, MN 56087, USA 44.2 -95.0
7 Springfi… Springfield, IL, USA 39.8 -89.7
8 Springfi… Springfield Plain, Oklahoma 74370, USA 36.7 -94.6
9 Springfi… 840 N Boonville Ave, Springfield, MO 65802, USA 37.2 -93.3
10 Springfi… Springfield-Branson National Airport (SGF), 2300 N Ai… 37.2 -93.4
11 Springfi… 955 E Trafficway St, Springfield, MO 65802, USA 37.2 -93.3
12 Springfi… 840 N Boonville Ave, Springfield, MO 65802, USA 37.2 -93.3
13 Springfi… Springfield, OH, USA 39.9 -83.8
14 Springfi… 305 S Market Ave, Springfield, MO 65806, USA 37.2 -93.3
동일한 방식으로 대한민국 대표(?) 도시를 5개 추출하여 위경도를 포함한 데이터프레임으로 제작해보자.
korea_city <- c("서울시", "대전시", "속초시", "부산", "성남시")
korea_city_geo <- purrr::map(korea_city, geocode)
korea_loc_tbl <- tibble(city = korea_city, json = korea_city_geo)
korea_loc_tbl
# A tibble: 5 x 2
city json
<chr> <list>
1 서울시 <named list [2]>
2 대전시 <named list [2]>
3 속초시 <named list [2]>
4 부산 <named list [2]>
5 성남시 <named list [2]>
unnest_auto()
명령어를 사용하여 원하는 결과를 추출해본다.
korea_loc_tbl %>%
unnest_auto(json) %>%
unnest_auto(results) %>%
unnest_auto(results) %>%
unnest_auto(geometry) %>%
unnest_auto(location) %>%
select(city, formatted_address, lat, lng)
# A tibble: 5 x 4
city formatted_address lat lng
<chr> <chr> <dbl> <dbl>
1 서울시 Seoul, South Korea 37.6 127.
2 대전시 Daejeon, South Korea 36.4 127.
3 속초시 Sokcho-si, Gangwon-do, South Korea 38.2 129.
4 부산 Busan, South Korea 35.2 129.
5 성남시 Seongnam-si, Gyeonggi-do, South Korea 37.4 127.
hoist()
필요한 것만 쏙!!!hoist()
함수를 사용해서 위도와 경도만 쏙 빼낼 수 있다.
korea_loc_tbl %>%
hoist(json,
lat = list("results", 1, "geometry", "location", "lat"),
lng = list("results", 1, "geometry", "location", "lng")
)
# A tibble: 5 x 4
city lat lng json
<chr> <dbl> <dbl> <list>
1 서울시 37.6 127. <named list [2]>
2 대전시 36.4 127. <named list [2]>
3 속초시 38.2 129. <named list [2]>
4 부산 35.2 129. <named list [2]>
5 성남시 37.4 127. <named list [2]>
unnest_*()
와 hoist()
두 함수를 조합하여 동일한 결과를 얻어낼 수도 있다.
korea_loc_tbl %>%
unnest_wider(json) %>%
hoist(results, first_result = 1) %>%
unnest_wider(first_result) %>%
unnest_wider(geometry) %>%
unnest_wider(location)
# A tibble: 5 x 11
city address_compone… formatted_addre… bounds lat lng location_type
<chr> <list> <chr> <list> <dbl> <dbl> <chr>
1 서울시… <list [2]> Seoul, South Ko… <name… 37.6 127. APPROXIMATE
2 대전시… <list [3]> Daejeon, South … <name… 36.4 127. APPROXIMATE
3 속초시… <list [3]> Sokcho-si, Gang… <name… 38.2 129. APPROXIMATE
4 부산 <list [3]> Busan, South Ko… <name… 35.2 129. APPROXIMATE
5 성남시… <list [3]> Seongnam-si, Gy… <name… 37.4 127. APPROXIMATE
# … with 4 more variables: viewport <list>, place_id <chr>, types <list>,
# status <chr>
데이터 과학자 이광춘 저작
kwangchun.lee.7@gmail.com