샘플링
샘플링 (Samples)
샘플링은 데이터셋에서 임의로 부분 집합을 선택하는 데 사용돼요. 전체 데이터를 건드리지 않고도 대략적인 모습을 빠르게 파악하고 싶을 때 아주 유용하답니다. 이 문서에서 DuckDB의 샘플링 방법들을 하나씩 알아볼게요.
출처: 문서
본문
예시
reservoir 샘플링으로 tbl에서 정확히 5개의 행을 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE 5;
system 샘플링으로 테이블의 대략 10%를 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE 10%;
경고 — 기본적으로 백분율을 지정하면 각 벡터가 그 확률로 샘플에 포함돼요. 테이블에 약 1만 행보다 적은 행이 있다면, 각 행에 확률을 독립적으로 적용하는
bernoulli샘플링 옵션을 지정하는 것이 합리적이에요. 그래도 행 수의 지정한 백분율보다 많거나 적을 때가 있지만, 행이 하나도 안 나올 가능성은 훨씬 적어져요. 정확히 10%(반올림까지)의 행을 얻으려면reservoir샘플링 옵션을 써야 해요.
bernoulli 샘플링으로 테이블의 대략 10%를 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (bernoulli);
reservoir 샘플링으로 테이블의 정확히 10%(반올림까지)를 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (reservoir);
고정 시드(100)를 사용한 reservoir 샘플링으로 테이블의 정확히 50행을 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE reservoir(50 ROWS)
REPEATABLE (100);
고정 시드(377)를 사용한 system 샘플링으로 테이블의 대략 20%를 샘플링하기:
SELECT *
FROM tbl
USING SAMPLE 20% (system, 377);
tbl2와의 조인 전에 tbl의 대략 20%를 샘플링하기:
SELECT *
FROM tbl TABLESAMPLE reservoir(20%), tbl2
WHERE tbl.i = tbl2.i;
tbl2와의 조인 후에 tbl의 대략 20%를 샘플링하기:
SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);
문법
샘플은 데이터셋에서 임의로 부분 집합을 추출하도록 해줘요. 샘플링은 데이터셋을 더 빠르게 탐색하고 싶을 때 유용해요. 종종 쿼리에 대한 정확한 답보다는 데이터가 어떻게 생겼고 무엇이 들어 있는지에 대한 대략적인 단서만 있으면 충분할 때가 있으니까요. 샘플링은 쿼리 엔진을 통과해야 하는 데이터 양을 줄여, 쿼리에 대한 대략적인 답을 더 빨리 얻도록 해줍니다.
DuckDB는 세 가지 종류의 샘플링 방법을 지원해요: reservoir, bernoulli, system. 기본적으로 DuckDB는 정확한 행 수를 샘플링할 때 reservoir 샘플링을, 백분율을 지정할 때 system 샘플링을 사용해요. 각 방법은 아래에서 자세히 설명합니다.
샘플은 샘플 크기가 필요해요. 이는 전체 모집단에서 몇 개의 요소를 샘플링할지에 대한 표시입니다. 샘플은 백분율(10% 또는 10 PERCENT)이나 고정 행 수(10 또는 10 ROWS)로 줄 수 있어요. 세 샘플링 방법 모두 백분율로는 샘플링할 수 있지만, 고정 행 수를 샘플링할 수 있는 것은 reservoir 샘플링뿐이에요.
샘플은 확률적이에요. 즉, 시드를 특별히 지정하지 않는 한 실행 간에 샘플이 달라질 수 있어요. 시드를 지정하는 것은 오직 멀티스레딩이 비활성화되어 있을 때(SET threads = 1)만 샘플이 동일함을 보장해요. 여러 스레드가 샘플에서 실행되는 경우, 고정 시드가 있더라도 샘플은 반드시 일관되지는 않아요.
샘플링 방법
reservoir
Reservoir sampling은 스트림 샘플링 기법으로, 샘플 크기와 같은 크기의 *reservoir(저수지)*를 유지하고 더 많은 요소가 들어올수록 요소를 임의로 교체해 임의 샘플을 선택해요. Reservoir sampling을 사용하면 (reservoir의 크기를 선택함으로써) 결과 샘플에서 원하는 요소 수를 정확히 지정할 수 있어요. 결과적으로 system·bernoulli 샘플링과 달리 reservoir 샘플링은 항상 같은 수의 요소를 출력해요.
Reservoir 샘플링은 작은 샘플 크기에만 권장되며, 백분율과 함께 사용하는 것은 권장하지 않아요. 그 이유는 reservoir 샘플링이 전체 샘플을 구체화(materialize)한 뒤 구체화된 샘플 안의 튜플을 임의로 교체해야 하기 때문이에요. 샘플 크기가 클수록 이 과정이 초래하는 성능 저하도 커져요.
Reservoir 샘플링은 멀티프로세싱을 사용할 때도 추가 성능 저하가 있어요. unbiased 샘플링을 보장하려면 reservoir가 여러 스레드 사이에서 공유되어야 하기 때문이에요. reservoir가 아주 작을 때는 큰 문제가 아니지만, 샘플이 크면 비용이 커져요.
모범 사례 — 가능하면 큰 샘플 크기로 reservoir 샘플링을 쓰지 마세요. Reservoir 샘플링은 전체 샘플을 메모리에 구체화해야 해요.
bernoulli
Bernoulli 샘플링은 샘플링 백분율을 지정할 때만 사용할 수 있어요. 아주 직관적인데요, 밑바탕 테이블의 모든 행이 지정한 백분율과 같은 확률로 포함되기 때문이에요. 결과적으로 같은 백분율을 지정해도 bernoulli 샘플링은 다른 수의 튜플을 반환할 수 있어요. 행의 기대 개수는 테이블의 지정된 백분율과 같지만, 약간의 분산이 있을 거예요.
Bernoulli 샘플링은 완전히 독립적이라(공유 상태가 없음) 여러 스레드와 함께 사용해도 성능 저하가 없어요.
system
System 샘플링은 bernoulli 샘플링의 변형인데, 한 가지 중요한 차이가 있어요. 모든 벡터가 샘플링 백분율과 같은 확률로 포함된다는 거예요. 이는 클러스터 샘플링의 한 형태예요. System 샘플링은 튜플별 선택을 수행할 필요가 없어 bernoulli 샘플링보다 효율적이에요.
행의 기대 개수는 여전히 테이블의 지정된 백분율과 같지만, 분산은 vectorSize 배 더 커요. 그래서 system 샘플링은 약 1만 행보다 적은 데이터셋에는 적합하지 않아요. 그런 경우 50 PERCENT를 요청해도 모든 행이 걸러지거나 모든 데이터가 포함될 수 있거든요.
테이블 샘플
TABLESAMPLE과 USING SAMPLE 절은 문법과 효과가 동일하지만, 한 가지 중요한 차이가 있어요. TABLESAMPLE은 지정된 테이블에서 직접 샘플링하는 반면, USING SAMPLE 절은 전체 FROM 절이 해석된 후에 샘플링해요. 쿼리 플랜에 조인이 있을 때 이 차이가 중요해집니다.
TABLESAMPLE 절은 본질적으로 USING SAMPLE 절을 가진 서브쿼리를 만드는 것과 동등해요. 즉, 다음 두 쿼리는 동일합니다.
조인 전에 tbl의 20%를 샘플링하기:
SELECT *
FROM
tbl TABLESAMPLE reservoir(20%),
tbl2
WHERE tbl.i = tbl2.i;
조인 전에 tbl의 20%를 샘플링하기:
SELECT *
FROM
(SELECT * FROM tbl USING SAMPLE reservoir(20%)) tbl,
tbl2
WHERE tbl.i = tbl2.i;
조인 후에 20%를 샘플링하기 (즉, 조인 결과의 20% 샘플링):
SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);
더 알아보기 (Learn more)
- 내부 벡터 개념은
internals/vector문서를 참고해 주세요.