R

[R 프로그래밍] 반복 함수

동호다찌 2022. 12. 26. 17:03

 

apply()

apply(X, MARGIN, FUN, …)는 X를 입력받아 행 또는 열 방향으로 함수를 적용하여 결과값을 반환합니다. MARGIN 인수가 1이면 행 방향으로, 2이면 열 방향으로 연산이 됩니다. apply 함수에 입력하는 데이터(X)는 배열, 매트릭스만 가능하고, 만일 데이터프레임이 모두 같은 데이터 타입이면 가능합니다. 반환되는 값은 벡터나 행렬입니다.

(x <- matrix(1:12, c(3,4)))
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    4    7   10
#> [2,]    2    5    8   11
#> [3,]    3    6    9   12


# 행 방향으로 평균
> apply(x, 1, mean)
[1] 5.5 6.5 7.5

# 열 방향으로 평균, mean함수 옵션 추가
> apply(x, 2, mean, na.rm = TRUE)   
[1]  2  5  8 11

 

벡터는 apply에 입력데이터로 사용할 수 없습니다(에러 발생). 만일 벡터를 사용하고자 한다면 배열로 변환하여 사용하여야 합니다.

x <- 1:12

# 벡터를 배열로 변환
> dim(x) <- c(1, length(x));
> x
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,]    1    2    3    4    5    6    7    8    9    10    11    12

> apply(x, 1, mean)
[1] 6.5

 

함수는 사용자 정의 함수를 만들어서 사용할 수 있습니다.

(x <- matrix(1:12, c(3,4)))
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    4    7   10
#> [2,]    2    5    8   11
#> [3,]    3    6    9   12

> apply(x, 2, function(x) {x*2});
     [,1] [,2] [,3] [,4]
[1,]    2    8   14   20
[2,]    4   10   16   22
[3,]    6   12   18   24

# 행방향으로 하면 행과 열이 바뀝니다.
> apply(x, 1, function(x) {x})
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12

> apply(x, 1, function(x) {x*2})
     [,1] [,2] [,3]
[1,]    2    4    6
[2,]    8   10   12
[3,]   14   16   18
[4,]   20   22   24

 

데이터프레임도 데이터가 모두 같은 타입이라면 apply를 적용할 수 있습니다. R의 기본 데이터셋인 iris에서 Factor 타입인 Species를 제거한 후 apply에 사용하도록 하겠습니다.

str(iris)
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
> x <- iris[, -5]; 
> str(x)
'data.frame':	150 obs. of  4 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 
> apply(x, 2, mean, na.rm = TRUE)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 
    
> apply(x, 2, function(x) {median(x*2-1, na.rm = TRUE)})
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
        10.6          5.0          7.7          1.6
        
        
        
> apply(x, 1, mean, na.rm = TRUE)
  [1] 2.550 2.375 2.350 2.350 2.550 2.850 2.425 2.525 2.225 2.400 2.700 2.500 2.325 2.125 2.800 3.000 2.750 2.575 2.875 2.675 2.675 2.675 2.350 2.650 2.575 2.450 2.600 2.600
 [29] 2.550 2.425 2.425 2.675 2.725 2.825 2.425 2.400 2.625 2.500 2.225 2.550 2.525 2.100 2.275 2.675 2.800 2.375 2.675 2.350 2.675 2.475 4.075 3.900 4.100 3.275 3.850 3.575
 [57] 3.975 2.900 3.850 3.300 2.875 3.650 3.300 3.775 3.350 3.900 3.650 3.400 3.600 3.275 3.925 3.550 3.800 3.700 3.725 3.850 3.950 4.100 3.725 3.200 3.200 3.150 3.400 3.850
 [85] 3.600 3.875 4.000 3.575 3.500 3.325 3.425 3.775 3.400 2.900 3.450 3.525 3.525 3.675 2.925 3.475 4.525 3.875 4.525 4.150 4.375 4.825 3.400 4.575 4.200 4.850 4.200 4.075
[113] 4.350 3.800 4.025 4.300 4.200 5.100 4.875 3.675 4.525 3.825 4.800 3.925 4.450 4.550 3.900 3.950 4.225 4.400 4.550 5.025 4.250 3.925 3.925 4.775 4.425 4.200 3.900 4.375
[141] 4.450 4.350 3.875 4.550 4.550 4.300 3.925 4.175 4.325 3.950


>  apply(x, 1, function(x) {median(x*2-1, na.rm = TRUE)})
  [1] 3.9 3.4 3.5 3.6 4.0 4.6 3.8 3.9 3.3 3.6 4.2 4.0 3.4 3.1 4.2 4.9 4.2 3.9 4.5 4.3 4.1 4.2 3.6 4.0 4.3 3.6 4.0 4.0 3.8 3.8 3.7 3.9 4.6 4.6 3.6 3.4 3.8 4.0 3.3 3.9 3.8 2.6
 [43] 3.5 4.1 4.7 3.4 4.4 3.6 4.2 3.7 6.9 6.7 7.0 5.3 6.4 6.3 7.0 4.7 6.5 5.6 4.5 6.2 5.2 6.6 5.5 6.5 6.5 5.8 5.7 5.4 7.0 5.8 6.4 6.5 6.2 6.4 6.6 7.0 6.4 5.1 5.2 5.1 5.6 6.8
 [85] 6.5 6.9 6.8 5.7 6.1 5.5 6.0 6.6 5.6 4.6 5.9 6.2 6.1 6.2 4.5 5.9 8.3 6.8 7.9 7.5 7.8 8.6 6.0 8.2 7.3 8.7 7.3 7.0 7.5 6.5 6.9 7.5 7.5 9.5 8.5 6.2 7.9 6.7 8.5 6.6 8.0 8.2
[127] 6.6 6.9 7.4 7.8 7.9 9.2 7.4 6.9 7.2 8.1 8.0 7.6 6.8 7.5 7.7 7.2 6.8 8.1 8.0 7.2 6.5 7.2 7.8 7.1

 

lapply()

lapply(X, FUN, …)는 X를 입력받아 함수를 적용하여 결과값을 반환합니다.

lapply 함수에 입력하는 데이터(X)는 벡터, 리스트 등도 가능하고, 반환되는 값은 리스트입니다.

 

apply는 X의 행이나 열 방향의 데이터가 한꺼번에 함수로 전달되는 반면에, lapply는 X의 데이터 요소가 하나 하나 함수로 전달됩니다. 행렬이나 배열의 요소는 기본적으로 벡터의 요소와 같은 방식이기 때문에 값이 하나 하나 전달됩니다.

# 아래와 같이 코드를 실행하면 x의 평균이 반환되지 않습니다.
# x값이 하나 하나 mean 함수에 전달되어 각각 계산되기 때문입니다.

> x <- matrix(1:6, c(2,3))
> x
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

> lapply(x, mean, na.rm = TRUE)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

[[4]]
[1] 4

[[5]]
[1] 5

[[6]]
[1] 6

 

lapply는 리스트로 반환이 됩니다. 이를 벡터로 변환하고자 한다면 unlist 함수를 적용합니다.

> x <- 1:3
> x
[1] 1 2 3

# 리스트 형태로 반환
> lapply(x, mean)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3


# 리스트 형태로 반환(함수적용)        
> lapply(x, function(x) {x*2+1})
[[1]]
[1] 3

[[2]]
[1] 5

[[3]]
[1] 7

# 리스트를 벡터로 변환하여 반환
> unlist(x)
[1] 1 2 3

> unlist(lapply(x, function(x) {x*2+1}))  
[1] 3 5 7

 

데이터프레임도 lapply 함수에 입력데이터로 사용 가능합니다. 데이터프레임의 각 요소 즉 각 변수별로 한꺼번에 함수에 전달됩니다.

str(iris)
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
> lapply(iris, mean)
$Sepal.Length
[1] 5.843333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333

$Species
[1] NA

Warning message:
In mean.default(X[[i]], ...) :
  인자가 수치형 또는 논리형이 아니므로 NA를 반환합니다

 

리스트가 입력되면 리스트의 각 요소가 한꺼번에 함수에 전달됩니다. 예를 들어 mean함수를 사용하면 리스트 각 요소별 평균값을 반환합니다.

> x <- list(a = 1:10, 
+           beta = exp(-3:3), 
+           logic = c(TRUE,FALSE,FALSE,TRUE))

> x
$a
 [1]  1  2  3  4  5  6  7  8  9 10

$beta
[1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183  7.38905610 20.08553692

$logic
[1]  TRUE FALSE FALSE  TRUE

> lapply(x, mean)
$a
[1] 5.5

$beta
[1] 4.535125

$logic
[1] 0.5

> lapply(x, quantile, probs = (1:3)/4)
$a
 25%  50%  75% 
3.25 5.50 7.75 

$beta
      25%       50%       75% 
0.2516074 1.0000000 5.0536690 

$logic
25% 50% 75% 
0.0 0.5 1.0

 

sapply()

sapply(X, FUN, …, simplify = TRUE, USE.NAMES = TRUE)는 단순화된(simplify) lapply 함수라 할 수 있습니다.

lapply는 리스트 형태로 반환되기 때문에 사용하기 불편한 점이 있는데 sapply는 기본적으로 벡터나 행렬 형태로 반환합니다. 만일 옵션 simplify = FALSE이면 lapply와 동일하게 리스트 형태로 반환됩니다.

> x <- 1:3
> x
[1] 1 2 3

> sapply(x, function(x) {x*2+1})
[1] 3 5 7
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
> sapply(iris, mean, na.rm = TRUE)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
    5.843333     3.057333     3.758000     1.199333           NA 
Warning message:
In mean.default(X[[i]], ...) :
  인자가 수치형 또는 논리형이 아니므로 NA를 반환합니다
> x <- list(a = 1:10, 
+           beta = exp(-3:3), 
+           logic = c(TRUE,FALSE,FALSE,TRUE))
> x
$a
 [1]  1  2  3  4  5  6  7  8  9 10

$beta
[1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183  7.38905610 20.08553692

$logic
[1]  TRUE FALSE FALSE  TRUE

> sapply(x, mean)
       a     beta    logic 
5.500000 4.535125 0.500000

> sapply(x, quantile, probs = (1:3)/4)
       a      beta logic
25% 3.25 0.2516074   0.0
50% 5.50 1.0000000   0.5
75% 7.75 5.0536690   1.0

 

vapply()

vapply(X, FUN, FUN.VALUE, …, USE.NAMES = TRUE)는 sapply와 유사합니다. 차이점은 반환되는 결과의 양식을 지정할 수 있습니다.

> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
# sapply로 fivenum 출력
> sapply(iris[, 1:4], fivenum, na.rm = TRUE)
     Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,]          4.3         2.0         1.00         0.1
[2,]          5.1         2.8         1.60         0.3
[3,]          5.8         3.0         4.35         1.3
[4,]          6.4         3.3         5.10         1.8
[5,]          7.9         4.4         6.90         2.5

> vapply(iris[, 1:4], fivenum, 
+ c("최소값" = 0, "1사분위수" = 0, "중위수" = 0, 
+   "3사분위수" = 0, "최대값" = 0),
+ na.rm = TRUE)
          Sepal.Length Sepal.Width Petal.Length Petal.Width
최소값             4.3         2.0         1.00         0.1
1사분위수          5.1         2.8         1.60         0.3
중위수             5.8         3.0         4.35         1.3
3사분위수          6.4         3.3         5.10         1.8
최대값             7.9         4.4         6.90         2.5

 

tapply()

tapply(X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)는 요인(factor) 변수를 기준으로 그룹별로 나누어서 함수를 적용합니다. X는 벡터입니다. INDEX는 요인 또는 요인 리스트가 들어가는 인수입니다. 만일 X에 데이터프레임을 넣고 싶으면 by()함수를 쓰면 됩니다.

# iris 데이터의 요인변수 Species 별로 평균 구하기
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
> tapply(iris$Sepal.Length, iris$Species, mean, na.rm = TRUE)
    setosa versicolor  virginica 
     5.006      5.936      6.588
# 팩터변수가 2개가 되면 교차표가 만들어짐
# x[, -1]은 wool과 tension으로 구성된 데이터프레임
> str(x <- warpbreaks)
'data.frame':	54 obs. of  3 variables:
 $ breaks : num  26 30 54 25 70 52 51 26 67 18 ...
 $ wool   : Factor w/ 2 levels "A","B": 1 1 1 1 1 1 1 1 1 1 ...
 $ tension: Factor w/ 3 levels "L","M","H": 1 1 1 1 1 1 1 1 1 2 ...
 
> tapply(x$breaks, x[, -1], sum)
    tension
wool   L   M   H
   A 401 216 221
   B 254 259 169
# 데이터 무작위 30개 만들어 x에 할당
> set.seed(234)
> x <- c(rnorm(10), runif(10), rnorm(10, 1))
> str(x)
 num [1:30] 0.661 -2.053 -1.499 1.471 1.459 ...

# 팩터 변수 생성(값 30개)
# gl(n, k)는 1부터 n까지 정수로된 팩터 레벨을 k만큼 반복해서 만듬 
# factor(rep(1:3, each = 10), levels = 1:3)와 동일
> fac <- gl(3, 10)   
> fac
 [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3
Levels: 1 2 3
  
> tapply(x, fac, mean)
         1          2          3 
-0.4222616  0.6059194  1.1161074 

> tapply(x, fac, range)
$`1`
[1] -3.036090  1.471233

$`2`
[1] 0.1917125 0.8825091

$`3`
[1] 0.06992777 2.01628335

 

mapply()

mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)는 sapply의 다변량 버전이라 할 수 있습니다. 여러 개의 인자를 입력할 수 있고, 반환하는 값은 리스트, 벡터, 배열 등이 가능합니다. MoreArgs는 함수에 들어갈 또다른 인수 리스트입니다.

# rep(1, 4), rep(2, 3), ..., rep(x = 4, times = 1)  => 리스트 반환
mapply(rep, 1:4, 4:1) 
#> [[1]]
#> [1] 1 1 1 1
#> 
#> [[2]]
#> [1] 2 2 2
#> 
#> [[3]]
#> [1] 3 3
#> 
#> [[4]]
#> [1] 4

# rep(1, 3), rep(2, 3), ..., rep(x = 4, times = 3)  => 행렬 반환
mapply(rep, 1:4, 3)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    2    3    4
#> [2,]    1    2    3    4
#> [3,]    1    2    3    4

# rep(times = 1, x = 4), rep(times = 2, x = 3), ... 
mapply(rep, times = 1:4, x = 4:1)
#> [[1]]
#> [1] 4
#> 
#> [[2]]
#> [1] 3 3
#> 
#> [[3]]
#> [1] 2 2 2
#> 
#> [[4]]
#> [1] 1 1 1 1

# rep(x = 23, times = 1), rep(x = 23, times = 2), ...  
mapply(rep, 1:4, MoreArgs = list(x = 23))
#> [[1]]
#> [1] 23
#> 
#> [[2]]
#> [1] 23 23
#> 
#> [[3]]
#> [1] 23 23 23
#> 
#> [[4]]
#> [1] 23 23 23 23
# sprintf(" %d%s ", 1, "a"), ....
mapply(function(i, s) { 
  sprintf(" %d%s ", i, s) 
  }, 
  i = 1:3, s = c("a", "b", "c"))
#> [1] " 1a " " 2b " " 3c "

MoreArgs 인수는 여러 개의 데이터를 한꺼번에 함수에 넣어줍니다. 이 것을 사용하지 않으면 데이터가 하나씩 함수에 들어갑니다.

sumsq <- function(x, mean = 0, sd = 1) {
  sum(((x - mean) / sd)^2)
}

set.seed(123)
x <- rnorm(10000)

# sumsq(x, mean = 1, sd = 1), sumsq(x, mean = 2, sd = 2), ...
mapply(sumsq, mean = 1:100, sd = 1:100, MoreArgs = list(x = x))
#>   [1] 20019 12517 11124 10635 10408 10285 10210 10162 10128 10104 10087 10073
#>  [13] 10063 10054 10047 10042 10037 10033 10030 10027 10025 10023 10021 10019
#>  [25] 10018 10017 10015 10014 10013 10013 10012 10011 10011 10010 10009 10009
#>  [37] 10009 10008 10008 10007 10007 10007 10006 10006 10006 10006 10006 10005
#>  [49] 10005 10005 10005 10005 10004 10004 10004 10004 10004 10004 10004 10004
#>  [61] 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003
#>  [73] 10003 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [85] 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [97] 10002 10002 10001 10001

# 만일 이렇게 mapply(sumsq, mean = 1:100, sd = 1:100, x = x) 하면
# sumsq(x = x[1], mean = 1, sd = 1), ... 이런식으로 계산되어
# 원하는 결과가 나오지 않음. 여기서 x[1]은 -0.560475647임

 

eapply()

eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE)는 환경(Environment)에 있는 변수나 함수들을 반복 계산해 줍니다.

# 새로운 환경 만들기; R에서 전역환경은 .GlobalEnv 임
env <- new.env()
env$a <- 10
env$b <- 20:23
env$c <- 30:35

# 전역변수 자료형 확인하기
eapply(env, typeof)
#> $a
#> [1] "double"
#> 
#> $b
#> [1] "integer"
#> 
#> $c
#> [1] "integer"

# env 환경변수의 각 요소에 2를 곱하기
eapply(env, function(x) {x * 2})
#> $a
#> [1] 20
#> 
#> $b
#> [1] 40 42 44 46
#> 
#> $c
#> [1] 60 62 64 66 68 70

 

by()

by(data, INDICES, FUN, …, simplify = TRUE)는 데이터프레임을 위한 tapply라 할 수 있다. 요인(factor) 변수를 기준으로 그룹별로 나누어서 함수를 적용합니다. data는 데이터프레임이나 행렬입니다. INDEX는 요인 또는 요인 리스트가 들어가는 인수입니다.

str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
by(iris, iris$Species, summary)
#> iris$Species: setosa
#>   Sepal.Length   Sepal.Width    Petal.Length   Petal.Width          Species  
#>  Min.   :4.30   Min.   :2.30   Min.   :1.00   Min.   :0.100   setosa    :50  
#>  1st Qu.:4.80   1st Qu.:3.20   1st Qu.:1.40   1st Qu.:0.200   versicolor: 0  
#>  Median :5.00   Median :3.40   Median :1.50   Median :0.200   virginica : 0  
#>  Mean   :5.01   Mean   :3.43   Mean   :1.46   Mean   :0.246                  
#>  3rd Qu.:5.20   3rd Qu.:3.67   3rd Qu.:1.57   3rd Qu.:0.300                  
#>  Max.   :5.80   Max.   :4.40   Max.   :1.90   Max.   :0.600                  
#> ------------------------------------------------------------ 
#> iris$Species: versicolor
#>   Sepal.Length   Sepal.Width    Petal.Length   Petal.Width         Species  
#>  Min.   :4.90   Min.   :2.00   Min.   :3.00   Min.   :1.00   setosa    : 0  
#>  1st Qu.:5.60   1st Qu.:2.52   1st Qu.:4.00   1st Qu.:1.20   versicolor:50  
#>  Median :5.90   Median :2.80   Median :4.35   Median :1.30   virginica : 0  
#>  Mean   :5.94   Mean   :2.77   Mean   :4.26   Mean   :1.33                  
#>  3rd Qu.:6.30   3rd Qu.:3.00   3rd Qu.:4.60   3rd Qu.:1.50                  
#>  Max.   :7.00   Max.   :3.40   Max.   :5.10   Max.   :1.80                  
#> ------------------------------------------------------------ 
#> iris$Species: virginica
#>   Sepal.Length   Sepal.Width    Petal.Length   Petal.Width         Species  
#>  Min.   :4.90   Min.   :2.20   Min.   :4.50   Min.   :1.40   setosa    : 0  
#>  1st Qu.:6.22   1st Qu.:2.80   1st Qu.:5.10   1st Qu.:1.80   versicolor: 0  
#>  Median :6.50   Median :3.00   Median :5.55   Median :2.00   virginica :50  
#>  Mean   :6.59   Mean   :2.97   Mean   :5.55   Mean   :2.03                  
#>  3rd Qu.:6.90   3rd Qu.:3.17   3rd Qu.:5.88   3rd Qu.:2.30                  
#>  Max.   :7.90   Max.   :3.80   Max.   :6.90   Max.   :2.50
# 팩터변수가 2개가 되면 그만큼 그룹으로 더 나뉘어짐
# x[, -1]은 wool과 tension으로 구성된 데이터프레임
str(warpbreaks)
#> 'data.frame':    54 obs. of  3 variables:
#>  $ breaks : num  26 30 54 25 70 52 51 26 67 18 ...
#>  $ wool   : Factor w/ 2 levels "A","B": 1 1 1 1 1 1 1 1 1 1 ...
#>  $ tension: Factor w/ 3 levels "L","M","H": 1 1 1 1 1 1 1 1 1 2 ...
by(warpbreaks[, 1], warpbreaks[, -1], summary)
#> wool: A
#> tension: L
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    25.0    26.0    51.0    44.6    54.0    70.0 
#> ------------------------------------------------------------ 
#> wool: B
#> tension: L
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    14.0    20.0    29.0    28.2    31.0    44.0 
#> ------------------------------------------------------------ 
#> wool: A
#> tension: M
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>      12      18      21      24      30      36 
#> ------------------------------------------------------------ 
#> wool: B
#> tension: M
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    16.0    21.0    28.0    28.8    39.0    42.0 
#> ------------------------------------------------------------ 
#> wool: A
#> tension: H
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    10.0    18.0    24.0    24.6    28.0    43.0 
#> ------------------------------------------------------------ 
#> wool: B
#> tension: H
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    13.0    15.0    17.0    18.8    21.0    28.0

 

split()

  • split(x, f, drop = FALSE, ...)은 f에 정의된 그룹(보통 팩터 또는 리스트)에 따라 벡터 x를 분리합니다. 반환되는 값은 리스트입니다.
  • unsplit(value, f, drop = FALSE)은 분리된 벡터 리스트 또는 데이터프레임인 value를 f에 정의된 그룹기준으로 원상태로 되돌립니다. 반환되는 값은 벡터나 데이터프레임입니다.
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

# iris 데이터프레임을 Species 기준으로 분리합니다. (결과 리스트 형태) 
x <- split(iris, iris$Species)
str(x)
#> List of 3
#>  $ setosa    :'data.frame':  50 obs. of  5 variables:
#>   ..$ Sepal.Length: num [1:50] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>   ..$ Sepal.Width : num [1:50] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>   ..$ Petal.Length: num [1:50] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>   ..$ Petal.Width : num [1:50] 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>   ..$ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ versicolor:'data.frame':  50 obs. of  5 variables:
#>   ..$ Sepal.Length: num [1:50] 7 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 ...
#>   ..$ Sepal.Width : num [1:50] 3.2 3.2 3.1 2.3 2.8 2.8 3.3 2.4 2.9 2.7 ...
#>   ..$ Petal.Length: num [1:50] 4.7 4.5 4.9 4 4.6 4.5 4.7 3.3 4.6 3.9 ...
#>   ..$ Petal.Width : num [1:50] 1.4 1.5 1.5 1.3 1.5 1.3 1.6 1 1.3 1.4 ...
#>   ..$ Species     : Factor w/ 3 levels "setosa","versicolor",..: 2 2 2 2 2 2 2 2 2 2 ...
#>  $ virginica :'data.frame':  50 obs. of  5 variables:
#>   ..$ Sepal.Length: num [1:50] 6.3 5.8 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 ...
#>   ..$ Sepal.Width : num [1:50] 3.3 2.7 3 2.9 3 3 2.5 2.9 2.5 3.6 ...
#>   ..$ Petal.Length: num [1:50] 6 5.1 5.9 5.6 5.8 6.6 4.5 6.3 5.8 6.1 ...
#>   ..$ Petal.Width : num [1:50] 2.5 1.9 2.1 1.8 2.2 2.1 1.7 1.8 1.8 2.5 ...
#>   ..$ Species     : Factor w/ 3 levels "setosa","versicolor",..: 3 3 3 3 3 3 3 3 3 3 ...

# 분리된 것을 다시 원상태로 돌립니다. (결과 데이터프레임)
y <- unsplit(x, iris$Species)
str(y)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

두 개 칼럼을 기준으로 분리할 수 있습니다. 2개 이상의 칼럼을 기준으로 할 때에는 리스트 구조로 묶어줍니다.

str(infert)  # R 내장 데이터 infert
#> 'data.frame':    248 obs. of  8 variables:
#>  $ education     : Factor w/ 3 levels "0-5yrs","6-11yrs",..: 1 1 1 1 2 2 2 2 2 2 ...
#>  $ age           : num  26 42 39 34 35 36 23 32 21 28 ...
#>  $ parity        : num  6 1 6 4 3 4 1 2 1 2 ...
#>  $ induced       : num  1 1 2 2 1 2 0 0 0 0 ...
#>  $ case          : num  1 1 1 1 1 1 1 1 1 1 ...
#>  $ spontaneous   : num  2 0 0 0 1 1 0 0 1 0 ...
#>  $ stratum       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ pooled.stratum: num  3 1 4 2 32 36 6 22 5 19 ...

x <- split(infert, list(infert$spontaneous, infert$education))
str(x, max.level = 1)
#> List of 9
#>  $ 0.0-5yrs :'data.frame':   9 obs. of  8 variables:
#>  $ 1.0-5yrs :'data.frame':   1 obs. of  8 variables:
#>  $ 2.0-5yrs :'data.frame':   2 obs. of  8 variables:
#>  $ 0.6-11yrs:'data.frame':   71 obs. of  8 variables:
#>  $ 1.6-11yrs:'data.frame':   33 obs. of  8 variables:
#>  $ 2.6-11yrs:'data.frame':   16 obs. of  8 variables:
#>  $ 0.12+ yrs:'data.frame':   61 obs. of  8 variables:
#>  $ 1.12+ yrs:'data.frame':   37 obs. of  8 variables:
#>  $ 2.12+ yrs:'data.frame':   18 obs. of  8 variables:

행의 갯수를 지정해서 데이터를 분리할 수 있습니다. 인수 f에 데이터를 나눌 기준이 될 벡터을 만들면 됩니다. 그러면 같은 수를 가진 행끼리 하나의 그룹으로 만들어집니다. 단, 이 기준 벡터의 수와 데이터의 갯수가 같아야 오류가 나지 않습니다.

# infert 데이터의 전체 행을 10개씩 구분하는 기준 벡터를 만듭니다. 
df <- iris     # 데이터
gnum <- 10     # 그룹의 갯수
f <- c(rep(1:floor((nrow(df)/gnum)), each = gnum),
       rep(ceiling(nrow(df)/gnum), each = nrow(df)%%gnum))

x <- split(df, f)
print(x[1:2]); cat("... 이하 생략 ...")
#> $`1`
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1           5.1         3.5          1.4         0.2  setosa
#> 2           4.9         3.0          1.4         0.2  setosa
#> 3           4.7         3.2          1.3         0.2  setosa
#> 4           4.6         3.1          1.5         0.2  setosa
#> 5           5.0         3.6          1.4         0.2  setosa
#> 6           5.4         3.9          1.7         0.4  setosa
#> 7           4.6         3.4          1.4         0.3  setosa
#> 8           5.0         3.4          1.5         0.2  setosa
#> 9           4.4         2.9          1.4         0.2  setosa
#> 10          4.9         3.1          1.5         0.1  setosa
#> 
#> $`2`
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 11          5.4         3.7          1.5         0.2  setosa
#> 12          4.8         3.4          1.6         0.2  setosa
#> 13          4.8         3.0          1.4         0.1  setosa
#> 14          4.3         3.0          1.1         0.1  setosa
#> 15          5.8         4.0          1.2         0.2  setosa
#> 16          5.7         4.4          1.5         0.4  setosa
#> 17          5.4         3.9          1.3         0.4  setosa
#> 18          5.1         3.5          1.4         0.3  setosa
#> 19          5.7         3.8          1.7         0.3  setosa
#> 20          5.1         3.8          1.5         0.3  setosa
#> ... 이하 생략 ...
# 위 예제에서 분리된 것을 같은 기준으로 하나로 묶습니다.
y <- unsplit(x, f)
str(y)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

 

Vectorize()

Vectorize(FUN, vectorize.args = arg.names, SIMPLIFY = TRUE, USE.NAMES = TRUE) 함수는 인자 FUN에 들어간 함수가 벡터 방식으로 작동하도록 만드는 함수입니다. 함수 중에는 함수의 인자가 단 하나의 값만 들어가야 하는 경우가 있어 인자에 여러 개의 값 즉 벡터형식으로 넣을 수 없는 함수들이 있습니다. Vectorize() 함수는 이 문제를 해결하여 줍니다. 이때 벡터형식으로 들어갈 인자를 vectorize.args에 지정합니다. 반환되는 결과는 벡터로 들어간 인자들이 각각 투입된 만큼의 결과들이 행렬이나 배열로 묶여서 나옵니다.

아래 예제는 iris$Species에서 vi나 se로 시작하는 행을 추출하고자 하는 것입니다.

# grepl함수는 pattern 인수에 단 하나의 값만 넣을 수 있습니다. 
# 만일 아래 같이 벡터형식으로 값을 여러 개 넣으면 첫번째 요소만 사용되어
# 원하는 결과를 얻을 수없습니다.
x <- grepl(pattern = c("^vi", "^se"), x = iris$Species)
#> Warning in grepl(pattern = c("^vi", "^se"), x = iris$Species): 인자 'pattern'는
#> 반드시 길이가 1 보다 커야 하고, 오로지 첫번째 요소만이 사용될 것입니다
head(x)
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE

# grepl 함수를 벡터작업이 가능한 함수 vgrepl로 만듭니다. 
# 벡터로 들어갈 인수는 pattern으로 지정합니다.
vgrepl <- Vectorize(grepl, vectorize.args = "pattern")
x <- vgrepl(pattern = c("^vi", "^se"), x = iris$Species)
head(x)
#>        ^vi  ^se
#> [1,] FALSE TRUE
#> [2,] FALSE TRUE
#> [3,] FALSE TRUE
#> [4,] FALSE TRUE
#> [5,] FALSE TRUE
#> [6,] FALSE TRUE

# 조건에 맞는 행들을 추출합니다.
str(iris[rowSums(x) > 0, ])
#> 'data.frame':    100 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

물론 Vectorize() 함수를 사용하지 않고 sapply() 함수를 사용하여 같은 결과를 얻을 수 있습니다.

x <- sapply(c("^vi", "^se"),
            function(pattern) {grepl(pattern = pattern, x = iris$Species)})
head(x)
#>        ^vi  ^se
#> [1,] FALSE TRUE
#> [2,] FALSE TRUE
#> [3,] FALSE TRUE
#> [4,] FALSE TRUE
#> [5,] FALSE TRUE
#> [6,] FALSE TRUE
sumsq <- function(x, mean = 0, sd = 1) {
  sum(((x - mean) / sd)^2)}

set.seed(123)
x <- rnorm(10000)

# mapply(sumsq, mean = 1:100, sd = 1:100, MoreArgs = list(x = x))와 같은 결과
vsumsq <- Vectorize(sumsq, c("mean", "sd"))
vsumsq(x, 1:100, 1:100)
#>   [1] 20019 12517 11124 10635 10408 10285 10210 10162 10128 10104 10087 10073
#>  [13] 10063 10054 10047 10042 10037 10033 10030 10027 10025 10023 10021 10019
#>  [25] 10018 10017 10015 10014 10013 10013 10012 10011 10011 10010 10009 10009
#>  [37] 10009 10008 10008 10007 10007 10007 10006 10006 10006 10006 10006 10005
#>  [49] 10005 10005 10005 10005 10004 10004 10004 10004 10004 10004 10004 10004
#>  [61] 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003
#>  [73] 10003 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [85] 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [97] 10002 10002 10001 10001
sumsq <- function(x, mean = 0, sd = 1) {
  sum(((x - mean) / sd)^2)
}

set.seed(123)
x <- rnorm(10000)

# sumsq(x, mean = 1, sd = 1), sumsq(x, mean = 2, sd = 2), ...
mapply(sumsq, mean = 1:100, sd = 1:100, MoreArgs = list(x = x))
#>   [1] 20019 12517 11124 10635 10408 10285 10210 10162 10128 10104 10087 10073
#>  [13] 10063 10054 10047 10042 10037 10033 10030 10027 10025 10023 10021 10019
#>  [25] 10018 10017 10015 10014 10013 10013 10012 10011 10011 10010 10009 10009
#>  [37] 10009 10008 10008 10007 10007 10007 10006 10006 10006 10006 10006 10005
#>  [49] 10005 10005 10005 10005 10004 10004 10004 10004 10004 10004 10004 10004
#>  [61] 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003 10003
#>  [73] 10003 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [85] 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002 10002
#>  [97] 10002 10002 10001 10001

# 만일 이렇게 mapply(sumsq, mean = 1:100, sd = 1:100, x = x) 하면
# sumsq(x = x[1], mean = 1, sd = 1), ... 이런식으로 계산되어
# 원하는 결과가 나오지 않음. 여기서 x[1]은 -0.560475647임

 

replicate()

replicate(n, expr, simplify = "array") 함수는 sapply() 함수의 간소화 버전이라 할 수 있습니다. expr 인자를 n번 만큼 반복수행합니다. expr 인자에는 주로 함수들이 많이 들어갑니다. 반환되는 결과는 기본적으로 배열입니다. 만일 반환되는 값을 리스트 형태로 받고자 한다면 simplify = FALSE로 지정합니다.

아래 예제는 rnorm(5, mean = 0, sd = 1)을 3번 반복하여 그 결과를 행렬형태로 얻는 것입니다.

set.seed(123)
replicate(n = 3, rnorm(5, mean = 0, sd = 1))
#>         [,1]   [,2]   [,3]
#> [1,] -0.5605  1.715  1.224
#> [2,] -0.2302  0.461  0.360
#> [3,]  1.5587 -1.265  0.401
#> [4,]  0.0705 -0.687  0.111
#> [5,]  0.1293 -0.446 -0.556

 

rep()

rep(x, ...) 함수는 특정값을 반복해서 만들어 반환해주는 함수입니다. ...에는 인자로 times, length.out, each가 들어갈 수 있습니다. times는 x를 몇 번 반복할지를 정해주는 인수입니다. length.out은 최종 반환되는 값들의 길이를 정해주는 인수입니다. each는 x가 여러개의 값으로 구성된 벡터일 경우 각각 몇 번 반복할지를 정해주는 인수 입니다.

rep.int(x, times)와 rep_len(x, length.out)는 rep() 함수와 기능이 거의 같으나 인수가 제한됩니다. 이 함수들은 rep() 함수보다 더 빠릅니다.

rep(1:5, 2)   # rep(x = 1:4, times = 2)와 동일
#>  [1] 1 2 3 4 5 1 2 3 4 5
rep(1:5, each = 2)    
#>  [1] 1 1 2 2 3 3 4 4 5 5
rep(1:5, c(2, 3, 2, 1, 2))  # rep(1:5, times = c(2, 3, 2, 1, 2)) 동일
#>  [1] 1 1 2 2 2 3 3 4 5 5

rep(1:5, each = 2, len = 5)
#> [1] 1 1 2 2 3
rep(1:2, each = 2, len = 7)  # 길이를 채우기 위해 다시 반복
#> [1] 1 1 2 2 1 1 2
rep(1:3, each = 2, times =3)
#>  [1] 1 1 2 2 3 3 1 1 2 2 3 3 1 1 2 2 3 3

rep(2, 5.9)  # 소수점 이하는 버리고 반복
#> [1] 2 2 2 2 2

rep(c("abc", "123"), times = 3)
#> [1] "abc" "123" "abc" "123" "abc" "123"
rep(c("abc", "123"), each = 3)
#> [1] "abc" "abc" "abc" "123" "123" "123"

rep.int(c("abc", "123"), 3)  # 기본인자는 times임. each 인자는 사용할 수 없음
#> [1] "abc" "123" "abc" "123" "abc" "123"
rep_len(c("abc", "123"), 5)  # 기본인자는 length.out임
#> [1] "abc" "123" "abc" "123" "abc"

 

seq()

seq(...) 함수는 일련번호 숫자를 생성하여 반환합니다. ...에는 인자로 from, to, by, length.out, along.with가 들어갈 수 있습니다.

  • from : 시작 숫자
  • to : 끝 숫자
  • by : 증가 간격
  • length.out : 생성되는 일련번호의 길이
  • along.with : 인자에 들어가는 값들의 길이만큼 일련번호 생성

seq.int()는 seq() 함수와 거의 동일하고, seq_along(along.with)와 seq_len(length.out)는 무조건 시작이 1이고, 각각 하나의 인자를 받아 일련번호를 생성합니다. 이 함수들은 1부터 시작하는 일련번호를 seq() 함수보다 약간 더 빠르게 만들어 줍니다.

가장 많이 사용하는 형태는 다음과 같습니다.

  • seq(from, to)
  • seq(from, to, by = )
  • seq(from, to, length.out = )
  • seq(along.with = )
  • seq(from)
  • seq(length.out = )
# seq(from = 2, to = 7과 동일)
> seq(2, 7)
[1] 2 3 4 5 6 7

> seq(7, 2)
[1] 7 6 5 4 3 2

> seq(2, 10, by = 2)
[1]  2  4  6  8 10

> seq(2, 10, by = pi)
[1] 2.000000 5.141593 8.283185

> seq(2, 10, length.out = 3)
[1]  2  6 10

# c(2:7)의 길이는 6이므로 6개 생성
> seq(along.with = c(2:7))
[1] 1 2 3 4 5 6

> seq(2, 10, along.with = c(2:7))
[1]  2.0  3.6  5.2  6.8  8.4 10.0

# 1부터 7까지 생성
> seq(7)
[1] 1 2 3 4 5 6 7

> seq(length.out = 12)
 [1]  1  2  3  4  5  6  7  8  9 10 11 12
> seq.int(2, 10, by = pi)
[1] 2.000000 5.141593 8.283185

# seq(length.out = 12)와 동일하나 약간 더 빠름
> seq_len(12)    
 [1]  1  2  3  4  5  6  7  8  9 10 11 12
 
> seq_along(c(2:7))
[1] 1 2 3 4 5 6
n <- 999999999999999

start <- proc.time()
x <- seq(length.out = n)
end <- proc.time()
end - start
#>    user  system elapsed 
#>       0       0       0

start <- proc.time()
x <- seq_len(n)
end <- proc.time()
end - start
#>    user  system elapsed 
#>       0       0       0

일련 날짜를 생성할 수 있습니다.

# 1년 간격
seq(as.Date("2001/1/1"), as.Date("2020/1/1"), "years")
#>  [1] "2001-01-01" "2002-01-01" "2003-01-01" "2004-01-01" "2005-01-01"
#>  [6] "2006-01-01" "2007-01-01" "2008-01-01" "2009-01-01" "2010-01-01"
#> [11] "2011-01-01" "2012-01-01" "2013-01-01" "2014-01-01" "2015-01-01"
#> [16] "2016-01-01" "2017-01-01" "2018-01-01" "2019-01-01" "2020-01-01"

# 월 간격
seq(as.Date("2001/1/1"), by = "month", length.out = 12)
#>  [1] "2001-01-01" "2001-02-01" "2001-03-01" "2001-04-01" "2001-05-01"
#>  [6] "2001-06-01" "2001-07-01" "2001-08-01" "2001-09-01" "2001-10-01"
#> [11] "2001-11-01" "2001-12-01"

# 분기 간격
seq(as.Date("2015/1/1"), as.Date("2020/1/1"), by = "quarter")
#>  [1] "2015-01-01" "2015-04-01" "2015-07-01" "2015-10-01" "2016-01-01"
#>  [6] "2016-04-01" "2016-07-01" "2016-10-01" "2017-01-01" "2017-04-01"
#> [11] "2017-07-01" "2017-10-01" "2018-01-01" "2018-04-01" "2018-07-01"
#> [16] "2018-10-01" "2019-01-01" "2019-04-01" "2019-07-01" "2019-10-01"
#> [21] "2020-01-01"

# 월 간격 반대로
seq(as.Date("2020-1-7"), as.Date("2019-1-17"), by = "-1 month")
#>  [1] "2020-01-07" "2019-12-07" "2019-11-07" "2019-10-07" "2019-09-07"
#>  [6] "2019-08-07" "2019-07-07" "2019-06-07" "2019-05-07" "2019-04-07"
#> [11] "2019-03-07" "2019-02-07"
seq(ISOdate(2015,1,1), ISOdate(2020,1,1), "years")
#> [1] "2015-01-01 12:00:00 GMT" "2016-01-01 12:00:00 GMT"
#> [3] "2017-01-01 12:00:00 GMT" "2018-01-01 12:00:00 GMT"
#> [5] "2019-01-01 12:00:00 GMT" "2020-01-01 12:00:00 GMT"

seq(c(ISOdate(2020,3,20)), by = "DSTday", length.out = 10)
#>  [1] "2020-03-20 21:00:00 KST" "2020-03-21 21:00:00 KST"
#>  [3] "2020-03-22 21:00:00 KST" "2020-03-23 21:00:00 KST"
#>  [5] "2020-03-24 21:00:00 KST" "2020-03-25 21:00:00 KST"
#>  [7] "2020-03-26 21:00:00 KST" "2020-03-27 21:00:00 KST"
#>  [9] "2020-03-28 21:00:00 KST" "2020-03-29 21:00:00 KST"

seq(c(ISOdate(2020,3,20)), by = "7 DSTdays", length.out = 5)
#> [1] "2020-03-20 21:00:00 KST" "2020-03-27 21:00:00 KST"
#> [3] "2020-04-03 21:00:00 KST" "2020-04-10 21:00:00 KST"
#> [5] "2020-04-17 21:00:00 KST"
seq(as.POSIXct("2015-3-14 17:22:15"), as.POSIXct("2015-3-15 9:12:25"),
    by = "hours")
#>  [1] "2015-03-14 17:22:15 KST" "2015-03-14 18:22:15 KST"
#>  [3] "2015-03-14 19:22:15 KST" "2015-03-14 20:22:15 KST"
#>  [5] "2015-03-14 21:22:15 KST" "2015-03-14 22:22:15 KST"
#>  [7] "2015-03-14 23:22:15 KST" "2015-03-15 00:22:15 KST"
#>  [9] "2015-03-15 01:22:15 KST" "2015-03-15 02:22:15 KST"
#> [11] "2015-03-15 03:22:15 KST" "2015-03-15 04:22:15 KST"
#> [13] "2015-03-15 05:22:15 KST" "2015-03-15 06:22:15 KST"
#> [15] "2015-03-15 07:22:15 KST" "2015-03-15 08:22:15 KST"

 

sequence()

sequence(nvec, ...) 함수는 연속적인 숫자를 만들어 내는 함수입니다. 인자 nvec에 들어간 숫자 벡터들 만큼 seq() 함수가 수행되어 연속적인 숫자가 만들어집니다. 예를 들면 nvec = c(3, 2)이면 seq(3), seq(2)가 수행됩니다.

 # c(seq(3), seq(2)) 또는 c(1:3, 1:2)와 동일

> sequence(nvec = c(3, 2))
[1] 1 2 3 1 2

# (1,2,3)(1,2)(1,2,3,4,5)
> sequence(nvec = c(3, 2, 5))
 [1] 1 2 3 1 2 1 2 3 4 5
 
 # 시작숫자 지정
 #(2,3,4)(2,3)
> sequence(c(3, 2), from = 2)
[1] 2 3 4 2 3

# 시작숫자, 간격 지정
#(2,0,-2)(2,0)
> sequence(c(3, 2), from = 2, by = -2)  
[1]  2  0 -2  2  0

#(1,3,5)(1,-1)
sequence(c(3, 2), by = c(2, -2))
#> [1]  1  3  5  1 -1

 

gl()

gl(n, k, length = n*k, labels = seq_len(n), ordered = FALSE) 함수는 요인 수준을 반복적으로 생성하여 반환합니다. gl은 generate levels의 약자로 생각됩니다.

  • n : 수준을 나태내는 숫자. n = 2이면 label을 따로 지정하지 않은 경우 수준이 1, 2로 표현됩니다.
  • k : 수준을 반복할 숫자. k = 8이면 8번 반복합니다.
  • length : 생성되는 총 데이터의 갯수
  • labels : 수준에 대한 이름
  • ordered : TRUE이면 요인의 순서가 정해집니다. (순서형 데이터)
# 수준 1, 2를 각각 8번 반복
> gl(n = 2, k = 8)
 [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
Levels: 1 2

# 수준 이름 지정
> gl(n = 2, k = 8, labels = c("Contol", "Treat")) 
 [1] Contol Contol Contol Contol Contol Contol Contol Contol Treat  Treat  Treat  Treat  Treat  Treat  Treat  Treat 
Levels: Contol Treat

# 수준 1, 2가 각각 1번씩 반복되며 10개 생성
> gl(n = 2, k = 1, length = 10)  
 [1] 1 2 1 2 1 2 1 2 1 2
Levels: 1 2

# 수준 1, 2가 각각 2번씩 반복되며 10개 생성
> gl(n = 2, k = 2, length = 10)
 [1] 1 1 2 2 1 1 2 2 1 1
Levels: 1 2

# 수준 1, 2가 M과 F로 순서있게 네이밍 되고, 각각 3번씩 반복되며 10개 생성
> gl(2, 3, 10, labels = c("M", "F"), ordered = TRUE)
 [1] M M M F F F M M M F
Levels: M < F

 

sweep()

sweep(x, MARGIN, STATS, FUN = "-", check.margin = TRUE, ...) 함수는 행렬, 데이터프레임 등에 통계량과 함수를 적용하여 그 결과를 반환하는 함수입니다. 데이터 x에 일률적으로 특정 숫자만큼 더하거나 빼기를 할 때 많이 사용합니다.

  • x : 입력 데이터로서 배열, 행렬, 데이터프레임
  • MARGIN : 행과 열 방향 지정. 1이면 열방향, 2이면 행방향
  • STATS : 적용할 요약 통계량 또는 수치
  • FUN : 적용할 함수 (기본값은 “-” 임)
> x <- matrix(1:12, ncol = 3);
> x
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

# 행렬 x를 행방향(2)으로 1씩 빼줌
> sweep(x, MARGIN = 2, STATS = 1, FUN = "-")
     [,1] [,2] [,3]
[1,]    0    4    8
[2,]    1    5    9
[3,]    2    6   10
[4,]    3    7   11

# 행렬 x를 열방향(1)으로 각각 1, 2, 3, 4를 더해줌
 sweep(x, 1, c(1,2,3,4), FUN = "+")  
     [,1] [,2] [,3]
[1,]    2    6   10
[2,]    4    8   12
[3,]    6   10   14
[4,]    8   12   16

# 행 기준 비율 계산
> round(sweep(x, 1, apply(x, 1, sum), FUN = "/") * 100, 1)
     [,1] [,2] [,3]
[1,]  6.7 33.3 60.0
[2,] 11.1 33.3 55.6
[3,] 14.3 33.3 52.4
[4,] 16.7 33.3 50.0

# 열 기준 비율 계산
> round(sweep(x, 2, apply(x, 2, sum), FUN = "/") * 100, 1)
     [,1] [,2] [,3]
[1,]   10 19.2 21.4
[2,]   20 23.1 23.8
[3,]   30 26.9 26.2
[4,]   40 30.8 28.6

# 전체 기준 비율 계산
> round(sweep(x, 2, sum(x), FUN = "/") * 100, 1)
     [,1] [,2] [,3]
[1,]  1.3  6.4 11.5
[2,]  2.6  7.7 12.8
[3,]  3.8  9.0 14.1
[4,]  5.1 10.3 15.4
# 데이터프레임의 숫자 열에 각각 1씩 빼기
str(iris)
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
y <- sweep(iris[1:4], MARGIN = 2, STATS = 1, FUN = "-") 
str(y)
#> 'data.frame':    150 obs. of  4 variables:
#>  $ Sepal.Length: num  4.1 3.9 3.7 3.6 4 4.4 3.6 4 3.4 3.9 ...
#>  $ Sepal.Width : num  2.5 2 2.2 2.1 2.6 2.9 2.4 2.4 1.9 2.1 ...
#>  $ Petal.Length: num  0.4 0.4 0.3 0.5 0.4 0.7 0.4 0.5 0.4 0.5 ...
#>  $ Petal.Width : num  -0.8 -0.8 -0.8 -0.8 -0.8 -0.6 -0.7 -0.8 -0.8 -0.9 ...

 

aggregate()

aggregate(x, ...) 함수는 데이터 x의 그룹별로 함수를 적용하여 그 결과를 반환합니다.

  • x : 입력 데이터
  • by : 그룹핑 변수(열)로서 리스트 형식이어야 함
  • FUN : 적용할 함수
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
 
# 종류별로 평균을 구함
> aggregate(iris[1:4], by = list(iris$Species), mean, na.rm = TRUE)
     Group.1 Sepal.Length Sepal.Width Petal.Length Petal.Width
1     setosa        5.006       3.428        1.462       0.246
2 versicolor        5.936       2.770        4.260       1.326
3  virginica        6.588       2.974        5.552       2.026

# 그룹핑 그룹에 이름을 지정할 수 있음(종류)
> aggregate(iris[1:4], by = list(종류 = iris$Species), mean, na.rm = TRUE)
        종류 Sepal.Length Sepal.Width Petal.Length Petal.Width
1     setosa        5.006       3.428        1.462       0.246
2 versicolor        5.936       2.770        4.260       1.326
3  virginica        6.588       2.974        5.552       2.026

# 적용할 함수에 인자들을 지정할 수 있음
aggregate(iris[1:4], by = list(종류 = iris$Species), mean, na.rm = TRUE, trim = 0.1)
> aggregate(iris[1:4], by = list(종류 = iris$Species), mean, na.rm = TRUE, trim = 0.1)
        종류 Sepal.Length Sepal.Width Petal.Length Petal.Width
1     setosa       5.0025      3.4150       1.4600      0.2375
2 versicolor       5.9375      2.7800       4.2925      1.3250
3  virginica       6.5725      2.9625       5.5100      2.0325
# R 내장 데이터 infert
> str(infert)
'data.frame':	248 obs. of  8 variables:
 $ education     : Factor w/ 3 levels "0-5yrs","6-11yrs",..: 1 1 1 1 2 2 2 2 2 2 ...
 $ age           : num  26 42 39 34 35 36 23 32 21 28 ...
 $ parity        : num  6 1 6 4 3 4 1 2 1 2 ...
 $ induced       : num  1 1 2 2 1 2 0 0 0 0 ...
 $ case          : num  1 1 1 1 1 1 1 1 1 1 ...
 $ spontaneous   : num  2 0 0 0 1 1 0 0 1 0 ...
 $ stratum       : int  1 2 3 4 5 6 7 8 9 10 ...
 $ pooled.stratum: num  3 1 4 2 32 36 6 22 5 19 ...

# 2개 변수로 그룹핑
> aggregate(infert[2:3], by = list(infert$education, infert$spontaneous), mean, na.rm = TRUE)
  Group.1 Group.2      age   parity
1  0-5yrs       0 36.55556 4.111111
2 6-11yrs       0 33.73239 1.718310
3 12+ yrs       0 29.67213 1.655738
4  0-5yrs       1 34.00000 4.000000
5 6-11yrs       1 31.18182 2.454545
6 12+ yrs       1 28.91892 1.702703
7  0-5yrs       2 30.00000 5.000000
8 6-11yrs       2 32.37500 3.062500
9 12+ yrs       2 31.55556 2.888889

'R' 카테고리의 다른 글

[R 프로그래밍] 논리 함수  (0) 2022.12.26
[R 프로그래밍] 행렬 함수  (0) 2022.12.26
[R 프로그래밍] 수학 함수  (0) 2022.12.22
[R 프로그래밍] while()  (0) 2022.12.22
[R 프로그래밍] while()  (0) 2022.12.22