R 데이터 구조
IT 위키
더 많은 작업
- R Data Types and Structures; R 자료형; R 자료 구조
- R (언어)에서 값의 종류(자료형)와 값을 담는 객체의 모양(벡터, 팩터, 행렬, 배열, 리스트, 데이터프레임)
R이 다루는 모든 것은 객체이다. 객체는 자료형(mode, type)과 구조를 가진다. 벡터처럼 모든 원소가 같은 자료형이어야 하는 구조를 원자(atomic) 구조라 하고, 리스트처럼 원소로 다른 객체를 담을 수 있는 구조를 재귀(recursive) 구조라 한다. 분석용 데이터는 대부분 데이터프레임으로 다루며, 원천 데이터를 분석 목적에 맞게 요약·변형해 데이터 마트를 만드는 작업도 데이터프레임을 중심으로 이루어진다.
| 자료형 | 예 | 설명 |
|---|---|---|
| numeric (double) | 3.14, 2 |
실수. 숫자를 그냥 쓰면 기본적으로 double 형이다. |
| integer | 5L, 1:3 |
정수. 숫자 뒤에 L을 붙이거나 : 연산자로 만든다.
|
| character | "abc" |
문자열. 큰따옴표나 작은따옴표로 감싼다. |
| logical | TRUE, FALSE |
논리값. 산술 연산에서는 TRUE가 1, FALSE가 0으로 계산된다. |
| complex | 2+3i |
복소수. sqrt(-17)은 경고와 함께 NaN이 되지만 sqrt(-17+0i)는 복소수로 계산된다.
|
- 자료형은
class(),typeof(),mode()로 확인하고is.numeric(),is.character()등으로 검사한다. as.numeric(),as.character(),as.integer()등으로 형을 바꾼다. 숫자로 바꿀 수 없는 문자열을as.numeric()에 넣으면 경고와 함께 NA가 된다.- 벡터에 서로 다른 자료형을 섞으면 더 일반적인 형으로 자동 변환된다. 순서는 logical → integer → double → complex → character이다. 예를 들어
c(1, "a", TRUE)는 세 원소가 모두 문자열인 character 벡터가 된다.
| 값 | 의미 | 생기는 예 | 검사 함수 |
|---|---|---|---|
| NA | 결측값(Not Available). 값이 있어야 할 자리에 값이 없다. | 설문 무응답, 변환 실패 | is.na()
|
| NULL | 객체 자체가 없음. 길이가 0인 빈 객체이다. | 리스트 원소 삭제, 값을 돌려주지 않는 함수 | is.null()
|
| NaN | 수학적으로 정의되지 않는 계산 결과(Not a Number) | 0/0, Inf - Inf |
is.nan()
|
| Inf, -Inf | 양·음의 무한대 | 1/0, -1/0 |
is.infinite()
|
is.na()는 NA와 NaN 모두에 TRUE를 돌려주고,is.nan()은 NaN에만 TRUE를 돌려준다.- NA는 자리를 차지하므로
length(c(1, NA, 3))은 3이다. NULL은 자리를 차지하지 않으므로length(c(1, NULL, 3))은 2이다. - NA가 섞인 벡터에
mean(),sum()을 쓰면 결과도 NA가 된다.na.rm = TRUE를 주면 NA를 빼고 계산한다. 결측값 처리는 결측치 문서를 참고한다.
| 구조 | 차원 | 서로 다른 자료형 혼합 | 주요 생성 함수 | 설명 |
|---|---|---|---|---|
| 벡터(vector) | 1차원 | 불가 (한 형으로 자동 변환) | c(), seq(), rep(), vector() |
R의 가장 기본 단위. 값 하나도 길이 1인 벡터이다. |
| 팩터(factor) | 1차원 | 불가 | factor(), as.factor() |
범주형 자료. 값과 함께 수준(levels) 목록을 가진다. ordered()로 순서가 있는 팩터를 만든다.
|
| 행렬(matrix) | 2차원 | 불가 | matrix(), rbind(), cbind() |
행과 열로 이루어진 벡터. dim() 속성을 가진다.
|
| 배열(array) | n차원 | 불가 | array() |
행렬을 3차원 이상으로 확장한 구조. 행렬은 2차원 배열이다. |
| 리스트(list) | 1차원 (재귀) | 가능 | list() |
원소마다 자료형과 길이가 달라도 되며, 리스트 안에 리스트를 넣을 수 있다. |
| 데이터프레임(data.frame) | 2차원 | 열 사이에는 가능, 한 열 안에서는 불가 | data.frame(), read.csv() |
길이가 같은 벡터(열)를 묶은 리스트. 행은 관측치, 열은 변수이다. |
- 데이터프레임은 겉모양은 행렬과 같은 표이지만 내부적으로는 리스트이다. 그래서 열마다 숫자, 문자, 팩터 등 다른 형을 가질 수 있다.
- R 4.0.0부터
data.frame()과read.csv()의stringsAsFactors기본값이 FALSE로 바뀌어, 문자열 열이 자동으로 팩터가 되지 않는다. 그 이전 버전을 기준으로 쓴 자료에서는 문자열이 팩터로 바뀐다고 설명하는 경우가 있다.
v <- c(10, 20, 30) # 숫자 벡터
f <- factor(c("남", "여", "여")) # 팩터, 수준은 "남", "여"
m <- matrix(1:6, nrow = 2) # 2행 3열 행렬, 열 방향으로 채움
a <- array(1:24, dim = c(2, 3, 4)) # 2 x 3 x 4 배열
l <- list(name = "Kim", scores = c(90, 85), passed = TRUE)
df <- data.frame(id = 1:3, grade = c("A", "B", "A"))
str(df) # 구조 확인
- R의 인덱스는 1부터 시작한다.
v[1]이 첫 번째 원소이다. - 음수 인덱스는 해당 위치를 제외한다.
v[-1]은 첫 원소를 뺀 나머지,x[-(1:5)]는 앞의 다섯 원소를 뺀 나머지이다. - 논리 벡터로 조건에 맞는 원소만 고를 수 있다.
v[v > 15] - 이름이 붙은 원소는 이름으로 고를 수 있다.
l["name"] - 행렬과 데이터프레임은
[행, 열]로 고른다. 한쪽을 비우면 전체를 뜻한다.m[1, ]은 1행 전체,df[, "grade"]는 grade 열 전체이다.
| 표기 | 리스트·데이터프레임에서의 결과 |
|---|---|
l[1] |
첫 번째 원소를 담은 리스트(부분 리스트) |
l[[1]] |
첫 번째 원소 그 자체(예: 문자열 벡터) |
l$name |
이름이 name인 원소 그 자체. l[["name"]]과 같다.
|
df$grade, df[["grade"]] |
grade 열을 벡터로 꺼낸다. |
df["grade"] |
grade 열 하나만 가진 데이터프레임 |
| 함수 | 용도 | 예 |
|---|---|---|
c() |
값을 이어 붙여 벡터를 만든다. | c(1, 2, 3)
|
seq() |
등차수열을 만든다. | seq(1, 10, by = 2), seq(0, 1, length.out = 5)
|
rep() |
값을 반복한다. times는 전체 반복, each는 원소별 반복이다. | rep(1:2, times = 3), rep(1:2, each = 3)
|
length() |
원소(리스트는 최상위 원소) 개수 | length(c(1, 2, 3))은 3
|
str() |
객체의 구조와 각 열의 자료형을 간단히 보여 준다. | str(df)
|
summary() |
숫자 열은 최솟값·사분위수·중앙값·평균·최댓값, 팩터 열은 수준별 빈도를 보여 준다. | summary(df)
|
head(), tail() |
앞·뒤 몇 행(기본 6행)을 본다. | head(df, 3)
|
rbind(), cbind() |
행 방향, 열 방향으로 붙인다. | rbind(df1, df2)
|
merge() |
공통 열(키)을 기준으로 두 데이터프레임을 결합한다. SQL의 조인과 같다. | merge(x, y, by = "id"), all = TRUE이면 완전 외부 조인
|
| 함수 | 입력 | 적용 단위 | 결과 |
|---|---|---|---|
apply(X, MARGIN, FUN) |
행렬, 배열, 데이터프레임 | MARGIN = 1이면 행, 2이면 열 | 벡터 또는 행렬 |
lapply(X, FUN) |
벡터, 리스트, 데이터프레임 | 각 원소(데이터프레임은 각 열) | 항상 리스트 |
sapply(X, FUN) |
lapply와 같음 | 각 원소 | 가능하면 벡터나 행렬로 단순화, 안 되면 리스트 |
tapply(X, INDEX, FUN) |
벡터와 그룹 변수(팩터) | INDEX의 그룹별 | 그룹별 결과를 담은 배열 |
m <- matrix(1:6, nrow = 2)
apply(m, 1, sum) # 행별 합계
apply(m, 2, mean) # 열별 평균
lapply(list(a = 1:3, b = 4:6), mean) # 리스트로 돌려줌
sapply(list(a = 1:3, b = 4:6), mean) # 이름 붙은 벡터로 돌려줌
tapply(iris$Sepal.Length, iris$Species, mean) # 품종별 평균
데이터 마트는 분석 목적에 맞게 변수를 가공·요약해 둔 데이터 집합이다. R에서는 원천 데이터프레임을 긴 형식과 넓은 형식 사이에서 바꾸거나, 그룹별로 요약하거나, 여러 표를 결합해 분석용 표를 만든다.
| 패키지 | 대표 함수 | 용도 |
|---|---|---|
| reshape2 | melt(), dcast(), acast() |
melt는 넓은 형식(열이 많은 표)을 식별 변수·변수 이름·값의 긴 형식으로 녹이고, dcast는 긴 형식을 다시 넓은 형식의 데이터프레임으로 재구성한다. 재구성하면서 집계 함수를 줄 수 있다. acast는 결과를 배열로 돌려준다. |
| sqldf | sqldf() |
R 데이터프레임 이름을 테이블 이름처럼 써서 SQL SELECT 문으로 조회·집계한다. |
| plyr | ddply() 등 |
나누고(split) 적용하고(apply) 합치는(combine) 작업을 한 함수로 처리한다. 함수 이름의 첫 글자는 입력, 둘째 글자는 출력 형태이다. ddply는 데이터프레임을 받아 데이터프레임을 돌려준다. |
| data.table | DT[i, j, by] |
data.frame을 확장한 자료 구조. 행 선택(i), 열 계산(j), 그룹(by)을 한 대괄호 안에서 처리하며, 큰 데이터의 집계·조인·열 추가가 빠르다. |
# reshape2: 넓은 형식 <-> 긴 형식
library(reshape2)
long <- melt(airquality, id.vars = c("Month", "Day")) # Ozone, Solar.R, Wind, Temp 를 variable, value 두 열로
wide <- dcast(long, Month + Day ~ variable) # 원래의 넓은 형식으로 되돌림
monthly <- dcast(long, Month ~ variable, fun.aggregate = mean, na.rm = TRUE) # 월별 평균
# sqldf: SQL 로 데이터프레임 집계
library(sqldf)
sqldf("SELECT Month, AVG(Temp) AS avg_temp FROM airquality GROUP BY Month")
# plyr: 그룹별 요약
library(plyr)
ddply(airquality, "Month", summarise, avg_temp = mean(Temp))
# data.table: DT[i, j, by]
library(data.table)
dt <- as.data.table(airquality)
dt[Temp > 80, .(n = .N, avg_wind = mean(Wind)), by = Month]
- 인덱스는 1부터 시작하고, 음수 인덱스는 해당 원소를 제외한다.
- 벡터·행렬·배열은 한 가지 자료형만 담는다. 섞으면 문자형 쪽으로 자동 변환된다. 서로 다른 형을 담으려면 리스트나 데이터프레임을 쓴다.
- NA(결측)와 NULL(객체 없음), NaN(정의되지 않는 계산)의 차이, 그리고
is.na()가 NaN에도 TRUE라는 점을 구분한다. - 리스트에서
[ ]는 부분 리스트,[[ ]]와$는 원소 자체를 돌려준다. - lapply는 리스트, sapply는 단순화된 벡터, tapply는 그룹별, apply는 행·열 방향으로 적용한다.
- reshape2의 melt(넓은 → 긴 형식)와 dcast(긴 → 넓은 형식)의 방향을 구분한다.