2.8 데이터 프레임(data frame)

학습목표: 데이터 프레임 클래스에 대해 알아보고, 데이터 프레임을 생성, 병합(merge), 연산에 대한 함수들에 대해 알아본다.

  • Excel 스프레드시트와 같은 형태
Table 2.6: 스프레드시트 기본 형태 예시
이름 국적 나이 순위
라파엘 나달 스페인 35 6
다닐 메드베데프 러시아 25 2
노박 조코비치 세르비아 34 1
권순우 대한민국 25 53

  • 데이터 프레임은 데이터 유형에 상관없이 2차원 형태의 데이터 구조
  • 행렬과 리스트를 혼합한 자료 형태 \(\rightarrow\) 동일한 길이의 벡터로 이루어진 리스트를 구성요소로 갖는 리스트
  • 행렬과 유사한 구조를 갖고 있지만 각기 다른 유형의 자료형태로 자료행렬을 구성할 수 있다는 점에서 행렬과 차이를 갖음
  • 행렬과 마찬가지로 변수(열)의 길이(행의 개수)는 모두 동일해야 함
  • R에서 가장 빈번하게 활용되고 있는 데이터 클래스임
  • 데이터 프레임의 각 열(컬럼)은 벡터로 간주

2.8.1 데이터 프레임 생성

  • 데이터 프레임 생성 함수: data.frame()
data.frame(
  # 값 또는 이름(tag) = 값
  ..., 
  # 논리값. 
  # 변수명(열 이름)이 구문 상 유효한 변수인지 또는 중복이 있는지 확인
  check.names, 
  # 논리값. 문자형 벡터의 factor 형 강제 변환 여부 
  stringsAsFactors, 
)

  • 데이터 프레임 생성 예시: 모 병원에서 얻은 환자의 인구학적 정보
id <- c(1:10)
sex <- rep(c("Female", "Male"), each = 5)
age <- c(34, 22, 54, 43, 44, 39, 38, 28, 31, 42)
sbp <- c(112, 118, 132, 128, 128, 124, 121, 119, 124, 109)
height <- c(165, 158, 161, 160, 168, 172, 175, 182, 168, 162)
weight <- c(52, 48, 59, 60, 48, 72, 73, 82, 64, 60)

df <- data.frame(id, sex, age, sbp, height, weight, 
                 stringsAsFactors = FALSE)
df
attributes(df); str(df); summary(df)
$names
[1] "id"     "sex"    "age"    "sbp"    "height" "weight"

$class
[1] "data.frame"

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10
'data.frame':   10 obs. of  6 variables:
 $ id    : int  1 2 3 4 5 6 7 8 9 10
 $ sex   : chr  "Female" "Female" "Female" "Female" ...
 $ age   : num  34 22 54 43 44 39 38 28 31 42
 $ sbp   : num  112 118 132 128 128 124 121 119 124 109
 $ height: num  165 158 161 160 168 172 175 182 168 162
 $ weight: num  52 48 59 60 48 72 73 82 64 60
       id            sex                 age             sbp       
 Min.   : 1.00   Length:10          Min.   :22.00   Min.   :109.0  
 1st Qu.: 3.25   Class :character   1st Qu.:31.75   1st Qu.:118.2  
 Median : 5.50   Mode  :character   Median :38.50   Median :122.5  
 Mean   : 5.50                      Mean   :37.50   Mean   :121.5  
 3rd Qu.: 7.75                      3rd Qu.:42.75   3rd Qu.:127.0  
 Max.   :10.00                      Max.   :54.00   Max.   :132.0  
     height          weight     
 Min.   :158.0   Min.   :48.00  
 1st Qu.:161.2   1st Qu.:53.75  
 Median :166.5   Median :60.00  
 Mean   :167.1   Mean   :61.80  
 3rd Qu.:171.0   3rd Qu.:70.00  
 Max.   :182.0   Max.   :82.00  
# stringsAsFactors = TRUE 인 경우 sex의 summary() 결과
df <- data.frame(id, sex, age, sbp, height, weight, 
                 stringsAsFactors = TRUE)
summary(df)
       id            sex         age             sbp            height     
 Min.   : 1.00   Female:5   Min.   :22.00   Min.   :109.0   Min.   :158.0  
 1st Qu.: 3.25   Male  :5   1st Qu.:31.75   1st Qu.:118.2   1st Qu.:161.2  
 Median : 5.50              Median :38.50   Median :122.5   Median :166.5  
 Mean   : 5.50              Mean   :37.50   Mean   :121.5   Mean   :167.1  
 3rd Qu.: 7.75              3rd Qu.:42.75   3rd Qu.:127.0   3rd Qu.:171.0  
 Max.   :10.00              Max.   :54.00   Max.   :132.0   Max.   :182.0  
     weight     
 Min.   :48.00  
 1st Qu.:53.75  
 Median :60.00  
 Mean   :61.80  
 3rd Qu.:70.00  
 Max.   :82.00  

  • summary() 함수는 객체의 클래스에 따라 요약 통계량을 출력해주는 함수
  • 데이터 프레임이 가지고 있는 변수들의 특징을 손쉽게 알아볼 수 있기 때문에 가장 많이 호출되는 함수 중 하나
  • 숫자형 벡터: 최솟값(minimum), 1/4 분위수(1st quantile), 중앙값(median), 평균(mean), 3/4 분위수(3rd quantile), 최댓값을 출력
  • 요인형 객체: 요인의 각 수준 별 빈도를 출력
  • 2차원 이상 table() 객체에 적용 시 \(\chi^2\) 검정(독립성 검정) 결과값을 출력함.

  • 이미 정의된 데이터 프레임에 데이터를 추가 가능
    • 예를 들어 dbp라는 벡터에 이완기 혈압(diastolic blood pressure) 데이터가 입력되어 있고 dfdbp 변수를 새롭게 추가 시 df$dbp <- x 형태로 추가
    • 위 형태로 이미 존재하고 있는 변수(열)에 새로운 값 재할당 가능
    • 이러한 형태로 문자형 벡터 추가 시 문자형 벡터는 자동으로 factor로 형 변환 되지는 않음
x <- 1:nrow(df)
dbp <- c(73, 70, 88, 82, 75, 77, 74, 81, 72, 64)

# df에 "dbp" 열을 생성하고 x 값 대입
df$dbp <- x
df
# df의 dbp에 dbp 벡터의 값을 재할당
df$dbp <- dbp
df
# df에 운동여부 exercyn 라는 변수 추가 
# exercyn 는 "Y" 또는 "N" 두 값을 가짐
df$exercyn <- c("Y", "Y", "N", "Y", "N", 
                "N", "N", "Y", "N", "Y")
str(df)
'data.frame':   10 obs. of  8 variables:
 $ id     : int  1 2 3 4 5 6 7 8 9 10
 $ sex    : Factor w/ 2 levels "Female","Male": 1 1 1 1 1 2 2 2 2 2
 $ age    : num  34 22 54 43 44 39 38 28 31 42
 $ sbp    : num  112 118 132 128 128 124 121 119 124 109
 $ height : num  165 158 161 160 168 172 175 182 168 162
 $ weight : num  52 48 59 60 48 72 73 82 64 60
 $ dbp    : num  73 70 88 82 75 77 74 81 72 64
 $ exercyn: chr  "Y" "Y" "N" "Y" ...

  • 행렬 및 벡터에서 언급 되었던 rownames(), colnames(), names(), dim(), ncol()/NCOL(),nrow()/NROW() 함수 적용 가능
rownames(df); colnames(df); names(df)
 [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
[1] "id"      "sex"     "age"     "sbp"     "height"  "weight"  "dbp"    
[8] "exercyn"
[1] "id"      "sex"     "age"     "sbp"     "height"  "weight"  "dbp"    
[8] "exercyn"
dim(df); ncol(df); nrow(df)
[1] 10  8
[1] 8
[1] 10
# rownames() 함수를 통해 행이름 변경
rownames(df) <- letters[1:10]
df
#colnames() 함수를 통해 열 이름 변경
varname_orig <- colnames(df)
colnames(df) <- paste0("V", 1:ncol(df))
df
# names() 함수와 colnames()는 거의 동일한 기능 수행
# 두 함수의 차이점? 
names(df)
[1] "V1" "V2" "V3" "V4" "V5" "V6" "V7" "V8"
names(df) <- varname_orig
df

참고: R Markdown에서 데이터 프레임의 데이터를 손쉽게 테이블로 출력하는 방법(html 문서)

  • R Markdwon의 YAML 부분에 다음과 같이 옵션을 추가하면 별다른 함수 처리 없이 데이터 프레임을 표 형태로 html 문서에 붙일 수 있음. 아래 예시에서 output 이후 df_print: paged 옵션을 추가
  • 옵션 추가 시 들여쓰기(탭 구분)은 YAML 문서의 트리 구조를 표현한 것이기 때문에 들여쓰기를 정확히 일치시켜야 함

---
title: "문서 제목"
author: "이름"
date: "`r Sys.Date()`"
output: 
  html_document: 
    df_print: paged
---

2.8.2 데이터 프레임 접근 및 필터링

접근방법

  • 리스트 데이터 접근 방식
# 추출(접근) 연산자(함수) `df$col_name` 형태로 접근
df$height
 [1] 165 158 161 160 168 172 175 182 168 162
# df[[index]] 또는 df[["col_name"]] 형태로 접근
df[[4]]
 [1] 112 118 132 128 128 124 121 119 124 109
df[["sex"]]
 [1] Female Female Female Female Female Male   Male   Male   Male   Male  
Levels: Female Male
w <- df[[4]]
attributes(w); str(w)
NULL
 num [1:10] 112 118 132 128 128 124 121 119 124 109
# df[index] 또는 df["col_name"] 형태로 접근
h <- df["height"]
attributes(h); str(h)
$names
[1] "height"

$row.names
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

$class
[1] "data.frame"
'data.frame':   10 obs. of  1 variable:
 $ height: num  165 158 161 160 168 172 175 182 168 162

  • 행렬 데이터 접근 방식
# df[idx_row, idx_col] 또는 df[row_name, col_name] 
# 형태 데이터 접근

# 열 index 접근
df[, 3];
 [1] 34 22 54 43 44 39 38 28 31 42
# 형 강제 변환 방지
df[, 3, drop = FALSE]
# 행 index 접근
df[8, ]
# 행과 열 index 접근
df[1:4, 5:6]
# 열 이름으로 접근
df[, c("sex", "sbp")]
# 행 이름으로 접근
df[c("d", "e", "f"), ]
# 행과 열 이름으로 접근
df[c("a", "f"), c("sex", "height", "dbp")]
# 행 또는 열 제외
df[-c(2:6), ]
df[-c(1, 5:7), -c(1, 8)]

필터링

  • 벡터, 행렬과 마찬가지로 비교 연산자를 이용해 조건에 맞는 부분 데이터 추출 가능
# %in% 연산자를 이용해 데이터 프레임의 부분 변수 추출
# id, age 열을 제외한 나머지 데이터 프레임 추출
varname_df <- names(df)
df[, !varname_df %in% c("id", "age")]
# 조건 연산자 사용 
# sex 가 Female이고 나이가 40 이상인 데이터 추출
df[df$sex == "Female" & df$age >= 40, ]
# id가 3보다 작은 데이터 추출
df[df[, 1] < 3, ]
# subset 함수 이용한 데이터 추출
# sbp 가 120 이상이고 dbp 가 80 이상인 데이터 추출
subset(df, sbp >= 120 & dbp >= 80)
# 성별, 수축기, 이완기 혈압 변수만 추출
subset(df, select = c(sex, sbp, dbp))
# id 변수 제거
subset(df, select = -c(id))

데이터 프레임 또는 리스트 접근 시 df$col_name 를 사용한다면 매번 데이터 프레임 이름과 $을 반복하기 때문에 코드가 불필요하게 복잡해짐. R에서는 데이터 프레임 내부의 열 이름을 직접 접근할 수 있도록 도와주는 몇 가지 함수(예: with(), attach() 등)가 있는데, with()within() 활용법에 대해 간략히 알아봄.

  • 위 예제에서 sex 가 Female이고 나이가 40 이상인 데이터 추출한다고 했을 때 with() 함수 사용
# with() 함수: 데이터 환경(객체 내)에서 주어진 표현식의 결과를 반환
with(
  data, #리스트 또는 데이터 프레임
  expr, # 실제 명령을 수행할 표현식, 
)

with(df, df[sex == "Female" & age >= 40, ])

  • within() 함수는 with()와 유사하지만 코드블록({...})을 이용해 보다 자유롭게 데이터를 수정 및 추가할 수 있음
df2 <- within(df, {
  hospital <- c("A", "B", "B", "A", "C", 
                "A", "A", "B", "C", "B")
  mean_age <- mean(age)
})

2.8.3 데이터 프레임 관련 함수

유틸리티 함수

  • 보통 데이터 분석은 외부에서 데이터를 읽은 후 특정 객체에 읽어온 데이터를 할당하는데, 이 경우 데이터가 저장된 객체는 대부분은 데이터 프레임 형태임.
  • 읽어온 데이터는 보통 많은 행(표본)으로 구성되어 있기 때문에 데이터를 손쉽게 살펴보는 방법이 필요
head()/tail() 함수
  • 객체(벡터, 행렬, 테이블, 데이터 프레임 등)의 처음 또는 끝에서 부터 몇 개의 데이터(default = 6L) 를 순차적으로 보여줌
# 앞에서 불러온 전복 데이터셋 확인
dim(abalone)
[1] 4177    9
#처음 1에서 6행 까지 데이터 출력
head(abalone)
# 제일 마지막 행부터 위로 6개 데이터 까지 출력
tail(abalone)

View() 함수
  • 2차원 데이터의 readble 한 스프레드시트 제공
View(abalone)

데이터 프레임 결합 및 분리 함수

rbind()/cbind() 함수
  • 행렬에서 사용한 rbind()/cbind()를 데이터 프레임에도 적용 가능
a = data.frame(x1 = rep(0,5), x2 = rep("x",5))
b = data.frame(x1 = rep(1,5), x2 = rep("d",5))
c = data.frame(x3 = rep(2,5), x4 = rep("z",5))
d <- list(1, "d")
e <- list(x5 = rep(4, 5), x6 = rep("y", 5))
# rbind()를 이용해 두 데이터 프레임 a-b 합치기
ab <- rbind(a, b)
ab
# 변수명이 다른 경우
rbind(a, c) #변수명이 다르기때문에 행으로 묶을 수 없다.
Error in match.names(clabs, names(xi)): 이전에 사용된 이름들과 일치하지 않습니다.
# rbind()를 이용해 데이터 프레임-리스트 합치기
abd <- rbind(ab, d)
abd
# cbind()를 이용해 두 데이터 프레임 a-c 합치기
ac <- cbind(a, c)
ac
# 행 길이가 다르면 작은 길이의 데이터를 재사용
cbind(a, d)
# cbind()를 이용해 두 데이터 프레임-리스트 합치기
ace <- cbind(ac, e)

merge() 함수
  • 두 데이터 프레임을 공통된 값을 기준으로 병합
  • Excel의 vlookup() 함수 또는 데이터베이스 SQL 쿼리 중 join과 동일한 역할을 함
  • cbind()의 경우는 단순히 열을 합치는 것이지만 merge()는 공통되는 열을 기준으로 두 데이터셋을 병합
  • 공통된 데이터가 있을 때만 데이터 병합 수행
# merge() 함수 인수 
merge(
  x, # 병합할 데이터 프레임
  y, # 병합할 데이터 프레임
  by, # 병합 기준으로 사용할 컬럼 (문자열 벡터)
  by.x, # 병합에 사용할 x와 y의 열 이름이 다른 경우
  by.y, # by.x와 by.y에 각각 공통 데이터에 해당하는 열 이름 지정
        # 둘 다 문자형 스칼라 또는 벡터값 인수로 받음
  all, # 논리값 이순
       # TRUE인 경우 x, y 중 공통된 값을 갖는 행이 없을 때
       # 해당 쪽을 NA를 채워 병합
       # 결과적으로 x, y 전체 행이 결과에 포함
  all.x, # x,y 중 특정 쪽에 공통된 값이 없더라도 항상 
  all.y, # 결과에 포함
)

  • merge() 함수 예시
d1 = data.frame(Name = c("Park", "Hanzo", "Mercy", "Soldier76" ),
                country = c("Korea", "Japan", "Swiss", "USA"))
d2 = data.frame(Age = c(19,38,37,56,31),
                Name = c("Park", "Hanzo", "Mercy", "Soldier76","Mei" ) )
d1; d2
dim(d1); dim(d2)
[1] 4 2
[1] 5 2
# 두 데이터 병합 01
merge(d1, d2, by = "Name")
# 두 데이터 병합 02
names(d2)[2] <- "Surname"
merge(d1, d2, by.x = "Name", by.y = "Surname")
# 두 데이터 병합 03
merge(d1, d2, 
      by.x = "Name", by.y = "Surname", 
      all = T)

split() 함수
  • Factor 형에서 언급한 split() 함수를 통해 그룹 별로 데이터 분할
  • 분할된 데이터는 리스트에 저장
split(df, df$sex)
$Female
  id    sex age sbp height weight dbp exercyn
a  1 Female  34 112    165     52  73       Y
b  2 Female  22 118    158     48  70       Y
c  3 Female  54 132    161     59  88       N
d  4 Female  43 128    160     60  82       Y
e  5 Female  44 128    168     48  75       N

$Male
  id  sex age sbp height weight dbp exercyn
f  6 Male  39 124    172     72  77       N
g  7 Male  38 121    175     73  74       N
h  8 Male  28 119    182     82  81       Y
i  9 Male  31 124    168     64  72       N
j 10 Male  42 109    162     60  64       Y

데이터 정렬 함수

sort() 함수
  • 데이터(벡터)의 정렬(오름차순 또는 내림차순) 결과 반환
# sort() 함수 인수
sort(
  x, # 정렬할 벡터
  decreasing, # 논리값, 내림차순 여부
              # default = FALSE
  na.last # 논리값. 결측 존재 시 NA 값 위치 지정
)         # TRUE: 정렬 후 결측은 마지막에 위치
          # FALSE: 맨 처음 NA 위치

  • 예시
# 오름차순 정렬
sort(df2$age)
 [1] 22 28 31 34 38 39 42 43 44 54
# 내림차순 정렬
sort(df$height, decreasing = TRUE)
 [1] 182 175 172 168 168 165 162 161 160 158

order() 함수
  • 데이터 정렬을 위해 순서에 대한 색인 생성 결과 반환
  • 데이터 프레임에서 특정 열 기준으로 데이터 정렬 시 주로 사용
# 나이 기준으로 오름차순으로 데이터 정렬
with(df, df[order(age), ])
# 키 순으로 내림차순 정렬
df[order(df$height, decreasing = T), ]