우리나라의 산업통계는 2017년부터 KSIC 10차 개정을 쓰고 있다. 그런데 2017년 이전 통계도 쓸 일이 있다 보니 9차 개정을 쓸 일이 있는데 R에서 쓸 수 있는 분류표가 없어서 따로 만들기로 했다.

 

통계분류포털(kssc.kostat.go.kr) 들어가서 한국표준산업분류>자료실>최신개정 들어가면 제9차 한국표준산업분류 분류항목표라는게 있고 KSIC2007.xls라는 파일을 받을 수 있다. 엑셀로 열어보면 대략 이렇다.

KSIC 9차 분류 항목표

대-중-소-세-세세분류가 하나로 합쳐져 있다. tidy한 데이터가 없는 것이 좀 아쉽긴 한데, 그래도 이 엑셀 파일은 인쇄해서 윗사람에게 바로 보여줄 수도 있고, 약간의 가공을 거치면 R에서도 쓸 수 있으니 통계청은 상당히 효율적으로 일을 한 것이다. 이제부터 R에서 쓸 수 있는 파일로 가공을 하려고 한다.

 

먼저, 엑셀 파일을 부른다. 여러 방법이 있는데 readxl 패키지의 read_excel() 함수를 썼다. 이 함수는 header=F 같은 옵션을 쓸 수 없는 대신 col_names= 옵션을 써서 변수 이름을 정해 주면 첫 줄이 변수 이름이 되는 걸 막을 수 있다.

install.packages("readxl")
library(readxl)
ksic9<-read_excel("KSIC2007.xls",col_names=c("code","name_ko","name_en"))

결과를 보면,

> head(ksic9)
# A tibble: 6 x 3
  code                                                                name_ko     name_en         
  <chr>                                                               <chr>       <chr>           
1 "A 농업, 임업 및 어업 (01 ~ 03)\nAgriculture, forestry and fishing" NA          NA              
2  NA                                                                 NA          NA              
3 "01"                                                                농업        Agriculture     
4  NA                                                                 NA          NA              
5 "011"                                                               작물 재배업 Growing of Crops
6  NA

엑셀 파일도 그랬지만 불러진 데이터는 한줄씩 띄우기가 되어 있으니 빈 줄을 삭제한다. code 변수 값이 NA인 행을 삭제하는 명령을 준다.

ksic91<-ksic9[which(!is.na(ksic9$code)),]

첫 줄에 A로 시작하는 이름이 대분류명이다. 엑셀 파일을 스크롤해서 내려보면 알겠지만 나중에 B로 시작하는 두번째 대분류가 나오고 그럴 것이다. code 변수의 첫글자를 일단 딴다. 만약 대분류라면 알파벳 대문자일 것이고 중-소-세-세세분류라면 숫자일 것이다. 옳게 된 대분류 코드가 나오면 그대로 유지하고 숫자가 나오면 위에 옳게 된 대분류 코드로 대체하면 된다.

ksic91$code_a<-substr(ksic91$code,1,1)
for(i in 1:nrow(ksic91)){
  if(grepl("[A-Z]",ksic91[i,]$code_a)) temp<-ksic91[i,]$code_a
  else ksic91[i,]$code_a<-temp
}

substr(string, a, b) 함수는 string 문자열 변수의 a번째부터 b번째까지의 글자를 반환한다. 위의 코드는 code 변수의 첫번째 한글자만 뽑아서 code_a 변수에 넣은 것이다. grepl() 함수는 문자열 안에 특정 문자열이 포함되어 있으면 TRUE를 반환하는 함수인데, 여기서 "[A-Z]"는 정규표현식으로, "대문자 A부터 대문자 Z까지 사이의 글자 중 아무 글자"라는 뜻이다. 즉 code_a 변수에 알파벳 대문자가 있으면 temp 변수에 그 대문자를 저장하고 만약 없으면 temp가 저장하고 있는 알파벳 대문자로 code_a 변수의 값을 대체하는 것으로, 모든 산업명에 대분류 코드를 집어 넣는다.

> ksic91<-ksic91[,c("code","code_a","name_ko","name_en")]
> head(ksic91)
# A tibble: 6 x 4
  code                                      code_a name_ko              name_en                                   
  <chr>                                     <chr>  <chr>                <chr>                                     
1 "A 농업, 임업 및 어업 (01 ~ 03)\nAgricul~ A      NA                   NA                                        
2 "01"                                      A      농업                 Agriculture                               
3 "011"                                     A      작물 재배업          Growing of Crops                          
4 "0111"                                    A      곡물 및 기타 식량작~ Growing of Cereal Crops and Other Crops f~
5 "01110"                                   A      곡물 및 기타 식량작~ Growing of Cereal Crops and Other Crops f~
6 "0112"                                    A      채소, 화훼작물 및 ~  Growing of Vegetables, Horticultural Spec~
> tail(ksic91)
# A tibble: 6 x 4
  code                                                 code_a name_ko         name_en                             
  <chr>                                                <chr>  <chr>           <chr>                               
1 "U 국제 및 외국기관(99)\nActivities of extraterrito~ U      NA              NA                                  
2 "99"                                                 U      국제 및 외국기~ Extra-Territorial Organizations and~
3 "990"                                                U      국제 및 외국기~ Extra-Territorial Organizations and~
4 "9900"                                               U      국제 및 외국기~ Extra-Territorial Organizations and~
5 "99001"                                              U      주한 외국공관   Foreign Embassies                   
6 "99009"                                              U      기타 국제 및 ~  Other Extra-Territorial Organizatio~

code_a라는 변수에 A부터 U까지의 대분류 코드가 잘 들어갔다. 보시다시피 대분류는 전부 21개이다. 대분류만 따로 모아서 별도의 표를 만든다.

ksic92<-data.frame(ksic91[grep("^[A-Z]",ksic91$code),1])

grep() 함수는 문자열 변수에 특정 문자열이 포함된 행의 번호를 반환한다. 정규표현식에서 앞에 ^ 표시는 "뒤에 나오는 글자로 시작하는"이란 뜻이다. 즉 "^[A-Z]"는 "알파벳 대문자로 시작하는"이란 뜻이다. 일단 첫줄만 볼 거 같으면,

> ksic92[1,]
[1] "A 농업, 임업 및 어업 (01 ~ 03)\nAgriculture, forestry and fishing"

대분류 코드로 시작하고 한글명, (중분류 코드 범위), 영문명 이렇게 들어가 있다. 잘보면 영문명 앞에 줄바꿈표 '\n'가 들어가 있는데 이걸 이용해서 문자열을 한국어와 영어로 분리한다. strsplit() 함수를 쓰면 사이에 낀 문자열을 기준으로 문자열을 둘로 분리할 수 있는데, 리스트로 만들어준다. 두 개의 변수로 된 데이터프레임으로 만들려면 다음과 같은 짓을 해야 한다.

 

1) unlist() : 리스트를 뽀개서 문자열로 만든다. 한국어명과 영어명이 하나씩 번갈아가며 나오는 문자열 배열이 된다.

2) matrix() : 한줄로 된 배열을 2 by 21 행렬로 변환한다.

3) t() : transpose 명령으로 행과 열을 맞바꿔 21 by 2 행렬이 된다.

4) data.frame() : matrix를 data.frame으로 변환한다.

ksic921<-strsplit(ksic92[,1],"\n")
ksic922<-unlist(ksic921)
ksic923<-t(matrix(ksic922,2,21))
ksic924<-data.frame(ksic923)

ksic924 데이터의 결과를 보면 이렇다.

KSIC 9차 대분류 작업중인 데이터

이제 원하는 글자들을 뜯어서 각각 코드명, 한국어명, 영어명으로 된 대분류표를 만들 수 있다. 다 만들었으니 csv 파일로 저장까지 하자.

code_a<-substr(ksic924$X1,1,1)
name_a_ko<-substr(ksic924$X1,3,100)
name_a_en<-ksic924$X2
ksic9a<-data.frame(code_a,name_a_ko,name_a_en)
write.csv(ksic9a,"ksic9a.csv")

완성된 대분류표이다. 변수 이름에 a를 집어 넣은 건 이게 대분류이기 때문이고 앞으로 중분류, 소분류를 계속 만들것이기 때문이다.

KSIC 9차 대분류표 완성

이제 중분류를 만들어 보자. 아까 만들었던 데이터로 돌아가야 한다. ksic91에서 code가 알파벳 대문자로 시작하는 것만 따로 모아서 대분류를 만들었는데, 이제는 code가 알파벳 대문자로 시작하지 않는 것만 따로 모은다. 여기서 또 헷갈리는 정규표현식 문법이 하나 등장한다. "^[A-Z]"라고 쓰면 "A-Z 사이의 문자 중 하나로 시작하는"인데, "^[^A-Z]"라고 쓰면 "A-Z 사이의 문자가 아닌 문자 중 하나로 시작하는"이란 뜻이 된다.(^ 표시는 [] 밖에 있을 때랑 안에 있을 때 다른 뜻으로 쓰인다.)

ksic93<-data.frame(ksic91[grep("^[^A-Z]",ksic91$code),])

결과를 열어 보면,

ksic 9차 중분류 만들기

code를 보면 두자리부터 다섯자리까지 다양한데, 이제부터 편하다. 앞에 두 자리는 무조건 중분류, 한자리 더 붙여서 세자리면 소분류, ... 이렇게 세세분류까지 가니까 하나씩 뜯어서 중-소-세-세세분류를 만들면 된다.

ksic93$code_b<-substr(ksic93$code,1,2)
ksic93$code_c<-substr(ksic93$code,1,3)
ksic93$code_d<-substr(ksic93$code,1,4)
ksic93$code_e<-substr(ksic93$code,1,5)
ksic93<-ksic93[,c("code_a","code_b","code_c","code_d","code_e","code","name_ko","name_en")]

요렇게 나온다.

대중소세세세 분류코드가 다 있는 분류표

이제 code 변수를 기준으로 데이터를 추출해 중분류표, 소분류표, 세분류표, 세세분류표를 만들 수 있게 되었다. 모든 분류표는 상위분류도 갖고 있으니 얼마나 좋은가. code 변수의 글자수(자릿수)를 기준으로 두자리면 중분류표로 보내고 세자리면 소분류표로 보내고... 하면 된다. 표 분리를 하고 나면 이젠 code 변수는 필요 없으니 처치한다.

ksic9b<-ksic93[which(nchar(ksic93$code)==2),c("code_a","code_b","name_ko","name_en")]
names(ksic9b)<-c("code_a","code_b","name_b_ko","name_b_en")
ksic9c<-ksic93[which(nchar(ksic93$code)==3),c("code_a","code_b","code_c","name_ko","name_en")]
names(ksic9c)<-c("code_a","code_b","code_c","name_c_ko","name_c_en")
ksic9d<-subset(ksic93[which(nchar(ksic93$code)==4),],select=-c(code_e,code))
names(ksic9d)<-c("code_a","code_b","code_c","code_d","name_d_ko","name_d_en")
ksic9e<-subset(ksic93[which(nchar(ksic93$code)==5),],select=-code)
names(ksic9e)<-c("code_a","code_b","code_c","code_d","code_e","name_e_ko","name_e_en")

중분류표인 ksic9b만 일단 살펴보자.

완성된 ksic 9차 중분류표

상위 분류인 대분류 코드를 갖고 있는데, 만일 대분류 산업명을 붙이고 싶으면 이미 만들어놓은 대분류표가 있으니 merge() 함수를 이용해 붙이면 된다.

ksic9ba<-merge(x=ksic9a,y=ksic9b,key=code_a,all.y=T)[,c("code_a","name_a_ko","code_b","name_b_ko")]

결과를 보자.

ksic 9차 대분류, 중분류 머지

소스의 풀버전은 여기(https://github.com/jujaeuk/KSIC-9-)에서 받을 수 있다.

Posted by jujaeuk
,

지난번과 같은 예제를 사용해 보겠다.

 

> fruit<-c("banana","apple","apple","banana","orange","banana")
> weight<-c(2,3,3,5,2,5)

 

바나나 데이터, 애플 데이터, 오렌지 데이터를 나누고 싶다. 이럴 때 split()를 쓴다.

 

> split(weight,fruit)
$apple
[1] 3 3
$banana
[1] 2 5 5
$orange
[1] 2

 

보는 바와 같이 결과는 리스트 형식으로 나온다. 이제 lapply()를 써서 분할된 각각의 데이터로 평균이든 합이든 구할 수 있다.

 

> lapply(split(weight,fruit),mean) 
$apple 
[1] 3 
$banana 
[1] 4 
$orange 
[1] 2

 

sapply()도 당연히 쓸 수 있다.

 

> sapply(split(weight,fruit),mean) 
 apple banana orange  
     3      4      2 

 

입력값이 벡터가 아니라 데이터 프레임이라도 상관 없다. 나이 변수를 하나 더 만들어서 컬럼 세 개짜리 데이터 프레임을 만들어 보자. (근데 과일 나이가 여섯살, 일곱살 이게 가능한가? 암튼)

 

> age<-c(4,5,4,6,7,7) 
> fruits<-data.frame(fruit,weight,age)
> fruits 
   fruit weight age 
1 banana      2   4 
2  apple      3   5 
3  apple      3   4 
4 banana      5   6 
5 orange      2   7 
6 banana      5   7

 

split() 함수의 두번째 argument는 factor니깐 세 개의 변수 중에 무엇을 factor로 할 건지를 정해야 한다. data.frame의 규칙을 따라 다음과 같이 $ 표시를 쓴다. 과일 이름을 기준으로 할거니까,

 

> split(fruits,fruits$fruit) 
$apple 
  fruit weight age 
2 apple      3   5 
3 apple      3   4 

$banana 
   fruit weight age 
1 banana      2   4 
4 banana      5   6 
6 banana      5   7 

$orange 
   fruit weight age 
5 orange      2   7

 

이제 lapply()를 쓸 수 있는데 이건 벡터가 아닌 데이터 프레임의 리스트이기 때문에 그냥은 못 쓴다. 칼럼별로 평균을 구하는 colMeans() 함수를 이용해 사용자 정의 함수를 하나 만든다.

 

> lapply(s,function(x) colMeans(x[,c("weight","age")])) 
$apple 
weight    age  
   3.0    4.5  

$banana 
  weight      age  
4.000000 5.666667  

$orange 
weight    age  
     2      7 

 

매우 비슷하게 sapply() 함수도 쓸 수 있다. 

 

> sapply(s,function(x) colMeans(x[,c("weight","age")])) 
       apple   banana orange 
weight   3.0 4.000000      2 
age      4.5 5.666667      7
Posted by jujaeuk
,

R, apply, lapply, sapply, tapply

R 2019. 9. 15. 11:03

1) apply
apply() 함수는 행렬이나 데이터 프레임을 행 단위로, 또는 열 단위로 행렬의 합이나 평균, 표준편차 등을 계산할 때 쓴다. 
먼저 행렬을 하나 아무렇게나 만든다.

 

> a<-matrix(c(2,5,3,4,5,2,4,3,5,2,1,1),3,4,byrow=T) 
> a  
     [,1] [,2] [,3] [,4]  
[1,]    2    5    3    4  
[2,]    5    2    4    3  
[3,]    5    2    1    1 


3 x 4 행렬이다. 첫번째 인자는 행렬 변수, 두번째 인자는 1 또는 2인데 1이면 행 단위로 계산한다는 뜻이고, 2이면 열 단위로 계산한다는 뜻이다. 세번째 인자는 연산할 함수 이름이다. 그러니까 위의 3 x 4 행렬의 원소들을 행 단위로 합하고 싶으면

 

> apply(a,1,sum)  
[1] 14 14  9 


같은 행렬의 원소들 각 열의 평균을 구하고 싶으면

 

> apply(a,2,mean)  
[1] 4.000000 3.000000 2.666667 2.666667 


2) lapply
apply랑 비슷한데 리스트나 데이터 프레임을 입력값으로 쓰고, 출력값은 리스트 형태로 나온다. 변수 별로 합이나 평균을 계산하기 때문에 apply에서 썼던 두번째 인자(1 또는 2)는 필요 없다.

 

> b<-data.frame(a)
> b 
  X1 X2 X3 X4 
1  2  5  3  4 
2  5  2  4  3 
3  5  2  1  1 
> lapply(b,mean) 
$X1 
[1] 4 

$X2 
[1] 3 

$X3 
[1] 2.666667 

$X4 
[1] 2.666667 


3) sapply
lapply와 거의 비슷한데 입력값은 리스트나 데이터 프레임을 쓰지만, 출력값은 리스트가 아닌, 벡터 형태를 쓴다.

 

> d<-c(1,2,3,4) 
> e<-c(3,2,4,3) 
> f<-list(d,e) 
> g<-data.frame(d,e)
> f 
[[1]] 
[1] 1 2 3 4 

[[2]] 
[1] 3 2 4 3 

> g 
  d e 
1 1 3 
2 2 2 
3 3 4 
4 4 3 
> sapply(f,mean) 
[1] 2.5 3.0 
> sapply(g,mean) 
  d   e  
2.5 3.0  
> class(g) 
[1] "data.frame" 
> class(sapply(g,mean)) 
[1] "numeric" 

 

4) tapply
변수가 여러 개 있을 때 한 변수를 다른 변수의 값을 기준으로 합이나 평균을 계산할 때 쓴다. 첫번째 인자는 계산하고자 하는 변수, 두번째 인자는 기준으로 삼는 변수, 세번째 인자는 함수 이름이 된다.

먼저 과일(fruit)과 중량(weight) 두 개의 변수를 만든다.

 

> fruit<-c("banana","apple","apple","banana","orange","banana") 
> weight<-c(2,3,3,5,2,5) 


둘 다 여섯개의 원소를 갖고 있는데 아직 data.frame() 함수를 써서 둘을 붙이진 않았지만 tapply() 함수를 쓸 땐 다음과 같이 합쳐진 테이블이라고 생각한다.

 

no

fruit

weight

1

banana

2

2

apple

3

3

apple

3

4

banana

5

5

orange

2

6

banana

5


즉 총 여섯개의 과일이 있고 그 중 바나나가 셋이며, 사과는 두 개, 오렌지는 하나이다. 과일들은 각각 중량 정보를 갖고 있다. tapply() 함수는 다음과 같이 쓴다.

 

> tapply(weight,fruit,mean) 
 apple banana orange  
     3      4      2  


과일 이름을 기준으로 사과끼리, 바나나끼리, 오렌지끼리 평균을 구해서 테이블을 만들었다. data.frame() 함수로 합친 데이터의 경우엔 변수 이름 쓸 때만 주의하면 된다.

 

> fruit_weight<-data.frame(fruit,weight) 
> tapply(fruit_weight$weight,fruit_weight$fruit,mean) 
 apple banana orange  
     3      4      2  


여기서 attach()를 쓰면 처음에 했던 것처럼 data.frame()이라도 변수 이름만 간단하게 쓰는게 가능하다.

 

> rm(fruit,weight) 
> attach(fruit_weight) 
The following objects are masked _by_ .GlobalEnv: 

    fruit, weight 

> tapply(weight,fruit,mean) 
 apple banana orange  
     3      4      2  
Posted by jujaeuk
,

윈스턴 챙이 쓰고 이제원이 옮긴 O'Reilly의 "R Graphics Cookbook"에 나은 예제를 썼다. 아래 소스를 직접 테스트해 보고 싶으면 데이터 라이브러리를 호출하기 바란다.

 

> install.packages("gcookbook")
> library(gcookbook)

 

R에서 두 개의 변수를 각각 x축, y축에 넣고 점으로 된 그래프인 산점도(scatterplot)을 그리려면 일단 기본 그래픽 함수로도 할 수 있다. 코드는

 

> plot(mtcars$wt, mtcars$mpg)

 

이렇게 쓰고 그림은 아래와 같이 나온다.

 

기본적인 요소만 갖춘 매우 심플한 그래프라 할 수 있다. 저 그래프가 너무 심심하다고 생각했는지 누군가 좀 더 예쁘게 그래프를 그려주는 라이브러리를 개발했다. 바로 'ggplot2'이다. 일단 라이브러리 설치부터 하자.

 

> install.packages("ggplot2")
> library(ggplot2)

ggplot2를 이용해 산점도를 그려보면 다음과 같다. 코드는

 

> ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point()

 

 

문법이 좀 독특하다. ggplot() 함수 안에 데이터와 aes()함수를 쓰고, aes() 안에 x축 변수와 y축 변수 이름을 쓴다.  aes란 aesthetic의 줄인 말이다. ggplot() 함수만으로는 그리려는 그래프가 무슨 그래프인지 정해지지 않았다. 당연히 ggplot() 함수까지만 쓰면 화면에 아무 그림도 없다. 산점도를 그리려면 ggplot() 뒤에 +를 쓰고 geom_point() 함수를 써야 한다.

 

ggplot2의 기본 형식은 이런식이다.ggplot() 함수를 먼저 쓰고, 그 다음에 geom_point() 함수 같은 함수를 써서 그리려고 하는 게 산점도인지 선 그래프인지 결정한다.

Posted by jujaeuk
,

지금까지의 텍스트 마이닝은 주로 웹으로부터 데이터를 긁어 모아 분석을 하는 거였는데 텍스트 마이닝을 좀 더 유용한 도구로 만들기 위해 웹 텍스트 뿐만 아니라 바이너리 형태로 보관되어 있는 pdf 문서 파일의 텍스트까지 긁을 수 있으면 좋겠다고 생각해서 방법을 찾아 보았다.

오픈소스 도구인 Xpdf는 pdf 파일에서 텍스트 정보를 추출해 txt 파일로 변환해준다. Xpdf를 설치하고 나서 명령 프롬프트에서 pdftotext 명령을 실행시키면 된다. 인터넷에서 알려주는 방법은 Xpdf를 설치한 다음에 R에서 tm 패키지를 설치하고 readPDF() 함수를 쓰라고 나와 있는데 해보면 잘 안된다. 내가 알아낸 방법은 tm 패키지 필요없이 system() 함수를 통해 명령 프롬프트에서 할 일을 R에서 해주는 방식으로 txt 파일을 생성한 다음에 그 txt 파일을 readLines() 함수로 불러들이는 것이다.

1. Xpdf 설치

압축 풀고 영어로 된 INSTALL 문서를 잘 읽어 보면 된다. INSTALL 읽기 싫어하는 사람을 위해서 한글판 설명서를 만들었다.

 

먼저, xpdfreader.com에 들어가서,

 

xpdf > Download > Download the Xpdf command line tools 들어가서 내 OS에 해당하는 파일을 찾고, Download language support packages for Xpdf에서 Korean을 클릭해 xpdf-korean.tar.gz를 다운 받는다.

 

- Windows (update 2020. 2. 13)

1) Windows 32/64-bit:download를 클릭해 zip 파일을 다운 받아서 압축을 푼다.

2) 압축을 풀면  xpdf-tools-win-x.xx라는 이름의 디렉토리가 생기는데 이 디렉토리 이름을 Xpdf로 바꿔서 C:\Program Files 디렉토리에 복사
3) C:\Program Files\Xpdf\DOC\sample-xpdfrc를 xpdfrc로 바꿈

4) xpdf-korean.tar.gz 파일 압축을 풀면 xpdf-korean 디렉토리가 생기는데 이 디렉토리를 korean으로 이름을 바꿔서 C:\Program Files\Xpdf에 복사
5) add-to-xpdfrc 파일 안에 있는 모든 내용을 복사한 후, xpdfrc 파일을 열어 맨 뒤에 붙이고, 방금 붙인 내용에서 UNIX 식으로 되어 있는 경로를 Windows에 맞게 C:\Program Files\Xpdf\korean으로 변경
6) 윈도우에서 내 PC 아이콘을 우클릭 > 속성 > 고급 시스템 설정 > 환경변수 들어가서 시스템 변수 아래 목록에서 Path 선택하고 편집
7) 새로 만들기 누르고 C:\Program Files\Xpdf 쓴 다음에 확인 누르고 리부팅

 

- macOS

1) xpdfbin-mac-3.04.tar.gz를 다운 받아서 압축을 푼다.
2) bin64 안에 있는 파일 전부를 /usr/local/bin에 복사
3) doc 안에 있는 파일 중 *.1은 /usr/local/man/man1로 복사하고, *.5는 /usr/local/man/man5로 복사
(원본에는 "Copy the man pages (*.1 and *.5) to /usr/local/man/man1 and /usr/local/man/man5."라고 되어 있음)
4) doc 안에 있는 sample-xpdfrc를 xpdfrc로 이름을 바꾸어 /usr/local/etc에 복사
5) foolabs.com/xpdf > Download > Language Support Packages 들어가서 xpdf-korean.tar.gz 파일을 다운 받아서 압축을 푼다.
6) xpdf-korean 디렉토리를 korean으로 이름을 바꿔서 /usr/local/share/xpdf에 복사
7) 그 안에 add-to-xpdfrc 파일 안에 있는 내용을 복사한 후, xpdfrc파일을 열어 맨 뒤에 붙임

2. R에서 Xpdf를 이용해 pdf를 txt로 변환

Xpdf를 설치하고, korean language support도 같이 설치했으면

1) R에 R.utils 패키지를 설치하고 라이브러리를 부른다.
2) system() 함수를 이용하여 pdftotext를 실행시킨다.

system() 함수는 프롬프트 명령을 R에서 실행시키게 해 주는 함수다. 맥에서 프롬프트 명령은

 

C:\Users\jaeuk> pdftotext -enc UTF-8 report.pdf

 

이걸 실행시키면 UTF-8 인코딩의  report.txt가 생겨나는데, 그리고 나서 R에서는 이렇게 써 주면 된다.

 

> system('pdftotext -enc UTF-8 report.pdf')


3) 위에 작업이 성공했으면 TXT 파일이 생겼을 테니 readLines() 함수로 TXT파일을 불러들인다.

 

> words<-readLines("report.txt",encoding="UTF-8")
Posted by jujaeuk
,

R, data sorting하기 order()

R 2018. 5. 21. 22:50

kor_pop.csv
다운로드

R에서 데이터를 특정 변수를 기준으로 정렬(sorting)할 때는 order() 함수를 쓴다.

> pop<-read.csv("kor_pop.csv")
> names(pop)<-c("area","population")
> pop
             area population
1      서울특별시    9805506
2      부산광역시    3440484
3      대구광역시    2461002
4      인천광역시    2913024
5      광주광역시    1501557
6      대전광역시    1535445
7      울산광역시    1166033
8  세종특별자치시     242507
9          경기도   12671956
10         강원도    1521751
11       충청북도    1603404
12       충청남도    2132566
13       전라북도    1833168
14       전라남도    1796017
15       경상북도    2682169
16       경상남도    3339633
17 제주특별자치도     623332
> pop1<-pop[order(pop$area),]
> pop1
             area population
10         강원도    1521751
9          경기도   12671956
16       경상남도    3339633
15       경상북도    2682169
5      광주광역시    1501557
3      대구광역시    2461002
6      대전광역시    1535445
2      부산광역시    3440484
1      서울특별시    9805506
8  세종특별자치시     242507
7      울산광역시    1166033
4      인천광역시    2913024
14       전라남도    1796017
13       전라북도    1833168
17 제주특별자치도     623332
12       충청남도    2132566
11       충청북도    1603404
> 

주의할 것: 1) area가 아니라 pop$area로 써야 한다. 2) order() 함수 다음에 반드시 쉼표(,)를 써 주어야 한다.
R에 sort()라는 함수도 있으며, 이 함수 또한 정렬 기능을 제공하는데 쓰임새는 order()와는 다르다. 백문이 불여일견

 

> fruit<-c("pineapple","orange","apple","banana")
> fruit
[1] "pineapple" "orange"    "apple"     "banana"   
> fruit1<-sort(fruit)
> fruit1
[1] "apple"     "banana"    "orange"    "pineapple"

 

보다시피, order() 함수가 여러 개의 변수로 구성된 data frame의 경우 어느 한 변수를 정하여 이를 기준으로 정렬하는 용도라면 sort() 함수는 그냥 문자열을 정렬할 때 쓴다.

Posted by jujaeuk
,

nzb.RData
다운로드

위에 파일을 다운로드 받아서 R 작업 디렉토리에 복사하고 R을 실행한 후 불러들인다. (지난번 예제의 결과를 저장한 것이다.) head() 명령을 써 보면,

 

> load("nzb.RData")
> head(nzb)
                 time CAPITL CENTRL DUNWOD GENESE  H Q HUD VL 
1 2017-12-01 00:00:00  28.00   8.96  22.16   8.20 6.46  21.73  
2 2017-12-01 01:00:00  27.50   8.23  21.54   7.50 5.72  21.11  
3 2017-12-01 02:00:00  27.15   7.72  21.11   6.97 5.26  20.69  
4 2017-12-01 03:00:00  26.20   7.58  20.42   6.86 5.21  20.01  
5 2017-12-01 04:00:00  27.73   7.84  21.53   7.09 5.27  21.09  
6 2017-12-01 05:00:00  27.62  11.82  22.88  11.04 9.54  22.52  

 

아마도 저런 모양의 데이터를 볼 수 있을 텐데, CAPITL 변수부터는 각 지역의 매시간 가격이고, 맨 앞에 time 변수는 시간을 나타내고 있다.
특정 변수만을 골라서 새로운 데이터 셋을 만들고 싶으면 지난 예제에 배운 대로

 

> nzb1<-subset(nzb, name==c("CAPITL","CENTRL"))

 

하거나, 더 간단하게

 

> nzb1<-nzb[c("CAPITL,"CENTRL")]

 

이렇게 하는 방법도 있다. 결과는 둘 다 같다. 만약 선택할 변수가 두어개라면 이런 식으로 쓰면 되는데, 변수가 50개 쯤 되는데 한 개의 변수를 뺀 나머지 49개 변수로 된 새로운 데이터를 만들어야 한다면 좀 귀찮다. 대신 뺄 변수명만을 쓰는 것으로 간단하게 처리할 명령어가 있으면 좋은데 이게 생각보다 복잡하다.
다음과 같이 하면 된다. 위에 데이터에서 time 변수 하나만 제외하고 나머지 모든 변수들로 된 새로운 데이터 셋을 만들고 싶으면,

 

> nzb2<-nzb[,!(names(nzb) %in% c("time"))]

 

이렇게 쓴다. 변수명 다음에 나오는 대괄호 안에는 변수의 조건을 쓰게 되어 있는데, 중간에 콤마(,)는 열과 행을 구분하는 기호이다. 콤마 왼쪽에 아무것도 없다는 것은 열에 대해서는 별다른 명령이 없고 모든 열을 다 선택하라는 뜻이고, 콤마 오른쪽에 있는 복잡한 식은 저 조건에 맞는 열을 선택하라는 뜻이다.
콤마 오른쪽에 있는 !(names(nzb) %in% c("time))가 뜻하는 것은 nzb 데이터 셋의 변수 이름 중에 time이라는 말이 포함된 것을 제외한 나머지를 선택하라는 뜻이 되겠다. (복잡하니까 그냥 외우면 편하다.)

Posted by jujaeuk
,
Error in as.POSIXct.POSIXlt(what, tz = tzone) : invalid 'tz' value​

nza.RData
다운로드

파일을 다운로드한 뒤에 R에서 불러보자.

 

> load("nza.RData")
> head(nza,20)
               time   name  lbmp
1  12/01/2017 00:00 CAPITL 28.00
2  12/01/2017 00:00 CENTRL  8.96
3  12/01/2017 00:00 DUNWOD 22.16
4  12/01/2017 00:00 GENESE  8.20
5  12/01/2017 00:00    H Q  6.46
6  12/01/2017 00:00 HUD VL 21.73
7  12/01/2017 00:00 LONGIL 30.48
8  12/01/2017 00:00 MHK VL  9.03
9  12/01/2017 00:00 MILLWD 22.35
10 12/01/2017 00:00 N.Y.C. 22.27
11 12/01/2017 00:00  NORTH  6.37
12 12/01/2017 00:00    NPX 25.33
13 12/01/2017 00:00    O H  8.70
14 12/01/2017 00:00    PJM 16.12
15 12/01/2017 00:00   WEST  8.70
16 12/01/2017 01:00 CAPITL 27.50
17 12/01/2017 01:00 CENTRL  8.23
18 12/01/2017 01:00 DUNWOD 21.54
19 12/01/2017 01:00 GENESE  7.50
20 12/01/2017 01:00    H Q  5.72

 

보다시피 time별로, name별로 lbmp라는 값이 있는 데이터의 첫 20행을 본 것이다. 16번째 데이터부터 첫번째 name이 다시 반복되는 것을 보니 매 시간 열 다섯개의 name에 대해 lbmp 값이 있는 데이터로 보인다. 이 데이터를 time by name으로 바꿔서 각각의 name을 변수 이름으로 하는 hourly data로 만들고 싶을땐 어떻게 하면 될까? 바로 cast() 함수를 쓰면 된다. cast() 함수는 reshape라는 패키지에 있다.

reshape 패키지를 아직 설치하지 않았으면 설치부터 한다.

 

> install.packages("reshape")

 

설치했으면 reshape 라이브러리를 부른다.

 

> library(reshape)

 

cast() 함수의 사용 방법은 간단하다.

 

> nzb<-cast(nza,time~name,value="lbmp")
> head(nzb,20)
               time CAPITL CENTRL DUNWOD GENESE   H Q HUD VL 
1  12/01/2017 00:00  28.00   8.96  22.16   8.20  6.46  21.73  
2  12/01/2017 01:00  27.50   8.23  21.54   7.50  5.72  21.11  
3  12/01/2017 02:00  27.15   7.72  21.11   6.97  5.26  20.69  
4  12/01/2017 03:00  26.20   7.58  20.42   6.86  5.21  20.01  
5  12/01/2017 04:00  27.73   7.84  21.53   7.09  5.27  21.09  
6  12/01/2017 05:00  27.62  11.82  22.88  11.04  9.54  22.52  
7  12/01/2017 06:00  32.95  23.82  30.92  22.64 21.76  30.68  
8  12/01/2017 07:00  31.53  30.37  32.42  29.45 28.71  32.33  
9  12/01/2017 08:00  30.65  29.33  31.57  28.32 27.75  31.40  
10 12/01/2017 09:00  30.64  29.17  31.64  28.03 27.59  31.32  
11 12/01/2017 10:00  29.01  27.71  29.99  26.62 26.17  29.75  
12 12/01/2017 11:00  27.57  26.25  28.58  25.20 24.52  28.16  
13 12/01/2017 12:00  26.73  25.48  27.80  24.48 23.09  27.31  
14 12/01/2017 13:00  26.19  25.01  27.18  24.02 23.21  26.90  
15 12/01/2017 14:00  25.51  24.24  26.47  23.42 22.23  26.23  
16 12/01/2017 15:00  26.47  25.22  27.36  24.45 24.01  27.14  
17 12/01/2017 16:00  32.49  30.74  33.48  29.74 29.43  33.16  
18 12/01/2017 17:00  39.19  37.27  40.26  36.14 35.73  40.00  
19 12/01/2017 18:00  36.44  34.80  37.50  33.77 33.29  37.30  
20 12/01/2017 19:00  33.41  31.80  34.48  30.83 30.57  34.29  

 

name 필드의 값이 변수 이름이 된 것을 볼 수 있다. 다른 방법도 있긴 한데 cast()가 제일 편하기 때문에 알 필요 없다.

 

여기서 한 가지 문제가 생길 수 있다. 만약에 time 변수를 strptime() 같은 함수를 써서 POSIXlt 포맷으로 만든 경우에 cast 함수를 쓰면 다음과 같은 에러가 발생한다.

 

Error in as.POSIXct.POSIXlt(what, tz = tzone) : invalid 'tz' value

 

tz 값을 제대로 해 주면 해결이 되런지 모르겠으나 그보다 제일 편한 방법은 cast를 하기 전에 POSIXlt 포맷의 시간을 문자열로 바꿔 버리는 것이다.

 

nza$time<-as.character(nza$time)
nzb<-cast(nza, time~name value="lbmp")
nzb$time<-strptime(nzb$time, "%m/%d/%Y %H:%M")

 

원한다면 cast()를 쓰고 나서 다시 strptime()을 써서 POSIXlt 포맷으로 변환하면 된다.

'R' 카테고리의 다른 글

R, data sorting하기 order()  (0) 2018.05.21
R, data에서 특정 변수 제외하기  (0) 2018.04.12
R 시계열 데이터 변환 aggregate()  (0) 2018.03.22
R 데이터 변형하기 - subset(), which()  (0) 2018.03.19
R에서 데이터 다루기  (0) 2018.03.13
Posted by jujaeuk
,