2 Objects — R의 객체
2 Objects — R의 객체
R에서 모든 값은 "객체(object)"라는 형태로 저장되고, 그 객체를 심볼(변수 이름)을 통해 다루게 돼요. 컴퓨터 메모리에 직접 접근하는 대신 R이 만들어 둔 다양한 데이터 구조를 쓰는 셈이죠. 이 장에서는 R에서 다루는 값들이 어떤 종류로 나뉘는지, 그리고 각각이 어떻게 구성되는지 하나씩 살펴볼게요.
출처: R 공식 매뉴얼
본문
어떤 컴퓨터 언어든 **변수(variable)**는 메모리에 저장된 데이터에 접근하는 수단이에요. 그런데 R은 컴퓨터 메모리에 직접 접근하지 않아요. 대신 R이 제공하는 여러 전용 데이터 구조를 쓰는데, 이걸 우리는 **객체(object)**라고 불러요. 이 객체들은 심볼(symbol)이나 변수를 통해 가리켜지죠. R에서 독특한 점은 심볼 자체도 하나의 객체라는 거예요. 그래서 심볼도 다른 객체와 똑같은 방식으로 다룰 수 있어요. 이건 다른 많은 언어와 달라서, R 프로그램 전반에 걸쳐 큰 영향을 주는 성질입니다.
이 장에서는 R이 제공하는 여러 데이터 구조를 간단히 소개할게요. 각각에 대한 자세한 논의는 뒤에 나오는 장들에서 다루니 여기서는 개괄만 잡아두면 돼요. R 전용 함수인 typeof는 R 객체의 타입을 돌려주는데, 참고로 R의 바탕이 되는 C 코드에서 모든 객체는 SEXPREC라는 typedef 구조체를 가리키는 포인터예요. 그리고 R의 각 데이터 타입은 C에서 SEXPTYPE으로 표현되며, 이 값이 구조체의 각 부분 정보를 어떻게 쓸지 결정하게 됩니다.
다음 표는 typeof가 돌려줄 수 있는 값들과 그 의미를 정리한 거예요.
typeof 값 |
의미 |
|---|---|
"NULL" |
NULL |
"symbol" |
변수 이름 |
"pairlist" |
페어리스트(pairlist) 객체 (주로 내부용) |
"closure" |
함수 |
"environment" |
환경(environment) |
"promise" |
지연 평가(lazy evaluation)를 구현하는 데 쓰는 객체 |
"language" |
R 언어 구조물 |
"special" |
인자를 평가하지 않는 내장 함수 |
"builtin" |
인자를 평가하는 내장 함수 |
"char" |
'스칼라' 문자열 객체 (내부 전용) *** |
"logical" |
논릿값을 담는 벡터 |
"integer" |
정수값을 담는 벡터 |
"double" |
실수값을 담는 벡터 |
"complex" |
복소수값을 담는 벡터 |
"character" |
문자값을 담는 벡터 |
"..." |
특별한 가변 길이 인자 *** |
"any" |
모든 타입과 일치하는 특별한 타입 (실제 객체는 없음) |
"expression" |
표현식(expression) 객체 |
"list" |
리스트 |
"bytecode" |
바이트 코드 (내부 전용) *** |
"externalptr" |
외부 포인터 객체 |
"weakref" |
약한 참조(weak reference) 객체 |
"raw" |
바이트를 담는 벡터 |
"S4" |
단순 객체가 아닌 S4 객체 |
'***' 표시가 붙은 타입의 객체는 사용자가 쉽게 손에 넣기 어려워요.
mode 함수는 Becker, Chambers & Wilks (1988)의 의미에서 객체의 모드(mode)를 알려주는데, S 언어의 다른 구현체와 더 잘 맞는 개념이에요. 마지막으로 storage.mode는 그 인자의 저장 모드(storage mode)를 돌려줍니다. 주로 C나 FORTRAN처럼 다른 언어로 작성된 함수를 호출할 때, R 객체가 그 루틴이 기대하는 데이터 타입을 갖도록 보장하기 위해 쓰여요. (S 언어에서는 정수나 실수 벡터가 모두 "numeric" 모드라서 두 저장 모드를 구분할 필요가 있거든요.)
> x <- 1:3
> typeof(x)
[1] "integer"
> mode(x)
[1] "numeric"
> storage.mode(x)
[1] "integer"
R 객체는 계산 과정에서 자주 다른 타입으로 강제 변환(coerced)되곤 해요. 명시적으로 변환해 주는 함수도 많이 있죠. R 언어로 프로그래밍할 때는 보통 객체의 타입이 계산 결과에 영향을 주지 않지만, 외부 언어나 운영체제를 다룰 때는 객체가 올바른 타입인지 꼭 확인해야 합니다.
2.1 Basic types — 기본 타입
2.1.1 Vectors — 벡터
벡터는 데이터가 들어 있는 연속된 셀(cell)들의 집합으로 생각하면 돼요. 셀들은 x[5] 같은 색인(indexing) 연산으로 접근할 수 있고, 자세한 내용은 Indexing에서 다뤄요.
R에는 여섯 가지 기본('원자(atomic)') 벡터 타입이 있어요: 논리(logical), 정수(integer), 실수(real), 복소수(complex), 문자(character, C에서는 'string'이라고도 해요), 그리고 raw. 각 벡터 타입에 해당하는 모드와 저장 모드는 다음 표와 같습니다.
| typeof | mode | storage.mode |
|---|---|---|
logical |
logical |
logical |
integer |
numeric |
integer |
double |
numeric |
double |
complex |
complex |
complex |
character |
character |
character |
raw |
raw |
raw |
4.2 같은 숫자 하나, "four point two" 같은 문자열 하나도 사실 길이 1짜리 벡터일 뿐이에요. 더 이상의 기본 타입은 없죠. 그리고 길이가 0인 벡터도 가능하고 아주 유용합니다.
문자 벡터의 한 요소를 가리켜 "문자열(character string)", 줄여서 string이라고 부르는 경우가 많아요.
2.1.2 Lists — 리스트
리스트("제네릭 벡터(generic vector)"라고도 불러요)는 또 다른 종류의 데이터 저장 방식이에요. 리스트에는 요소(element)가 있고, 각 요소에는 어떤 타입의 R 객체든 담을 수 있어요. 즉 한 리스트 안의 요소들이 꼭 같은 타입일 필요가 없죠. 리스트 요소는 서로 다른 세 가지 색인 연산으로 접근하며, 자세한 설명은 Indexing에서 다룹니다.
리스트도 벡터예요. 그래서 리스트와 구분이 필요할 때 기본 벡터 타입들을 가리켜 "원자 벡터(atomic vector)"라고 부르는 거예요.
2.1.3 Language objects — 언어 객체
R 언어 그 자체를 구성하는 객체는 세 종류가 있어요: call, expression, name이죠. R에는 이미 "expression" 타입의 객체가 있으니, 다른 맥락에서 expression이라는 말을 쓰는 건 피하는 게 좋아요. 특히 문법적으로 올바른 표현식(expression)은 statement라고 부르기로 합니다.
이 객체들의 모드는 각각 "call", "expression", "name"이에요.
표현식에서 quote 메커니즘을 쓰면 이 객체들을 직접 만들 수 있고, as.list와 as.call 함수로 리스트와 오갈 수 있어요. 파싱 트리(parse tree)의 구성 요소는 표준 색인 연산으로 뽑아낼 수 있습니다.
2.1.3.1 Symbol objects — 심볼 객체
심볼(symbol)은 R 객체를 가리키는 것이에요. 보통 R 객체의 이름은 심볼이죠. 심볼은 as.name 함수나 quote 함수로 만들 수 있습니다.
심볼은 모드가 "name", 저장 모드가 "symbol", 타입이 "symbol"이에요. as.character와 as.name으로 문자 문자열과 서로 변환할 수 있어요. 파싱된 표현식의 원자(atom)로 자연스럽게 나타나는데, 예를 들어 as.list(quote(x + y))를 실행해 보면 그 모습을 확인할 수 있습니다.
2.1.4 Expression objects — 표현식 객체
R에서는 "expression" 타입의 객체를 만들 수 있어요. 표현식은 하나 이상의 statement를 담고 있고, statement는 문법적으로 올바른 토큰(token)들의 모음이에요. 표현식 객체는 파싱은 됐지만 아직 평가되지 않은 R statement를 담는 특별한 언어 객체입니다.
핵심 차이는 두 가지예요. 하나는 표현식 객체가 여러 개의 표현식을 동시에 담을 수 있다는 점이고, 다른 하나는 "expression" 타입의 객체는 명시적으로 eval에 넘길 때만 평가되지만, 다른 언어 객체는 뜻밖의 상황에서 평가될 수 있다는 점이에요.
표현식 객체는 리스트와 매우 비슷하게 동작하며, 구성 요소 접근 방식도 리스트와 같습니다.
2.1.5 Function objects — 함수 객체
R에서 함수는 객체이고, 다른 객체와 똑같이 다룰 수 있어요. 함수(정확히는 함수 클로저(function closure))는 세 가지 기본 구성 요소를 갖습니다: 형식 인자 목록(formal argument list), 몸통(body), 환경(environment)이죠.
인자 목록은 쉼표로 구분된 인자들의 나열이에요. 각 인자는 심볼일 수도, 'symbol = default' 형태일 수도, 특별한 인자 ...일 수도 있습니다. 두 번째 형태는 인자에 기본값을 지정할 때 써요. 함수 호출 때 그 인자에 값을 주지 않으면 이 기본값이 사용되죠. ... 인자는 특별해서 얼마든지 많은 인자를 담을 수 있어요. 인자 개수를 미리 알 수 없거나, 받은 인자를 그대로 다른 함수에 넘겨줘야 할 때 주로 사용됩니다.
몸통은 파싱된 R statement예요. 보통은 중괄호로 감싼 statement들의 모음이지만, statement 하나, 심볼, 심지어 상수일 수도 있어요.
함수의 환경은 그 함수가 만들어질 당시 활성화돼 있던 환경이에요. 그 환경에 바인딩된 심볼은 함수가 포착해 활용할 수 있죠. 이렇게 함수 코드와 환경의 바인딩이 결합된 것을 함수형 프로그래밍 이론에서는 '함수 클로저'라고 불러요. 이 문서에서는 주로 '함수(function)'라고 쓰지만, 붙어 있는 환경의 중요성을 강조할 때는 '클로저(closure)'라는 말을 쓸게요.
클로저 객체의 세 부분은 formals, body, environment 구문으로 뽑아내고 조작할 수 있어요 (세 함수 모두 대입 연산의 왼쪽에도 쓸 수 있죠). 특히 마지막 environment는 원치 않는 환경 포착을 제거하는 데 쓸 수 있습니다.
함수를 호출하면 **평가 환경(evaluation environment)**이라는 새 환경이 만들어지는데, 이 환경의 enclosure(둘러싸는 환경, Environments 참고)가 바로 함수 클로저의 환경이에요. 새 환경은 처음에는 함수에 전달된 평가되지 않은 인자들로 채워지고, 평가가 진행되면서 그 안에 지역 변수들이 생겨납니다.
as.list와 as.function으로 함수를 리스트 구조와 오가는 기능도 있어요. 다만 이건 S와의 호환성을 위해 넣은 것이고, 그 사용은 권장하지 않습니다.
2.1.6 NULL
NULL이라는 특별한 객체가 있어요. 객체가 없다는 것을 나타내거나 지정할 필요가 있을 때 사용하죠. 길이가 0인 벡터나 리스트와 혼동하면 안 됩니다.
NULL 객체는 타입도, 수정할 수 있는 속성도 없어요. R에는 NULL 객체가 딱 하나뿐이고, 모든 인스턴스가 그 하나를 가리켜요. NULL인지 검사하려면 is.null을 쓰고, NULL에는 속성을 설정할 수 없습니다.
2.1.7 Builtin objects and special forms — 내장 객체와 특수 형식
이 두 종류의 객체는 R의 내장 함수를 담고 있어요. 코드 목록에서 .Primitive로 표시되는 함수들, 그리고 .Internal 함수를 통해서 접근하기 때문에 사용자 눈에 객체로는 잘 안 보이는 함수들이 여기에 해당하죠. 둘의 차이는 인자 처리 방식에 있어요. 내장 함수(builtin)는 자기 인자를 모두 평가한 뒤 값을 내부 함수에 넘겨줍니다 (call-by-value 방식을 따르죠). 반면 특수 함수(special)는 평가되지 않은 인자를 그대로 내부 함수에 넘겨줘요.
R 언어 입장에서 보면 이 객체들은 그저 또 다른 종류의 함수일 뿐이에요. is.primitive 함수로 해석형 함수와 구분할 수 있습니다.
2.1.8 Promise objects — 프로미스 객체
프로미스(promise) 객체는 R의 지연 평가(lazy evaluation) 메커니즘의 한 부분이에요. 이 객체는 값(value), 표현식(expression), 환경(environment) 세 칸(slot)으로 이루어져 있죠. 함수가 호출되면 인자가 매칭된 뒤, 각 형식 인자가 프로미스에 바인딩됩니다. 그 형식 인자에 주어진 표현식과, 함수가 호출된 환경을 가리키는 포인터가 프로미스에 저장되어요.
그 인자에 실제로 접근하기 전까지는 프로미스에 연결된 값이 없어요. 인자에 접근하면, 저장된 표현식이 저장된 환경에서 평가되고 그 결과가 돌아옵니다. 이 결과는 프로미스가 다시 저장해 두기도 하죠. substitute 함수는 표현식 칸의 내용을 뽑아내는데, 이를 통해 프로그래머는 프로미스에 연결된 값이나 표현식 중 원하는 쪽을 접근할 수 있어요.
R 언어 안에서 프로미스 객체는 거의 대부분 암묵적으로만 등장합니다. 실제 함수 인자가 바로 이 타입이거든요. 또 delayedAssign 함수로 표현식에서 프로미스를 만들 수도 있어요. 다만 R 코드에서 어떤 객체가 프로미스인지 검사하거나, 프로미스의 환경을 알아내는 일반적인 방법은 없습니다.
2.1.9 Dot-dot-dot — ...
... 객체 타입은 페어리스트(pairlist)의 한 형태로 저장돼요. ...의 구성 요소는 C 코드에서 보통의 페어리스트 방식으로 접근할 수 있지만, 해석형 코드에서 객체로 직접 접근하기는 쉽지 않아요. 게다가 그런 객체가 존재한다고 가정하는 것 자체도 피해야 하는데, 미래에 바뀔 수 있기 때문입니다.
다만 이 객체를 (프로미스가 강제 평가되면서!) 리스트로 포착할 수는 있어요. 예를 들어 table 함수 안에서 이런 코드를 볼 수 있죠.
args <- list(...)
## ....
for (a in args) {
## ....
...를 페어리스트 객체로 구현한 것은 R API의 일부로 취급해서는 안 돼요. base R 밖의 코드는 ...에 대한 이 현재의 구현 설명에 기대면 안 됩니다. 반면 위의 list(...) 방식이나 다른 "dot-접근" 함수들 — ...length(), ...elt(), ...names() — 그리고 "예약어"인 ..1, ..2 등은 안정적인 R API에 속해요. 이에 대한 자세한 내용은 ?dots 도움말 페이지에서 볼 수 있어요.
함수가 형식 인자로 ...를 갖고 있다면, 어떤 형식 인자에도 매칭되지 않는 실제 인자들은 모두 ...와 매칭됩니다.
2.1.10 Environments — 환경
환경(environment)은 두 가지로 이루어져 있다고 생각하면 돼요. 하나는 심볼-값 쌍의 집합인 **프레임(frame)**이고, 다른 하나는 바깥 환경을 가리키는 포인터인 **엔클로저(enclosure)**예요. R이 어떤 심볼의 값을 찾을 때 먼저 프레임을 살펴보고, 일치하는 심볼이 있으면 그 값을 돌려줘요. 없으면 엔클로저(바깥 환경)로 가서 같은 과정을 반복하죠. 이렇게 환경은 트리 구조를 이루는데, 엔클로저가 부모 역할을 합니다. 이 환경 트리는 부모가 없는 빈 환경(empty environment)을 뿌리로 두고, 이 빈 환경은 emptyenv()로 접근할 수 있어요. 바로 이 빈 환경이 base 패키지 환경( baseenv() 함수로 접근)의 직접 부모입니다.
환경은 함수 호출에 의해 암묵적으로 만들어지기도 해요 (Function objects와 Lexical environment에서 설명해요). 이 경우 환경에는 함수 지역 변수(인자 포함)가 담기고, 그 엔클로저는 현재 호출된 함수의 환경이 되죠. 환경은 new.env로 직접 만들 수도 있어요. 환경 프레임의 내용은 ls, names, $, [, [[, get, get0으로 읽고, $<-, [[<-, assign, 그리고 eval, evalq로 조작할 수 있습니다.
parent.env 함수로 환경의 엔클로저에 접근할 수 있어요.
다른 대부분의 R 객체와 달리 환경은 함수에 전달되거나 대입에 쓰일 때 복사되지 않아요. 그래서 같은 환경을 여러 심볼에 대입해 두고 하나를 바꾸면 나머지도 함께 바뀝니다. 특히 환경에 속성을 붙이는 일은 뜻밖의 결과를 낳을 수 있으니 주의하세요.
2.1.11 Pairlist objects — 페어리스트 객체
페어리스트(pairlist) 객체는 Lisp의 dotted-pair 리스트와 비슷해요. R 내부에서 널리 쓰이지만 해석형 코드에서는 좀처럼 눈에 띄지 않죠. 다만 formals가 페어리스트를 돌려주고, pairlist 함수로 만들 수는 있습니다. 길이가 0인 페어리스트는 NULL인데, 이건 Lisp에서 기대할 수 있는 모습이지만 길이 0인 리스트와는 대조적이에요. 각 객체는 CAR 값, CDR 값, TAG 값 세 칸을 갖습니다. TAG 값은 문자열이고, CAR과 CDR은 각각 보통 리스트 항목(머리)과 나머지(꼬리)를 나타내며 NULL 객체로 끝나요. (CAR/CDR이라는 용어는 전통적인 Lisp 용어로, 1960년대 초 IBM 컴퓨터의 주소·감소 레지스터에서 유래했어요.)
페어리스트는 R 언어에서 제네릭 벡터("리스트")와 똑같이 다뤄져요. 특히 요소 접근도 같은 [[]] 문법을 씁니다. 다만 제네릭 벡터가 보통 더 효율적이라 페어리스트의 사용은 권장되지 않아요. 내부 페어리스트를 R에서 접근하면 (부분집합을 만들 때를 포함해) 보통 제네릭 벡터로 변환됩니다.
페어리스트가 사용자 눈에 보이는 아주 드문 경우 중 하나가 .Options예요.
2.1.12 The "Any" type — "Any" 타입
실제로 객체가 "Any" 타입이 되는 건 불가능하지만, 그럼에도 타입 값으로는 유효해요. 아주 드문 상황에서 쓰이는데, 예를 들어 as.vector(x, "any")는 타입 변환을 하지 말라는 뜻으로 사용됩니다.
2.2 Attributes — 속성
NULL을 제외한 모든 객체에는 속성(attribute)을 하나 이상 붙일 수 있어요. 속성은 모든 요소에 이름이 붙어 있는 페어리스트로 저장되지만, name=value 쌍의 집합으로 생각하는 게 맞아요. 속성 목록은 attributes로 얻고 attributes<-로 설정하며, 개별 구성 요소는 attr과 attr<-로 접근합니다.
몇몇 속성에는 전용 접근자 함수가 있어요 (예: factor의 levels<-). 그런 함수가 있으면 그걸 쓰는 게 좋습니다. 구현 세부사항을 숨겨줄 뿐 아니라 추가 작업까지 수행해 주니까요. R은 특별한 속성이 걸린 attr<-과 attributes<- 호출을 가로채서 일관성 검사를 강제하기도 해요.
행렬(matrix)과 배열(array)은 그저 dim 속성(선택적으로 dimnames도)이 붙은 벡터일 뿐입니다.
속성은 R에서 클래스 구조를 구현하는 데 쓰여요. 객체에 class 속성이 있으면 평가(dispatch) 과정에서 그 속성이 검토됩니다. R의 클래스 구조에 대한 자세한 내용은 Object-oriented programming에서 다루죠.
2.2.1 Names — 이름
names 속성은 있으면 벡터나 리스트의 각 요소에 이름표를 붙여줘요. 객체를 출력할 때도 names 속성이 있으면 그걸로 요소에 이름을 달아 보여줍니다. names 속성은 색인에도 쓸 수 있는데, 예를 들어 quantile(x)["25%"]처럼 사용할 수 있어요.
이름은 names와 names<- 구문으로 읽고 쓸 수 있어요. names<-는 names 속성이 올바른 타입과 길이를 갖도록 필요한 일관성 검사를 수행합니다.
페어리스트와 1차원 배열은 특별하게 취급돼요. 페어리스트 객체에는 가상의 names 속성이 쓰이는데, 실제로는 리스트 요소들의 태그에서 구성됩니다. 1차원 배열의 경우 names 속성은 실제로 dimnames[[1]]에 접근하는 것이에요.
2.2.2 Dimensions — 차원
dim 속성은 배열(array)을 구현하는 데 쓰여요. 배열의 내용은 열 우선(column-major) 순서로 벡터에 저장되고, dim 속성은 배열의 각 차원 크기를 지정하는 정수 벡터예요. R은 벡터의 길이가 차원 크기들의 곱과 같도록 보장해요. 차원 중 하나 이상의 길이는 0이 될 수 있습니다.
벡터는 1차원 배열과 달라요. 1차원 배열은 길이 1짜리 dim 속성이 있지만, 벡터는 dim 속성이 없으니까요.
2.2.3 Dimnames — 차원 이름
배열은 dimnames 속성으로 차원마다 따로 이름을 붙일 수 있어요. 이 속성은 문자 벡터들의 리스트예요. dimnames 리스트 자체에도 이름이 있어서, 배열을 출력할 때 차원 제목으로 사용됩니다.
2.2.4 Classes — 클래스
R에는 정교한 클래스 시스템이 있어요. 주로 class 속성을 통해 제어되죠. 이 속성은 객체가 상속하는 클래스들의 목록을 담은 문자 벡터예요. R의 "제네릭 메서드(generic methods)" 기능의 토대가 바로 이 속성입니다.
이 속성은 사용자가 거의 제약 없이 접근하고 조작할 수 있어요. 그런데 객체가 해당 클래스 메서드가 기대하는 구성 요소를 정말로 갖고 있는지는 검사하지 않습니다. 그래서 class 속성을 바꿀 때는 신중해야 하고, 가능하다면 전용 생성·변환 함수를 쓰는 게 좋아요.
2.2.5 Time series attributes — 시계열 속성
tsp 속성은 시계열의 매개변수인 시작(start), 끝(end), 빈도(frequency)를 담는 데 쓰여요. 주로 월별·분기별 데이터처럼 주기적인 부분 구조를 가진 계열을 다룰 때 사용됩니다.
2.2.6 Copying of attributes — 속성의 복사
객체를 수정할 때 속성을 복사할지 말지는 복잡한 문제인데, 몇 가지 일반적인 규칙이 있어요 (Becker, Chambers & Wilks, 1988, pp. 144–6).
- 스칼라 함수(벡터에 요소 단위로 작동하고 결과가 입력과 비슷한 함수)는 속성을 보존해야 합니다 (class는 예외일 수 있어요).
- 이항 연산은 보통 더 긴 인자에서 대부분의 속성을 복사해요. 길이가 같으면 두 인자에서 복사하되 첫 번째 인자의 값을 우선하죠. 여기서 '대부분'은
names,dim,dimnames를 뺀 전부를 뜻해요. 이 세 가지는 연산자 코드가 적절히 새로 설정합니다. - 부분집합(subsetting)은 (빈 인덱스가 아니라면) 일반적으로
names,dim,dimnames를 제외한 모든 속성을 버리고, 이 세 가지는 적절히 다시 설정해요. 반대로 부분 대입(subassignment)은 길이가 바뀌어도 일반적으로 속성을 보존합니다. 강제 변환(coercion)은 모든 속성을 버려요. - 정렬(sorting)의 기본 메서드는 객체와 함께 정렬되는
names를 제외한 모든 속성을 버립니다.
2.3 Special compound objects — 특수 복합 객체
2.3.1 Factors — 팩터
팩터(factor)는 유한한 개수의 값을 가질 수 있는 항목(성별, 사회적 계층 등)을 표현하는 데 쓰여요. 팩터는 levels 속성과 "factor" 클래스를 갖습니다. 선택적으로 contrasts 속성도 가질 수 있는데, 이것은 팩터를 모델링 함수에서 쓸 때 사용할 모수화(parametrisation) 방식을 제어해요.
팩터는 순수한 명목(nominal)일 수도 있고, 순서가 있는 범주(ordered categories)일 수도 있어요. 후자의 경우 그렇게 정의해야 하며 c("ordered"," factor")라는 클래스 벡터를 가져야 합니다.
팩터는 현재 정수 배열로 실제 등급(levels)을 지정하고, 정수에 매핑되는 이름들의 배열을 하나 더 두는 방식으로 구현돼 있어요. 안타깝게도 사용자들이 계산을 조금 더 쉽게 하려고 이 구현 방식을 이용하는 경우가 많아요. 하지만 이것은 구현상의 문제일 뿐이고, 모든 R 구현에서 보장되는 사항은 아닙니다.
2.3.2 Data frame objects — 데이터 프레임 객체
데이터 프레임(data frame)은 SAS나 SPSS의 데이터셋, 즉 "케이스별 변수" 행렬과 가장 비슷한 R 구조물이에요.
데이터 프레임은 모두 같은 길이(행렬의 경우 같은 행 수)를 가진 벡터, 팩터, 행렬들의 리스트예요. 게다가 데이터 프레임은 보통 변수에 이름표를 다는 names 속성과 케이스에 이름표를 다는 row.names 속성을 가집니다.
데이터 프레임은 다른 구성 요소와 같은 길이인 리스트 하나를 담을 수도 있어요. 그 리스트는 길이가 다른 요소들을 담을 수 있어서, 들쭉날쭉한 배열(ragged array)을 위한 데이터 구조를 제공하죠. 다만 이 글을 쓰는 시점 기준으로 그런 배열은 일반적으로 제대로 처리되지 않습니다.
더 알아보기
- 다음 챕터인 3 Evaluation of expressions에서 R의 평가(evaluation) 방식과 일어나는 일을 이어서 다뤄요.
- R Language Definition 전체 문서를 보면 객체, 환경, 함수가 어떻게 어우러지는지 전체 그림을 잡을 수 있어요.
?typeof,?mode,?attributes,?factor,?data.frame등 해당 주제의 R 도움말을 참고하면 실제 동작을 직접 확인할 수 있습니다.