Tools (127)

반응형

R 커뮤니티의 최신 유행을 Follow 하기 좋은 방법 


R 커뮤니티의 성숙은 매우 빠르게 이루어지고 있고, 그러한 주류에 속하기 위해서는 뉴스를 빠르게 습득해야할 필요가 있습니다. 이와 관련하여 이전 글에서 R과 데이터과학의 트렌드를 알 수 있는 블로그들을 소개했습니다 


하지만 더욱 빠르게 정보를 습득할 수 있는 방법은 트위터의 rstats 해쉬태그를 이용하는 것입니다.  


https://twitter.com/hashtag/rstats?src=hash&lang=en&lang=en

해당 트윗에 들어가면 실시간으로 R 커뮤니티에 올라오는 새로운 소식들을 만나볼 수 있습니다. 



블로그나, 최신 책을 이용하는 방법도 있지만, 트위터는 몇 분마다 한 번씩 글이 올라오니 가장 최신 정보를 생생하게 알 수 있다는 장점이 있어 좋은것 같네요.



이외에도 다양한 트위터들이 있으니 Follow 하면 도움이 될 것 같습니다.]


또 Reddit 의 Rstats 페이지도있습니다. (https://www.reddit.com/r/rstats/)

반응형
반응형
Object Oriented Programming in R

R 베이스타입

  • 모든 베이스 R 개체는 그 객체가 메모리에 저장되는 방법을 기술하는 C 구조가 있다.
  • 베이스 타입은 R 코어팀만 만들 수 있다. 실제 객체 시스템은 아니다.
  • typeof 함수는 R base type 을 반환한다. (vector, list, function, builtin)
typeof(c("a")) # character vector 
#> [1] "character"
typeof(mean) # 함수는 closure
#> [1] "closure"
typeof(sum) # 원시함수는 built in 
#> [1] "builtin"
typeof(abs) # builtin 
#> [1] "builtin"
typeof(pnorm) # closure 
#> [1] "closure"
typeof(array(c(1.1, 2.2, 3.3), 3)) 
#> [1] "double"
typeof(matrix(1:3, 3)) 
#> [1] "integer"
df <- data.frame(x = 1:10, y = letters[1:10]) # dataframe 의 base type 은 list 이다. 
typeof(df)
#> [1] "list"

S3

  • S3는 가장 단순한 OO 시스템이고, base와 stats 패키지에서 사용된 유일한 OO 시스템이다.
  • CRAN 에 있는 많은 패키지들에서 가장 공통적을 사용되는 시스템이다.
  • pryr 패키지의 otype 함수는 해당 객체가 base type 그 자체인지 어떤 base type 에 기반한 S3 인지 S4 등 인지를 출력한다.
library(pryr)
df <- data.frame(x = 1:10, y = letters[1:10])
otype(df) # data frame은 S3 객체이다. 
#> [1] "S3"
otype(c("A")) # vector는 S3 객체가 아니다. R 베이스타입이다. 
#> [1] "base"
otype(c(1,2,3)) 
#> [1] "base"
otype(array(1:3, 3)) # array는 R 베이스타입이다.
#> [1] "base"
otype(matrix(1:3, 3)) # matrix 는 R 베이스타입이다. 
#> [1] "base"
otype(list(a=c(1:3))) # list 는 R 베이스타입이다. 
#> [1] "base"
otype(factor(1)) # factor는 S3 객체이다. 
#> [1] "S3"

S3 클래스 정의하기

# 한 번에 클래스를 생성하고 할당하기
# structuer(base object, class = class_name)
# structure 함수는 해당 attribute 를 갖는 객체를 만드는 함수이다. 
foo <- structure(list(), class="foo") 
foo
#> list()
#> attr(,"class")
#> [1] "foo"

# 클래스를 생성하고 난 후 설정하기
foo <- list()
class(foo) <- "foo"

상속 (Inheritance)

inherits(foo, "foo") # foo 객체가 foo class 를 상속하는지 체
#> [1] TRUE
  • 예를들어, glm 은 lm 클래스의 하위 클래스이다.
model <- glm(mpg ~ cyl + hp, data=mtcars) 
class(model) # glm class 는 이와 같이 상속받은 lm 클래스를 class 에 포함하고 있다. 
#> [1] "glm" "lm"
inherits(model, "lm") 
#> [1] TRUE
  • lm 에는 없고, glm 에는 있는 generic function 찾아보기
methods(class="glm") # lm 에 추가하여 glm 에 추가로 정의된 generic function 이다. 
#>  [1] add1           anova          coerce         confint       
#>  [5] cooks.distance deviance       drop1          effects       
#>  [9] extractAIC     family         formula        influence     
#> [13] initialize     logLik         model.frame    nobs          
#> [17] predict        print          residuals      rstandard     
#> [21] rstudent       show           slotsFromS3    summary       
#> [25] vcov           weights       
#> see '?methods' for accessing help and source code
methods(class="lm") 
#>  [1] add1           alias          anova          case.names    
#>  [5] coerce         confint        cooks.distance deviance      
#>  [9] dfbeta         dfbetas        drop1          dummy.coef    
#> [13] effects        extractAIC     family         formula       
#> [17] hatvalues      influence      initialize     kappa         
#> [21] labels         logLik         model.frame    model.matrix  
#> [25] nobs           plot           predict        print         
#> [29] proj           qr             residuals      rstandard     
#> [33] rstudent       show           simulate       slotsFromS3   
#> [37] summary        variable.names vcov          
#> see '?methods' for accessing help and source code

클래스의 생성자 만들기

  • 생성자 함수는 일반적으로 클래스와 동일한 이름을 갖도록 한다.
foo <- function(x) { 
  if (!is.numeric(x)) stop("X must be numeric")
  structure(list(x), class="foo")
}
  • 생성자를 통해 변수 생성하기
foo_var <- foo(c(1,2,3)) 
print(foo_var)
#> [[1]]
#> [1] 1 2 3
#> 
#> attr(,"class")
#> [1] "foo"
str(foo_var) 
#> List of 1
#>  $ : num [1:3] 1 2 3
#>  - attr(*, "class")= chr "foo"
typeof(foo_var)
#> [1] "list"
otype(foo_var)
#> [1] "S3"
class(foo_var)
#> [1] "foo"
length(foo_var) # length generic function 의 default 가 실행되며, 여기서 base type 이 list 임을 확인하고, list 의 길이 1을 반환한다.
#> [1] 1
  • R 에서는 기존 객체의 클래스를 변경할 수 있다.
mod <- lm(log(mpg) ~ log(disp), data = mtcars) 
class(mod) 
#> [1] "lm"
typeof(mod)  
#> [1] "list"
print(mod)
#> 
#> Call:
#> lm(formula = log(mpg) ~ log(disp), data = mtcars)
#> 
#> Coefficients:
#> (Intercept)    log(disp)  
#>      5.3810      -0.4586
  • lm 클래스에 데이터프레임 class 를 추가하기
class(mod) <- "data.frame"
print(mod) # data frame s3 method 를 호출한다. 
#>  [1] coefficients  residuals     effects       rank          fitted.values
#>  [6] assign        qr            df.residual   xlevels       call         
#> [11] terms         model        
#> <0 rows> (or 0-length row.names)
mod$coefficients
#> (Intercept)   log(disp) 
#>   5.3809725  -0.4585683

S3 새로운 메소드와 제너릭 생성하기

  • 새로운 제너릭을 만들고 싶을 대, UseMethod() 를 call 하는 function 을 만든다.
  • UseMethod 는 제너릭 메소드의 이름과, argument를 input 으로 받는다.
f <- function(x) UseMethod("f") # 이렇게하면 f 라는 이름의 제너릭이 생성된 것이다. 

# 제너릭은 메소드가 없다면 쓸모가 없다. 아래와 같이 제너릭 메소드를 구현할 수 있다.
f.a <- function(x) "Class a" 
a <- structure(list(), class = "a") 
class(a) 
#> [1] "a"
  • 제너릭 함수의 호출
f(a)
#> [1] "Class a"
  • 원래 있는 제너릭에 메소드를 추가하기
mean.a <- function(x) "a"
mean(a)
#> [1] "a"

메소드 디스패치

  • S3에서 메소드 디스패치하는 법은 심플하다.
  • default 메소드를 정의하면 해당 클래스에 대한 메소드가 없을 경우 실행됨
f <- function(x) UseMethod("f") 
f.a <- function(x) "Class a" 
f.default <- function(x) "Unknown class" 

f(structure(list(), class = "a"))
#> [1] "Class a"
# b메소드에 대한 메소드가 없기 때문에 a 클래스에 대한 행된다. 
f(structure(list(), class = c("b", "a")))
#> [1] "Class a"
# c class에 대한 메소드 구현이 없기 때문에 default 메소드가 실행된다.
f(structure(list(), class=c("c"))) 
#> [1] "Unknown class"
c <- structure(list(), class = "c")
# Call the correct method:
f.default(c)
#> [1] "Unknown class"
  • 다른 클래스의 제네릭 메소드를 실행할 수도 있다.
f.a(c)
#> [1] "Class a"
  • S3 object가 아닌 것도, S3 제너릭 메소드를 실행할 수 있다.
  • 이 경우 R base type 을 이용해 메소드를 실행한다.
  • 이 R base type 을 알아내는 것은 힘들 수 있지만 아래와 같은 함수로 가능하다.
iclass <- function(x) {
  if (is.object(x)) {
    stop("x is not a primitive type", call. = FALSE)
  }

  c(
    if (is.matrix(x)) "matrix",
    if (is.array(x) && !is.matrix(x)) "array",
    if (is.double(x)) "double",
    if (is.integer(x)) "integer",
    mode(x)
  )
}
iclass(matrix(1:5)) 
#> [1] "matrix"  "integer" "numeric"
iclass(array(1.5))
#> [1] "array"   "double"  "numeric"

Group 제너릭

  • Group 제너릭이라는 것도 있는데 상당히 advanced 된 내용이다.
  • 여러개의 제너릭들을 한데 모아, 다양한 클래스의 제너릭 메소드를 정의함
  • 예를 들어, abs, sign, sqrt 등의 제너릭은 Math라는 이름의 그룹 제너릭이다.

Exercises

  • Read the source code for t() and t.test() and confirm that t.test() is an S3 generic and not an S3 method. What happens if you create an object with class test and call t() with it
# t 는 원래 matrix 나 datafame 등을 받아, transpose 를 return 하는 함수이다. 
array(1:6, list(2,3))
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
t(array(1:6, list(2,3)))
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
#> [3,]    5    6
# t.test 는 제너릭이다.
a <- structure(c(1,2,3,4,5), class="test")

# 해당 코드는 one sample t-test 를 실행한다! 
# test 클래스를 보고, t.test 를 실행하기 때문 
# 이것은 t.test가 t 제너릭 메소드가 아니라 제너릭이기 때문이다. 
# 만약에 t.test가 t의 제너릭 메소드였다면, t(c(1,2,3,4,5)) 가 실행될 것이다. 
# 따라서 어떤 generic 을 생성할 때는 .을 포함하지 않도록 하는 것이 좋다. 
# t 를 실행하고 싶은데, t.test가 실행되는 등의 현상이 발생할 수 있다. 
t(a)
#> 
#>  One Sample t-test
#> 
#> data:  a
#> t = 4.2426, df = 4, p-value = 0.01324
#> alternative hypothesis: true mean is not equal to 0
#> 95 percent confidence interval:
#>  1.036757 4.963243
#> sample estimates:
#> mean of x 
#>         3
# 이런것도 가능하다. 
t.test <- function(a){
  print(a)
}
t(a)
#> [1] 1 2 3 4 5
#> attr(,"class")
#> [1] "test"
rm(t.test)
  • What classes have a method for the Math group generic in base R? Read the source code. How do the methods work?
  • abbc, sign, sqrt 등은 “Math” 그룹 제너릭이다.

Group “Math”:

abs, sign, sqrt, floor, ceiling, trunc, round, signif … 등등

methods(Math)
#> [1] Math,nonStructure-method Math,structure-method   
#> [3] Math.Date                Math.POSIXt             
#> [5] Math.data.frame          Math.difftime           
#> [7] Math.factor             
#> see '?methods' for accessing help and source code
  • R has two classes for representing date time data, POSIXct and POSIXlt, which both inherit from POSIXt. Which generics have different behaviours for the two classes? Which generics share the same behaviour?
methods(class="POSIXt") # 세 클래스에서 공통으로 정의된 제너릭 메소드
#>  [1] +            -            Axis         Math         Ops         
#>  [6] all.equal    as.character coerce       cut          diff        
#> [11] hist         initialize   is.numeric   julian       months      
#> [16] pretty       quantile     quarters     round        seq         
#> [21] show         slotsFromS3  str          trunc        weekdays    
#> see '?methods' for accessing help and source code
methods(class="POSIXct") # POSIXct 의 제너릭 메소드. 이 메소드는 POSIXt 에 추가하여 POSIXct 에서 새롭게 구현된 것이다.
#>  [1] Summary       [             [<-           [[            as.Date      
#>  [6] as.POSIXlt    as.data.frame as.list       c             coerce       
#> [11] format        initialize    length<-      mean          print        
#> [16] rep           show          slotsFromS3   split         summary      
#> [21] weighted.mean xtfrm        
#> see '?methods' for accessing help and source code
methods(class="POSIXlt") # POSIXlt 의 제너릭 메소드. 메 메소드는 마찬가지로 POSIXlt 에서 새롭게 구현된 것이다. 
#>  [1] Summary       [             [<-           [[            anyNA        
#>  [6] as.Date       as.POSIXct    as.data.frame as.double     as.list      
#> [11] as.matrix     c             coerce        duplicated    format       
#> [16] initialize    is.na         length        length<-      mean         
#> [21] names         names<-       print         rep           show         
#> [26] slotsFromS3   sort          summary       unique        weighted.mean
#> [31] xtfrm        
#> see '?methods' for accessing help and source code
  • Which base generic has the greatest number of defined methods?
library("methods") 
objs <- mget(ls("package:base"), inherits = TRUE)
funs <- Filter(is.function, objs) 
generics <- Filter(function(x) ("generic" %in% pryr::ftype(x)), funs)
  
sort(
  lengths(sapply(names(generics), function(x) methods(x), USE.NAMES = TRUE)),
  decreasing = TRUE
  )[1]
#> print 
#>   208
  • UseMethod() calls methods in a special way. Predict what the following code will return, then run it and read the help for UseMethod() to figure out what’s going on. Write down the rules in the simplest form possible.
y <- 1
g <- function(x) { 
  y <- 2
  UseMethod("g")
}
g.numeric <- function(x) y
g(10) # Internal variable 을 먼저 찾기 때문에 2가 된다.
#> [1] 2
#> [1] 2

h <- function(x) {
  x <- 10
  UseMethod("h")
}
h.character <- function(x) paste("char", x)
h.numeric <- function(x) paste("num", x)

h("a")
#> [1] "char a"
#> [1] "char a"
  • Internal generics don’t dispatch on the implicit class of base types. Carefully read ?“internal generic” to determine why the length of f and g is different in the example below. What function helps distinguish between the behaviour of f and g?
f <- function() 1 
g <- function() 2 
class(g) <- "function" 
class(f) 
#> [1] "function"
#> [1] "function"
class(g)
#> [1] "function"
#> [1] "function"

length.function <- function(x) "function" 

length(f)
#> [1] 1
#> [1] 1
length(g)
#> [1] "function"
#> [1] "function"

Answer : f는 implicit class 가 function 인 것이고, g 는 class=function 으로 정의된 것이다.

library(pryr)
# fytype 함수는 해당 function 이 internal/S3/S4/RC 인지를 알려준다. 
ftype(f) 
#> [1] "function"
ftype(g) 
#> [1] "function"
ftype(t.test) 
#> [1] "s3"      "generic"
ftype(length) 
#> [1] "primitive" "generic"
# 둘 다 function 으로 되는데 이것이 implicit class 인지는 아래처럼 확인할 수 있다. 
is.object(f) # f는 implicit class "function"" 이기 때문에 FALSE가 반환된다. 
#> [1] FALSE
is.object(g) # g는 s3 객체이기 때문에 TRUE 가 반환된다.
#> [1] TRUE


반응형
반응형

R 트렌드를 볼 수 있는 블로그


해들리 위컴은 R 과 data science 의 최신 기법을 항상 숙지하고 있기 위해 300개 이상의 블로그를 follow-up 한다고 한다. 그만큼 최신 정보를 빠르게 습득하는 것은 매우 중요하다고 할 수 있다. 본 포스팅에서는 R 관련 유명 블로그들 중 몇 개를 추려 포스팅해보고자 한다. 


1. R studio blog  (https://blog.rstudio.com/, Since 2011)


R Studio 가 R 에서 차지하는 위상이 높은 만큼, R studio 의 블로그도 R 커뮤니티에서 매우 유명한 블로그이다. R studio 의 자체적인 라이브러리들과 상업적인 프로그램들에 관한 뉴스와 여러 이벤트들에 관한 글도 올라온다. 한 달에 4개 주기로 글이 올라온다. 


2. R Bloggers (https://www.r-bloggers.com/, Since 2005)



R-bloggers는 가장 유명한 R 블로그라고 할 수 있다. 가장 오래된 블로그이기도 하다. (Since 2005). R-bloggers 는 R 을 통해 효과적이고 효율적인 어플리케이션, 솔루션을 만들기 위한 튜토리얼과 팁들에 관하여 집중적으로 글이 올라온다. 한달에 34개 주기의 글이 올라온다. 


3. Simply statistics (https://simplystatistics.org/, Since 2011)


Simply statistics 블로그는 R 관련 MOOC 의 강의자들이 협력하여 만든 블로그이다. R과 Data science 관련한 전반적인 글이 올라온다. 기술적인 글 보다는 Interview, News 등 가벼운 주제 에 관한 글이 주로 올라오는듯해 가볍게 볼 수 있는 블로그이다. 글이 많이 올라오지 않으나 깊이가 있다. 


아래는 이 블로그의 주인들과 관련된 MOOC 목록이다. 


Data Science Specialization on Coursera

by Jeff Leek, Roger Peng, Brian Caffo. 


Data analysis for life sciences on edx

Rafael Irizarry and Mike Love
health science 와 genomics 에 초점을 맞춘 강의이다. 


Genomic Data Science Specialization on Coursera

Jeff Leek, Steven Salzberg, James Taylor, Ela Pertea, Liliana Florea, Ben Langmead, and Kasper Hansen

genome data 에 초점을 맞춘 강의이다.


4. Yihui's Blog | R statistics (yihui.name/en, Since 2007)



Yihui Xie 는 R studio 의 software engineer 이다. knitr, bookdown, blogdown, xaringan, DT 등의 유명한 R 패키지를 만든 R 커뮤니티에서 이름값이 높은 사람이다. 또한 그는 2008년부터 중국의 R 컨퍼런스를 만들고 운영하고 있다. 그가 R 에 관해 갖고 있는 경험과 생각을 배우고 싶다면 follow 하면 좋다. (1주에 2개의 포스트 정도가 올라온다.)


5. Shirin's playgRound (shirinsplayground.netlify.com, Since 2017)


Shirin 은 원래 생물학자였으나, 생명정보학자 그리고 데이터 과학자로 직업을 바꾸며 활동하고 있다. 그녀는 머신러닝과 데이터 시각화에 관심이 많고, 항상 모든 일을 R 을 사용해 처리한다. 이 블로그를 follow 하면, 그녀가 일을 할 때 어떤 Tool 을 선택하고, R 에 관해 어떤 생각을 갖고 있는지를 알 수 있다. (1달에 4개 정도의 포스트가 올라온다.) 


6. R views https://rviews.rstudio.com/

-An R community blog edited by RStudio


R studio 가 관리하는 블로그이다. R 이용 관련 상당히 유용한 정보들이 많이 이용하는 듯 하다.


* 이외에도 수많은 R 블로그들이 있다. 이 포스트는 R 의 유명한 블로그들 40개를 정리하였다.  

* Feed spot (https://www.feedspot.com) 은 이러한 블로그들을 follow 할 수 있는 플랫폼이다. 무료 유저의 경우 12개의 사이트를 follow 할 수 있으며, 새로운 글이 올라왔을 때, 이를 간편하게 확인할 수 있다. 

반응형
반응형

R Studio 팁과 트릭


1. Alt-Shift-K


R studio 에서 쓸 수 있는 shortcut 을 보여준다. shortcut 을 바꾸고 싶으면 tools-modify keyboard shortcut 을 선택한다.


2. ctrl+shift+f


ctrl+f 는 현재 파일에서 해당문자를 찾는다면, ctrl+shift+f 는 현재 프로젝트에서 해당문자를 찾는다. 이것저것 파일을 뒤져가며 찾지말고 ctrl+shift+f 를 활용하자. 


3. code snippet 


Tools-Global options-Code-Edit code snippet


mat 를 입력한 후, shift-tab 을 누르면, matrix 생성자와 인자가 자동완성된다. 이를 code snippet 이라 하는데, 이는 함수의 구조를 자동완성으로 쉽게 타이핑할 수 있으며, 자주 사용하는 코드를 복사-붙여넣기가 아니라 단축키를 통해 만들어낼 수 있다는 장점이 있다.


4. Navigation


tab 을 navigation 하기 위해 마우스보다는 키보드 단축키를 활용하자. 


현재 tab을 닫을 때는 ctrl+w

모든 tab을 닫을 때는 ctrl+shift+w

현재 tab을 제외하고 모두 닫을 때는 ctrl+alt+sift+w (네 손가락을 이용하기 때문에 힘들 수 있다.)


그리고 navigation 할 때는 ctrl+. 을 활용하면 좋다. 



참고

https://www.youtube.com/watch?v=kuSQgswZdr8&list=PLL8uQat--_mDkJfhpsYDhPyNvCEsNy0er&index=3

반응형
반응형

Function

Three Component of function

body : 함수 내부의 내용
formal : 함수 전달 인자
environment : 함수가 정의된 환경
f <- function(x) x^2
f
#> function(x) x^2

formals(f)
#> $x
body(f)
#> x^2
environment(f)
#> <environment: R_GlobalEnv>

Three base types of function

Closure (사용자 정의 함수, print, mean 등의 대부분의 함수)
Built-in (sum, max, +, - 등)
Special (if, for, function, log 등) 

Built-in & Special function

  • .Primitive() 함수를 통해 C 코드를 곧바로 실행시킴
  • 또는 .Internal, .External R 내부 함수 실행
  • BASE 패키지에만 있음
formals(sum)
#> NULL
body(sum)
#> NULL
environment(sum)
#> NULL
typeof(sum)
#> [1] "builtin"

formals(`log`)
#> NULL
body(`log`)
#> NULL
environment(`log`)
#> NULL
typeof(`log`)
#> [1] "special"

Every operation is a function call

To understand computations in R, two slogans are helpful:

  1. Everything that exists is an object.
  2. Everything that happens is a function call.
x <- 10; y <- 5
x + y
#> [1] 15
`+`(x, y)
#> [1] 15
for (i in 1:2) print(i)
#> [1] 1
#> [1] 2
`for`(i, 1:2, print(i))
#> [1] 1
#> [1] 2
if (i == 1) print("yes!") else print("no.")
#> [1] "no."
`if`(i == 1, print("yes!"), print("no."))
#> [1] "no."

function 의 argument 를 list 형식으로 전달하기

args <- list(1:10, na.rm = TRUE)
do.call(mean, args)
#> [1] 5.5

Default and missing value

f <- function(a = 1, b = 2) {
  c(a, b)
}
f()
#> [1] 1 2

Special types of function

  1. Infix function
    • +, * 처럼 인수의 중간에 함수가 있는 경우
    • 유저가 infix function 을 정의하려면 앞 뒤로 %를 붙임
    • built-in function 은 % 가 없어도 됨 (+, ! & 등)
# 문자열을 잇는 함수 만들기 
`%+%` <- function(a, b) paste0(a, b)
"new" %+% " string"
#> [1] "new string"
`%+%`("new", "string")
#> [1] "newstring"
  1. Replacement function
    • 해당 위치의 원소를 바꾸는 특별한 함수
    • xxx<- 의 형태로 함수의 이름을 만들어야함
    • 아래 형태를 가져야 함
x <- 1:10
`modify<-` <- function(x, position, value) {
  x[position] <- value
  x
}
# 재할당하지 않아도, x의 첫 번째 원소를 바꾼다.
modify(x, 1) <- 10 
# x <- `modify<-`(x, 1, 10) 와 같다.

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

replacement function의 예 : names<-

x <- c(a = 1, b = 2, c = 3)
names(x)
#> [1] "a" "b" "c"
names(x)[2] <- "two"
names(x)
#> [1] "a"   "two" "c"

# 실제로 내부적으로 아래 코드처럼 실행됨 
# `*tmp*` <- names(x)
# `*tmp*`[2] <- "two"
# names(x) <- `*tmp*`

data frame의 열 이름 바꾸기

df <- data.frame(a = c(1:3), b = c(2:4), c = c(3:5))
colnames(df)[2] <- 'd'
df
#>   a d c
#> 1 1 2 3
#> 2 2 3 4
#> 3 3 4 5

Excercise

  1. Boostrap + Random feature selection 구현하기
  • sample 10개를 생성하라 (사용자 정의 함수와 base 패키지의 replicate 함수를 이용)
make_boostraps <- function(data, n, row_perc, col_perc, label) {
  make_one_bootstrap <- function(data, row_perc, col_perc, label) {
    num_row <- nrow(data)
    num_col <- length(data)
    
    raws_to_keep <- sample(num_row, row_perc * num_row, replace = TRUE)
   
    cols_names <- colnames(BreastCancer)
    features <- cols_names[!(cols_names %in% label)]
    cols_to_keep <- c(label, sample(features, col_perc * num_col))
    
    return(data[raws_to_keep, cols_to_keep])
  }
  return(replicate(n,
    make_one_bootstrap(data, row_perc, col_perc, label = label),
    simplify = FALSE
  ))
}
library(mlbench)
data("BreastCancer")
bootstraps <- make_boostraps(BreastCancer,
  n = 10,
  row_perc = 0.5,
  col_perc = 0.7,
  label = c("Id", "Class")
)
str(bootstraps)
#> List of 10
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1276091" "1212422" "566509" "1240603" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 1 1 1 1 1 ...
#>   ..$ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 3 3 5 3 3 5 1 1 3 5 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 2 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 1 1 1 1 2 1 1 1 1 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 1 1 1 2 1 1 1 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 1 1 1 2 1 1 1 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 3 1 1 1 2 3 1 2 1 1 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1295508" "1126417" "1182404" "1299596" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 2 1 1 1 1 2 1 1 2 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 1 1 1 3 1 1 4 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 3 2 2 2 3 4 2 2 6 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 3 1 1 1 1 8 1 1 1 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 4 4 1 1 1 NA 9 1 1 1 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 6 1 1 1 1 3 1 1 4 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 1 3 3 1 3 1 8 2 2 4 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 2 1 1 1 9 1 1 3 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id           : chr [1:349] "466906" "640712" "1258549" "1211202" ...
#>   ..$ Class        : Factor w/ 2 levels "benign","malignant": 1 1 2 2 1 2 1 1 2 1 ...
#>   ..$ Epith.c.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 2 10 10 2 10 2 2 4 2 ...
#>   ..$ Cl.thickness : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 9 7 1 10 4 5 5 1 ...
#>   ..$ Marg.adhesion: Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 10 10 1 2 1 1 10 1 ...
#>   ..$ Bl.cromatin  : Factor w/ 10 levels "1","2","3","4",..: 1 2 10 4 2 5 3 2 5 1 ...
#>   ..$ Cell.shape   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 10 10 3 10 1 1 10 1 ...
#>   ..$ Cell.size    : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 10 5 1 10 1 1 10 1 ...
#>   ..$ Bare.nuclei  : Factor w/ 10 levels "1","2","3","4",..: 1 1 10 10 1 10 1 1 10 1 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "646904" "1115293" "646904" "1321264" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 2 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 2 1 1 4 2 1 1 1 4 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 3 2 3 2 3 4 2 3 1 8 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 1 1 1 7 10 1 1 1 10 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 2 2 1 3 6 1 2 2 6 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 2 8 8 1 1 1 10 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 2 8 8 3 1 1 7 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 4 1 1 1 2 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1324572" "1026122" "1272166" "1201870" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 1 2 10 1 2 1 1 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 2 2 1 2 4 2 2 2 2 ...
#>   ..$ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 2 5 4 4 10 2 3 6 4 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 1 1 1 2 5 1 1 1 1 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 2 1 1 2 3 8 3 1 1 1 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 2 1 1 1 1 10 1 1 1 1 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 3 1 6 1 3 3 3 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1241232" "733823" "873549" "543558" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 2 2 2 2 2 1 2 2 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 3 1 8 1 1 2 1 1 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 1 5 10 9 8 7 10 10 1 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 2 3 4 4 6 4 3 4 2 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 4 6 5 3 8 5 4 7 10 1 ...
#>   ..$ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 3 5 10 6 8 9 5 6 8 3 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 3 4 3 5 8 4 4 8 7 3 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: NA 10 7 5 9 10 5 10 4 1 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1218105" "1212232" "673637" "1136142" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 2 1 1 1 2 1 2 1 1 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 5 1 1 1 1 1 2 1 1 1 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 10 1 5 1 4 1 10 NA 2 1 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 7 2 5 2 8 3 1 2 7 3 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 10 1 1 1 10 1 3 1 1 1 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 10 1 1 1 10 2 5 3 1 1 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 9 1 1 1 6 1 4 1 1 3 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 10 1 1 1 5 1 6 1 1 1 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1182404" "1343068" "1200847" "1253955" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 2 2 2 1 1 1 1 1 1 ...
#>   ..$ Cl.thickness   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 3 8 6 8 4 3 3 3 3 4 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 5 10 10 1 1 1 1 8 1 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 6 8 5 2 2 2 1 8 2 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 10 7 1 1 1 2 1 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 2 7 1 1 1 1 1 1 1 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 10 4 1 1 1 1 3 1 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 2 2 10 5 2 2 1 2 5 2 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1287775" "1043068" "1303489" "846423" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 1 1 2 1 2 1 1 2 1 ...
#>   ..$ Epith.c.size   : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 2 2 4 3 2 2 2 5 2 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 2 1 1 10 10 3 1 1 10 1 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 3 3 8 1 1 6 1 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 1 6 3 5 1 1 6 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 4 3 5 1 1 3 1 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 3 2 2 7 3 2 2 3 6 2 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 1 1 8 5 1 1 1 8 1 ...
#>  $ :'data.frame':    349 obs. of  9 variables:
#>   ..$ Id             : chr [1:349] "1031608" "867392" "1198641" "803531" ...
#>   ..$ Class          : Factor w/ 2 levels "benign","malignant": 1 1 2 2 1 1 2 1 1 1 ...
#>   ..$ Cell.shape     : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 2 6 10 3 1 5 2 1 1 ...
#>   ..$ Mitoses        : Factor w/ 9 levels "1","2","3","4",..: 1 1 2 1 1 1 1 1 1 1 ...
#>   ..$ Marg.adhesion  : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 1 3 10 1 1 8 1 3 6 ...
#>   ..$ Bl.cromatin    : Factor w/ 10 levels "1","2","3","4",..: 2 2 4 8 2 3 8 2 3 1 ...
#>   ..$ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 1 3 5 3 2 6 1 1 1 ...
#>   ..$ Bare.nuclei    : Factor w/ 10 levels "1","2","3","4",..: 1 1 10 2 NA 1 10 1 1 1 ...
#>   ..$ Cell.size      : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 2 10 10 4 1 6 1 1 1 ...


반응형
반응형

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


반응형
반응형

R의 데이터 구조


  • 프로그램에서 다양한 타입의 데이터를 변수에 저장하는 것이 필요하다.
  • 운영체제는 사용자가 지정한 데이터 타입에 맞게 변수에 메모리를 할당한다. 
  • R의 기본 데이터 타입 (base object) 은 Vector (atomic vector 와 list) 이고, 이를 기반으로 구현된 아래의 데이터 구조가 많이 사용된다.
    • Atomic vector
    • List
    • Matrix
    • Array
    • Factors
    • Data frame

모든 데이터 타입은 Vector 로부터 시작

Matrix 와 Array 는 atomic vector 를 기반으로 만들어진 base object 이다. 
Factor 와 Data frame 은 각각 atomic vector와 list 에 기반한 S3 클래스이다. 

Two types of Vector

  • Vector 는 Atomic vector 와 list 로 나눌 수 있다.
  • Atomic vector 는 원자가 동질적이고, list 는 이질적일 수 있다.
  • Atomic vector 는 1차원 구조이지만 list 는 다차원일 수 있다 (nested structure).

Atomic vector

  • 네 가지 atomic vector (logical, integer, double, character)
  • raw와 complex 도 있지만 잘 사용하지 않는다.
lgl_var <- c(TRUE, FALSE)
int_var <- c(1L, 6L, 10L)
dbl_var <- c(1, 2.5, 4.5)
chr_var <- c("these are", "some strings")
str(lgl_var)
##  logi [1:2] TRUE FALSE
str(int_var)
##  int [1:3] 1 6 10
str(dbl_var)
##  num [1:3] 1 2.5 4.5
str(chr_var)
##  chr [1:2] "these are" "some strings"

Coercion

  • 만약 vector에 다른 타입의 데이터가 들어가면 강제로 형변환된다 (Coercion).
  • 이 때, 우선순위는 logical, numeric, double, character 이다.
c(1, "a")
## [1] "1" "a"
c(TRUE, 1)
## [1] 1 1
c(1, 1.1)
## [1] 1.0 1.1
c(3.0, TRUE)
## [1] 3 1
c(c(1,2,3), 1)
## [1] 1 2 3 1
c(list(c(1,2,3)), "a") 
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] "a"

List

  • List 는 원자가 어떤 타입이든 될 수 있고, list 도 될 수 있다.
x <- list(1:3, "a", c(TRUE, FALSE, TRUE), c(2.3, 5.9))
str(x)
## List of 4
##  $ : int [1:3] 1 2 3
##  $ : chr "a"
##  $ : logi [1:3] TRUE FALSE TRUE
##  $ : num [1:2] 2.3 5.9
x <- list(list(list(list())))
str(x)
## List of 1
##  $ :List of 1
##   ..$ :List of 1
##   .. ..$ : list()

Excercises (Vector, list)

  1. atomic vector 의 6가지 종류는 무엇이고 list 와의 차이점은 무엇인가?
  • integer, double, character, complex, raw, boolean.
  • homogenous vs heterogenous
  • 1d vs nested structure
  1. 아래 코드의 결과는?
c(1, FALSE)     
## [1] 1 0
c("a", 1)        
## [1] "a" "1"
c(list(1), "a")  
## [[1]]
## [1] 1
## 
## [[2]]
## [1] "a"
c(TRUE, 1L)     
## [1] 1 1
  1. list 를 vector 로 변환하기 위해 사용하는 unlist 는 무엇을 하는가?
  • coercion, nested structure 제거, 1d structure 로 변환
a <- list(c(1,2,3,4,5), c(1,2,3))
a
## [[1]]
## [1] 1 2 3 4 5
## 
## [[2]]
## [1] 1 2 3
unlist(a)
## [1] 1 2 3 4 5 1 2 3

Attributes

  • 모든 object 는 attribute를 가질 수 있다.
  • attr 함수를 통해 attribute를 지정하거나 조회할 수 있다.
  • attribute가 중요한 이유는
    • attribute 를 통해 데이터의 구조를 설정하기도 한다.
    • 많은 함수에서 attribute 에 따른 기능을 구현한다.
    • attribute는 R 에서 객체를 구현하는 한 가지 방법이다.
    • 새로운 해결 전략을 만들 수도 있고, 문제를 해결하는데 시간을 단축할 수 있다.

Attribute 생성 방법

  1. attr 함수
y <- 1:10
attr(y, "my_attribute") <- "This is a vector"
attr(y, "my_attribute")
## [1] "This is a vector"
  1. structure 함수
    • attirubte가 추가된 새로운 object를 반환한다.
structure(1:10, my_attribute = "This is a vector")
##  [1]  1  2  3  4  5  6  7  8  9 10
## attr(,"my_attribute")
## [1] "This is a vector"
  • attribute 조회
  • attributes 함수를 이용하면 list 형태로 반환
attributes(y)
## $my_attribute
## [1] "This is a vector"

세 가지 중요한 attribute

  • 아래 attributes 는 R 기본 함수에서 사용법이 약속 되어있다.
    • Names : 각 element 에 지정될 수 있는 chracter vector 이다.
    • Dimensions : Matrix 와 Array에서 쓰인다.
    • Class : S3 object system 에서 쓰인다.
  • 데이터를 변형해도 이 attribute 는 사라지지 않는다.
  • 다양한 함수에서 이 attribute 를 통해 기능을 구현하고 있다.

Names

  • name attribute를 만드는 법은 1) names 함수를 이용 하는 방법, 2) 변수를 생성할 때 지정하는 방법이 있다.
  • names 함수는 object의 name 을 만들거나 조회하는 함수이다.

  • names 함수를 이용하는 방법

v <- c(1, 2, 3)
names(v) <- c('a')
names(v)
## [1] "a" NA  NA
  • 변수를 생성할 때 지정
y <- c(a = 1, 2, 3) 
names(y)
## [1] "a" ""  ""
  • attr 함수를 통해서도 name 확인 가능
attr(y, "names")
## [1] "a" ""  ""

Factors

  • Factor는 integer vector 를 기반으로한 S3 object 이다.
  • Factor는 ’미리 정한 값’만 인자로 넣을 수 있는 Vector 로 볼 수 있다.
  • Factor는 범주형 변수를 저장할 때만 쓰인다.
  • Factor의 중요한 두 개의 attributes,
    • class : 이것은 factor 가 factor 임을 알려주고, integer vector 와 다른 쓰임을 갖게 한다.
    • levels : level은 ‘미리 정한 값’ 을 정의한다.
library(pryr)
x <- factor(c("a", "b", "b", "a"))
x
## [1] a b b a
## Levels: a b
typeof(x) # factor 의 base type 은 integer vector 이다.
## [1] "integer"
otype(x)  # factor 는 S3 object 이다.
## [1] "S3"
class(x)  
## [1] "factor"
str(x)
##  Factor w/ 2 levels "a","b": 1 2 2 1
# getAnywhere(str.default) # factor의 str은 default 로 들어가며, if 문에서 is.factor 로 체크가 된다. 
class(x)
## [1] "factor"
levels(x) # levels 함수는 해당 객체의 levels attribute 를 반환하는 함수이다. 
## [1] "a" "b"
  • factor는 interger vector 의 확장 버전이다.
# Factor 를 c 로 결합하면 강제 형변환 (coercion) 되면서, integer vector 로 변한다.
c(factor(c("a", "b", "c")), factor("b")) 
## [1] 1 2 3 1
  • factor는 범주형 변수에서처럼 값이 한정적일 때 levels를 미리 정의할 수 있다.
sex_char <- c("m", "m", "m") 
sex_factor <- factor(sex_char, levels = c("m", "f")) 
table(sex_char) 
## sex_char
## m 
## 3

Excercise (Attributes, Names, Factor)

  • “test” attribute 를 갖는 vector 생성하기
a <- 1:5 
attr(a, "test") <- "my attribute" 
a
## [1] 1 2 3 4 5
## attr(,"test")
## [1] "my attribute"
a <- structure(1:5, test = "my attribute") 
a 
## [1] 1 2 3 4 5
## attr(,"test")
## [1] "my attribute"

“comment” attribute 만들고 출력해보기

b <- structure(1:5, comment = "my attribute")
b # print.default 함수에서 comment attribute는 출력이 안되도록 구현 
## [1] 1 2 3 4 5
# ?attributes 
# Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set. 

Matrices and arrays

  • dim attribute 를 atomic vector 에 추가하면 배열 (array) 이 된다.
  • Matrix 는 array 의 한 종류로 2차원 array 이다.
  • array 는 atomic vector에 dimension 이 할당된 것이다.
# Matrix 

# 2x3 matrix 생성
a <- matrix(1:6, ncol = 3, nrow = 2)
a
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
# 2x3x2 array 생성
b <- array(1:12, c(2, 3, 2)) 
b
## , , 1
## 
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
## 
## , , 2
## 
##      [,1] [,2] [,3]
## [1,]    7    9   11
## [2,]    8   10   12
# dim attribute 를 추가함으로써 array 를 생성할 수도 있다. 
d <- 1:6
dim(d) <- c(3, 2)
d
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
## [3,]    3    6
e <- 1:6
attr(e, "dim") <- c(3,2)
e
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
## [3,]    3    6
  • matrix, array 의 특성
library(pryr)

# matrix, array 의 base object 는 numeric vector 이다. 
typeof(a)
## [1] "integer"
typeof(b)
## [1] "integer"
typeof(d)
## [1] "integer"
# matrix, array는 base object 이다. 
otype(a) 
## [1] "base"
otype(b) 
## [1] "base"
otype(d) 
## [1] "base"
# matrix, array 는 atomic vector에 dim attribute 가 추가된 것이다.  
attributes(a)
## $dim
## [1] 2 3
attributes(b)
## $dim
## [1] 2 3 2
attributes(d)
## $dim
## [1] 3 2
  • Charcter array 도 생성 가능
a <- array(c("a", "b", "c", "d", "e", "f"), c(2,3))
a
##      [,1] [,2] [,3]
## [1,] "a"  "c"  "e" 
## [2,] "b"  "d"  "f"
  • rownames와 colnames는 matrix 의 name attribute 를 설정하기 위해 사용됨
rownames(a) <- c("A", "B")
colnames(a) <- c("a", "b", "c")
a
##   a   b   c  
## A "a" "c" "e"
## B "b" "d" "f"
attributes(a)
## $dim
## [1] 2 3
## 
## $dimnames
## $dimnames[[1]]
## [1] "A" "B"
## 
## $dimnames[[2]]
## [1] "a" "b" "c"
  • length 는 전체 원소의 갯수를 출력함
  • array 는 integer vector 기반이므로 원소의 갯수 출력
length(a) 
## [1] 6
  • dimnames 를 통해 array 에서 각 dimension 에 대한 변수의 이름을 설정할 수 있음
dimnames(b) <- list(c("one", "two"), c("a", "b", "c"), c("A", "B"))
b
## , , A
## 
##     a b c
## one 1 3 5
## two 2 4 6
## 
## , , B
## 
##     a  b  c
## one 7  9 11
## two 8 10 12

Dataframe

  • Dataframe은 R 에서 데이터 분석 시 가장 일반적으로 사용되는 데이터구조
  • Dataframe 은 list 를 기반으로 만들어진 S3 클래스이다.
    • 2-dimensional structure
    • 일반적으로 각 element 는 equal-length vector
  • Dataframe은 matrix 와 list 의 성질을 동시에 갖음

data frame 의 생성

df <- data.frame(x = 1:3, y = c("a", "b", "c"))
df
##   x y
## 1 1 a
## 2 2 b
## 3 3 c

Data frame 의 성질

library(pryr)
otype(df) # data frame은 list 를 기반으로 만들어진 s3 클래스이다. 
## [1] "S3"
typeof(df) # data frame의 base object는 list 이다. 
## [1] "list"
class(df) # data frame의 s3 클래스 이름은 data.frame 이다. 
## [1] "data.frame"
dim(df) # data frame 은 matrix 의 성질을 갖는다. 
## [1] 3 2
  • data frame 에서는 character vector를 자동으로 factor 로 변형한다.
# 이를 해제하기 위해서는 stringAsFactors = FALSE 로 설정한다.
df <- data.frame(
  x = 1:3,
  y = c("a", "b", "c"),
  stringsAsFactors = FALSE)
str(df)
## 'data.frame':    3 obs. of  2 variables:
##  $ x: int  1 2 3
##  $ y: chr  "a" "b" "c"

Data frame 의 병합

cbind(df, data.frame(z = 3:1))
##   x y z
## 1 1 a 3
## 2 2 b 2
## 3 3 c 1
rbind(df, data.frame(x = 10, y = "z"))
##    x y
## 1  1 a
## 2  2 b
## 3  3 c
## 4 10 z

Special columns

  • 사용하는 경우는 많이 없지만 개념적으로 가능함
df <- data.frame(x = 1:3)
df$y <- list(1:2, 1:3, 1:4)
df
##   x          y
## 1 1       1, 2
## 2 2    1, 2, 3
## 3 3 1, 2, 3, 4
  • data frame 생성 시 컬럼으로 넣으려면 I 키워드를 통해 가능하다.
dfl <- data.frame(x = 1:3, y = I(list(1:2, 1:3, 1:4)))
dfl
##   x          y
## 1 1       1, 2
## 2 2    1, 2, 3
## 3 3 1, 2, 3, 4
dfl$y[[1]]
## [1] 1 2
  • matrix 도 data frame 의 컬럼으로 추가할 수 있다.
dfm <- data.frame(x = 1:3, y = I(matrix(1:9, nrow = 3)))
dfm
##   x y.1 y.2 y.3
## 1 1   1   4   7
## 2 2   2   5   8
## 3 3   3   6   9
dfm[2, "y"]
##      [,1] [,2] [,3]
## [1,]    2    5    8

Excercise (Data frame)

  • Data frame은 어떤 attribute 를 갖는가?
df <- data.frame(
  x = 1:3,
  y = c("a", "b", "c"),
  stringsAsFactors = FALSE)
attributes(df)
## $names
## [1] "x" "y"
## 
## $class
## [1] "data.frame"
## 
## $row.names
## [1] 1 2 3


반응형
반응형



Jupyter Lab – 단축키와 매직 기능

단축키 (Shortcut) 을 숙지하고 잘 사용할 수 있다면 더욱 생산적인 작업을 가능하게 할 것입니다. 본 포스팅에서는 자주 사용하는 Jupyter lab 의 단축키를 정리해보고자 합니다. 예를 들어, 주피터 랩에서는 새로운 cell 을 만들거나 작업이 끝난 cell 을 삭제할 일이 많습니다. 단축키를 사용하면, 단축키를 사용하지 않고 키보드를 통해 새로운 cell 을 추가하고 삭제하는 것보다 훨씬 빠르게 작업을 할 수 있습니다. 그리고 외우는데 시간이 오래걸리지도 않습니다!

Jupyter Lab의 단축키

  1. ESC  를 눌러 커맨드 모드로 진입하여 ENTER 를 통해 cell 을 수정할 수 있습니다. 아래 커맨드는 커맨드 모드에서 동작합니다. 
  2. A 는 현재 cell 위에 새로운 cell 을 추가합니다.
  3. B 는 현재 cell 밑에 새로운 cell 을 추가합니다.
  4. D + D D를 연속해서 두번 누르면 현재 cell 을 삭제합니다. 
  5. M 은 Markdown 셀로 변환하며, Y 는 Code 셀로 변환하고  R 은 Raw Cell 로 변환합니다.
  6. CTRL + B 화면을 더 크게 사용할 수 있습니다. 왼쪽 파일 탐색기가 사라집니다.
  7. SHIFT + M 두 개의 셀을 한개의 셀로 Merge 합니다.
  8. CTRL + SHIFT + – 현재 커서 위치를 기준으로 두 개의 셀로 구분합니다. 
  9. SHIFT+J or SHIFT + DOWN 현재 셀에서 아래쪽 위치로 새로운 셀을 같이 선택합니다. 
  10. SHIFT + K or SHIFT + UP 현재 셀에서 위쪽 위치로 새로운 셀을 같이 선택합니다. 
  11. CTRL + / 선택한 코드를 주석처리합니다.

기타 잘 알려지지 않은 단축키

  1. CTRL+D: 한줄 삭제하는 단축키입니다. 
  2. CTRL+[ or CTRL+]: 단체 indentation / Tab과 Shift+Tab 으로도 가능하지만, text editor 에서는 Shift+tab 이 안먹혀서 CTRL+[ 을 쓰면 유용합니다.  

Jupyter Lab 매직 기능 (Magic function)

매직 기능은 Ipython kernel 에서 제공하는 것이며, Jupyter lab 과 Jupyhter notebook 모두에서 작동합니다. 이는 어떤 언어를 선택하든 (예를 들어, Jupyter lab 에서 R을 사용하든 Python 을 사용하든) 동작합니다.

  1. %matplotlib inline 플롯을 화면 안에서 보여준다.
  2. %lsmagic 매직 기능에 어떤것들이 있는지 출력해준다. 
  3. %env 

    • %env 모든 환경변수를 출력한다.
    • %env var 해당 이름의 환경변수를 출력한다.
    • (%env var val) or (%env var=val) 환경변수를 설정한다.
  4. %run 

    • %run file_name 해당 이름의 .py 파일 또는 .ipynb 파일을 셀 안에서 실행한다. 
  5. %load 

    • %load source 해당 파일을 셀 안에 로드한다.
  6. %who = will list all variables that exist in the global scope. It can be used to see what all data_frames or any other variable is there in memory. 
    • %who: 현재 전역 환경의 모든 변수를 리스트한다. (메모리에 어떤 변수들이 올라와 있나 확인할 수 있다.)
    • %who df: 현재 선언된 dataframe 을 볼 수 있다. 
    • %whos: %who 와 비슷하지만 각 변수들에 대해 상세한 설명을 볼 수 있다. 
  7. %time 한 셀이 실행된 시간을 볼 수 있다. 
  8. %timeit 10만 번 실행하여 평균 시간을 잰다. 
  9. %writefile 

    • %writefile file_name 해당 파일의 셀의 아웃풋을 쓴다.
    • %writefile -a file_name 해당 파일의 셀의 아웃풋을 덧붙인다.

Jupyter configuration file (환경설정) 변경하기

주피터 랩에서는 print 문을 쓰지 않고 어떤 변수를 cell 에 입력했을 때, 가장 마지막 줄만 실행해서 결과로 내보냅니다. 이것이 불편하다면 아래 문장을 실행하면 됩니다. 개인적으로 유용하다고 생각합니다. 이렇게 하면 일일히 print 문으로 감싸줄 필요가 없게 됩니다.   

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

하지만 위 코드를 실행하는 것은 영구적으로 설정을 변경하지는 않는데, 영구적으로 설정을 변경하기 위해서는 configuration file 을 변경하면 됩니다. 이에 대한 설명은 해당 링크를 참고하시면 됩니다. configuration file 을 찾은 후에 아래 코드를 삽입해줍니다.

c.InteractiveShell.ast_node_interactivity = "all"

이외에도 주피터 랩에서는 더 많은 단축키와 매직 기능이 있습니다.



반응형
반응형

해들리 위컴은 어떻게 수많은 R 패키지를 개발할 수 있었을까?


R 에는 수많은 패키지들이 존재한다. 이들 중 어떤 패키지를 활용해서 데이터를 분석하고 결과를 도출해야 할까? 힘드게 문법을 읽힌 패키지가 아무도 쓰지 않는 패키지 였다면? 개발자가 더 이상 유지보수를 하지 않는다면? 다른 패키지의 함수들과 호환이 되지 않는다면? 우리의 시간은 제한되어 있기에 패키지 선정에 신중해야하며 위험이 존재하기도 한다. R packages for data science 라는 슬로건을 내걸은 Tidyverse 는 R 유저들에게 고민에 빠진 R 유저들에게 그 방법을 제시한다. Tidyverse 는 같은 철학은 공유하는 데이터과학을 위한 R 패키지들의 모임이며, 함께 사용할 수 있도록 디자인되었다. 


Hadleyverse 라고도 불리는 Tidyverse 패키지들


놀라운 것은 tidyverse 에 속한 패키지들의 많은 부분이 한 명에 의해 개발되었다는 것이다. 해들리위컴은 R 패키지 그룹인 Tidyverse 에 속한 dplyr, readr, tidyr, ggplot 와 같은 패키지들의 개발자로 유명하며, 그가 R 에 기여한 공헌도는 어마어마하다. 특히 tidy data 의 개념과  Leland Wilkinson 의 The Grammar of Graphics 을 구현한 층층이 쌓아 그래프를 그리는 ggplot2 는 가장 유명하다. 


2014년 Quora 에 어떻게 해들리 위컴이 이렇게 많이 R 에 기여할 수 있었는지에 대해 질문이 올라왔는데, 해들리 위컴이 직접 답변을 달았다. 그 내용은 아래와 같다. 


1. 글쓰기 (Writing)


그는 매일 아침 60~90분 동안 글쓰기를 한다. 아침에 일어나서 가장 먼저 하는 것이 글을 쓰는 것이다. 그가 글 쓰기를 중요시하는 첫 번재 이유는 직접 쓴 글은 스스로 참고하는데 유용하기 때문이다. 예를 들어, 그는 C++ 을 항상 코딩하는 것이 아니기 때문에, 참조가 필요한데 자신이 쓴 Rcpp 에 대한 글을 참조한다고 한다. 두 번째로, 글쓰기는 지식과 Tool 의 갭을 매울 수 있고, 이는 새로운 문제를 해결하는 방법을 고안하는데 도움이 된다고 한다. 


2. 읽기 (Reading)


그는 많이 읽는다. 약 300개의 blog 를 follow 하고 있고, Twitter 나 Stack Overflow 의 R tag 가 달린 글을 자주 본다고 한다. 물론 대부분의 글은 자세히 보지는 않지만, 이러한 수많은 글에 스스로를 노출시킴으로써 기술적인 변화를 따라가는데 도움되며, 새로운 프로그래밍 언어와 다른 사람들이 R 을 통해 데이터를 다루는 방법을 꾸준히 보고 어떠한 문제가 생겼을 때, 그 '이름' 을 빠르게 인식할 수 있다. 따라서 googling 등을 통해 가능한 해결법을 찾는 것을 더욱 빠르게 한다. 


3. 청킹 (Chunking)


문맥의 전환 (Coxtext switching)  은 매우 비싼 것이다. 많은 패키지들을 한 번에 작업하면, 이도저도 아니게 된다. 따라서 그는 대부분의 시간에서는 자신의 패키지들에 관한 어떠한 이슈나 아이디어를 점점 쌓아가고, 그렇게 쌓인 것 많아졌을 때 쯤, 그 패키지를 개선하는 작업에 며칠 간의 시간을 보낸다. 


마지막으로 그는 오래전부터 R 패키지 개발에 시간을 보냈기 때문에 그러한 경험이 축적되어 새로운 패키지를 순식간에 만들어낼 수 있는 지식과 경험을 보유하고 있다고 할 수 있다. 그의 학위 논문도 데이터를 exploration 하고 modeling 하는 R 패키지에 관한 것이었다. (http://had.co.nz/thesis/) 결국 그가 지금까지 R 커뮤니티에 공헌한 것들은 이러한 수년 간의 경험과 지식의 정리, 그리고 기술적 진보를 이해하는데 들인 노력의 산물이라고 할 수 있을 것이다.  


참고

https://www.quora.com/How-is-Hadley-Wickham-able-to-contribute-so-much-to-R-particularly-in-the-form-of-packages

반응형
반응형


Apply 계열 함수 정리


 Overview

FunctionDescription
applyApply functions over array margins
byApply a function to a data frame split by factors
eapplyApply a function over values in an environment
lapplyApply a function over a list or vector
mapplyApply a function to multiple list or vector arguments
rapplyRecursively apply a function to a list
tapplyApply a function over a ragged array

출처 - https://csgillespie.github.io/efficientR/programming.html



  • 데이터를 다룰 때, 원자별, 그룹별로 함수를 적용할 경우가 많다. 
  • Apply 계열의 함수는 데이터 구조를 갖는 R object 를 input 으로 받아 원소 별 혹은 그룹별로 함수를 적용시키는 것
  • input 과 output 의 형태에 따라 여러 종류로 나뉜다.
    • apply (input : array, output : array)
    • lapply (input : list or vector, output : list)
    • sapply (input : list or vector, output : vector or array)
    • vapply (input : list or vector, output : vector or array)
    • tapply (input : list or vector and factor, output : vector or array)
    • mapply (input : list or vector, output : vector or array)

apply

x <- matrix(1:9, c(3,3))  
x 
#>      [,1] [,2] [,3]
#> [1,]    1    4    7
#> [2,]    2    5    8
#> [3,]    3    6    9
  • Apply 함수는 행렬의 행 또는 열 방향으로 특정 함수를 적용한다.
  • apply(array, 방향, 함수)
  • 1: 행, 2: 열
apply(x, 1, function(x) {2*x}) 
#>      [,1] [,2] [,3]
#> [1,]    2    4    6
#> [2,]    8   10   12
#> [3,]   14   16   18
# apply 함수는 vector 에 적용할 수 없다. 
# dim attribute 가 있어야 함
# apply(c(1,2,3), 1, function(x) {2*x}) 
# 에러 출력

lapply

  • apply 함수의 단점은 input 으로 array 만 입력할 수 있다는 것이다. 
  • 일반적으로 vector 를 input 넣는 경우가 많은데, 이를 위해 lapply 가 존재한다. 
  • 입력으로 vector 또는 list 를 받아 list 를 반환한다.
result <- lapply(1:3, function(x) x*2)
result
#> [[1]]
#> [1] 2
#> 
#> [[2]]
#> [1] 4
#> 
#> [[3]]
#> [1] 6
unlist(result) # vector 로 바꾸고 싶으면 unlist 
#> [1] 2 4 6
  • 데이터 프레임에도 lapply 를 적용할 수 있다.
  • 데이터 프레임은 list 에 기반한 s3 object 이기 때문
head(iris)
#>   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
typeof(iris)
#> [1] "list"
lapply(iris[, 1:4], mean)
#> $Sepal.Length
#> [1] 5.843333
#> 
#> $Sepal.Width
#> [1] 3.057333
#> 
#> $Petal.Length
#> [1] 3.758
#> 
#> $Petal.Width
#> [1] 1.199333
y <- lapply(iris[, 1:4], function(x) {x > 3})
head(lapply(y, function(x) x[1:5]))
#> $Sepal.Length
#> [1] TRUE TRUE TRUE TRUE TRUE
#> 
#> $Sepal.Width
#> [1]  TRUE FALSE  TRUE  TRUE  TRUE
#> 
#> $Petal.Length
#> [1] FALSE FALSE FALSE FALSE FALSE
#> 
#> $Petal.Width
#> [1] FALSE FALSE FALSE FALSE FALSE

sapply

  • sapply 는 list 대신 행렬 or 벡터로 반환한다.
  • lapply는 list 를 반환하므로 list 를 다시 unlist 하는 것이 번거롭다.
  • lapply 의 wrapper 이다.
y <- sapply(iris[,1:4], function(x) {x > 3})
typeof(y) # Logical matrix 로 반환한다.
#> [1] "logical"
class(y)
#> [1] "matrix"
head(y)
#>      Sepal.Length Sepal.Width Petal.Length Petal.Width
#> [1,]         TRUE        TRUE        FALSE       FALSE
#> [2,]         TRUE       FALSE        FALSE       FALSE
#> [3,]         TRUE        TRUE        FALSE       FALSE
#> [4,]         TRUE        TRUE        FALSE       FALSE
#> [5,]         TRUE        TRUE        FALSE       FALSE
#> [6,]         TRUE        TRUE        FALSE       FALSE
z <- sapply(iris[, 1:4], mean)
z
#> Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#>     5.843333     3.057333     3.758000     1.199333

vapply

  • FUN.VALUE argument 에 output format 을 명확히 정의해서 더 안전함
y <- vapply(iris[, 1:4], function(x) {x > 3}, numeric(length(iris[, 1]))) # numeric vector 로의 반환 
typeof(y)
#> [1] "double"
class(y)
#> [1] "matrix"
head(y)
#>      Sepal.Length Sepal.Width Petal.Length Petal.Width
#> [1,]            1           1            0           0
#> [2,]            1           0            0           0
#> [3,]            1           1            0           0
#> [4,]            1           1            0           0
#> [5,]            1           1            0           0
#> [6,]            1           1            0           0

z <- vapply(iris[, 1:4], function(x) {x > 3}, logical(length(iris[, 1]))) # logical vector 로의 반환 
typeof(z)
#> [1] "logical"
class(z)
#> [1] "matrix"
head(z)
#>      Sepal.Length Sepal.Width Petal.Length Petal.Width
#> [1,]         TRUE        TRUE        FALSE       FALSE
#> [2,]         TRUE       FALSE        FALSE       FALSE
#> [3,]         TRUE        TRUE        FALSE       FALSE
#> [4,]         TRUE        TRUE        FALSE       FALSE
#> [5,]         TRUE        TRUE        FALSE       FALSE
#> [6,]         TRUE        TRUE        FALSE       FALSE

tapply

  • tapply 는 그룹별 처리를 위한 함수이다. 
  • 그룹을 인자로 주고 (factor 형), 원소별 처리가 아니라 그룹별 처리를 함 
tapply(1:10, rep(c(1,2), each=5), sum) # 그룹 인자는 알아서 factor 형으로 변환이 됨 
#>  1  2 
#> 15 40
  • iris 데이터에서 Species 별로 Sepal.Length 의 평균 구하기
tapply(iris$Sepal.Length, iris$Species, mean)
#>     setosa versicolor  virginica 
#>      5.006      5.936      6.588

mapply

  • sapply 와 비슷하지만 여러개의 인자를 넘긴다.
mapply(function(i, s) {
       sprintf(" %d%s ", i, s)
}, 1:3, c("a", "b", "c"))
#> [1] " 1a " " 2b " " 3c "
  • iris 데이터에서 Sepal.Length 와 Sepal.Width 를 합친 새로운 변수 생성
  • 인자가 몇 개 올지 모르므로, 앞선 함수들에서는 data 를 먼저 인자로 넘겼지만 mapply 에서는 함수를 먼저 인자로 넘긴다.
y <- mapply(`+`, iris$Sepal.Length, iris$Sepal.Width)
# 위 코드는 mapply(function(a, b) a+b, iris$Sepal.Length, iris$Sepal.Width) 와 같다. 
head(iris[,c("Sepal.Length", "Sepal.Width")])
#>   Sepal.Length Sepal.Width
#> 1          5.1         3.5
#> 2          4.9         3.0
#> 3          4.7         3.2
#> 4          4.6         3.1
#> 5          5.0         3.6
#> 6          5.4         3.9
y[1:5]
#> [1] 8.6 7.9 7.9 7.7 8.6


반응형
반응형