R) 전처리 - 엑셀-01

R) 전처리 - 엑셀-01

엑셀에서 R로 넘어가는 일은 언제나 어렵다. 전처리 엑셀 시리즈에서는 공공데이터등 각종 엑셀파일을 깔끔하게 R로 정리하는 사례를 소개하고자 한다. 그 첫 번째는 농작물별 생산지 정보가 되겠다.

같이 실습을 하고자 한다면 아래 파일을 다운로드 받아서 작업폴더에 위치시키자.

예제 파일: locations.xlsx

예제 파일을 열어보면 다음과 같다.

locations.xlsx 파일

셀 병합은 엑셀의 대표적인 기능이지만, 다른 프로그램 또는 언어가 이를 읽어올 때는 꽤 귀찮아진다. 파일 입출력 1편에서 소개했던 readxl 패키지로 엑셀 파일을 읽어오자.

파일 읽어오기

1
2
3
4
library("readxl")
df = read_excel("locations.xlsx")
## New names:
## * `` -> ...2

변수명에 셀 병합이 되어있었기 때문에 read_excel() 함수가 임의의 변수명 “…2”를 할당하였다고 메세지를 출력한다. 데이터프레임 객체로 핸들링하기 위해 as.data.frame() 함수로 객체 속성 변환 후 객체를 살펴보도록 하자.

1
2
3
4
5
6
7
8
9
df = as.data.frame(df)
head(df)
## 품 목 ...2 조 사 지 역
## 1 식량작물류 쌀 김제, 당진, 평택, 상주, 김해
## 2 <NA> 콩 제천, 안동, 무안
## 3 <NA> 봄 감 자 보성, 고령, 구미, 당진, 서산
## 4 <NA> 고 랭 지 감 자 평창, 정선, 횡성
## 5 <NA> 가 을 감 자 제주, 서귀포
## 6 <NA> 고 구 마 해남, 논산, 여주

결측치와 띄어쓰기 등 고쳐야할 부분이 많지만, 색인에 기본이 되는 변수명 부터 최우선으로 고쳐보자. 향후 인덱싱에 문제가 생기지 않도록 다음과 같이 영어로 변환하는 것을 권장한다.

1
2
3
4
5
6
7
8
9
colnames(df) = c("category", "name", "loc_prod")
head(df)
## category name loc_prod
## 1 식량작물류 쌀 김제, 당진, 평택, 상주, 김해
## 2 <NA> 콩 제천, 안동, 무안
## 3 <NA> 봄 감 자 보성, 고령, 구미, 당진, 서산
## 4 <NA> 고 랭 지 감 자 평창, 정선, 횡성
## 5 <NA> 가 을 감 자 제주, 서귀포
## 6 <NA> 고 구 마 해남, 논산, 여주

띄어쓰기 처리

띄어쓰기를 고쳐보자. 먼저 작물명(name) 상태가 어떠한지 알아보도록 하겠다.

1
2
3
4
5
6
7
8
9
10
11
unique(df$name)
## [1] "쌀" "콩" "봄 감 자" "고 랭 지 감 자"
## [5] "가 을 감 자" "고 구 마" "봄 배 추" "고 랭 지 배 추"
## [9] "가 을 배 추" "월 동 배 추" "봄 무" "고 랭 지 무"
## [13] "가 을 무" "월 동 무" "수 박" "참 외"
## [17] "오 이" "방 울 토 마 토" "딸 기" "건 고 추"
## [21] "마 늘(난지형)" "양 파" "사 과" "배"
## [25] "감 귤" "단 감" "포 도" "복 숭 아"
## [29] "장 미" "국 화" "쇠 고 기" "돼 지 고 기"
## [33] "닭 고 기" "계 란" "오 렌 지" "바 나 나"
## [37] "키 위" "참 깨"

도대체 왜 한 칸씩 띄어쓰기를 했는지는 모르겠지만 gsub() 함수로 바로 날려버리자.

1
2
3
4
5
6
7
8
9
df[, "name"] = gsub(pattern = " ", replacement = "", df$name)
head(df)
## category name loc_prod
## 1 식량작물류 쌀 김제, 당진, 평택, 상주, 김해
## 2 <NA> 콩 제천, 안동, 무안
## 3 <NA> 봄감자 보성, 고령, 구미, 당진, 서산
## 4 <NA> 고랭지감자 평창, 정선, 횡성
## 5 <NA> 가을감자 제주, 서귀포
## 6 <NA> 고구마 해남, 논산, 여주

마늘의 경우 “마 늘(난지형)” 이라고 되어있었는데 괄호를 포함하여 괄호 내용을 제거하고자 하는 경우 gsub() 함수와 정규표현식을 활용하여 깔끔하게 제거할 수 있다.

1
2
3
4
5
6
7
8
9
# 괄호 제거
df[, "name"] = gsub(pattern = "\\(.*?\\)", replacement = "", df$name)
df[20:24, ]
## category name loc_prod
## 20 조미채소류 건고추 안동, 제천, 영양, 정읍, 해남
## 21 <NA> 마늘 무안, 해남, 창녕, 남해, 서산
## 22 <NA> 양파 무안, 함평, 해남, 창녕, 합천
## 23 과 일 류 사과 영주, 청송, 의성, 안동, 충주
## 24 <NA> 배 나주, 천안, 안성, 상주, 아산

지역 처리

특수문자 확인

작물명은 일단 됐고, 이제 생산지(loc_prod)를 처리해보자. 파일이 얼마 크지 않기 때문에 미리 파일을 열어본 독자는 쉼표랑 소괄호 이외의 특수문자는 없다는 것을 인지했겠지만, 언제나 코드는 손으로 처리할 수 없고 눈으로 파악하기 어려울 만큼 데이터가 많다는 가정하에 작성하는 것이 좋다.

다음의 한글을 전부 제거하는 코드이다.

1
2
unique(gsub(pattern = "[가-힣]", replacement = "", df$loc_prod))
## [1] ", , , , " ", , " ", " " , " "" ", , ()" ", , , "

쉼표, 띄어쓰기, 소괄호가 있다는 것을 확인할 수 있다.

띄어쓰기 제거

지역명을 쉼표로 구분해서 확인해보면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unique(unlist(strsplit(df$loc_prod, split = ",")))
## [1] "김제" " 당진" " 평택" " 상주" " 김해"
## [6] "제천" " 안동" " 무안" "보성" " 고령"
## [11] " 구미" " 서산" "평창" " 정선" " 횡성"
## [16] "제주" " 서귀포" "해남" " 논산" " 여주"
## [21] "서산" " 예산" " 나주" " 태백" "고창"
## [26] " 해남" " 진도" "나주" " 고창" " 부여"
## [31] " 강릉" " 영암" "제주 동부" " 제주 서부" "함안"
## [36] "성주" " 김천" "구례" " 천안" " 진천"
## [41] "부여" " 담양" " 보성" "논산" "안동"
## [46] " 제천" " 영양" " 정읍" "무안" " 창녕"
## [51] " 남해" " 함평" " 합천" "영주" " 청송"
## [56] " 의성" " 충주" " 안성" " 아산" "제주시"
## [61] " 서귀포시" "창원" " 진주" "상주" " 영동"
## [66] "음성" " 이천" "고양" "마산" " 태안"
## [71] "전국" "부산" " 창원" " 광주(경기)" " 성남"
## [76] " 남양주"

지역명을 구분을 쉼표 대신 슬래시(/)로 치환하기 위해서는 “, “를 “/“로 바꾸도록 바로 gsub() 함수를 사용할 수도 있고, 정규표현식을 사용하여 훨씬 간결하게 처리를 할 수 있지만, 여기서는 띄어쓰기를 먼저 제거하며 돌아가는 방식을 택하도록 하겠다.

1
2
3
4
5
6
7
8
9
df[, "loc_prod"] = gsub(pattern = " ", replacement = "", df$loc_prod)
head(df)
## category name loc_prod
## 1 식량작물류 쌀 김제,당진,평택,상주,김해
## 2 <NA> 콩 제천,안동,무안
## 3 <NA> 봄감자 보성,고령,구미,당진,서산
## 4 <NA> 고랭지감자 평창,정선,횡성
## 5 <NA> 가을감자 제주,서귀포
## 6 <NA> 고구마 해남,논산,여주

쉼표 치환

쉼표를 슬래시로 치환하는 이유는 csv 형식으로 파일을 저장하거나 불러올 때 문제가 생기는 것을 사전에 방지하기 위함이다.

1
2
3
4
5
6
7
8
9
df[, "loc_prod"] = gsub(pattern = ",", replacement = "/", df$loc_prod)
head(df)
## category name loc_prod
## 1 식량작물류 쌀 김제/당진/평택/상주/김해
## 2 <NA> 콩 제천/안동/무안
## 3 <NA> 봄감자 보성/고령/구미/당진/서산
## 4 <NA> 고랭지감자 평창/정선/횡성
## 5 <NA> 가을감자 제주/서귀포
## 6 <NA> 고구마 해남/논산/여주

결측치 처리

이제 결측치 처리를 하겠다. 앞의 결측치를 보고 for 반복문을 떠올렸다면 즉시 반성하도록 하자. zoo 패키지는 결측치 처리에 도움이 되는 함수가 있는 대표적인 패키지이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
library("zoo")
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
df[, "category"] = na.locf0(df$category)
head(df)
## category name loc_prod
## 1 식량작물류 쌀 김제/당진/평택/상주/김해
## 2 식량작물류 콩 제천/안동/무안
## 3 식량작물류 봄감자 보성/고령/구미/당진/서산
## 4 식량작물류 고랭지감자 평창/정선/횡성
## 5 식량작물류 가을감자 제주/서귀포
## 6 식량작물류 고구마 해남/논산/여주

na.locf0() 함수는 결측치가 아닌 값을 기준으로 뒤 또는 앞으로 결측치를 관측치로 채워넣는다. 채워넣는 방향을 결정하는 fromLast 인자는 기본값이 FALSE로 되어있어 굳이 명시하지 않아도 의도대로 처리되는 것을 볼 수 있다.

필요하다면 다음과 같이 저장하도록 하자.

1
write.csv(df, "locations_handled.csv", row.names = FALSE)

지역 분리(선택)

필요한 경우 텍스트를 나누어야 하는데 본 파일의 내부 텍스트가 UTF-8로 이루어져 있었는지 바로 실행이 되지 않았다. 그리하여 다음과 같이 코드를 작성하였다.

1
2
3
4
5
6
7
8
9
10
11
12
library("splitstackshape")
df[, "loc_prod"] = iconv(df$loc_prod, from = "UTF-8", to = "euc-kr") # 1
df_split = cSplit(indt = df, splitCols = "loc_prod", sep = "/") # 2
df_split = as.data.frame(df_split) # 3
head(df_split)
## category name loc_prod_1 loc_prod_2 loc_prod_3 loc_prod_4 loc_prod_5
## 1 식량작물류 쌀 김제 당진 평택 상주 김해
## 2 식량작물류 콩 제천 안동 무안 <NA> <NA>
## 3 식량작물류 봄감자 보성 고령 구미 당진 서산
## 4 식량작물류 고랭지감자 평창 정선 횡성 <NA> <NA>
## 5 식량작물류 가을감자 제주 서귀포 <NA> <NA> <NA>
## 6 식량작물류 고구마 해남 논산 여주 <NA> <NA>

1번 코드에서 iconv() 함수를 사용하여 인코딩을 UTF-8에서 euc-kr로 바꿨는데 혹시나 Mac OS 사용자라면 1번 코드는 굳이 필요하지 않으니 참고하자. 그리고 3번 코드는 splitstackshape 패키지의 cSplit() 함수의 출력물이 data.table 이기 때문에 as.data.frame() 함수를 추가하였다. 물론 data.table 객체를 다루는 사람이라면 이 코드 또한 굳이 작성하지 않아도 된다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×