속도를 위한 벡터화
명시적인 반복문을 벡터화 연산으로 바꾸어 속도를 크게 높입니다.
속도를 위한 벡터화은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
벡터화가 중요한 이유
R은 해석되는 언어이므로 for 반복문은 반복할 때마다 함수 호출 전달, 범위 확인, 자료형 강제 변환 등의 오버헤드가 발생합니다. 벡터화된 연산은 이 작업을 훨씬 빠르게 실행되는 컴파일된 C 코드로 넘깁니다.
벡터화는 base R에서 사용할 수 있는 가장 효과적인 단일 최적화 방법입니다.
반복문과 cumsum() 예제
for 반복문과 기본 제공 함수 cumsum()으로 누적 합계를 계산해 보면 성능 차이가 분명하게 드러납니다. cumsum()은 C 수준의 컴파일된 코드를 호출하여 전체 벡터를 한 번에 처리합니다.
n <- 500000
x <- rnorm(n)
t_loop <- system.time({
result <- numeric(n)
result[1] <- x[1]
for (i in 2:n) result[i] <- result[i-1] + x[i]
})['elapsed']
t_vec <- system.time({
result2 <- cumsum(x)
})['elapsed']
cat('Loop :', t_loop, 's
')
cat('cumsum:', t_vec, 's
')ifelse()와 for + if 비교
ifelse(condition, yes, no)는 전체 벡터에 걸쳐 조건을 한 번에 평가하는 벡터화된 조건문입니다. 요소별로 처리하는 for + if 반복문을 C 수준의 단일 처리로 대체합니다.
n <- 300000
x <- rnorm(n)
t_loop <- system.time({
result <- numeric(n)
for (i in seq_len(n)) result[i] <- if (x[i] > 0) x[i] else -x[i]
})['elapsed']
t_vec <- system.time({
result2 <- ifelse(x > 0, x, -x)
})['elapsed']
cat('for+if :', t_loop, 's
')
cat('ifelse :', t_vec, 's
')결과 벡터 사전 할당
반복문을 피할 수 없다면 반복문 전에 결과 벡터를 사전 할당하십시오. 반복문 안에서 c(result, new_val)로 벡터를 늘리면 매 반복마다 전체 벡터가 복사되어 총 메모리 작업량이 O(n^2)가 됩니다.
n <- 20000
t_grow <- system.time({
result <- c()
for (i in seq_len(n)) result <- c(result, i^2)
})['elapsed']
t_prealloc <- system.time({
result2 <- numeric(n)
for (i in seq_len(n)) result2[i] <- i^2
})['elapsed']
cat('Growing vector:', t_grow, 's
')
cat('Pre-allocated :', t_prealloc, 's
')올바른 사전 할당 자료형
사전 할당 중 암시적인 자료형 강제 변환을 피하려면 데이터에 맞는 자료형 생성자를 사용하십시오.
numeric(n)— 배정밀도 부동 소수점 값integer(n)— 정수character(n)— 빈 문자열logical(n)— FALSE 값vector('list', n)— NULL로 이루어진 리스트
n <- 5
cat('numeric :', numeric(n), '
')
cat('integer :', integer(n), '
')
cat('logical :', logical(n), '
')
cat('character:', character(n), '
')
cat('list len :', length(vector('list', n)), '
')colSums() 및 rowSums()와 apply() 비교
행렬 연산에서는 colSums(m), rowSums(m), colMeans(m), rowMeans(m)가 고도로 최적화된 C 루틴입니다. 이 함수들은 각 행마다 R 함수 sum을 한 번씩 호출하는 apply(m, 1, sum)보다 일관되게 빠릅니다.
m <- matrix(rnorm(1000 * 2000), nrow = 1000)
t_apply <- system.time(apply(m, 2, sum))['elapsed']
t_colsums <- system.time(colSums(m))['elapsed']
cat('apply(m,2,sum):', t_apply, 's
')
cat('colSums(m) :', t_colsums, 's
')벡터화된 산술 연산은 항상 빠릅니다
벡터의 기본 산술 연산인 +, -, *, /, ^, sqrt(), log(), exp()는 모두 벡터화되어 있습니다. 이 연산들은 한 번의 C 호출로 전체 벡터의 각 요소에 대해 작동합니다. 항상 반복문보다 이 연산들을 우선 사용하십시오.
x <- 1:1000000
t1 <- system.time(y <- x^2 + 2*x + 1)['elapsed']
t2 <- system.time({
y2 <- numeric(length(x))
for (i in seq_along(x)) y2[i] <- x[i]^2 + 2*x[i] + 1
})['elapsed']
cat('Vectorized:', t1, 's
')
cat('Loop :', t2, 's
')반복문 대신 논리적 부분 집합 사용
논리 조건으로 벡터를 필터링하는 작업은 벡터화되어 있습니다. 반복하면서 조건에 따라 추가하는 대신 논리 인덱스를 만든 다음 한 번에 부분 집합을 추출하면 기본 C 코드가 한 번만 순회합니다.
x <- rnorm(500000)
t_loop <- system.time({
pos <- c()
for (v in x) if (v > 0) pos <- c(pos, v)
})['elapsed']
t_vec <- system.time({
pos2 <- x[x > 0]
})['elapsed']
cat('Loop filter:', t_loop, 's
')
cat('Logical idx:', t_vec, 's
')인덱스 작업을 위한 which()와 tabulate()
TRUE 값의 위치가 필요할 때는 which(condition)이 벡터화되어 있어 빠릅니다. tabulate(bin_vector)은 정수 범위가 조밀할 때 table()보다 정수의 출현 횟수를 빠르게 셉니다.
x <- sample(1:10, 100000, replace = TRUE)
t_table <- system.time(table(x))['elapsed']
t_tabulate <- system.time(tabulate(x, nbins = 10))['elapsed']
cat('table() :', t_table, 's
')
cat('tabulate():', t_tabulate, 's
')
idx <- which(x == 5)
cat('Positions of 5: first 5 =', head(idx, 5), '
')반복문을 사용해도 괜찮은 경우
모든 반복문이 나쁜 것은 아닙니다. 다음과 같은 경우에는 반복문을 사용해도 괜찮습니다.
- 각 반복이 이전 결과에 의존하는 경우(순차적 의존성)
- 반복 횟수가 적은 경우(< 1000)
- 반복문의 본문에서 벡터화된 대응 방법이 없는 복잡한 함수를 호출하는 경우
이러한 경우에는 사전 할당에 집중하고 반복문 안에서 구조의 크기를 늘리지 않도록 하십시오.
# Sequential dependency -- loop is correct here
fib <- function(n) {
result <- integer(n)
result[1] <- 1L
if (n >= 2) result[2] <- 1L
for (i in seq_len(n)[-c(1,2)]) result[i] <- result[i-1] + result[i-2]
result
}
cat('Fibonacci:', fib(10), '
')벡터화 요약
빠른 R 코드를 위한 주요 벡터화 규칙은 다음과 같습니다.
- 순차적 누적에는
cumsum/cumprod/diff를 사용합니다. - 요소별 조건문에는
ifelse()를 사용합니다. numeric(n)/vector('list',n)으로 결과를 사전 할당합니다.apply()보다colSums/rowSums/colMeans/rowMeans를 사용합니다.- 논리적 부분 집합 추출이 필터링 반복문보다 효율적입니다.
빠른 확인: 사전 할당
큰 n에 대해 반복문 안에서 result <- c(result, new_val)로 벡터를 확장하면 왜 그렇게 느려질까요?
벡터화 복습
벡터화는 R에서 성능을 높이는 가장 중요한 방법입니다.
- 벡터화된 함수(
cumsum,ifelse, 산술 연산자)는 컴파일된 C 코드를 호출하므로 동일한 작업을 하는 R 반복문보다 10~100배 빠릅니다. - 피할 수 없는 반복문을 시작하기 전에 결과 컨테이너를 사전 할당하여 O(n^2) 복사를 방지합니다.
- 행렬 집계에서는
colSums/rowSums가apply()보다 효율적입니다. - 논리적 부분 집합 추출을 사용하면 필터링 반복문을 깔끔하고 빠르게 대체할 수 있습니다.
자주 묻는 질문
“속도를 위한 벡터화” 강의는 무료인가요?
네 — “속도를 위한 벡터화” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“속도를 위한 벡터화”에서 뭘 배우나요?
명시적인 반복문을 벡터화 연산으로 바꾸어 속도를 크게 높입니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
R Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“속도를 위한 벡터화” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.