B R 관련 여러 주제들

이 장에서는 하나의 장으로 독립적으로 다루기에는 잡다한 R 관련 주제를 다룬다.

B.1 R의 파일 및 폴더 관련 명령어등

B.1.1 작업 디렉토리

6.6 절에서 설명한 바와 같이 R은 파일을 읽거나 쓸 때 상대 경로를 제공하면 작업 디렉토리(working directory)로 설정된 폴더를 기준으로 파일의 위치를 결정한다.

현재의 작업 디렉토리를 확인하려면 다음 명령을 수행한다.

getwd()
[1] "/home/sys4ppl/Dropbox/Work/Writing/R-programming-2nd"

어떤 폴더를 작업 디렉토리로 설정하려면 다음처럼 setwd(폴더_절대경로)로 설정한다. 다음은 현재의 작업 디렉토리 밑의 source라는 폴더를 작업 디렉토리로 설정한 예이다.

setwd(file.path(getwd(), "src"))
getwd()
[1] "/home/sys4ppl/Dropbox/Work/Writing/R-programming-2nd/src"

B.1.2 폴더 내의 파일 목록 확인하기

현재의 작업 디렉토리 안에 있는 파일과 하위폴더 목록은 다음처럼 확인한다.

list.files()
[1] "README.txt"  "script-ex.R" "test"       

작업 디렉토리 이외의 폴더의 파일과 하위폴더 목록을 확인하려면 path=폴더_절대경로 인수를 사용한다. 다음은 현재의 작업 디렉토리에 test 하위폴더가 있는 경우에 이 하위폴더의 파일 목록을 확인하는 예이다.

list.files(path=file.path(getwd(), "test"))
[1] "script-ex-test.R" "test-result"     

pattern 인수를 사용하면 정규식 표현을 사용하여 특정 형식의 파일이름을 가진 파일의 목록만 확인할 수 있다. 작업 디렉토리에서 파일의 확장자가 .R인 파일을 나열해 보자.

list.files(pattern = "\\.R$")
[1] "script-ex.R"

현재 폴더뿐 아니라 그 아래의 하위폴더를 순환적으로 접근하여 모든 파일을 나열하려면 recursive=TRUE 인수를 사용한다.

list.files(pattern = "\\.R$", recursive = T)
[1] "script-ex.R"           "test/script-ex-test.R"

순환적으로 하위폴더의 파일도 나열할 때 하위폴더의 이름도 포함하여 파일명을 나열하려면 include.dir=TRUE 인수를 사용한다.

list.files(pattern = "\\.R$", recursive = T, include.dirs = T)
[1] "script-ex.R"           "test/script-ex-test.R"

파일을 나열할 때 절대경로로 나열하렴 full.names=TRUE 인수를 사용한다.

list.files(pattern = "\\.R$", recursive = T, full.names = T)
[1] "./script-ex.R"           "./test/script-ex-test.R"

파일은 제외하고 하위폴더만 나열하려면 list.dirs() 함수를 사용한다.

list.dirs()
[1] "."                  "./test"             "./test/test-result"

list.dirs() 함수는 순환적으로 하위폴더 아래의 하위폴더들을 모두 나열한다. 만약 현재의 작업 디렉토리 아래의 하위폴더만 나열하려면 recursive=FALSE 인수를 사용한다.

list.dirs(recursive=F)
[1] "./test"

B.1.3 파일과 폴더 존재 확인

파일이 있는지 확인하려면 file.exists() 함수를 사용한다.

file.exists(c("script-ex.R", "script.R"))
[1]  TRUE FALSE

폴더가 있는지를 확인하려면 dir.exists() 함수를 사용한다.

B.1.4 폴더의 생성과 삭제

다음은 현재 폴더에 test2 폴더가 없으면 생성하는 예이다.

if (!dir.exists("test2")) dir.create("test2")
list.files()
[1] "README.txt"  "script-ex.R" "test"        "test2"      

빈 폴더의 삭제는 다음과 같이 수행한다.

unlink("test2")

폴더에 파일이나 하위폴더가 있어도 삭제하려면 다음과 같이 수행한다.

unlink("test2", recursive=T)
list.files()
[1] "README.txt"  "script-ex.R" "test"       

B.1.5 파일의 생성, 복사, 이동, 삭제

다음은 빈 파일을 생성한다.

file.create("new_file.csv")
[1] TRUE
list.files()
[1] "new_file.csv" "README.txt"   "script-ex.R"  "test"        

파일의 복사는 다음과 같이 한다.

file.copy("new_file.csv", "newer_file.csv")
[1] TRUE
list.files()
[1] "new_file.csv"   "newer_file.csv" "README.txt"     "script-ex.R"   
[5] "test"          

기존 파일을 이동하려면 다음과 같이 한다.

file.rename("newer_file.csv", "test/newer_file.csv")
[1] TRUE
list.files(recursive=T)
[1] "new_file.csv"          "README.txt"            "script-ex.R"          
[4] "test/newer_file.csv"   "test/script-ex-test.R"

파일의 삭제는 다음과 같이 한다.

file.remove(c("new_file.csv", "test/newer_file.csv"))
[1] TRUE TRUE
list.files(recursive=T)
[1] "README.txt"            "script-ex.R"           "test/script-ex-test.R"

B.2 R에서 계산 정확도를 향상시키기

현재 R은 IEC 60559 부동소수점 계산을 사용하고 있다. 부동소수점 숫자를 저장하기 위해 64 비트를 사용하고 있고, 이중 53 비트는 유효숫자를 저장하고 11 비트는 지수부 정보를 저장한다. 위와 관련된 정보는 base 패키지의 .Machine 변수를 출력해 보면 확인할 수 있다.

.Machine[grep("^double[.](digits|exponent)", names(.Machine))]
$double.digits
[1] 53

$double.exponent
[1] 11

컴퓨터는 이진수로 숫자를 저장하기 때문에, 숫자의 유효숫자와 지수가 표현 범위에 있는 숫자라면 이진수로 표현할 수 있는 정수와 분모가 2의 거듭제곱으로 표현된 유리수에 대해서만 정확하게 표현할 수 있다. 그 외의 숫자는 정수나 2의 거듭제곱을 분모로 가진 유리수로 근사하여 저장하므로 숫자를 저장할 수 있는 정밀도는 제한된다. 그렇기 때문에 다음처럼 수학적으로 같아야할 숫자가 다른 숫자로 간주된다.

a <- sqrt(2)
a^2 == 2
[1] FALSE
a^2 
[1] 2
a^2 - 2
[1] 4.440892e-16

\(\sqrt{2}\)를 제곱하면 2가 되어야 할 것이다. 그러나 \(\sqrt{2}\)는 근사적인 값으로 저장되기 때문에 제곱의 결과가 정확히 2가 되지 않는다. 위의 예에서 \(\sqrt{2}\)의 제곱을 출력하면 2로 표현되는 것은 R의 출력에서 유효숫자의 크기가 제한되어 있기 때문이다. 숫자 출력의 유효숫자의 자리수를 늘리면 정확히 2는 아님을 확인할 수 있다.

options("digits")  # 현재의 유효숫자 자리수 출력
$digits
[1] 7
options(digits = 22) # 유효숫자 자리수를 22로 조정
a^2
[1] 2.000000000000000444089

일반적인 상황에서는 근사적인 이진수 표현으로 숫자를 저장하기 때문에 발생하는 오차는 큰 문제가 되지 않는다. 그러나 정밀한 계산을 하거나 매우 작은 확률 값을 곱하는 계산을 하는 경우에는 이러한 오차가 문제가 될 수 있다.

R에서는 Rmpfr 패키지를 사용하면 부동소수점의 정밀도를 증가시킬 수 있다. 다음 명령으로 Rmpfr 패키지를 설치해 보자.15

install.packages("Rmpfr")

Rmpfr 패키지 설치가 완료되었으면 이 패키지를 메모리로 적재한다. 그리고 정밀한 계산이 필요한 변수를 mpfr() 함수를 이용하여 높은 정밀도의 숫자를 생성할 수 있다. mpfr() 함수의 첫 번째 인수는 높은 정밀도를 필요로 하는 숫자를, 두 번째 인수는 유효숫자의 비트 수를 지정한다.

library(Rmpfr)
b <- mpfr(2, precBits = 200)
a_hp <- sqrt(b)
a_hp^2 == 2
[1] TRUE
a_hp^2 - 2
1 'mpfr' number of precision  200   bits 
[1] 0
a_hp^2
1 'mpfr' number of precision  200   bits 
[1] 2

200 비트로 유효숫자를 표현하자 2와 \(\sqrt{2}\)의 제곱이 같다고 표현된다. 다음 예처럼 mpfr() 함수로 생성된 높은 정밀도의 숫자는 더 정확한 정밀도로 계산이 수행되는 것을 확인할 수 있다.

b
1 'mpfr' number of precision  200   bits 
[1] 2
log(b)
1 'mpfr' number of precision  200   bits 
[1] 0.69314718055994530941723212145817656807550013436025525412067998
log(2)
[1] 0.6931471805599452862268
sin(b)
1 'mpfr' number of precision  200   bits 
[1] 0.90929742682568169539601986591174484270225497144789026837897305
sin(2)
[1] 0.9092974268256817094169

다음은 1부터 24까지의 계승(factorials)을 일반적인 숫자와 mpfr 숫자로 구한 결과이다 (Mächler 2015) 일반적인 숫자로 계승을 구하면 22까지는 정확한 결과를 주지만, 23과 24의 계승은 정확한 결과가 아니다. 왜냐하면 이 계승은 1의 자리가 0이어야 하기 때문이다. 반면 mpfr 숫자로 구한 계승은 정확한 결과를 반환하는 것을 볼 수 있다.

options(scipen = 10)
factorial(1:24)
 [1]                        1                        2                        6
 [4]                       24                      120                      720
 [7]                     5040                    40320                   362880
[10]                  3628800                 39916800                479001600
[13]               6227020800              87178291200            1307674368000
[16]           20922789888000          355687428096000         6402373705728000
[19]       121645100408832000      2432902008176640000     51090942171709440000
[22]   1124000727777607680000  25852016738884978212864 620448401733239409999872
factorial(mpfr(1:24, 120))
24 'mpfr' numbers of precision  120   bits 
 [1]                        1                        2                        6
 [4]                       24                      120                      720
 [7]                     5040                    40320                   362880
[10]                  3628800                 39916800                479001600
[13]               6227020800              87178291200            1307674368000
[16]           20922789888000          355687428096000         6402373705728000
[19]       121645100408832000      2432902008176640000     51090942171709440000
[22]   1124000727777607680000  25852016738884976640000 620448401733239439360000

마찬가지로 Rmpfr 패키지의 chooseMpfr.all() 함수를 사용하면 더 정확한 조합의 값을 얻을 수 있다.

n <- 60
choose(n, 1:n)
 [1]                 60               1770              34220             487635
 [5]            5461512           50063860          386206920         2558620845
 [9]        14783142660        75394027566       342700125300      1399358844975
[13]      5166863427600     17345898649800     53194089192720    149608375854525
[17]    387221678682300    925029565741050   2044802197953900   4191844505805495
[21]   7984465725343800  14154280149473100  23385332420868600  36052387482172424
[25]  51915437974328288  69886166503903472  88004802264174752 103719945525634528
[29] 114449595062769136 118264581564861152 114449595062769136 103719945525634528
[33]  88004802264174752  69886166503903472  51915437974328288  36052387482172424
[37]  23385332420868600  14154280149473100   7984465725343800   4191844505805495
[41]   2044802197953900    925029565741050    387221678682300    149608375854525
[45]     53194089192720     17345898649800      5166863427600      1399358844975
[49]       342700125300        75394027566        14783142660         2558620845
[53]          386206920           50063860            5461512             487635
[57]              34220               1770                 60                  1
chooseMpfr.all(n)
60 'mpfr' numbers of precision  57   bits 
 [1]                 60               1770              34220             487635
 [5]            5461512           50063860          386206920         2558620845
 [9]        14783142660        75394027566       342700125300      1399358844975
[13]      5166863427600     17345898649800     53194089192720    149608375854525
[17]    387221678682300    925029565741050   2044802197953900   4191844505805495
[21]   7984465725343800  14154280149473100  23385332420868600  36052387482172425
[25]  51915437974328292  69886166503903470  88004802264174740 103719945525634515
[29] 114449595062769120 118264581564861424 114449595062769120 103719945525634515
[33]  88004802264174740  69886166503903470  51915437974328292  36052387482172425
[37]  23385332420868600  14154280149473100   7984465725343800   4191844505805495
[41]   2044802197953900    925029565741050    387221678682300    149608375854525
[45]     53194089192720     17345898649800      5166863427600      1399358844975
[49]       342700125300        75394027566        14783142660         2558620845
[53]          386206920           50063860            5461512             487635
[57]              34220               1770                 60                  1
Mächler, Martin. 2015. “Arbitrarily Accurate Computation with r: The Rmpfr Package.”

References

Mächler, Martin. 2015. “Arbitrarily Accurate Computation with r: The Rmpfr Package.”

  1. 리눅스를 사용한다면 GMP와 MPFR 라이브러리가 먼저 설치되어 있어야 한다. 우분투를 사용한다면 apt install libgmp-dev libmpfr-dev 명령으로 두 라이브러리를 설치할 수 있다.↩︎