Collaborated R - 다른 툴 함께 쓰기

예전엔 정말로 통계/생물과에서는 R, 공대생은 Matlab, 공장이나 은행은 SAS, 경영학과는 Excel, 수학 관련은 Mathmatica라는 선입견이 있었습니다.  물론 Stata나 SPSS등 다른 툴을 선호하시는 분도 많았고 linux와 같이 소개된 Octave가 점차 널리 쓰이기도 하면서 실제로는 더 많은 종류의 툴들이 공존하고 있었는데, 어찌보면 비효율적일 수도 있고 어찌보면 하나의 범용적인 툴보다 각자 더 특화될 수 있는 장점이 있을 것 같기도 합니다.

저는 그다지 부지런하지 못해서 '확실히 편리하고 강력한 기술'을 소개받는다고 해도, 그걸 배우려고 하기보다는 나이탓, 시간탓을 해가며 미련하게 익숙한 툴만 고집할 때가 많습니다.  파이선이 뭔가요 먹는건가요 우걱우걱. 단지 게으름 탓이면서 최적화라는 포장으로 :) 레거시에서 꼼수를 활용한 해결책을 찾아 내려는 시간낭비를 하기도 하구요.  효율성이 뭔가요 먹는건가요 우걱우걱.  

하지만 요즈음 데이터 관련 발표나 소개에서 R이 빠지는 곳 없이 등장하는 것을 보다 보면 이젠 모든 분야에 R이 쓰이고 있는 듯 싶어서 나도 R을 배워야만 할 것 같은 조바심이 들게 됩니다.  (politically correct diversity는 없는 겁니까;;)  새롭고(?) 강력하며, 무료이기까지 하다니까 다들 열광하는 것이 당연하겠지만, 이미 R외의 다른 툴에 익숙하고 그 툴로 큰 어려움 없이 작업 하시는 분들이라면 조금 거북하실지도 모르겠습니다.

여러가지 이유로 새롭게 R을 시작하시는 분들에게 R은 왠지 초기 진입장벽이 높은 것으로 유명합니다 — 사실 막상 사용해보면 다른 툴보다 더 불친절할 건 없는데도 말이죠.  그래서 본격적인  R의 대용량 처리에 대해 이야기 하기 앞서 오늘은 다른 툴/언어의 코드와 R을 연동시킬 수 있는 몇 가지 잘 알려진 패키지들을 소개하겠습니다.

여기에 소개된 예제들은 각각 인터넷 상에 공개되어있는 자료들로, 약간의 리소스와 효율성을 대가로 SQL, Excel, Matlab, SAS, Java 같은 기존의 코드나 데이터를 사용할 수 있는 예제들입니다.1)

Rube Goldberg Machine - TV Remote

적외선 통신의 원리 같은건 몰라도 TV리모컨 정도는 만들 수 있지 말입니다.
그림출처: An Engineer's Aspect: 29 Rube Goldberg Machines on Oct. 29

1. Matlab / Octave

R은 Functional language(이하 FL이라고 칭하겠습니다)를 표방하고 있습니다.  비슷?한 언어로는 J도 있고, OCaml, LISP 등의 인공지능용 언어들이 여기에 해당합니다.  명령구문이 아닌 수식과 선언만으로 이루어지는 FL이 다른 언어에 익숙하신 분들에게 생소할 수도 있지만, 공학과 머신러닝 분야에서 많이 사용되고 있는 Matlab/Octave의 문법구조와는 매우 유사합니다 — 아직도 많은 공학 분야에서 Matlab이 활용되고 있지만, 점차 상업용 라이센스에 대한 부담으로 R로 옮겨가는 추세 인 듯 합니다.2)

이미 Matlab에서 작성된 자료가 있다면 R.matlab 패키지를 사용해서 R에서 Matlab의 데이터와 명령을 수행할 수 있습니다.

아래와 같이 readMat/writeMat 함수로 간단하게 Matlab형식의 데이터 파일(.mat)을 읽어들이고 작성할 수 있습니다. (출처)

1
2
3
4
5
6
>library(R.matlab)
>data(volcano)
>data(cars)
>head(volcano[,1:6])
>head(cars)
>writeMat('mtdata.mat', volc=volcano, cars=cars)
1
2
3
4
>library(R.matlab)
>mtarrays <- readMat('mtdata.mat', fixNames=TRUE)
>volcan <- mtarrays[['volc']]
>print(head(volcan[, 1:6]))

그 외에도 직접 Matlab서버를 실행시키고 연동하여 정보를 주고 받는 것도 가능합니다. (출처)

1
2
3
4
>library(R.matlab)
>Matlab$startServer()
>matlab <- Matlab()
>isOpen <- open(matlab)

다음과 같은 메시지가 나오면 Matlab서버의 기동에 성공한 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
                             < M A T L A B >
                  Copyright 1984-2004 The MathWorks, Inc.
                  Version 7.0.1.24704 (R14) Service Pack 1
                             September 13, 2004
  To get started, type one of these: helpwin, helpdesk, or demo.
  For product information, visit www.mathworks.com.
Matlab v7.x or higher detected.
Saving with option -V6.
Added InputStreamByteWrapper to dynamic Java CLASSPATH.
----------------------
Matlab server started!
----------------------
Trying to open server socket (port 9999)...done.

다음은 Matlab 서버와 R이 변수값을 주고 받는 예제입니다.

1
2
3
4
5
6
7
># set a couple of variables in MATLAB
>evaluate(matlab, "y=20; z=x+y")
Received cmd: 1
"eval" string: "y=20; z=x+y"
z =
    30
Sent byte: 0
1
2
3
4
5
6
>z <- getVariable(matlab, "z")
Received cmd: 1
"eval" string: "variables = {'z'};"
Sent byte: 0
Received cmd: 2
save(tmpname, '-V6', 'z')

더 자세한 예제는 R.Matlab 도움말을 참조하시길 바랍니다.3)

이 외에도 RcppOctave 패키지를 이용해서 Octave는 물론 Matlab 소스를 실행시킬 수 있습니다.  RcppOctave는 원칙적으로 Octave를 위한 패키지이지만, Octave와 Matlab의 높은 호환성 덕분에 일정 수준 이하의 Matlab 코드 (.m 형식 파일)도 직접 실행이 가능합니다.

패키지가 설치되었다면, .CallOctave 함수를 호출함으로써 아래와 같이 Octave명령을 수행할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>.CallOctave('version')
## [1] "3.8.1"
>.CallOctave('sqrt', 10)
## [1] 3.162278
>.CallOctave('eye', 3)
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
>.CallOctave('eye', 3, 2)
## [,1] [,2]
## [1,] 1 0
## [2,] 0 1
## [3,] 0 0

아래의 예제는 Octave와 Matlab의 스크립트 형식인 .m 파일을 읽어 들여 스크립트 내부에서 정의된 함수를 사용하는 예제입니다.

1
2
3
4
5
>sourceExamples('ex_functions.m')
# several functions are now defined
>o_ls()
## [1] "fun1" "fun2" "fun_noargout" "fun_varargin"
## [5] "fun_varargout"
1
2
3
4
5
6
7
8
9
10
11
12
13
>.CallOctave('fun1')
## [1] 0.6776693 0.7570356 0.6148638 0.3308931
>.CallOctave('fun2')
## $a
## [1] 0.8452558 0.4754382 0.4435868 0.2553756
##
## $b
## [,1] [,2] [,3]
## [1,] 0.4847515 0.9711548 0.7379941
## [2,] 0.4536776 0.9089370 0.9022478
##
## $c
## [1] "some text"

Matlab/Octave에서 작성된 스크립트를 직접 R 문법으로 migration하시려면 이 곳에서 기본적인 문법차이를 참조하시길 바랍니다.

2. SAS

'비싼 가격에도 SAS를 쓰는 이유는 나중에 버그로 인한 소송이 발생했을때 SAS에 책임을 넘길 수 있기 때문이다' 라는 농담이 있을 정도로, 각 분야에서 널리 사용되며 상업적으로 오랫동안 신뢰를 쌓아온 SAS에서는 이미 공식적으로 내부 패키지 내에 R 인터페이스를 내장하고 있습니다. (참조)  또한 SAS Macro를 이용하여 SAS 내부에서 R코드를 수행할 수도 있습니다. (참조)

여기서는 R에서 SAS에 접속하는 단순한 방법과 변수의 전달을 소개하도록 하겠습니다.

SAS의 XPORT 형식 파일을 R에서 읽어들이기 위해서는 foregin패키지 혹은 sasXPORT패키지가 필요합니다.

1
2
>library(foreign)
>data <- read.xport("<path to your SAS file>")
1
2
>library(SASxport)
>data <- read.xport("<path to your SAS file>")

foreign패키지가 SAS외에도 SPSS, Stata, Weka나 dBase와 같은 다양한 통계툴의 데이터를 읽고 쓰기위해 R core 팀에 의해 관리되고 있다면  SASxport는 SAS데이터에 특화된 패키지로 최근까지 계속 update되고 있는 점이 장점입니다. (2016.3)

R에서 SAS 명령을 수행하기 위해서는 아쉽게도 시스템 호출을 이용하는 수밖에 없습니다.  아래의 예제는 윈도우 OS상태에서 미리 작성해둔 .sas 파일을 수행합니다. (출처)

1
2
>setwd("c:\\Program Files\\SASHome 9.4\\SASFoundation\\9.4\\")
>return.code <- shell("sas.exe -SYSIN c:\\temp\\myprogram.sas")

수행된 스크립트에서 저장한 결과파일(.sas7bdat)을 haven 패키지를 이용하여 읽어들이는 예제입니다.

1
2
3
>library("haven")
># Import the dataset:
>myData <- read_sas("c:\\temps\\baseball.sas7bdat")

3. SQL

FL에 익숙하지 않은 분들이 R을 처음 접할 때의 가장 큰 진입장벽 중 하나는 R의 subset 명령들입니다.  많은 분들이 기존의 방식대로 loop문으로 해결하려고 하는데, SQL이 익숙하시다면 sqldf 패키지를 이용하여 R의 모든 matrix/data frame의 값을 SQL명령으로 subset/aggregate 할 수 있습니다. 4)

아래는 sqldf를 이용하여 SQL을 수행하는 예제입니다. (출처) 샘플데이터는 PASWR 패키지의 데이터인 titanic3 데이터를 사용했습니다.

1
2
3
4
5
6
7
8
>#install if packages are not installed already
>install.packages("sqldf")
>install.packages("PASWR")
>library(sqldf)
>data(titanic3, package=”PASWR”)
>colnames(titanic3)
>head(titanic3)
>sqldf(‘select age, count(*) from titanic3 where age is not null group by age’)

ggplot2과 sqldf를 이용한 연령별 생존자의 히스토그램입니다.  ggplot에 대해서는 여기서 다루기에는 분량이 많으므로, 기회가 된다면 다른 관련 포스팅에서 다루도록 하겠습니다.

1
2
3
>library(ggplot2)
>DF=sqldf('select age from titanic3 where age != "NA"')
>qplot(DF$age,data=DF, geom=”histogram”)

타이타닉 생존자 나이 분포

4. MS Excel

아무래도 데이터 분석을 한다고 하면 Excel을 빼고 얘기하기 어렵습니다.  Microsoft의 Excel은 그 편리한 사용성과 강력한 기능으로 윈도우에 대한 호불호를 넘어서 가장 널리 쓰이는 도구이면서도, 부실한 메모리 관리덕분에 '무언가 본격적인 분석에는 사용할 수 없는' 반쪽짜리 툴 취급을 당하기도 합니다.  그렇지만 실제로는 Excel은 그 자체로도 이미 강력한 분석 툴이며 PowerPivot이나 PowerView등의 보조프로그램/Add-in 덕택으로 점차 본격적인 BI툴로 자리매김 하려고 하고 있습니다.5)

널리 사용되는 툴인 만큼 가장 많은 패키지에서 Excel 시트를 읽어들이는 기능을 지원하고 있습니다.  Java를 이용하거나 (XLConnect, xlsx), DCOM (RExcel), .NET (ExcelDNA), Perl과 ODBC드라이버 (r2excel), 전용 버전의 R을 사용하는 등 (BERT) 다양한 패키지가 있습니다만, 각각의 장단점이 있으므로 필요에 따라 선택하시길 바랍니다. 6)

아래는 XLConnect를 이용하여 Excel 파일을 가져오는 예제입니다.

R에서 Excel파일을 다룰때 가장 강력하고 널리 쓰이는 XLConnect이지만, XLConnect와 xlsx는 모두 Java를 이용하여 — 정확히는 아래에서 소개할 rJava 패키지를 이용하여 — 파일을 읽어들이므로 용량이 큰 경우 시간이 많이 걸리는 점을 주의해야합니다.7)   그 외에도 xlsx패키지는 sum()과 같은 Excel 내부 함수로 정의된 셀을 제대로 가져올 수 없는 문제가 있습니다.

1
2
3
>library(XLConnect)
>wb <- loadWorkbook("myfile.xlsx")
>myDf <- readWorksheet(wb, sheet = "Sheet1", header = TRUE)

단순한 읽기/쓰기 외에도 파일에 Sheet를 삽입하거나, R에서 작성한 이미지를 삽입하는 것도 가능합니다. (출처)

1
2
3
4
5
6
7
8
9
10
11
12
>library("XLConnect")
# Generate a black sheet
>outDir<-c("c:/mydata")
>fileXls <- paste(outDir,"newFile.xlsx",sep='/')
>unlink(fileXls, recursive = FALSE, force = FALSE)
>exc <- loadWorkbook(fileXls, create = TRUE)
>createSheet(exc,'Input')
>saveWorkbook(exc)
# Input values on the sheet
>input <- data.frame('inputType'=c('Day','Month'),'inputValue'=c(2,5))
>writeWorksheet(exc, input, sheet = "input", startRow = 1, startCol = 2)
>saveWorkbook(exc)

반대로 Excel 내부에서 R함수를 이용하려면 — RExcel이나 ExcelDNA, BERT등과 같이 — DCOM, .NET등 윈도우전용 라이브러리에 의존해야 합니다.  윈도우를 OS로 사용하는 경우에만 쓸 수 있는 방법이고, 내부 인터페이스 설계에 따라 부하가 발생하기 쉬운 구조입니다만 Excel에서 다양한 R의 함수를 사용하려는 분들에게는 유용할 것입니다.

상용인 RExcel을 제외하자면, BERT가 R과 엑셀간의 매우 강력한 통신을 지원합니다.  BERT는 특정 R버전 (2016년9월 현재 v3.2.3)만을 지원하는 단점이 있지만 RExcel과 달리 GPL로 배포되고 있어서, 간단한 설치 후 Excel의 셀에서 =R.Fibonacci(10)과 같이 Excel 함수와 동일하게 R함수를 호출할 수 있고, 자신만의 R함수를 작성하여 Excel에서 사용할 수도 있습니다.

BERT를 이용한 Excel 기반에서의 R 활용법에 대해서는 BERT의 git repository를 참조하시기 바랍니다.

5. Java

널리 사용되고 있는 프로그래밍 언어인 Java에는 이미 Weka와 같은 많은 머신러닝 관련 라이브러리가 있으므로 굳이 R과 연동하는 것이 불필요할 듯 하고,  R역시 외부 프로그래밍언어의 호출이 어쩔수 없는 경우가 아니라면 비효율적이므로 권장하지 않고 있습니다.  그렇지만 사용자기반이 넓은 두 플랫폼이 손쉽게 연동할 수 있다면 보다 다양한 활용이 가능할 것이기 때문에, rJava는 아마도  XLConnect(Excel)만큼이나 가장 많은 분들이 관심을 갖는 패키지일 것입니다.

rJava는 Java와 객체를 공유하기 위한 low-level 인터페이스로, R에서 자바의 객체를 생성하거나 실행하도록 해줍니다.  반대로 Java class에서 R의 객체를 참조하기 위해서는 rJava 패키지에 포함되어있는 JRI 를 사용하면 됩니다.

* 이 때 Java와 R의 플랫폼 버전 — 32bit/64bit — 이 동일한 지 반드시 확인하시기 바랍니다. 

R내부에서 Java 메소드/클래스를 호출하는 예제는 아래와 같습니다. (출처)

1
2
3
4
>library(rJava)
>.jinit()
>jObj=.jnew("JClass")
>result=.jcall(jObj,"[D","method1")

여기서 JClass는 호출하려는 클래스로 사용자의 ClassPath에 정의된 경로에 있어야 하고, method1JClass 내부의 double[]을 반환하는 메소드로 static으로 정의되어야 합니다. .jcall에서 사용된 "[D"는 JNI에서 double형 배열을 의미하는 예약어입니다.

Java class에서 R을 호출하려는 경우에는 작성하려는 class에 아래 3가지 라이브러리를 참조해야 합니다. (출처)

1
2
3
<YOUR_R_HOME>\library\rJava\jri\JRI.jar
<YOUR_R_HOME>\library\rJava\jri\JRIEngine.jar
<YOUR_R_HOME>\library\rJava\jri\REngine.jar

다음은 실제 JRI를 이용한 예제 소스입니다.  여기서는 1부터 5까지 숫자의 평균을 구하고 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.rosuda.JRI.Rengine;
public class Temp {
    public static void main(String a[]) {
        // Create an R vector in the form of a string.
        String javaVector = "c(1,2,3,4,5)";
        // Start Rengine.
        Rengine engine = new Rengine(new String[] { "--no-save" }, false, null);
        // The vector that was created in JAVA context is stored in 'rVector' which is a variable in R context.
        engine.eval("rVector=" + javaVector);
        //Calculate MEAN of vector using R syntax.
        engine.eval("meanVal=mean(rVector)");
        //Retrieve MEAN value
        double mean = engine.eval("meanVal").asDouble();
        //Print output values
        System.out.println("Mean of given vector is=" + mean);
    }
}


1) 어떤 툴 간에도 텍스트 파일 형태의 입력/출력과, 시스템 호출을 이용해서 직접 명령어를 실행하는 방식은 가능하므로 특수한 경우가 아니면 여기서는 생략하겠습니다.

2) Matlab의 runtime library는 무료입니다. 단, Matlab의 특성 상 각 버전 별 하위호환을 보장하지 않으며, 사용자가 배포하려는 스크립트의 해당 버전 runtime library 배포 여부는 전적으로 원 스크립트 작성자에게 책임이 있습니다.

3) DCOM을 이용하면 Matlab에서 R code 실행도 가능합니다만 (출처) 상용 라이브러리를 이용하기때문에 여기서 소개하지 않겠습니다.

4) 오라클의 데이터 마이닝 모듈(ODM)에 연결하여 SQL을 수행할 수 있는 RODM이라는 패키지는 2012년 이후 더 이상 업데이트 되고 있지 않아서 제외했습니다.

5) Office 2016에서는 PowerPivot, PowerMap, PowerView등의 기존의 모든 BI관련 Add-In들이 Professional 버전 혹은 Subscribe버전(Office 365)에서만 지원됩니다.

6) DCOM이나 .NET을 이용하는 패키지의 경우 (RExcel, ExcelDNA) 윈도우 버전만 지원합니다.  여기서 소개된 패키지들 중에서 RExcel은 상용패키지이므로 사용시 별도의 라이센스와 비용이 추가될 수 있습니다.

7) 이를 극복하기 위해서 xlsx패키지의 경우 컬럼형식을 자동으로 추측하지 않는 read.xlsx2() 함수를 별도로 제공하고 있습니다.  하지만 (2016년9월기준)  2014년 이 후 더 이상의 업데이트가 이루어지고 있지 않으므로 이용에 참고하시기 바랍니다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.