2.1 데이터 프레임

데이터 프레임은 R의 핵심적인 자료구조이다. 이는 엑셀과 같이 숫자, 문자 등 다양한 데이터를 하나의 테이블에 담을 수 있는 자료구조이다. 이를 잘 활용하면 엑셀의 기능들을 R에서도 자유자재로 사용할 수 있다.

2.1.1 데이터 프레임의 생성

먼저 데이터 데이터 프레임을 생성하는 방법은 다음과 같다.

이러한 데이터 프레임의 생성은 data.frame() 함수를 이용하여 생성할 수 있다.

df <- data.frame(col1=c('a', 'b', 'c', 'd', 'e'), 
                 col2=c(2, 4, 6, 8, 10))
df
##   col1 col2
## 1    a    2
## 2    b    4
## 3    c    6
## 4    d    8
## 5    e   10
  • data.frame() 함수의 인수는 엑셀 시트의 각 컬럼을 나타낸다.
  • 각 컬럼의 이름을 각 인수의 이름으로 지정할 수 있다.

2.1.1.1 data.frame() 함수의 형식

data.frame(..., 
            row.names = NULL, 
            check.rows = FALSE,
            check.names = TRUE, 
            fix.empty.names = TRUE,
            stringsAsFactors = default.stringsAsFactors())`

주요 인수 :

  • ... : 이 인수는 폼 값이나 tag = value 형태이다. 구성 요소명(컬럼 명)은 tag 이름으로 또는 deparsed 인수 자체로 생성된다.
  • row.names = : NULL, 단일 정수, 또는 행 이름으로 사용될 컬럼을 지정하는 문자열, 또는 데이터 프레임을 위한 행 이름을 주는 문자 또는 정수 벡터
  • check.rows = : FALSE 값이 디폴트 값. TRUE이면, 행들에 대한 길이와 이름에 대한 일관성 검토하고 위배되면 데이터 프레임을 생성하지 않는다.
  • check.names = : 논리값. TRUE이면 데이터 프레임에 있는 변수 이름들이 문법적으로 타당하고 증복이 없는 변수 이름들인지 검토한다. 필요하면 (make.names에 의해) 조정된다.
  • fix.empty.names = : 논리값 이름이 붙여지지 않은 컬럼(someName = arg 형태로 지정되지 않으면)이 자동으로 생성된 이름이나 name”.”을 가지고 있는지를 나타내는 논리값. “” 이름이 유지되어야 하지만 check.names가 false일 때에도 FALSE로 설정되어야 한다.
  • stringsAsFactors = default.stringsAsFactors() : 버전 4.0.0 이후로 기능이 없어짐. 이전 버전의 경우, 디폴트로 문자 벡터 컬럼의 경우 factor 형으로 생성되었음.

2.1.1.2 컬럼명을 지정하지 않은 경우

data.frame() 함수의 인수에 이름을 지정하지 않고 데이터 프레임을 생성하는 다음과 같은 경우를 생각해 보자.

df1 <- data.frame(1:5, letters[1:5])
str(df1)
## 'data.frame':    5 obs. of  2 variables:
##  $ X1.5        : int  1 2 3 4 5
##  $ letters.1.5.: chr  "a" "b" "c" "d" ...
df1
##   X1.5 letters.1.5.
## 1    1            a
## 2    2            b
## 3    3            c
## 4    4            d
## 5    5            e
  • df1의 첫 번째 컬럼의 값은 1:5, 두 번째 컬럼의 값은 letters[1:5]으로 지정하고 있으나, 컬럼명을 지정하지 않음.
  • 자동으로 첫 번째 컬럼명은 x1.5으로 그리고 두 번째 컬럼명은 letters.1.5으로 자동생성된다.

데이터 프레임을 생성하면서 컬럼 이름을 생성하고 싶지 않은 경우는 fix.empty.names = FALSE 인수를 사용한다. 그러면 생성되는 데이터 프레임의 컬럼 이름이 생성되지 않는다.

df2 <- data.frame(1:5, letters[1:5], fix.empty.names = FALSE)
str(df2)
## 'data.frame':    5 obs. of  2 variables:
##  $ : int  1 2 3 4 5
##  $ : chr  "a" "b" "c" "d" ...
df2
##      
## 1 1 a
## 2 2 b
## 3 3 c
## 4 4 d
## 5 5 e
  • fix.empty.names = FALSE 옵션을 설정하면, 컬럼명이 자동생성되지 않고 공란(“”)으로 남는다.

2.1.1.3 컬럼명을 지정한 경우

데이터 프레임을 생성할 때 각 컬럼의 이름을 지정하고 싶은 경우는 다음과 같이 각 요소에 이름을 지정해 주면 된다.

df3 <- data.frame(a = 1:5, b = letters[1:5])  
str(df3)
## 'data.frame':    5 obs. of  2 variables:
##  $ a: int  1 2 3 4 5
##  $ b: chr  "a" "b" "c" "d" ...
df3
##   a b
## 1 1 a
## 2 2 b
## 3 3 c
## 4 4 d
## 5 5 e
  • df3는 데이터 값과 더불어 각 컬럼의 이름(ab)을 가지고 있다.

2.1.1.4 row.namesa로 지정한 경우

df4 <- data.frame(a = 1:5, b = letters[1:5], row.names = "a")  
str(df4)
## 'data.frame':    5 obs. of  1 variable:
##  $ b: chr  "a" "b" "c" "d" ...
df4
##   b
## 1 a
## 2 b
## 3 c
## 4 d
## 5 e
  • df4row.names = “a”로 지정함으로써, 벡터 a = 1:5df4의 행 이름이 된다.
  • 즉, df4b 컬럼 만을 갖는 데이터 프레임이다.

2.1.1.5 row.names =b로 지정한 경우

df5 <- data.frame(a = 1:5, b = letters[1:5], row.names = "b")  
str(df5)
## 'data.frame':    5 obs. of  1 variable:
##  $ a: int  1 2 3 4 5
df5
##   a
## a 1
## b 2
## c 3
## d 4
## e 5
  • df5row.names = “b”를 지정함으로써 b의 값들(letters[1:5])이 df5의 행 이름이 된다.
  • 즉, df5a 컬럼 하나만 있는 데이터 프레임이다.

2.1.1.6 각 컬럼의 행의 길이가 다른 경우

데이터 프레임은 데이터 프레임을 구성하고 있는 각 컬럼에 대하여 동일한 수의 행을 가져야 한다. 특정 컬럼의 행의 갯수가 다른 컬럼의 행의 갯수와 다르게 지정되면 데이터 프레임은 생성되지 않는다. 별로의 행의 갯수가 모두 같아야 한다.

다음과 같이 a 컬럼은 요소가 6개, b컬럼은 요소가 5개로 지정되는 경우 에러 메시지가 나타나고 데이터 프레임은 생성되지 않는다.

df6 <- data.frame(a = 1:6, b = letters[1:5])
## Error in data.frame(a = 1:6, b = letters[1:5]): arguments imply differing number of rows: 6, 5
str(df6)
## Error in str(df6): 객체 'df6'를 찾을 수 없습니다
df6                 
## Error in eval(expr, envir, enclos): 객체 'df6'를 찾을 수 없습니다

만일 각 컬럼의 행의 갯수가 다른 데이터 구조를 만들고 싶으면 리스트(list) 구조를 이용해야 한다.

2.1.1.7 컬럼명이 같은 경우 : check.names = 인수의 사용

check.names = TRUE 인수를 사용하는 경우,

df7 <- data.frame(a = 1:5, a = letters[1:5], check.names = TRUE)  
str(df7)
## 'data.frame':    5 obs. of  2 variables:
##  $ a  : int  1 2 3 4 5
##  $ a.1: chr  "a" "b" "c" "d" ...
df7
##   a a.1
## 1 1   a
## 2 2   b
## 3 3   c
## 4 4   d
## 5 5   e
  • 두 개의 컬럼명이 모두 a로 되어 있다.
  • df7의 경우는 check.names = TRUE로 옵션을 설정하여, 컬럼명이 자동 조정되었다.

check.names = FALSE 인수를 사용하는 경우에는 다음과 같이 두 개의 컬럼 이름이 같게 된다.

df8 <- data.frame(a = 1:5, a = letters[1:5], check.names = FALSE)  
str(df8)
## 'data.frame':    5 obs. of  2 variables:
##  $ a: int  1 2 3 4 5
##  $ a: chr  "a" "b" "c" "d" ...
df8
##   a a
## 1 1 a
## 2 2 b
## 3 3 c
## 4 4 d
## 5 5 e
  • 두 개의 컬럼명이 모두 a로 되어 있다.
  • df8의 경우는 check.names = FASLE로 옵션을 설정하여, 컬럼명이 조정되지 않았다.
  • 이런 경우는 데이터 관리상 바람직하지 않다.

2.1.2 행/열 결합에 의한 데이터 프레임 생성하기

데이터 프레임은 열을 우선적으로 하는 데이터 구조로 본다면, 각 열들은 개별적인 벡터 형태를 갖는다. 반면에, 데이터 프레임을 행을 중심으로 데이터 구조를 본다면, 각 행들은 각각의 요소가 서로 다른 데이터 유형을 갖는 리스트 구조를 갖는다.

따라서 요소의 갯수가 같은 여러 개의 벡터들은 열 결합으로 데이터 프레임을 생성할 수 있고, 요소의 갯수가 같고, 각 요소 별로 동일한 데이터 유형을 갖는 여러 개의 리스틀들은 행 결합으로 데이터 프레임을 생성할 수 있다.

2.1.2.1 열 결합으로 데이터 프레임 생성하기

다음과 같이 vec1vec2 등 두 개의 벡터가 있다고 가정하자.

vec1 <- c('one','two','three'); vec1
## [1] "one"   "two"   "three"
vec2 <- c(1,2,3); vec2
## [1] 1 2 3

이 두 개의 벡터는 요소의 갯수가 모두 3개로 같다. 따라서 이 벡터를 열로 결합하여 데이티 프레임을 생성할 수 있는데, 이 때에도 data.frame() 함수를 이용하는 것이 좋다.

vec <- data.frame(vec1, vec2)
str(vec)
## 'data.frame':    3 obs. of  2 variables:
##  $ vec1: chr  "one" "two" "three"
##  $ vec2: num  1 2 3
class(vec)
## [1] "data.frame"
  • 각 컬럼의 데이터 유형이 그대로 유지되었음을 알 수 있다.
  • 클래스는 data.frame이다.

문자형 컬럼을 factor 형으로 만들고 싶은 경우에는 stringsAsFactors = T를 인수로 이용한다.

vec <- data.frame(vec1, vec2, stringsAsFactors = T)
str(vec)
## 'data.frame':    3 obs. of  2 variables:
##  $ vec1: Factor w/ 3 levels "one","three",..: 1 3 2
##  $ vec2: num  1 2 3
class(vec)
## [1] "data.frame"
  • vec1 컬럼이 factor 형임을 알 수 있다.

2.1.2.2 행 결합으로 데이터 프레임 생성하기

반면에 리스트 구조의 경우는 각 요소별로 데이터 유형을 달리할 수 있는 데이터 구조이다.

예를 들어, 다음과 같은 세 개의 리스트가 있다고 가정하자.

list1 <- list('one', 1)
list2 <- list('two', 2)
list3 <- list('three', 3)

이제 이 3개의 리스트를 행을 결합해서 데이터 프레임을 만들기 위해서는 rbind() 함수와 data.frame() 함수를 함께 사용해야 한다.

df_list <- rbind(list1, list2, list3)
class(df_list)
## [1] "matrix" "array"
df_list <- data.frame(df_list)
class(df_list)
## [1] "data.frame"
  • rbind()로 결합한 데이터는 "matrix" "array" 클래스이다.
  • 이를 data.frame() 함수를 이용하면 "data.frame" 클래스를 갖게 된다.

여러 개의 벡터를 열 결합하여 데이터 프레임을 생성하는 경우에도 cbind()를 사용할 수 있는데, 이 때에도 data.frame() 함수를 이용해야 하므로, 결국 cbind() 함수를 이용한 열의 결합은 불필요하다.

2.1.3 데이터 프레임의 구조 확인

데이터 프레임은 컬럼의 집합으로 구성된 데이터 구조이다. 이러한 데이터 프레임의 각 컬럼들의 데이터 타입이 어떻게 구성되는지를 파악하는 것은 매우 중요한 데, 데이터 프레임의 구조(structure, 컬럼의 구성)str() 함수로 확인할 수 있다.

str() 함수

str() 함수의 도움말은 다른 함수의 도움말을 보는 것과 마찬가지로 ?를 이용하여 확인할 수 있다.

? str()
## starting httpd help server ... done

이제 df1의 데이터 구조는 다음과 같이 확인할 수 있다.

str(df1)
## 'data.frame':    5 obs. of  2 variables:
##  $ X1.5        : int  1 2 3 4 5
##  $ letters.1.5.: chr  "a" "b" "c" "d" ...
  • 'data.frame': 5 obs. of 2 variables: : 데이터 프레임 구조로 2개의 컬럼으로 구성되며 각 컬럼에 5개의 행이 있음을 알려 준다.
  • $ X1.5 : int 1 2 3 4 5 : 첫번째 컬럼의 이름과 데이터 유형, 그리고 데이터 값들을 보여 준다
  • $ letters.1.5.: chr "a" "b" "c" "d" ... : 두번째 컬럼의 이름과 데이터 유형, 그리고 데이터 값들을 보여 준다

2.1.4 데이터 프레임의 데이터 확인

데이터 프레임의 데이터를 확인하는 방법은 다음과 같다.

1) 데이터 프레임 개체 이름을 입력하는 방법 : head()나 tail() 함수 함께 사용
2) R Studio의 Global Environment의 Data에서 마우스로 df를 클릭하는 방법. 이는 `base` R의 `View()` 함수를 사용하는 것과 같다.
3) `tibble` 패키지 또는 `dplyr` 패키지의 `glimpse()` 함수 사용

앞에서 생성한 df1 데이터를 보기 위한 첫번째 방법은 단순히 개체이름을 입력하는 것이다. 이때 행의 갯수가 많은 경우는 head() 또는 tail() 함수를 이용한다.

head(df1)
##   X1.5 letters.1.5.
## 1    1            a
## 2    2            b
## 3    3            c
## 4    4            d
## 5    5            e
View(df1)
tibble::glimpse(df1)
## Rows: 5
## Columns: 2
## $ X1.5         <int> 1, 2, 3, 4, 5
## $ letters.1.5. <chr> "a", "b", "c", "d", "e"
  • View() 함수를 사용하면 RStudio에서는 별도의 탭 화면이 생기면서 데이터 내용이 출력된다.

2.1.5 데이터 프레임의 통계적 요약 정보

데이터 프레임의 경우 summary() 함수를 이용하여 다음과 같이 각 컬럼별 통계적 요약 정보를 확인할 수 있다.

summary(df1)
##       X1.5   letters.1.5.      
##  Min.   :1   Length:5          
##  1st Qu.:2   Class :character  
##  Median :3   Mode  :character  
##  Mean   :3                     
##  3rd Qu.:4                     
##  Max.   :5
  • 숫자형 컬럼의 경우는 최댓값, 최솟값, 4분위수, 중앙값과 평균값 등이 출력된다.
  • 문자형 컬럼의 경우는 행의 갯수와 컬럼의 Class와 Mode가 출력된다.

2.1.6 iris 데이터 세트의 예

R에 내장되어 있는 iris 데이터 세트는 데이터 프레임 구조를 갖고 있다.

2.1.6.1 iris의 데이터 구조

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 ...
  • 'data.frame': 150 obs. of 5 variables: : 5개의 열과 150개의 행으로 구성된 데이터 프레임 구조.
  • 각각의 컬럼에 대하여 $로 구분되는 컬럼의 이름, 데이터 유형, 그리고 데이터 예를 보여주고 있다.

2.1.6.2 iris의 데이터 내용

iris 데이터 세트의 데이터 내용을 확인해 보자. 행의 갯수가 150개 이므로 첫 6개의 행을 보기 위해 head() 함수를 사용한다.

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

참고로 head() 함수와 tail() 함수의 사용에 대하여 간단히 소개한다.

  • head(iris, n=10) : iris 데이터 세트의 첫 10개 행을 볼 수 있다. n = 인수로 행의 갯수를 조절할 수 있다.
  • tail(iris, n=10) : iris 데이터 세트의 마지막 10개 행을 볼 수 있다. n = 인수로 행의 갯수를 조절할 수 있다.

2.1.6.3 iris의 통계적 요약 정보

summary() 함수를 이용하여 iris 데이터 세트에 대한 통계적 요약 정보를 확인해 보자.

summary(iris)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##        Species  
##  setosa    :50  
##  versicolor:50  
##  virginica :50  
##                 
##                 
## 
  • 4개의 숫자형 컬럼들은 통계적 요약 정보로 min, 1st Qu., Median, Mean, 3rd Qu., Max. 등의 요약 정보를 출력한다.
  • 문자형 컬럼이 Species 컬럼의 경우는 컬럼이 갖는 값의 종류별 데이터의 갯수를 보여준다.

2.1.7 데이터 프레임의 행/열의 이름 지정하기

데이터 프레임의 각 컬럼과 행에 대하여 이름을 지정하고 수정할 수 있다.

2.1.7.1 데이터 세트

다음과 같은 예제 데이터를 가지고 데이터 프레임의 행/열의 이름을 지정하고 수정하는 방법에 대해 학습하기로 한다.

df1 <- data.frame(a=1:3, b=letters[4:6], c=7:9); df1
##   a b c
## 1 1 d 7
## 2 2 e 8
## 3 3 f 9

2.1.7.2 컬럼 이름 확인과 수정

데이터 프레임의 컬럼 이름을 확인하고 수정하는 데에는 colnames() 함수를 사용한다.

colnames(df1)                               # df1의 컬럼 이름 확인
## [1] "a" "b" "c"
colnames(df1) <- c("1열", "2열", "3열")     # df1의 컬럼 이름 수정
str(df1)
## 'data.frame':    3 obs. of  3 variables:
##  $ 1열: int  1 2 3
##  $ 2열: chr  "d" "e" "f"
##  $ 3열: int  7 8 9
  • colnames(df1) : df1의 컬럼 이름이 a, b, c 임을 확인할 수 있다.
  • colnames(df1) <- c("1열", "2열", "3열") : df1의 컬럼 이름을 ("1열", "2열", "3열")로 수정한다.

2.1.7.3 행 이름 확인과 수정

데이터 프레임의 행 이름을 확인하고 수정하는 데에는 rownames() 함수를 사용한다.

rownames(df1)                                # df1의 행 이름 확인
## [1] "1" "2" "3"
rownames(df1) <- c('행1','행2','행3')        # df1의 행 이름 변경
df1
##     1열 2열 3열
## 행1   1   d   7
## 행2   2   e   8
## 행3   3   f   9

2.1.8 데이터 프레임의 데이터에 접근하기

데이터 프레임은 행과 열과 구성되는 표의 구조를 갖는다. 이러한 데이터 프레임의 데이터에 대한 접근은 다음과 같은 세 가지 방법으로 접근할 수 있다.

  • 한 셀에 위치한 특정 요소 값 접근
  • 행 전체 접근
  • 열 전체 접근

한편, 이러한 데이터를 참조할 때는 또한 다음과 같은 방법으로 참조할 수 있다. - 행과 열의 위치(색인) 번호 - 행과 열의 이름 - 조건에 의한 참조 : 행과 열의 논리형 벡터

따라서 아래에서는 이러한 두 가지 참조 방법으로 각각의 데이터에 접근해 보기로 한다.

2.1.8.1 예제 데이터 세트

데이터 프레임의 데이터에 대한 접근을 위해 다음과 같은 데이터 세트를 예로 만든다.

df2 <- data.frame(col1 = letters[1:5],
                 col2 = c(2, 4, 6, 8, 10),
                 col3 = LETTERS[1:5]
                 )
df2
##   col1 col2 col3
## 1    a    2    A
## 2    b    4    B
## 3    c    6    C
## 4    d    8    D
## 5    e   10    E

2.1.8.2 색인 번호 참조

색인 번호를 참조한 데이터의 접근을 인덱싱(indexing) 이라고 한다.

데이터 프레임은 행과 열로 구성되는데, 각 열과 행은 기본적으로 1부터 N까지의 번호가 자동으로 부여된다.

그리고 이러한 행과 열에 대하여 df2[m, n]과 같이 행렬 형식으로 데이터 프레임의 m 행, n 열의 요소 값에 접근할 수 있다.

2.1.8.2.1 행 번호 접근

예제 데이터 df2에 대해 행 데이터는 다음과 같이 접근할 수 있다.

df2[2, ]                # 2번쨰 행
##   col1 col2 col3
## 2    b    4    B
df2[c(1,3), ]           # 1, 3번째 행
##   col1 col2 col3
## 1    a    2    A
## 3    c    6    C
df2[-2, ]               # 2번째 행 제외
##   col1 col2 col3
## 1    a    2    A
## 3    c    6    C
## 4    d    8    D
## 5    e   10    E
  • [행 번호,열 번호]로 구성되는 대괄호 안에 열 번호를 생략하면 모든 열을 표시
  • df2[c(1, 3), ] : 동시에 여러 행을 지정하기
  • df2[-2, ] : - 기호로 행을 제외할 수 있다.
2.1.8.2.2 열 번호 접근

예제 데이터 df2에 대한 열 데이터는 다음과 같이 접근할 수 있다.

df2[  , 2]                # 2번쨰 열
## [1]  2  4  6  8 10
df2[ ,  c(1,3)]           # 1, 3번째 열
##   col1 col3
## 1    a    A
## 2    b    B
## 3    c    C
## 4    d    D
## 5    e    E
df2[ , -2]                # 2번째 열 제외
##   col1 col3
## 1    a    A
## 2    b    B
## 3    c    C
## 4    d    D
## 5    e    E
  • [행 번호,열 번호]로 구성되는 대괄호 안에서 행 번호를 생략하면 모든 행을 표시한다.
  • df2[ , 2] : 두 번째 열을 표시하는데, 벡터 형식임을 주목하라.
  • df2[ , c(1,3)] : 1, 3번째 열의 데이터에 접근한다.
  • df2[ , -2] : 2번째 열을 제외한 나머지 열의 데이터에 접근한다
2.1.8.2.3 특정 셀의 데이터 접근

특정 열의 특정 행의 값을 접근하기 위해서는 다음과 같이 행의 번호와 열의 번호를 지정하면 된다.

df2[1, 3]                # 1행, 3열의 값
## [1] "A"
df2[c(1, 3), c(1, 2)]    # (1, 3) 행에 대해 (1, 2)열의 값
##   col1 col2
## 1    a    2
## 3    c    6
df2[-c(2, 4, 5), -3]              # 위의 두 번째와 같음
##   col1 col2
## 1    a    2
## 3    c    6

2.1.8.3 컬럼 이름으로 접근하기

데이터 프레임의 각 컬럼은 df2$컬럼명과 같이 $기호를 이용하여 참조할 수 있고, 다음과 같이 색인 번호 대신에 컬럼명을 기입해서 참조할 수도 있다.

df2$col1                                 # column이름으로 접근하기
## [1] "a" "b" "c" "d" "e"
df2[ , "col1"]                           # [  ]안에 컬럼 이름을 기입해서 접근하기
## [1] "a" "b" "c" "d" "e"
df2[ , c("col1", "col3")]
##   col1 col3
## 1    a    A
## 2    b    B
## 3    c    C
## 4    d    D
## 5    e    E

2.1.8.4 조건에 의한 행의 접근

각 컬럼과 행에 대하여 조건을 적용하여 접근하는 것은 뒤에서 학습하는 filter() 함수를 이용할 때 적용되는 접근방법이다.

여기서는 filter() 함수와 같이 작용하는 조건에 의한 행의 접근에 대하여 알아보가로 한다.

예를 들어, df2col2 값이 5보다 큰 행만 출력하고 싶다면, 다음과 같이 하면 된다.

cond <- df2$col2 > 5      # df의 col2의 요소 값 각각에 대해 그 값이 5보다 큰지 확인한다.
cond
## [1] FALSE FALSE  TRUE  TRUE  TRUE
df2[cond, ]               # 논리형 벡터 cond를 행의 요소로 해서 df의 모든 열을 출력한다.
##   col1 col2 col3
## 3    c    6    C
## 4    d    8    D
## 5    e   10    E
  • cond를 출력해 보면, df2$col2의 값이 5보다 큰 경우에만 TRUE값을 갖는다.
  • df2[cond, ] : 논리형 벡터 cond에서 TRUE이 요소만 df2의 모든 열을 출력한다.

또한 위의 스크립트는 다음과 같이 간단하게 한 줄로 표현할 수도 있다.

df2[df2$col2 > 5, ] 
##   col1 col2 col3
## 3    c    6    C
## 4    d    8    D
## 5    e   10    E

연습문제

R에 기본 내장되어 있는 iris 데이터를 활용하여 아래 질문에 답하시오.

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
  1. 첫번째, 3번째 컬럼을 선택하시오.
head(iris[, c(1, 3)])
##   Sepal.Length Petal.Length
## 1          5.1          1.4
## 2          4.9          1.4
## 3          4.7          1.3
## 4          4.6          1.5
## 5          5.0          1.4
## 6          5.4          1.7
  1. 3번째 컬럼을 빼고 선택하시오.
head(iris[, -3])
##   Sepal.Length Sepal.Width Petal.Width Species
## 1          5.1         3.5         0.2  setosa
## 2          4.9         3.0         0.2  setosa
## 3          4.7         3.2         0.2  setosa
## 4          4.6         3.1         0.2  setosa
## 5          5.0         3.6         0.2  setosa
## 6          5.4         3.9         0.4  setosa
  1. 각 row의 Sepal.LengthSepal.Width의 값을 더하여 Sepal.Sum이라는 컬럼을 추가하시오.
Sepal.Sum <- iris$Sepal.Length + iris$Sepal.Width
head(cbind(iris[, -3], Sepal.Sum))
##   Sepal.Length Sepal.Width Petal.Width Species Sepal.Sum
## 1          5.1         3.5         0.2  setosa       8.6
## 2          4.9         3.0         0.2  setosa       7.9
## 3          4.7         3.2         0.2  setosa       7.9
## 4          4.6         3.1         0.2  setosa       7.7
## 5          5.0         3.6         0.2  setosa       8.6
## 6          5.4         3.9         0.4  setosa       9.3

참고) iris[1, ]iris[1, ,drop=T]의 차이는?

iris[1,]
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
class(iris[1,])
## [1] "data.frame"
  • iris[1,] : df의 첫 번째 행을 출력. 그 결과는 데이터 프레임
iris[1, ,drop=T]
## $Sepal.Length
## [1] 5.1
## 
## $Sepal.Width
## [1] 3.5
## 
## $Petal.Length
## [1] 1.4
## 
## $Petal.Width
## [1] 0.2
## 
## $Species
## [1] setosa
## Levels: setosa versicolor virginica
class(iris[1, ,drop=T])
## [1] "list"
  • df[1, , drop=T] : df의 첫 번째 행을 출력하되, drop=T에 의해 그 결과를 list로 출력함.

2.1.9 데이터 프레임에 행/열 추가하기

데이터 프레임에 새로운 행과 열을 추가하기 위해서는 데이터 프레임의 행과 열의 특성을 이해할 필요가 있다.

데이터 프레임의 각 열이 갖는 구조는 벡터구조이다. 반면에 각 행의 구조는 리스트 구조이다. 따라서 추가하려는 행의 데이터는 리스트 구조여야 하며, 또한 추가하려는 열의 구조는 벡터 구조가 되어야 한다.

다음과 같이 vec 데이터 프레임을 생성한다.

vec1 <- c('one','two','three'); vec1
## [1] "one"   "two"   "three"
vec2 <- c(1,2,3); vec2
## [1] 1 2 3
vec <- data.frame(vec1, vec2)

2.1.9.1 데이터 프레임에 열 추가하기

데이터 프레임에 열을 추가하기 위한 전제 조건이 있다. 그것은 추가하려는 열의 데이터 구조가 벡터여야 하며, 그 벡터의 요소 갯수가 데이터 프리엠의 행의 갯수와 같아야 한다는 것이다.

데이터 프레임에 새로운 열을 추가하는 방법은 다양하다.

  • cbind() 함수 또는 data.frame() 함수를 이용하는 방법
  • 치환 연산자 <- 를 이용하는 방법
2.1.9.1.1 함수를 이용하는 방법

앞의 예에서 vec 데이터 프레임에 vec3와 같은 (논리형) 벡터를 새로운 열로 추가하려면 다음과 같이 cbind() 함수를 이용할 수 있다.

vec3 <- c(TRUE, FALSE, FALSE)
new_vec <-  cbind(vec, vec3)
str(new_vec)
## 'data.frame':    3 obs. of  3 variables:
##  $ vec1: chr  "one" "two" "three"
##  $ vec2: num  1 2 3
##  $ vec3: logi  TRUE FALSE FALSE
class(new_vec)
## [1] "data.frame"
  • 세번째 컬럼으로 논리형 vec3가 추가되었다.
2.1.9.1.2 치환 연산자를 이용하는 방법

앞의 예는 다음과 같이 해도 결과는 똑 같다.

vec3 <- c(TRUE, FALSE, FALSE)
new_df <- vec                           # vec와 똑 같은 new_df 생서
new_df$vec3 <-  vec3                    # new_df에 vec3 컬럼을 새로 만들고, vec3 벡터 값을 대입
str(new_df)
## 'data.frame':    3 obs. of  3 variables:
##  $ vec1: chr  "one" "two" "three"
##  $ vec2: num  1 2 3
##  $ vec3: logi  TRUE FALSE FALSE
class(new_df)
## [1] "data.frame"
  • 새로 삽입되는 new_df$vec3 컬럼은 new_df의 세 번쨰 컬럼이므로 new_df[ , 3]와 같이 해도 된다.

2.1.9.2 데이터 프레임에 새로운 행 추가

데이터 프레임에 새로운 행을 추가할 수 있다. 다만, 데이터 프레임의 행은 리스트 구조이기 때문에 추가하려는 행도 데이터 프레임의 각 열의 데이터 유형과 일치하는 리스트 구조여야 한다는 전제가 있다

앞의 예에서 new_df에 새로운 행을 추가하는 것을 다음과 같다. 다만, 새로 추가되는 행의 리스트 구조로, new_df에 있는 컬럼들의 데이터 유형과 같아야 한다.

new_row <- list("four", 4, TRUE)     # 또는 list(vec1 = "four", vec2 = 4, vec3 = TRUE)
new_df1 <- rbind(new_df, new_row)
new_df1
##    vec1 vec2  vec3
## 1   one    1  TRUE
## 2   two    2 FALSE
## 3 three    3 FALSE
## 4  four    4  TRUE

2.1.10 데이터 프레임의 행/열 삭제

다음과 같이 new_df1vec3의 컬럼을 제거할 수 있다.

  • 치환 연산자 <-를 이용하여 특정 컬럼에 NULL 값을 대입하는 방법
  • 특정 컬럼의 색인 번호에 (-)를 적용하여 컬럼을 제거하는 방법

다음의 스크립트는 위의 두 가지 방법에 대한 스크립의 예를 보여주고 있다. 여기서 new_df2new_df1은 같은 결과를 보여준다.

new_df2 <- new_df1[ , -3]               # 색인 번호 이용
new_df2
##    vec1 vec2
## 1   one    1
## 2   two    2
## 3 three    3
## 4  four    4
# 또는

new_df1$vec3 <- NULL                    # 컬럼에 NULL 값을 대입
new_df1
##    vec1 vec2
## 1   one    1
## 2   two    2
## 3 three    3
## 4  four    4
  • 제거하고자 하는 컬럼의 색인 번호를 - 기호로 제거한다.
  • 제거하고자 하는 컬럼에 NULL값을 대입한다.

2.1.11 데이터 프레임 컬럼의 데이터 타입 변환

2.1.11.1 데이터 타입‘만’ 알고 싶을때 : class() 함수

데이터 타입은 class() 함수로 확인할 수 있다.

숫자형 벡터

class(c(1,2))
## [1] "numeric"

문자형 벡터

class(c("a", "b"))
## [1] "character"

논리형 벡터

class(c(T, FALSE))
## [1] "logical"

날짜형 벡터

class(as.Date(c("2020-09-01", "2020-09-31")))
## [1] "Date"
  • 문자형의 날짜를 as.Date() 함수를 이용하여 날짜형(Date)로 변경해야 함.

행렬

class(matrix(c(1,2)))
## [1] "matrix" "array"
  • 행렬은 배열의 특수한 형태임.

리스트

class(list(1,2))
## [1] "list"
  • list() 함수에 의해 데이터 구조가 리스트로 지정됨.

데이터 프레임

class(data.frame(1,2))
## [1] "data.frame"

2.1.11.2 데이터 타입과 데이터 모양에 대한 추가정보까지 : str() 함수

str() 함수는 데이터의 구조에 대한 요약 정보를 보여준다.

2.1.11.2.1 숫자형 벡터
str(c(1,2))
##  num [1:2] 1 2
  • 데이터 유형(num)과 요소의 갯수([1:2]), 그리고 데이터 값( 1 2 ) 등을 확인할 수 있다.
2.1.11.2.2 행렬
str(matrix(c(1,2)))
##  num [1:2, 1] 1 2
  • 데이터 유형(num), 요소의 갯수([1:2, 1]), 여기서 행의 갯수는 [1:2], 열의 갯수가 1개 임., 그리고 데이터 값( 1 2 ) 등을 확인할 수 있다.
2.1.11.2.3 리스트
str(list(c(1,2)))
## List of 1
##  $ : num [1:2] 1 2
  • List of 1 : 데이터 구조가 List'형, 컬럼의 갯수는1`개
  • $ : num [1:2] 1 2 : 컬럼 명이 지정되어 있지 않으며($ 다음에 컬럼 이름이 표시됨), 데이터 유형은 num, 요소의 갯수는 [1:2], 실제 데이터는 1 2
2.1.11.2.4 데이터 프레임
str(data.frame(1,2))
## 'data.frame':    1 obs. of  2 variables:
##  $ X1: num 1
##  $ X2: num 2
  • 'data.frame': 1 obs. of 2 variables: : 데이터 구조는 data.frame, 행의 갯수는 1개(1 obs.), 열의 갯수는 2개(2 variables)
  • $ X1: num 1 : 첫 번째 열은 X1($ X1), 이 열의 데이터 유형은 숫자형(num), 값은 1
  • $ X2: num 2 : 두 번째 열은 X2($ X2), 이 열의 데이터 유형은 숫자형(num), 값은 2

2.1.11.3 데이터 유형만 확인

2.1.11.3.1 숫자형 확인 : is.numeric() 함수

숫사형 벡터의 확인

is.numeric(c(1,2,3))
## [1] TRUE

문자형 벡터의 확인

is.numeric(c('a','b','c'))
## [1] FALSE
2.1.11.3.2 행렬 확인 : is.matrix() 함수
is.matrix(matrix(c(1,2)))
## [1] TRUE
2.1.11.3.3 관련 함수들

다음 함수들을 사용하여 데이터 타입을 손쉽게 판단할 수 있다.

함수 설명
is.factor(x) 주어진 객체 x가 팩터인가
is.numeric(x) 주어진 객체 x가 숫자를 저장한 벡터인가
is.character(x) 주어진 객체 x가 문자열을 저장한 벡터인가
is.matrix(x) 주어진 객체 x가 행렬인가
is.array(x) 주어진 객체 x가 배열인가
is.data.frame(x) 주어진 객체 x가 데이터 프레임인가

2.1.11.4 데이터 구조간의 변환

2.1.11.4.1 list를 vector로
lst <- list(1,2,3,4)
unlist(lst)
## [1] 1 2 3 4
2.1.11.4.2 list를 dataframe으로
lst <- list(x=c(1,2),y=c(3,4))
data.frame(lst)
##   x y
## 1 1 3
## 2 2 4
2.1.11.4.3 matrix를 데이터 프레임으로
mat <- matrix(c(1,2,3,4), ncol=2)
data.frame(mat)
##   X1 X2
## 1  1  3
## 2  2  4
2.1.11.4.4 벡터를 factor로, factor를 벡터

문자열을 Factor로

x <- c("m","f")
as.factor(x)
## [1] m f
## Levels: f m
  • Levels 로 Factor임을 확인함.

Factor를 다시 숫자형으로

as.numeric(as.factor(x))
## [1] 2 1

타입을 강제로 변환(Coercing)하고자 할 때도 있을 것이다. 문자열 벡터를 팩터로 변환하는 경우 등이 그 예다. 이러한 변환을 하는 한 가지 방법은 타입 이름이 ‘typename’이라 할 때 ‘as.typename( )’이라는 함수를 사용하는 것이다.

2.1.11.4.5 관련 함수 목록

다음에 관련 함수의 목록을 보였다.

함수 의미
as.factor(x) 주어진 객체 x를 팩터로 변환
as.numeric(x) 주어진 객체 x를 숫자를 저장한 벡터로 변환
as.character(x) 주어진 객체 x를 문자열을 저장한 벡터로 변환
as.matrix(x) 주어진 객체 x를 행렬로 변환
as.array(x) 주어진 객체 x를 배열로 변환
as.data.frame(x) 주어진 객체 x를 데이터 프레임으로 변환

2.1.12 연습문제

  1. , 1,2 로 바꿔보자
vec <- c('남','여','남','남','여')
vec <- ifelse(vec == "남", "여", "남"); vec
## [1] "여" "남" "여" "여" "남"

참고 : , 2, 1 로 바꾸면?

vec <- c('남','여','남','남','여')
as.numeric(factor(vec, levels=c('여','남')))
## [1] 2 1 2 2 1

2.1.13 실습 과제

첨부한 파일을 다운 받고 R에서 변수 data로 불러들여라.

toyota_sample.csv

data <- read_csv("https://insightteller.tistory.com/attachment/cfile2.uf@99A3723359F55A7E16D9D1.csv")
## 
## -- Column specification --------------------------------------------------------
## cols(
##   Id = col_double(),
##   Model = col_character(),
##   Price = col_double(),
##   Age = col_double(),
##   Mfg_Month = col_double(),
##   Mfg_Year = col_double(),
##   KM = col_double(),
##   Fuel_Type = col_character()
## )
data
## # A tibble: 7 x 8
##      Id Model                     Price   Age Mfg_Month Mfg_Year    KM Fuel_Type
##   <dbl> <chr>                     <dbl> <dbl>     <dbl>    <dbl> <dbl> <chr>    
## 1     1 TOYOTA Corolla 2.0 D4D H~ 13500    23        10     2002 46986 Diesel   
## 2     2 TOYOTA Corolla 2.0 D4D H~ 13750    23        10     2002 72937 Diesel   
## 3     3 TOYOTA Corolla 2.0 D4D H~ 13950    24         9     2002 41711 Diesel   
## 4     4 TOYOTA Corolla 2.0 D4D H~ 14950    26         7     2002 48000 Diesel   
## 5     5 TOYOTA Corolla 2.0 D4D H~ 13750    30         3     2002 38500 Diesel   
## 6     6 TOYOTA Corolla 2.0 D4D H~ 12950    32         1     2002 61000 Diesel   
## 7     7 ?TOYOTA Corolla 2.0 D4D ~ 16900    27         6     2002 94612 Diesel

2.1.14 apply() 함수 이해하기

  • 행렬 혹은 data.frame에서 각 row, column에 대해 평균을 계산한다든지, 특정 함수를 적용하고 싶을 때가 있다.
  • 이럴 때, 가장 기본적으로 생각하는 게 for() loop를 활용하여 각 row(혹은 column) 별로 함수를 적용하는 것이다.

예를 들어,

mat <- matrix(c(1,2,3,4,5,6,7,8,9), nrow=3)
# 각 row의 평균을 계산하고 싶다면
for (i in seq(1:nrow(mat))){
  print(mean(mat[i,]))
}
## [1] 4
## [1] 5
## [1] 6

그런데 많은 양의 데이터를 for() loop 하는 것은 비효율적이다.

  • 매번 for() loop를 돌 때마다 함수를 불러와야 하기 때문이다.
  • 따라서 최대한 for() loop를 줄이는 것이 중요하다!!
  • 그 때 사용하는 함수가 바로 apply() 함수이다.
  • apply() 함수는 한 번만 함수를 불러와서 모든 데이터에 적용하기 때문에 훨씬 시간을 줄일 수 있다.

2.1.14.1 데이터 세트

mat <- matrix(c(1,2,3,4,5,6,7,8,9), nrow=3)

2.1.14.2 행별로 함수 적용 : margin = 1

mat 데이터 세트의 행별(1) 평균값(mean) 산출

apply(mat, 1, mean)             # mean이라는 함수를 row(1)로 적용
## [1] 4 5 6

mat 데이터 세트의 행별(1) 범위(range) 산출

apply(mat, 1, range)            # range 함수는 각행의 최소, 최댓값 2개를 반환함
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    7    8    9

2.1.14.3 열별로 함수 적용 : margin = 2

mat 데이터 세트의 열별(2) 평균값(mean) 산출

apply(mat, 2, mean)             # mean이라는 함수를 column(2) 별로 적용
## [1] 2 5 8

2.1.15 연습문제

iris 데이터 세트에 적용해보자

  1. apply() 함수를 활용하여 irisSpecies를 제외한 4개 변수에 대해 평균을 아래와 같이 계산하라.
apply(iris[, -5], 2, mean)
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##     5.843333     3.057333     3.758000     1.199333
  1. apply() 함수를 활용하여 Sepal.Length, Sepal.Width의 최소, 최대값을 아래와 같이 구하라. (최소, 최댓값을 구하는 함수는 range)
apply(iris[, c(1,2)], 2, range)
##      Sepal.Length Sepal.Width
## [1,]          4.3         2.0
## [2,]          7.9         4.4

2.1.15.1 list, 벡터에 대한 for loop 계산

  • 데이터 프레임과 마찬가지로 list, vector에 대해서도 for() loop를 최소화 하는 것이 좋다.
  • 대신, apply()와 비슷하게 lapply(), sapply()를 사용한다.
  • 좀 더 구체적으로 예를 들어보면, list(1,2,3)을 제곱한 값을 반환하고 싶다고 하자.
  • 그런데 아래와 같이 계산하면 실행이 안 된다. list는 vector처럼 연산 함수가 적용되지 않기 떄문이다.

2.1.15.2 lapply() 함수와 sapply() 함수

이럴 때, for() loop이 아닌 lapply() 함수 또는 sapply()함수를 사용한다.

`lapply(벡터 혹은 리스트, 함수)`
`sapply(벡터 혹은 리스트, 함수)`
2.1.15.2.1 lapply() 함수

앞에 예를 들었듯이, list(1,2,3)을 제곱하고 싶다면,

lst <- list(1,2,3)
lapply(lst, function(x){x^2})
## [[1]]
## [1] 1
## 
## [[2]]
## [1] 4
## 
## [[3]]
## [1] 9
2.1.15.2.2 sapply() 함수

그런데 lapply()의 결과는 list로 나오기 때문에, 벡터로 나오게 하고 싶다면 sapply()를 이용한다.

sapply(lst, function(x){x^2})
## [1] 1 4 9
2.1.15.2.3 iris 데이터 세트의 예

lapply() 함수를 이용하여, 모든 행에 대하여 1:4열의 합계(sum)를 구한다.

lst <- lapply(iris[, 1:4], sum)
class(lst)
## [1] "list"

sapply() 함수를 이용하여, 모든 행에 대하여 1:4열의 합계(sum)를 구한다.

vec <- sapply(iris[,1:4], sum)
class(vec)
## [1] "numeric"
  • lapply(), sapply()도 data.frame 에 적용할 수 있는데,
  • apply()는 결과값이 data.frame인 반면,
  • lapply(), sapply()는 결과값이 각각 list, vector 라는 차이가 있다.
  • 그리고 기본적으로 각 column에 대해 함수가 적용된다.

좀 더 예를 들면,

iris 데이터 세트의 각 열의 데이터 타입을 보고 싶다면?

sapply(iris, class)
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
##    "numeric"    "numeric"    "numeric"    "numeric"     "factor"

3보다 큰 값을 갖는지 확인

y <- sapply(iris[, 1:4], function(x){ x > 3 })
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

2.1.16 연습문제

  1. iris 데이터를 0~1 사이 값으로 바꿔라.

hint: 서로 다른 변수의 데이터가 scale이 다를 경우(어떤 변수는 -10~0 사이인데, 다른 변수는 10~1000인 경우), 정규분포를 활용한 정규화 뿐만 아니라, min, max를 활용하여 0~1사이로 바꾸는 방법도 있다. 즉, 다음 함수를 각 row에 적용하면 된다. (x−min(x)) / (max(x)−min(x))

min <- sapply(iris[, -5], min); min
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##          4.3          2.0          1.0          0.1
max <- sapply(iris[, -5], max); max
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##          7.9          4.4          6.9          2.5
ran <- max - min; ran
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##          3.6          2.4          5.9          2.4
x <- apply(iris[, -5], 1, function(x) {(x - min) / ran})
head(t(x))
##      Sepal.Length Sepal.Width Petal.Length Petal.Width
## [1,]   0.22222222   0.6250000   0.06779661  0.04166667
## [2,]   0.16666667   0.4166667   0.06779661  0.04166667
## [3,]   0.11111111   0.5000000   0.05084746  0.04166667
## [4,]   0.08333333   0.4583333   0.08474576  0.04166667
## [5,]   0.19444444   0.6666667   0.06779661  0.04166667
## [6,]   0.30555556   0.7916667   0.11864407  0.12500000

2.1.17 apply() 함수의 사촌들

이제부터는 apply() 함수와 비슷한 원리지만, 각 상황에 맞게 tapply(), mapply() 가 있는데, 자주 사용되지는 않지만, 간단히 살펴보기로 한다.

2.1.17.1 tapply()

tapply() : 각 집단에 따라 데이터를 처리하고 싶을때

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)
##     setosa versicolor  virginica 
##      5.006      5.936      6.588

2.1.18 연습문제

  1. SpeciesSepal.Width 의 분산은?
tapply(iris$Sepal.Width, iris$Species, var)
##     setosa versicolor  virginica 
## 0.14368980 0.09846939 0.10400408

2.1.18.1 mapply()

여러 벡터에 동일한 함수를 적용하고 싶을때 사용한다. 아래와 같이 최대공약수를 구하는 함수 gcd()가 있다고 하자.

gcd <- function(a,b) {
  if (b==0) return(a)
  else return(gcd(b, a%%b))
}

gcd(6,4)
## [1] 2

그러나, 아래와 같이 두 벡터의 각 원소간 값을 input으로 하고 싶을때 아래와 같은 문법은 오류가 발생합니다.

gcd(c(3,6,9), c(12,15,18))
## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라는 조
## 건이 있고, 첫번째 요소만이 사용될 것입니다

## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라는 조
## 건이 있고, 첫번째 요소만이 사용될 것입니다

## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라는 조
## 건이 있고, 첫번째 요소만이 사용될 것입니다
## [1] 3 6 9

이 경우 mapply() 함수 활용

mapply(gcd, c(3,6,9), c(12,15,18))
## [1] 3 3 9
**최대공약수 함수** : 여기서 중요한 부분은 “**유클리드 호제법**”이다.

간단히 말하자면, "**두 양의 정수 A >= B에 대해, A가 B의 배수인 경우에 최대공약수는 B이며, 그렇지 않은 경우에는 최대공약수는 B와 A%%B (A를 B로 나눈 나머지)의 최대공약수이다.**"라고 할 수 있습니다.

이를 코드로 표현하면 다음과 같다.
gcd <- function(a,b) {
  if (b==0) return(a)
  else return(gcd(b, a%%b))
}

출처: https://kjwsx23.tistory.com/259 [香格里拉]


[참고 : Sampling]

기계학습 모델링을 사용하다 보면, 무작위로 데이터를 추출해야 할 경우가 생긴다. 이럴 때 sample() 함수를 사용한다.

# 1~10에서 무작위로 5개 추출
sample(1:10, 5)                     # 중복 허락 하지 않고.
## [1] 10  9  3  2  8
sample(1:10, 5, replace=T)          # 중복을 허락해서 추출
## [1]  4 10  2  1  6

iris 데이터에서 임의로 전체의 15% 데이터 추출하기

index <- 1:nrow(iris)               # 1부터 iris 행의 개수
train_idx <- sample(index, round(nrow(iris)*0.15))
head(iris[train_idx,])
##     Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
## 104          6.3         2.9          5.6         1.8  virginica
## 57           6.3         3.3          4.7         1.6 versicolor
## 132          7.9         3.8          6.4         2.0  virginica
## 125          6.7         3.3          5.7         2.1  virginica
## 47           5.1         3.8          1.6         0.2     setosa
## 127          6.2         2.8          4.8         1.8  virginica

출처: https://insightteller.tistory.com/entry/R-기초-실습-3-dataframe?category=628138 [Be a Insight teller]