R의 데이터 타입
R의 모든 값은 소수의 원자적 타입 중 하나를 가집니다. 그중 네 가지가 거의 모든 일을 합니다.
- double -
3.14,-2,1e6같은 소수를 포함한 숫자입니다. R이 "numeric"이라 부르는 것이며, 여러분이 입력하는 모든 숫자의 기본값입니다. - integer - 정확히 저장되는 정수로,
42L처럼L접미사와 함께 씁니다. - character -
"hello"처럼 따옴표로 감싼 텍스트입니다. - logical - 두 개의 진리값
TRUE와FALSE(그리고 결측값NA. 결측값 참고).
가끔 신호 처리 계산에 쓰이는 complex(2 + 3i)도 있지만, 몇 년 동안 쓸 일이 없을 수도 있습니다. typeof()는 값이 어떤 타입인지 알려 줍니다.
첫 줄을 눈여겨보세요. 정수처럼 보여도 그냥 숫자는 double입니다. typeof(42)는 "double"이고, 42L만이 정수입니다. 일상적인 분석에서는 이 차이가 거의 문제되지 않습니다. R이 조용히 서로 변환해 주기 때문입니다. 다만 typeof()의 헷갈리는 출력 상당수를 이 사실이 설명해 줍니다.
typeof()와 class(): 왜 서로 다르게 말할까
R에는 "이게 뭐지?"에 답하는 것처럼 보이는 함수가 두 개 있는데, 사실 미묘하게 다른 질문에 답합니다.
typeof()는 저장 타입, 즉 값이 메모리에 어떻게 놓여 있는지를 알려 줍니다.class()는 동작 타입, 즉 어떤print나summary를 실행할지 결정하는 R의 메서드 디스패치가 사용하는 이름표를 알려 줍니다.
단순한 값에서는 어휘만 다릅니다.
typeof()는 double(저장 방식), class()는 numeric(동작 방식)이라고 말합니다. 구조체에서는 그 간격이 더 벌어집니다.
행렬은 평범한 정수 벡터로 저장되지만 행렬처럼 동작합니다. R이 이를 격자로 출력하게 하는 것이 class()입니다. 같은 분리가 데이터 프레임(typeof는 "list", class는 "data.frame")과 날짜(typeof는 "double", class는 "Date")에서도 나타납니다.
실용적인 규칙은 이렇습니다. 일상적인 의미에서 무언가가 무엇인지 알고 싶으면 class()를, R이 내부에서 무엇을 하는지 디버깅할 때는 typeof()를 쓰세요.
is.*()로 타입 확인하기
각 타입에는 TRUE나 FALSE를 돌려주는 짝 함수가 있습니다. 입력을 검증해야 하는 함수 안에서 유용합니다.
알아 둘 만한 함정이 하나 있습니다. is.numeric()은 double과 integer 모두에 대해 TRUE입니다(둘 다 숫자이기 때문입니다). 반면 is.integer()는 저장 방식을 확인하므로 평범한 42에 대해서는 FALSE입니다.
수학적으로 명백히 정수인 숫자에 대해 is.integer(7)이 FALSE라는 사실에 놀란 적이 있다면, 이유가 바로 이것입니다. 7은 double이고 7L이 integer입니다.
as.*()로 타입 변환하기
명시적 변환은 대상 타입마다 하나씩 있는 as.* 계열 함수를 사용합니다.
기억할 동작이 두 가지 있습니다. as.integer()는 반올림이 아니라 0 방향으로 잘라 냅니다. 7.9는 7이 됩니다. 그리고 변환이 말이 되지 않을 때 R은 오류를 던지지 않고 NA를 돌려주며 경고를 냅니다.
실행해 보면 NA와 함께 "NAs introduced by coercion" 경고가 보입니다. 이는 정말 유용합니다. 지저분한 텍스트로 된 열 전체를 숫자로 변환할 때, 해석되지 않는 항목이 스크립트를 멈추는 대신 NA가 되니까요. 하지만 동시에 조용한 NA가 데이터에 스며들 수 있다는 뜻이기도 합니다. 변환이 중요한 상황이라면 이후에 새로 생긴 NA를 확인하세요.
논리값은 숫자와 서로 기대한 대로 변환됩니다. as.numeric(TRUE)는 1이고 as.logical(0)은 FALSE입니다. 논리 벡터에 sum()을 쓰면 TRUE의 개수가 세어지는 이유입니다.
암묵적 강제 변환: 벡터의 단일 타입 규칙
R의 원자적 벡터는 하나의 타입만 담을 수 있습니다. c()로 서로 다른 값을 합치면 R은 고정된 사다리를 따라 존재하는 것 중 가장 유연한 타입으로 모든 값을 조용히 승격시킵니다.
logical → integer → double → character
논리값은 1과 0이 되었고, 정수는 double이 되었으며, 마지막 줄에서는 모든 것이 텍스트가 되었습니다. 어떤 값이든 문자열로 쓸 수 있기 때문에 character가 항상 이깁니다. typeof()로 확인할 수 있습니다.
강제 변환은 "왜 내 열이 텍스트지?" 버그의 1순위 원인입니다. 숫자 열에 섞인 "N/A" 문자열 하나가 벡터 전체를 문자형으로 바꿔 놓습니다. 그런 일이 생기면 문제가 된 값을 먼저 고친 다음 as.numeric()으로 되돌리세요.
핵심 정리
- 주력 타입은 네 가지입니다. double("numeric"), integer(
42L), character, logical. typeof()는 저장 방식이고class()는 동작 방식입니다.3.14는numeric처럼 동작하는double입니다.is.numeric()계열로 확인하고as.numeric()계열로 변환합니다. 실패한 변환은 오류가 아니라 경고와 함께NA를 돌려줍니다.- 벡터는 한 가지 타입만 담습니다. 섞으면 logical → integer → double → character 순으로 강제 변환됩니다.
다음 순서는 숫자 타입을 더 자세히 살펴보는 것입니다. 반올림, 수학 함수, 그리고 모든 언어가 공유하는 부동소수점의 놀라움을 다룹니다.
자주 묻는 질문
R의 기본 데이터 타입에는 어떤 것이 있나요?
원자적 타입은 double(소수를 포함한 숫자로, R이 "numeric"이라 부르는 것), integer(42L처럼 L 접미사로 쓰는 정수), character(따옴표 안의 텍스트), logical(TRUE/FALSE), 그리고 거의 쓰이지 않는 complex입니다. 벡터, 데이터 프레임, 리스트 같은 더 큰 구조는 모두 이것들로 만들어집니다.
R에서 typeof()와 class()의 차이는 무엇인가요?
typeof()는 값이 메모리에 어떻게 저장되는지(저수준 타입)를 알려 주고, class()는 값이 무엇처럼 동작하는지를 알려 주며 이것이 R의 메서드 디스패치를 결정합니다. 단순한 소수에서는 표현만 다릅니다. typeof(3.14)는 "double"이지만 class(3.14)는 "numeric"입니다. 데이터 프레임 같은 구조에서는 typeof()가 "list"라고 하는 반면 class()는 "data.frame"이라고 합니다.
R에서 타입은 어떻게 변환하나요?
as.* 계열 함수를 사용합니다. as.numeric("3.5")는 3.5를, as.character(42)는 "42"를 주고, as.integer(7.9)는 7로 잘라 냅니다. 변환이 불가능하면 R은 오류를 내지 않고 NA를 돌려주면서 경고를 냅니다. as.numeric("abc")는 NA입니다.
R에서 강제 변환(coercion)이란 무엇인가요?
하나의 벡터에 서로 다른 타입을 섞으면 R은 logical → integer → double → character 순서를 따라 가장 유연한 타입으로 모든 값을 조용히 변환합니다. 그래서 c(1, 2, "three")는 문자 벡터 "1" "2" "three"가 됩니다.