3장. 객체, 그들의 모드와 속성
3장. 객체, 그들의 모드와 속성
이번 장에서는 R이 다루는 모든 것의 바탕이 되는 객체(object) 개념을 살펴봐요. 객체가 어떤 종류인지(mode), 길이가 얼마나 되는지, 그리고 객체에 붙는 추가적인 속성(attribute)이 무엇인지를 차근차근 이해하면, 뒤에서 배울 벡터·행렬·데이터 프레임을 훨씬 편안하게 받아들일 수 있어요.
출처: R 공식 매뉴얼
본문
3.1 고유 속성: mode와 length
R이 다루는 대상 하나하나를 전문 용어로 객체라고 불러요. 숫자형(실수) 값으로 이뤄진 벡터, 복소수 벡터, 논리값 벡터, 문자열 벡터가 대표적인 예죠. 이들은 모든 구성 요소가 같은 타입, 다시 말해 같은 mode를 가지기 때문에 "원자적(atomic)" 구조라고 해요. 여기서 mode는 numeric(숫자형), complex(복소수형), logical(논리형), character(문자형), raw(원시형) 중 하나예요.
어떤 벡터든 그 값을 이루는 요소는 모두 같은 mode여야 해요. 그래서 특정 벡터는 logical, numeric, complex, character, raw 중 하나로 명확하게 구분되죠. (이 규칙의 겉보기 예외는 "값이 없음"을 뜻하는 특별한 값 NA인데, 사실 NA에도 여러 타입이 있어요.) 그리고 벡터는 비어 있어도 여전히 mode를 가질 수 있답니다. 예를 들어 빈 문자형 벡터는 character(0)으로, 빈 숫자형 벡터는 numeric(0)으로 표현돼요.
R은 리스트(list)라는 객체도 다뤄요. 리스트는 mode가 list인데, 각각 다른 mode를 가질 수 있는 객체들을 순서대로 늘어놓은 것이에요. 리스트는 구성 요소가 그 자체로 다시 리스트일 수 있기 때문에 "원자적"이 아니라 "재귀적(recursive)" 구조라고 불러요.
그 밖에 재귀적 구조로는 mode가 function인 것과 expression인 것이 있어요. 함수는 R 시스템을 이루는 객체로, 사용자가 직접 작성한 함수도 그 범주에 들어가요(이 안내서 뒤에서 자세히 다룹니다). expression 객체는 R의 고급 기능에 속해서 이 안내서에서 직접 다루지는 않지만, R에서 모델링에 쓰는 formulae(수식)를 이야기할 때 간접적으로 만나게 돼요.
어떤 객체의 mode란, 그 객체를 이루는 근본 구성 요소의 기본 타입을 뜻해요. 이건 객체가 가진 "속성(property)" 중 하나인데, 모든 객체가 가지는 또 다른 속성이 바로 length(길이)예요. mode(object) 함수와 length(object) 함수를 쓰면 어떤 구조물이든 그 mode와 length를 바로 알아낼 수 있어요[11].
객체의 나머지 속성들은 보통 attributes(object)로 확인해요(3.3절 "속성 가져오기와 설정하기" 참고). 이런 이유로 mode와 length는 객체의 "고유 속성(intrinsic attributes)"이라고도 불러요.
예를 들어 z가 길이 100인 복소수 벡터라면, 어떤 표현식 속에서 mode(z)는 문자형 문자열 "complex"이고 length(z)는 100이에요.
R은 mode가 바뀌면 안 될 이유가 없다 싶으면 거의 어디서든 mode의 변화를 허용해 줘요(다소 억지스러워 보이는 경우도 몇몇 있지만요). 예를 들어 이렇게 벡터를 만들고
> z <- 0:9
다음처럼 써 주면
> digits <- as.character(z)
이제 digits는 c("0", "1", "2", …, "9")라는 문자형 벡터가 돼요. 여기에 강제 변환(coercion), 즉 mode를 바꾸는 작업을 한 번 더 하면 다시 숫자형 벡터로 되돌아와요:
> d <- as.integer(digits)
이제 d와 z는 같은 값이 되어요[12]. as.something() 형태의 함수는 아주 많이 있는데, 한 mode에서 다른 mode로 강제 변환하거나, 객체가 아직 갖고 있지 않은 어떤 속성을 새로 부여하는 데 쓰여요. 각각의 도움말 파일을 직접 찾아보면서 익숙해지는 걸 추천해요.
3.2 객체의 길이 바꾸기
"빈" 객체라도 여전히 mode는 가질 수 있어요. 예를 들어
> e <- numeric()
라고 하면 e는 mode가 numeric인 빈 벡터 구조물이 돼요. 마찬가지로 character()는 빈 문자형 벡터를 만드는 식이죠. 한번 크기를 가진 객체가 만들어지면, 이전 범위를 벗어나는 인덱스 값으로 새 요소를 지정하기만 해도 새 구성 요소를 추가할 수 있어요. 즉
> e[3] <- 17
이렇게 하면 e는 길이 3인 벡터가 돼요(이 시점에서 처음 두 요소는 모두 NA예요). 이는 어떤 구조물이든, 추가하려는 요소들의 mode가 원래 객체의 mode와 일치하기만 하면 늘 적용되는 규칙이에요.
이렇게 객체의 길이가 자동으로 조정되는 일은 아주 자주 쓰여요. 예를 들어 입력을 받는 scan() 함수가 그렇죠(scan() 함수에 대한 설명 참고).
반대로 객체의 크기를 줄이는 데는 대입(assignment) 하나면 충분해요. 만약 alpha가 길이 10인 객체라면,
> alpha <- alpha[2 * 1:5]
라고 하면 짝수 인덱스에 해당하는 이전 요소들만 남긴 길이 5짜리 객체가 돼요(물론 이전 인덱스는 유지되지 않아요). 그러면 이제 처음 세 값만 남기고 싶을 때
> length(alpha) <- 3
처럼 쓸 수 있고, 벡터를 (결측값으로) 늘리는 것도 같은 방식으로 가능해요.
3.3 속성 가져오기와 설정하기
attributes(object) 함수는 그 객체에 현재 정의된 모든 비고유 속성을 리스트로 돌려줘요. 특정 속성 하나만 골라내고 싶을 때는 attr(object, name) 함수를 쓰면 돼요. 이 함수들은 실제로는 좀 특별한 상황, 예를 들어 어떤 특정한 목적을 위해 새 속성을 만들 때(가령 R 객체에 생성 날짜나 연산자 정보를 붙이고 싶을 때)가 아니면 잘 쓰이지 않아요. 그래도 이 개념 자체는 매우 중요해요.
속성을 대입하거나 삭제할 때는 조금 주의해야 해요. 속성은 R에서 쓰는 객체 시스템의 핵심 일부이기 때문이에요.
속성 함수가 대입의 왼쪽에 오면, object에 새 속성을 붙이거나 기존 속성을 바꾸는 역할을 해요. 예를 들어
> attr(z, "dim") <- c(10,10)
라고 하면 R은 z를 마치 10×10 행렬인 것처럼 취급해요.
3.4 객체의 클래스
R의 모든 객체는 *클래스(class)*를 가지고 있고, class 함수가 이를 알려줘요. 단순한 벡터의 클래스는 그냥 mode와 같아요. 예를 들어 "numeric", "logical", "character", "list"가 그렇죠. 그런데 "matrix", "array", "factor", "data.frame" 같은 값도 클래스로 올 수 있어요.
객체의 클래스라는 특별한 속성은 R에서 객체지향 방식[13]의 프로그래밍을 가능하게 해 주는 장치예요. 예를 들어 어떤 객체의 클래스가 "data.frame"이라면, 출력할 때도 특정한 방식으로 표시되고, plot() 함수도 그래프로 특정한 방식으로 그려 주며, summary() 같은 이른바 일반 함수(generic function)도 인자를 그 클래스에 맞게 다르게 반응해요.
클래스의 효과를 잠시 제거하고 싶다면 unclass() 함수를 쓰면 돼요. 예를 들어 winter가 클래스 "data.frame"이라면
> winter
라고 하면 행렬과 비슷한 데이터 프레임 형태로 출력되지만,
> unclass(winter)
라고 하면 보통의 리스트 형태로 출력돼요. 이 기능을 쓸 일은 아주 특별한 상황뿐이지만, 그중 하나가 바로 클래스와 일반 함수라는 개념에 익숙해지는 연습을 할 때예요.
일반 함수와 클래스에 대해서는 "클래스, 일반 함수, 객체 지향" 장에서 더 다루게 되지만, 거기서도 간단히만 살펴볼게요.
더 알아보기
- 이번 장에서 만난
mode(),length(),attributes(),attr(),class(),unclass()함수는 R 객체를 다루는 가장 기초적인 도구예요. - 다음 장에서는 "순서 있는 요인과 순서 없는 요인(Ordered and unordered factors)"을 다루면서, 범주형 데이터를 표현하는 factor 객체를 배워요.
- 행렬·배열·데이터 프레임 같은 구조는 뒤의 장에서 자세히 다뤄요.