-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththird.Rmd
More file actions
533 lines (406 loc) · 17.1 KB
/
third.Rmd
File metadata and controls
533 lines (406 loc) · 17.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
---
title: "빅데이터 분석의 첫걸음 R코딩"
output: github_document
---
```{r setup, include = F}
knitr::opts_chunk$set(echo = T)
```
- Author: 장용식, 최진호
- Book: <https://book.naver.com/bookdb/book_detail.nhn?bid=16324211>
- coding은 example들을 제외하고는 programming으로 넘겼습니다.
---
## 7. 지도 활용하기
> 구글맵은 구글이 개발한 데스크탑 웹매핑 서비스로서, 구글맵 API를 통해 위성 사진, 지도 Street View, Google Traffic, 두 지점 간의 최적 경로 등의 다양한 서비스를 제공한다.
- 이외에도 데이터 시각화 전문회사, Stamen의 Stamen Maps나 네이버, 다음의 지도를 이용할 수도 있다.
> API (Application Programming Interface) 특정 기능 서비스를 위한 인터페이스
- Google map API는 1년 무료, Google Cloud API: <https://cloud.google.com/>
- Naver Cloud API: <https://www.ncloud.com>
### 구글맵 출력
- Google Cloud API가 1년 무료라서 실행은 시키지 않는다.
```{r eval = F}
## install.packages("ggmap")
library(ggmap)
register_google(key = "AI...") ## Google Map API
```
```{r eval = F}
map <- get_googlemap(center = c(126.975684, 37.572752)) # 세종문화회관
ggmap(map)
```
> getcode(enc2utf8(location)): 한글 지역명인 경우 utf8 형식으로 바꿔야 한다. (반환: 경도, 위도)
- 세종문화회관을 중심으로 지도가 뜬다.
> get_googlemap(center, maptype = "terrain", zoom = 10, size = c(X, Y), marker)<br />- maptype: "terrain"(default, 지형정보 기반 지도), "satellite"(위성 지도), "roadmap"(도로명 표시), "hybrid"(위성과 도로명)<br />- marker: 현재 지점을 나타낼 때 나오는 그것.
> ggmap(extent)<br />- extent: "panel"(default, 축 정보 다 포함), "normal", "device"(여백 없음, 축 정보 없음)
#### 단양팔경을 지도 위에
| 지명 | 주소 |
| :-----------: | --------------------------------------- |
| 도담삼봉/석문 | 충청북도 단양군 매포읍 삼봉로 644-33 |
| 구담봉/옥순봉 | 충청북도 단양군 단성면 월악로 3827 |
| 사인암 | 충청북도 단양군 대강면 사인암2길 42 |
| 하선암 | 충청북도 단양군 단성면 선암계곡로 1337 |
| 중선암 | 충청북도 단양군 단성면 선암계곡로 868-2 |
| 상선암 | 충청북도 단양군 단성면 선암계곡로 790 |
```{r eval = F}
library(ggplot2)
register_google(key = "AI...") ## Google Map API
names <- c("도담삼봉/석문", "구담/옥순봉", "사인암", "하선암", "중선암", "상선암")
addr <- c("충청북도 단양군 매포읍 삼봉로 644-33", "충청북도 단양군 단성면 월악로 3827",
"충청북도 단양군 대강면 사인암2길 42", "충청북도 단양군 단성면 선암계곡로 1337",
"충청북도 단양군 단성면 선암계곡로 868-2", "충청북도 단양군 단성면 선암계곡로 790")
gc <- geocode(enc2utf8(addr))
df <- data.frame(name = names, lon = gc$lon, lat = gc$lat)
cen <- c((max(df$lon) + min(df$lon))/2, (max(df$lat) + min(df$lat))/2)
map <- get_googlemap(center = cen, maptype = "roadmap", zoom = 12, marker = gc)
ggmap(map) +
geom_text(data = df, aes(x = lon, y = lat), size = 5, label = df$name)
```
#### 지진 발생 지역 분포
- 국내 지진 정보는 기상청 날씨누리 -> 지진, 화산 -> 발생기간 입력을 통해 엑셀 파일로 얻을 수 있지만, Google Map API를 이유로 실행 안 하고 넘어갑니다.
```{r eval = F}
## install.packages("openxlsx")
library(ggmap)
library(ggplot2)
library(openxlsx)
register_google(key = "AI...") ## Google Map API
df <- read.xlsx(file.choose(), sheet = 1, starRow = 4)
```
> file.choose(): 파일 시스템을 열어 직접 파일을 선택한다.
```{r eval = F}
head(df)
tail(df)
```
```{r eval = F}
df[, 5] <- gsub(" N", "", df[, 5])
df[, 6] <- gsub(" E", "", df[, 6])
df2 <- data.frame(lon = df[, 6], lat = df[, 5], mag = df[, 3])
str(df2)
```
어째 숫자가 string으로 읽혔는지 다 factor가 됐더라고
```{r eval = F}
df2[, 1] = as.numeric(as.character(df2[, 1]))
df2[, 2] = as.numeric(as.character(df2[, 2]))
df2[, 3] = as.numeric(as.character(df2[, 3]))
str(df2)
```
```{r eval = F}
## attach(df2)
cen <- c((max(df2$lon) + min(df2$lon))/2, (max(df2$lat) + min(df2$lat))/2)
```
```{r eval = F}
map <- get_googlemap(center = cen, zoom = 6)
ggmap(map) +
geom_point(data = df2, aes(x = lon, y = lat), color = "red", size = df2$mag, alpha = 0.5)
```
### 연습용 project
(한국환경공단) 미세먼지 or 초미세먼지 분포
```{r eval = F}
## install.packages("openxlsx")
library(ggmap)
library(ggplot2)
library(openxlsx)
register_google(key = "AI...") ## Google Map API
df <- read.xlsx(file.choose(), sheet = 1)
```
```{r eval = F}
df2 <- data.frame(lon = df[, 6], lat = df[, 5], mag = df[, 3])
cen <- c((max(df2$lon) + min(df2$lon))/2, (max(df2$lat) + min(df2$lat))/2)
```
```{r eval = F}
map <- get_googlemap(center = cen, zoom = 6)
ggmap(map) +
geom_point(data = df2, aes(x = lon, y = lat), color = c("lightblue", "orange"),
size = c(df2$PM10, df2$PM25), alpha = 0.5)
```
- 흠 이렇게 하면 한 그림으로 나오겠지?
---
## 8. Web scraping
| 용어 정리 | description |
| :-------------------------: | ------------------------------------------------------------------------ |
| 웹크롤링 (Web crawling) | 주로 포털 등에서 자동으로 웹 사이트의 링크 정보를 수집하여 저장하는 기술 |
| 웹 스크래핑 (Web scraping) | 웹 사이트로부터 웹문서를 다운로드 받아 필요한 정보를 추출하는 기술 |
| 텍스트 마이닝 (Text mining) | 비정형 텍스트 데이터에서 정보를 찾아내는 기술 |
- DOM (Document Object Model)은 트리 구조로 이루어져 있으며, 어떤 데이터를 스크래핑할 건지 확실하게 정해야 한다. 그렇지 않으면 엄청난 양의 필요없는 데이터가 딸려올 수 있어 전처리 과정에서 곤욕을 치를 수 있다.
```{r message = F, warning = F}
## install.packages("rvest")
library(rvest)
```
#### 1. 웹문서 전체 인식
```{r}
url <- "https://www.data.go.kr/tcs/dss/selectDataSetList.do"
html <- read_html(url)
html
```
#### 2. 웹문서에서 가져오고자 하는 데이터 추출
- [@id="apiDataList"]/div[2]/ul/li[2]/dl/dt/a/span[2]
```{r}
title <- html_nodes(html, "#apiDataList") %>%
html_nodes("ul") %>% html_nodes("a") %>%
html_nodes("span.title") %>% html_text()
title
```
```{r}
desc <- html_nodes(html, "#contents") %>%
html_nodes("#apiDataList") %>%
html_nodes(".result-list") %>%
html_nodes(".ellipsis") %>% html_text()
desc
```
#### 3. 전처리: 제어 문자(escape ch, \r, \n) 없애기 (데이터 정제)
```{r}
title <- gsub("\r\n\t", "", title) %>%
gsub(pattern = "\t", replacement = "") %>%
gsub(pattern = '_', replacement = ' ')
## title <- trimws(title, "both")
title
```
```{r}
## desc <- gsub("\r\n", "", desc)
desc <- trimws(desc, "both")
desc
```
```{r}
api <- data.frame(title, desc)
api
```
#### 4. 10페이지를 웹스크래핑
- 4.20을 기준으로 페이지를 새롭게 구성하여 (xsl를 이용한 것으로 보인다) page에 대한 parsing이 상당히 어려워졌다. xsl parsing을 구조체만 알아내면 쉽게 할 수 있는데 지금으로썬 뭐인지 모르겠다.
- 만약에 xsl로 만든 게 맞고 js에 반응해 다음으로 움직이는데 이게 js로 배열화시켜놓은 거라면, js가 관여하는 (for-each 안에) 데이터 태그 이름을 알아내거나 (for-each) 위치를 저장해서 계속 부르면 다른 값을 주지 않을까?
- 일단 ul을 기준으로 계속 호출해봤는데 같은 값밖에 가져오지 않는다.
```{r eval = F}
url.api <- "https://www.data.go.kr/tcs/dss/selectDataSetList.do"
## base url은 일단 위와 같은 곳으로 적었다.
titles <- NULL; descs <- NULL
for (page in 1:10) {
url <- paste(url.api, page, sep = "")
html <- read_html(url.api)
title <- html_nodes(html, "#apiDataList") %>%
html_nodes("ul") %>% html_nodes("a") %>%
html_nodes("span.title") %>% html_text()
desc <- html_nodes(html, "#contents") %>%
html_nodes("#apiDataList") %>%
html_nodes(".result-list") %>%
html_nodes(".ellipsis") %>% html_text()
title <- gsub("\r\n\t", "", title) %>%
gsub(pattern = "\t", replacement = "") %>%
gsub(pattern = '_', replacement = ' ')
desc <- trimws(desc, "both")
titles <- c(titles, title)
descs <- c(descs, desc)
}
titles
```
```{r eval = F}
api <- data.frame(title = titles, desc = descs)
head(api)
```
### 연습용 project
- **아니 어떻게 XPath를 따와서 만든 건데, 안 될 수가 있지?**
- 유튜브 "알라딘 OST" 페이지 가져오기
- URL DOM 이해하기 및 parsing 객체 확인
- 페이지 내 영상 제목만 가져와 구성하기
```{r eval = F}
library(rvest)
url = "https://www.youtube.com/results?search_query=%EC%95%8C%EB%9D%BC%EB%94%98+OST"
html = read_html(url)
```
- [@id="video-title"]/yt-formatted-string
- document.querySelector("#video-title > yt-formatted-string")
```{r eval = F}
title = html_nodes(html, "#video-title") %>%
html_nodes("yt-formatted-string") %>%
html_text()
title
```
---
## 9. 공공데이터 활용
- OPEN API를 받아야 해서 실행시키지 않았다.
```{r eval = F}
library(XML)
library(ggplot2)
```
```{r eval = F}
api <- "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst"
api_key <- "DE..." ## 공공 데이터 포털에서 승인 받은 키
numOfRows <- 10
pageNo <- 1
itemCode <- "PM10" ## 아이템 코드: 미세먼지
dataGubun <- "HOUR" ## (한) 시간 단위 미세먼지 데이터
searchCondition <- "MONTH" ## 요청 데이터 기간: 한달
```
- 당연히 api에 할당한 주소 또한 달라졌을 것이다.
```{r eval = F}
url <- paste(api, "?serviceKey=", api_key, "&numOfRows=", numOfRows, "&pageNo=", pageNo,
"&itemCode=", itemCode, "&dataGubun=", dataGubun, "&searchCondition=", searchCondition)
```
```{r eval = F}
xmlFile <- xmlParse(url)
xmlRoot(xmlFile)
```
- 혹시나 해서 html 읽기를 xmlParse 및 xmlRoot( )로 해봤는데 안 먹는다.
```{r eval = F}
df <- xmlToDataFrame(getNodeSet(xmlFile, "//items/item"))
df
```
- XSL file parsing 때의 file system을 기반으로 한 주소를 사용한다.
- 전체에서 모든 items element 내 item을 (list 형태로) 불러온다.
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul)) +
geom_bar(stat = "identity", fill = "green")
```
> df 내 dataTime, seoul을 축으로 설정하고 y 데이터에 맞춰 초록색의 막대를 그린다.
#### 초록색으로 통일
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul)) +
geom_bar(stat = "identity", fill = "green") +
theme(axis.text.x = element_text(angle = 90)) +
labs(title = "시간대별 서울지역의 미세먼지 농도 변화", x = "측정일시", y = "농도")
```
#### rainbow( )을 이용한 여러 색으로 칠하기
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul, fill = dataTime)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90)) +
labs(title = "시간대별 서울지역의 미세먼지 농도 변화", x = "측정일시", y = "농도") +
scale_fill_manual(values = rainbow(10))
```
#### 쓸모없는 범례를 지우자
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul, fill = dataTime)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90), legend.position = "none") +
labs(title = "시간대별 서울지역의 미세먼지 농도 변화", x = "측정일시", y = "농도") +
scale_fill_manual(values = rainbow(10))
```
#### 가로로 출력해보자
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul, fill = dataTime)) +
geom_bar(stat = "identity") +
theme(legend.position = "none") +
labs(title = "시간대별 서울지역의 미세먼지 농도 변화", x = "측정일시", y = "농도") +
scale_fill_manual(values = rainbow(10)) +
coord_flip()
```
### 지도: 지역별 미세먼지 농도 비교
```{r eval = F}
library(XML)
library(ggmap)
```
#### 1. 데이터 불러오기
```{r eval = F}
api <- "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst"
api_key <- "DE..." ## 공공 데이터 포털에서 승인 받은 키
numOfRows <- 10
pageNo <- 1
itemCode <- "PM10" ## 아이템 코드: 미세먼지
dataGubun <- "HOUR" ## (한) 시간 단위 미세먼지 데이터
searchCondition <- "MONTH" ## 요청 데이터 기간: 한달
url <- paste(api, "?serviceKey=", api_key, "&numOfRows=", numOfRows, "&pageNo=", pageNo,
"&itemCode=", itemCode, "&dataGubun=", dataGubun, "&searchCondition=", searchCondition)
xmlFile <- xmlParse(url)
xmlRoot(xmlFile)
df <- xmlToDataFrame(getNodeSet(xmlFile, "//items/item"))
df
```
- 이전에 서울 내 미세먼지 데이터를 가져올 때와 완전히 동일하다.
```{r eval = F}
pm <- df[1, 4:20]
```
#### 2. 구글 지도 가져오기
```{r eval = F}
register_google(key = "AI...") ## Google Map API
cities <- c("서울시", "부산시", "대구시", "인천시", "광주시", "대전시", "울산시", "경기도", "강원도",
"충청북도", "충청남도", "전라북도", "전라남도", "경상북도", "경상남도", "제주시", "세종시")
## 일부 이름이 주소 인식이 안 되어 지역명을 모두 한글로 변경
gc <- geocode(enc2utf8(cities)) ## 지역별 좌표(경도, 위도)
gc
```
```{r eval = F}
df2 <- data.frame(지역명 = cities, 미세먼지 = t(pm), 경도 = gc$lon, 위도 = gc$lat, stringsAsFactors = F)
## names(df2)[2] <- "미세먼지"
df2
```
```{r eval = F}
str(df2)
```
```{r eval = F}
df2[, 2] <- as.numeric(df2[, 2])
```
#### 3. 지도에 그려보자
```{r eval = F}
cen <- as.numeric(geocode(enc2utf8("전라북도")))
map <- get_googlemap(center = cen, zoom = 7)
ggmap(map) +
geom_point(data = df2, aes(x = 경도, y = 위도), color = rainbow(length(df2$미세먼지)),
size = df2$미세먼지 * 0.3, alpha = 0.5)
```
> geom_point(aes: 원의 위치, alpha: 투명도)
### 연습용 project
1. 특정 지역(서울)의 시간대별 미세먼지 농도의 변화
- 서울 지역의 초미세먼지 데이터 불러오기
- 막대 그래프 그리기
```{r eval = F}
api <- "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst"
api_key <- "DE..." ## 공공 데이터 포털에서 승인 받은 키
numOfRows <- 10
pageNo <- 1
itemCode <- "PM25" ## 아이템 코드: 초미세먼지
dataGubun <- "HOUR"
searchCondition <- "MONTH"
```
```{r eval = F}
url <- paste(api, "?serviceKey=", api_key, "&numOfRows=", numOfRows, "&pageNo=", pageNo,
"&itemCode=", itemCode, "&dataGubun=", dataGubun, "&searchCondition=", searchCondition)
```
```{r eval = F}
xmlFile <- xmlParse(url)
xmlRoot(xmlFile)
df <- xmlToDataFrame(getNodeSet(xmlFile, "//items/item"))
df
```
```{r eval = F}
ggplot(data = df, aes(x = dataTime, y = seoul, fill = dataTime)) +
geom_bar(stat = "identity") +
theme(legend.position = "none") +
labs(title = "시간대별 서울지역의 초미세먼지 농도 변화", x = "측정일시", y = "농도") +
scale_fill_manual(values = rainbow(10))
```
2. 특정 시간대의 지역별 초미세먼지 농도 비교
```{r eval = F}
api <- "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst"
api_key <- "DE..." ## 공공 데이터 포털에서 승인 받은 키
numOfRows <- 10
pageNo <- 1
itemCode <- "PM25" ## 아이템 코드: 초미세먼지
dataGubun <- "HOUR"
searchCondition <- "MONTH"
url <- paste(api, "?serviceKey=", api_key, "&numOfRows=", numOfRows, "&pageNo=", pageNo,
"&itemCode=", itemCode, "&dataGubun=", dataGubun, "&searchCondition=", searchCondition)
xmlFile <- xmlParse(url)
xmlRoot(xmlFile)
df <- xmlToDataFrame(getNodeSet(xmlFile, "//items/item"))
pm <- df[1, 4:20]
```
```{r eval = F}
register_google(key = "AI...") ## 구글 지도 API
cities <- c("서울시", "부산시", "대구시", "인천시", "광주시", "대전시", "울산시", "경기도", "강원도",
"충청북도", "충청남도", "전라북도", "전라남도", "경상북도", "경상남도", "제주시", "세종시")
## 일부 이름이 주소 인식이 안 되어 지역명을 모두 한글로 변경
gc <- geocode(enc2utf8(cities)) ## 지역별 좌표 검색(경도, 위도)
gc
```
```{r eval = F}
df2 <- data.frame(지역명 = cities, 초미세먼지 = t(pm), 경도 = gc$lon, 위도 = gc$lat,
stringsAsFactors = F)
## names(df2)[2] <- "초미세먼지"
df2
str(df2)
```
```{r eval = F}
df2[, 2] <- as.numeric(df2[, 2])
```
```{r eval = F}
cen <- as.numeric(geocode(enc2utf8("전라북도")))
map <- get_googlemap(center = cen, zoom = 7)
ggmap(map) +
geom_point(data = df2, aes(x = 경도, y = 위도), color = rainbow(length(df2$초미세먼지)),
size = df2$초미세먼지 * 0.3, alpha = 0.5)
```