set.seed()가 먼저입니다
실행할 때마다 바뀌는 무작위 추출은 교육, 채점, 디버깅, 과학 어느 쪽에도 쓸모가 없습니다 - 시뮬레이션이 오늘은 0.146, 내일은 0.153이라고 한다면 보고서에는 어떤 숫자를 넣어야 할까요? set.seed()는 생성기의 시작점을 고정해, 뒤따르는 "무작위성"의 흐름 전체를 정확히 반복 가능하게 만듭니다:
같은 시드면 어떤 기계에서든 언제나 동일한 추출값이 나옵니다. 이 숫자들은 유사 난수입니다: 무작위성에 대한 모든 통계 검정을 통과하도록 설계된 결정론적 수열이고, 시드는 그 수열의 어디서 시작할지를 정합니다. 시드 값 자체에는 의미가 없습니다 - 42, 7, 20260807 - 아무거나 고르되 적어 두세요. 들여야 할 습관은 이것입니다: 무작위성을 쓰는 스크립트 맨 위에 set.seed() 하나. (추출값을 소비하면 상태가 진행되므로, 호출 순서 도 재현성에 영향을 준다는 점에 유의하세요.)
d/p/q/r 체계: 표 하나로 라이브러리 전체를 해독하기
R은 모든 분포 함수의 이름을 접두사 + 분포족으로 짓습니다. 이 격자를 한 번 보고 나면 통계 라이브러리 전체를 읽을 수 있습니다:
| 접두사 | 답하는 질문 | 정규분포 예시 |
|---|---|---|
r | 무작위 추출값을 달라 | rnorm(5) |
d | 밀도: x에서 곡선의 높이는? | dnorm(0) |
p | 확률: P(X ≤ x)는? | pnorm(1.96) |
q | 분위수: 이 백분위에 있는 x는? | qnorm(0.975) |
p와 q는 서로의 역함수이며, 신뢰구간에서 qt(0.975, df)로 만난 그 짝입니다:
분포족 이름만 바꾸면 모든 것이 그대로 이어집니다: runif/dunif/punif/qunif, rbinom/..., rpois/..., rt/..., rexp/.... 접두사 네 개를 익히면 수십 개의 분포를 얻습니다.
rnorm(): 정규분포 추출
rnorm(n, mean, sd)는 종 모양 곡선에서 값을 뽑습니다 - 측정값 같은 데이터를 시뮬레이션할 때의 주력 함수죠:
표본 평균과 표준편차는 100과 15에 정확히 맞지 않고 그 근처에 떨어집니다: 그 간극이 바로 표집 잡음이며 n이 커질수록 줄어듭니다. 마지막 줄은 훔쳐 갈 만한 기법입니다: 논리형 벡터에 mean()을 쓰면 TRUE의 비율이 나오고, 130을 넘는 비율은 이론값 1 - pnorm(130, 100, 15) ≈ 2.3%에 가깝게 나옵니다. 기본값은 mean = 0, sd = 1(표준정규분포)입니다. iq의 히스토그램을 그리면 익숙한 종 모양이 보입니다.
runif(), rbinom(), rpois()
세 개의 분포족이 더해지면 시뮬레이션 수요 대부분이 채워집니다:
runif()는 범위 전체에 값을 고르게 흩뿌립니다("run if"가 아니라 "uniform"입니다 - 누구나 한 번은 잘못 읽습니다). rbinom(n, size, prob)은 각각 size번의 시행으로 이루어진 실험을 n번 시뮬레이션해 각 실험의 성공 횟수를 반환합니다 - 그래서 위의 각 값은 10번 던져 나온 앞면 수입니다. rpois(n, lambda)는 알려진 평균 발생률로 독립적으로 일어나는 사건의 개수를 생성합니다: 시간당 고객 문의, 페이지당 오타 같은 것이죠.
sample(): 표본추출과 섞기
r* 함수들이 분포에서 값을 만들어 내는 반면, sample()은 이미 갖고 있는 값에서 뽑습니다:
replace 인자가 전부입니다: FALSE(기본값)는 카드를 나눠 주는 방식이라 각 값이 한 번만 나타날 수 있고 가진 것보다 많이 요청하면 오류가 납니다. TRUE는 주사위를 굴리는 방식이라 매번 초기화됩니다. 자신의 데이터에서 복원 추출하는 것이 부트스트랩의 엔진입니다. 벡터만 넘겨 호출하면 sample(x)는 무작위 순열을 반환합니다 - 섞기의 관용구죠.
데이터 프레임의 행을 표본추출할 때는 행 인덱싱 안에서 sample()을 씁니다:
sample(nrow(mtcars), 5)가 무작위 행 번호 5개를 고르고, 인덱싱이 그 행들을 꺼냅니다. 큰 데이터셋을 표본 점검하거나 훈련/시험 세트를 나눌 때의 표준 동작입니다.
간단한 몬테카를로 시뮬레이션
여기가 이 도구 상자 전체의 결실입니다. 질문: 어떤 공정의 측정값이 N(100, 15)를 따르고 그중 10개를 평균 낼 때, 그 평균이 105를 넘을 확률은 얼마일까요? 답을 유도하는 대신 시뮬레이션해 봅시다 - 실험을 1만 번 수행하고 세는 것이죠:
시뮬레이션 결과가 정확한 값(약 0.146)과 천분의 몇 이내로 일치합니다. 몬테카를로를 한숨에 말하면 이렇습니다: 한 번의 시행을 함수로 작성하고, replicate()로 수천 번 반복한 뒤, 성공의 mean()을 취하라. 여기서 정확한 답이 존재한 이유는 설정이 교과서처럼 단순했기 때문입니다 - 질문이 지저분해지는 순간(이상한 분포, 상관된 추출값의 최댓값, 규칙 기반 게임) 해석적 경로는 닫히지만 시뮬레이션 레시피는 그대로 계속 작동합니다. 확인식의 15 / sqrt(10)에 주목하세요: 평균은 개별 추출값보다 덜 흔들리며, 신뢰구간의 폭을 좌우하는 것과 같은 제곱근 법칙입니다.
시뮬레이션용이지 보안용이 아닙니다
지켜야 할 경계가 하나 있습니다: R의 기본 생성기(메르센 트위스터)는 통계적 품질과 속도를 위해 만들어졌지 비밀 유지를 위해 만들어진 것이 아닙니다. 시드가 주어지면 출력이 결정되고 관측된 출력으로부터 내부 상태를 재구성할 수 있는데, 비밀번호나 토큰 등 보안과 관련된 무엇에든 치명적인 결함입니다. 시뮬레이션, 부트스트랩, 교육에는 훌륭하지만 암호학에는 목적에 맞게 만들어진 라이브러리(예: openssl 패키지)를 쓰고 sample()이나 runif()는 절대 쓰지 마세요.
핵심 정리
- 맨 위에서
set.seed()를 한 번 호출하면 모든 "무작위" 결과가 재현 가능해집니다 - 같은 시드, 같은 추출값입니다. - d/p/q/r 접두사가 분포 라이브러리 전체를 해독해 줍니다: 무작위 추출, 밀도, 누적확률, 분위수입니다.
rnorm(n, mean, sd),runif(n, min, max),rbinom(n, size, prob),rpois(n, lambda)가 시뮬레이션 수요 대부분을 덮습니다.sample()은 자신의 값에서 뽑습니다 - 주사위에는replace = TRUE, 카드에는 기본값, 섞기에는size생략입니다.df[sample(nrow(df), k), ]가 행을 표본추출합니다.- 몬테카를로 = 한 번의 시행 함수 +
replicate()+mean()- 수학으로 편히 닿지 못하는 확률 문제에 답하는 레시피입니다. - R의 난수 생성기는 시뮬레이션용이지 암호학용이 아닙니다.
다음 주제: 디버깅 - R의 오류 메시지가 실제로 무엇을 뜻하는지, 그리고 트레이스백을 읽는 법입니다.
자주 묻는 질문
R에서 set.seed()는 무엇을 하나요?
R 난수 생성기의 시작점을 고정해, 뒤이어 나오는 "무작위" 추출이 실행할 때마다 동일하게 나오도록 만듭니다. 무작위성을 쓰는 스크립트라면 맨 위에서 한 번 호출하세요 - set.seed(42) - 그러면 시뮬레이션이 재현 가능해집니다: 동료도, 채점자도, 미래의 나도 같은 숫자를 봅니다.
R에서 난수는 어떻게 생성하나요?
분포를 고르세요: 정규 추출에는 rnorm(n, mean, sd), 균등분포에는 runif(n, min, max), 성공 횟수에는 rbinom(n, size, prob), 사건 개수에는 rpois(n, lambda)를 씁니다. 이미 존재하는 값에서 뽑으려면 sample(x, size)를 사용하세요.
rnorm, dnorm, pnorm, qnorm의 차이는 무엇인가요?
하나의 분포에 네 개의 접두사입니다: r은 무작위 값을 뽑고, d는 밀도 곡선의 높이를 주며, p는 누적확률 P(X ≤ x)를, q는 그 역함수인 주어진 백분위의 값을 줍니다. 같은 네 접두사가 R이 아는 모든 분포에 적용됩니다: runif/dunif/punif/qunif, rbinom/dbinom/pbinom/qbinom 등등이죠.
R에서 데이터 프레임의 행을 무작위로 표본추출하려면 어떻게 하나요?
sample()로 행을 인덱싱합니다: df[sample(nrow(df), 5), ]는 비복원으로 5개 행을 고릅니다. 복원추출(부트스트랩의 핵심 동작)을 원하면 replace = TRUE를 추가하세요.