Subsetting

Subsetting 의 6가지 종류

x <- c(2.1, 4.2, 3.3, 5.4)
# 1.positive integer 
x[c(3, 1)] 
#> [1] 3.3 2.1

# 2. negative integer 
x[-c(3, 1)] 
#> [1] 4.2 5.4

# 3. Logical vector
x[c(TRUE, TRUE, FALSE, FALSE)] # logical vector 는 그 position 의 값이 TRUE 인 것만 반환한다.
#> [1] 2.1 4.2
x[c(TRUE, FALSE)] # 길이가 짧을 경우 반복해서 적용된다. 
#> [1] 2.1 3.3
x[x > 3]
#> [1] 4.2 3.3 5.4

# 4. nothing. 원래 값을 반환
x[] 
#> [1] 2.1 4.2 3.3 5.4

# 5. 0. zero-length vector를 반환한다. 
x[0] 
#> numeric(0)

# 6. character vector (name 이 있는 경우)
(y <- setNames(x, letters[1:4]))
#>   a   b   c   d 
#> 2.1 4.2 3.3 5.4
y[c("d", "c", "a")]
#>   d   c   a 
#> 5.4 3.3 2.1

z <- c(abc = 1, def = 2)
z[c("a", "d")] 
#> <NA> <NA> 
#>   NA   NA

List

a <- list(a = c(1,2,3), b = c("a", "b"), 
          c = array(c(1,2,3), c(1,3)))
a
#> $a
#> [1] 1 2 3
#> 
#> $b
#> [1] "a" "b"
#> 
#> $c
#>      [,1] [,2] [,3]
#> [1,]    1    2    3
a$a 
#> [1] 1 2 3
a[1] # []는 항상 list 를 반환한다.
#> $a
#> [1] 1 2 3
a[[1]] # 해당 위치의 element 를 반환한다. 
#> [1] 1 2 3

Matrices and array

a <- matrix(1:9, nrow = 3) 
colnames(a) <- c("A", "B", "C")  
a
#>      A B C
#> [1,] 1 4 7
#> [2,] 2 5 8
#> [3,] 3 6 9

# integer vector를 통한 subsetting 
a[1:2, ]
#>      A B C
#> [1,] 1 4 7
#> [2,] 2 5 8

# logical vector와 character vector (name attribute 가 있을 때) 를 통한 subsetting 
a[c(TRUE, FALSE, TRUE), c("B", "A")]
#>      B A
#> [1,] 4 1
#> [2,] 6 3
# 한 개의 vector를 통한 subsetting
(vals <- outer(1:5, 1:5, FUN = "paste", sep = ","))
#>      [,1]  [,2]  [,3]  [,4]  [,5] 
#> [1,] "1,1" "1,2" "1,3" "1,4" "1,5"
#> [2,] "2,1" "2,2" "2,3" "2,4" "2,5"
#> [3,] "3,1" "3,2" "3,3" "3,4" "3,5"
#> [4,] "4,1" "4,2" "4,3" "4,4" "4,5"
#> [5,] "5,1" "5,2" "5,3" "5,4" "5,5"

# 중간에 , 가 없는 경우 R 은 column 우선
vals[c(4, 15)] 
#> [1] "4,1" "5,3"

# matrix 또는 array 를 통한 subsetting
vals <- outer(1:5, 1:5, FUN = "paste", sep = ",")
select <- matrix(ncol = 2, byrow = TRUE, c(
  1, 1,
  3, 1,
  2, 4
))
vals[select]
#> [1] "1,1" "3,1" "2,4"

# 위 방법은 이 결과와 같음 
c(vals[1, 1], vals[3, 1], vals[2, 4])
#> [1] "1,1" "3,1" "2,4"

Data frame

df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])

# Raw subsetting 
df[df$x == 2, ]
#>   x y z
#> 2 2 2 b
df[c(1, 3), ]
#>   x y z
#> 1 1 3 a
#> 3 3 1 c

# Column subsetting
# Data frame은 matrix 와 list 의 성질을 동시에 갖는다.
df[c("x", "z")] # list 처럼 다루는 방법
#>   x z
#> 1 1 a
#> 2 2 b
#> 3 3 c
df[, c("x", "z")] # matrix 처럼 다루는 방법
#>   x z
#> 1 1 a
#> 2 2 b
#> 3 3 c

str(df["x"]) # list 로 subset 된다. 
#> 'data.frame':    3 obs. of  1 variable:
#>  $ x: int  1 2 3
str(df[, "x"]) # vector 로 subset 된다. 
#>  int [1:3] 1 2 3

S3 Object

  • S3 object 는 atomic vector, list, array 위에서 만들어짐. 따라서 위의 방법들을 활용할 수 있음.
  • str 을 사용해 S3 object 의 구조를 파악하고 위 방법들을 똑같이 활용할 수 있음

Excercise : S3 Object subsetting

  • lm fitting 후 r square 추출하기
library(pryr)
mod <- lm(mpg ~ wt, data = mtcars)
summ <- summary(mod)
attributes(summ)
#> $names
#>  [1] "call"          "terms"         "residuals"     "coefficients" 
#>  [5] "aliased"       "sigma"         "df"            "r.squared"    
#>  [9] "adj.r.squared" "fstatistic"    "cov.unscaled" 
#> 
#> $class
#> [1] "summary.lm"
summ$r.squared
#> [1] 0.7528328

Simplifying vs. Preserving subsetting

  • Simplifying : subsetting 시 output을 최대한 간단하게 표현하는 것
  • Preserving : subsetting 시 output 을 input 과 같게 유지하는 것

Vector

x <- c(a = 1, b = 2) 
x[1] # preserving 
#> a 
#> 1
x[[1]] # simplifying. name attribute 를 버린다. 
#> [1] 1

List

y <- list(a = 1, b = 2)
y[1] # preserving
#> $a
#> [1] 1
y[[1]] # simplifying
#> [1] 1
y$a # simplifying
#> [1] 1

Factor

z <- factor(c("a", "b")) 
z[1] # preserving
#> [1] a
#> Levels: a b
z[1, drop = TRUE] # simplifying
#> [1] a
#> Levels: a

Matrix

a <- matrix(1:4, nrow = 2)
a[1, , drop = FALSE] # preserving
#>      [,1] [,2]
#> [1,]    1    3
a[1, ] # simplifying
#> [1] 1 3

Data frame

df <- data.frame(a = 1:2, b = 1:2)
df[1] # preserving
#>   a
#> 1 1
#> 2 2
df[[1]] # simplifying
#> [1] 1 2
df[, "a", drop = FALSE] # preserving
#>   a
#> 1 1
#> 2 2
df[, "a"] # simplifying
#> [1] 1 2
df$a
#> [1] 1 2

$와 [[]] 의 차이

var <- "cyl" 
mtcars$var 
#> NULL

[[]] 는 column 명을 변수로 줄 수 있음

# Instead use [[
mtcars[[var]]
#>  [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

$ 는 partial matching 을 사용함 [[]] 는 안됨

x <- list(abc = 1) 
x$a # $ does partial matching 
#> [1] 1

Subsetting and assignment

  • R 에서는 subset 하는 것처럼 값을 할당할 수 있음
x <- 1:5
x[c(1, 2)] <- 2:3
x
#> [1] 2 3 3 4 5

Excercise

Boostrap + Random feature selection 구현하기 - 전체 샘플의 50 % 크기를 중복을 허용해 추출하고, 열의 70 % 를 임의로 골라 Subsetting 해보기. (ID, Class 열 제외)

library(mlbench)
data("BreastCancer")
head(BreastCancer)
#>        Id Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size
#> 1 1000025            5         1          1             1            2
#> 2 1002945            5         4          4             5            7
#> 3 1015425            3         1          1             1            2
#> 4 1016277            6         8          8             1            3
#> 5 1017023            4         1          1             3            2
#> 6 1017122            8        10         10             8            7
#>   Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses     Class
#> 1           1           3               1       1    benign
#> 2          10           3               2       1    benign
#> 3           2           3               1       1    benign
#> 4           4           3               7       1    benign
#> 5           1           3               1       1    benign
#> 6          10           9               7       1 malignant

num_row <- nrow(BreastCancer)
num_col <- length(BreastCancer)

raws_to_keep <- sample(num_row, 0.5 * num_row, replace = TRUE) # integer vector 를 통한 subsetting 

cols_names <- colnames(BreastCancer)
features <- cols_names[!(cols_names %in% c('Id', "Class"))] # logical vector를 통한 subsetting 
cols_to_keep <- c('Id', "Class", sample(features, 0.7 * num_col)) # character vector 를 통한 subsetting 

BreastCancerSample <- BreastCancer[raws_to_keep, cols_to_keep]
head(BreastCancerSample)
#>          Id     Class Mitoses Cell.size Normal.nucleoli Cell.shape
#> 513 1299994    benign       1         1               1          1
#> 374  521441    benign       1         1               1          1
#> 433 1277629    benign       1         1               2          1
#> 444  734111    benign       1         1               1          1
#> 649 1315807 malignant      10        10              10         10
#> 180 1202812 malignant       1         3               1          3
#>     Cl.thickness Epith.c.size Bl.cromatin
#> 513            5            2           1
#> 374            5            2           2
#> 433            5            2           2
#> 444            1            2           1
#> 649            5           10          10
#> 180            5            6           3