1차원 백터 내부 원소의 원인모를 복제? 그것은 바로 벡터 리사이클링(vector recycling)이 원인이다. 어떤 상황에서 어떻게 동작하는지 알아보도록 하자.
언제 발생할까?
1 | aa = rep(1, 4) |
1번 코드의 경우 별 문제 없이 계산된다. 그런데 어떻게 보면 2번 코드는 에러가 나야 정상이 아닐까? 원소 끼리 곱해진다고 하지만 2번 코드는 길이가 4와 2인 객체간 원소를 곱하는 이라 길이가 다른 두 객체의 연산이인데 멀쩡하게 연산이 되었다. 2번 코드의 연산 결과는 다음과 같다.
1 | aa * c(1, 2, 1, 2) # 1 |
앞에서 봐던 것 처럼 서로 길이가 다른(원소 개수가 다른) 두 벡터를 연산할 경우 원소의 개수가 가장 긴 객체를 기준으로 길이를 맞춘 이후에 연산을 하게 된다. 그 것을 구현하면 1~3번 코드와 같고, 실제로는 3번 코드의 동작에 가깝다.
1 | aa * 1:3 |
이렇게 하면 벡터 리사이클링이 되는데 동작을 보면 length.out
인자를 사용한 3번 코드처럼 써야 이렇게 구현이 된다. 그리고 꼭 뒤에 오는 벡터의 길이가 앞에 오는 벡터의 길이 보다 짧다고 벡터 리사이클링이 동작하는 것은 아니다.
1 | 1:2 * rep(1, 4) |
앞, 뒤 배치에 관계없이 벡터 리사이클링은 두 객체의 길이(원소의 개수)가 다를 때 발생한다.
자주 만나는 경우는?
주로 데이터프레임을 다룰 때 발생한다. 먼저 데이터프레임을 생성하는 예제를 보도록 하자.
1 | data.frame(aa = 1, |
1번 코드와 2번 코드는 원래대로라면 각 변수에 할당된 원소의 길이가 다르기 때문에 에러가 발생해야 정상이나, 벡터 리사이클링 때문에 짧은 길이의 벡터가 복제되었다. 마침 길이가 긴 bb 변수의 원소 개수는 4개이고, aa변수의 원소는 각각 1개, 2개로 4의 약수라서 다행이도(?) 에러 없이 실행된 것을 볼 수 있다.
이번에는 데이터프레임 값 치환 예제를 보자.
1 | df = data.frame(aa = 1:4, |
데이터프레임의 특정 변수에 값 하나를 넣으면 해당 변수의 값 전체가 바뀐다. 보통 그 특성이겠거니 하고 넘어가는 경우가 많은데, 정확하게는 벡터 리사이클링이다. 데이터프레임을 생성할 때 처럼 1번과 2번코드 모두 에러없이 정상동작 하는 것을 볼 수 있다. 하지만 3번코드와 같이 데이터프레임 row 개수인 4의 약수가 아닌 길이가 3인 1차원 벡터가 입력되는 경우 여지없이 에러가 발생한다.
주의점
벡터 리사이클링은 그 자체로 편리할 수 있다. 연산하려고 하는 벡터의 길이가 다른데도 별탈없이 연산이 되기 때문이다. 하지만 에러가 덜나고 편리하다고 마냥 좋은 것이 아니다. 사용자가 의도하고 해당 코드를 작성하면 괜찮지만 초심자가 해당 기능을 충분히 숙지하지 못한 상태에서 사용하거나 코드를 잘못 입력하였으나 에러가 발생하지 않고 동작할 경우 최종 결과값이 어디서 부터 잘못되었는지 조사하기 굉장히 까다롭다.
그리고 벡터 리사이클링은 matrix나 data frame의 생성부터 시작하여 필터링, 원소 치환 등 다양한 영역에서 등장하니 향후 객체를 다룰 때 주의하도록 하자.
<객체 시리즈 - 기본 1차원 벡터>
R) 기본 1차원 벡터 - 생성
R) 기본 1차원 벡터 - 조작
R) 기본 1차원 벡터 - 연산
R) 기본 1차원 벡터 - 리사이클링