4. 집계 연산 선택과 사용
집계는 여러 기록을 하나의 값으로 요약하는 계산입니다. 예를 들어 10, 20, 30을 합계로 계산하면 60, 평균으로 계산하면 20입니다. 피벗의 각 셀은 행과 열 조건에 해당하는 기록을 모아 선택한 방식으로 계산합니다.
AUIPivot은 17개 내장 연산을 제공합니다. 원하는 연산을
setValueFields()의 operation에 지정합니다. 직접
만든 계산 방법은 registerCustomAggregator()로 등록할 수 있습니다.
| 선택할 목적 | 연산 |
|---|---|
| 합계 | SUM |
| 개수 | COUNT |
| 중복을 제외한 개수 | DISTINCT_COUNT |
| 최솟값 | MIN |
| 최댓값 | MAX |
| 산술 평균 | AVG |
| 중앙값 | MEDIAN |
| 곱 | MULTIPLY |
| 열 합계 대비 비율 | RATIO |
| 행 합계 대비 비율 | ROW_RATIO |
| 전체 합계 대비 비율 | GRAND_TOTAL_RATIO |
| 전체 개수 대비 비율 | GRAND_TOTAL_COUNT_RATIO |
| 두 합계의 비율 | RATIO_OF_SUMS |
| 모집단 분산 | VARIANCE |
| 모집단 표준편차 | STD_DEVIATION |
| 표본 분산 | SAMPLE_VARIANCE |
| 표본 표준편차 | SAMPLE_STD_DEVIATION |
| 직접 정한 집계 방법 | registerCustomAggregator() |
4.1 연산을 지정하는 방법
dataField에는 계산할 필드 이름, operation에는 연산 이름을 지정합니다. 아래 예제의
VALUE는 설명용 필드 이름입니다. 실제 데이터의 가격, 수량, 점수 등 계산하려는 필드 이름으로 바꿔
사용하세요.
// 같은 필드의 합계와 평균을 나란히 표시합니다.
AUIPivot.setValueFields(pid, [
{dataField: "VALUE", operation: "SUM", customLabelText: "합계"},
{dataField: "VALUE", operation: "AVG", customLabelText: "평균", formatString: "#,##0.00"}
]);
현재 보고서의 설정을 바꾼 뒤에는 updatePivot()으로 다시 계산합니다. 처음 데이터를 넣을 때는 setGridData()가 계산을 시작합니다.
customLabelText는 제목, formatString은 숫자의 표시 형식입니다. 예를 들어
0.00%는 계산값 0.25를 25.00%로 표시합니다. 표시 형식을 바꿔도 실제 계산값은 바뀌지 않습니다.
같은 필드에 같은 연산을 제목만 달리하여 중복 배치할 수는 없습니다.
아래 설명은 작은 숫자로 계산 방법을 보여 줍니다. 각 절의 자동차 판매 데모에서는 같은 연산을 실제 데이터에 적용한 모습을 볼 수 있습니다.
4.2 SUM — 합계
값을 모두 더합니다.
계산 예: 10, 20, 30의 합계는 10 + 20 + 30 = 60입니다.
// VALUE 필드의 값을 모두 더합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "SUM", formatString: "#,##0"}]);
주의: 수량 필드에 SUM을 지정하면 수량의 합계를 구합니다. 기록이 몇 건인지를 세려면 COUNT를 사용합니다.
4.3 COUNT — 개수
지정한 필드에서 빈 값이 아닌 값의 개수를 셉니다. 값이 같아도 각 기록을 따로 셉니다.
계산 예: 10, 10, 30은 3개입니다. 10, null, 30은 null을 제외하여 2개입니다.
// VALUE 필드에 값이 있는 기록의 개수를 셉니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "COUNT", formatString: "#,##0"}]);
주의: null, undefined, 빈 문자열은 제외합니다. 모든 기록을 세려면 모든 기록에 값이 있는 필드를 선택하세요. 수량이 2와 3인 두 기록의 COUNT는 2, SUM은 5입니다. 숫자만 세는 연산은 아니며 자료형별 처리 차이는 입력값 처리를 참고하세요.
4.4 DISTINCT_COUNT — 중복을 제외한 개수
같은 값이 여러 번 나타나도 한 번만 셉니다.
계산 예: A, A, B, C는 A, B, C로 구분되므로 3개입니다. COUNT를 사용하면 4개입니다.
// VALUE 필드에서 서로 다른 값이 몇 개인지 셉니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "DISTINCT_COUNT", formatString: "#,##0"}]);
주의: null, undefined, 빈 문자열은 제외합니다. 전체 개수도 원본에서 중복을 다시 제거하여 계산합니다. A, B가 있는 그룹과 B, C가 있는 그룹의 전체 고유 개수는 4가 아니라 3입니다.
4.5 MIN — 최솟값
가장 작은 값을 찾습니다.
계산 예: 30, 10, 20의 최솟값은 10입니다.
// VALUE 필드에서 가장 작은 값을 찾습니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "MIN", formatString: "#,##0"}]);
주의: 원본 순서와 관계없이 숫자의 크기로 비교합니다. null이나 빈 문자열은 숫자 변환 시 0이 될 수 있으므로, 값이 없다는 뜻으로 넣은 데이터가 있다면 입력값 처리도 확인하세요.
4.6 MAX — 최댓값
가장 큰 값을 찾습니다.
계산 예: 30, 10, 20의 최댓값은 30입니다.
// VALUE 필드에서 가장 큰 값을 찾습니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "MAX", formatString: "#,##0"}]);
주의: MIN과 함께 사용하면 값의 범위를 볼 수 있습니다. 숫자 변환 방식은 MIN과 같습니다.
4.7 AVG — 산술 평균
합계를 개수로 나눕니다. 각 기록을 같은 비중으로 계산하는 일반적인 평균입니다.
계산 예: 10, 20, 30의 평균은 (10 + 20 + 30) / 3 = 20입니다.
// VALUE 필드의 합계를 개수로 나눕니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "AVG", formatString: "#,##0.00"}]);
주의: 별도 수량 필드가 있어도 자동으로 반영하지 않습니다. 수량을 반영한 평균이 필요하면 RATIO_OF_SUMS의 예를 참고하세요. AVG는 SUM과 COUNT를 사용하므로 숫자가 아닌 값이 섞이면 원하는 평균과 달라질 수 있습니다.
4.8 MEDIAN — 중앙값
값을 작은 순서로 늘어놓았을 때 가운데에 있는 값입니다. 개수가 짝수이면 가운데 두 값의 평균을 구합니다.
계산 예: 10, 20, 100의 중앙값은 20입니다. 10, 20, 30, 500의 중앙값은 (20 + 30) / 2 = 25입니다. 뒤의 예에서 산술 평균은 140이므로 큰 값 하나에 영향을 받는 정도가 다릅니다.
// VALUE 필드를 숫자 순서로 정렬했을 때의 가운데 값을 구합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "MEDIAN", formatString: "#,##0.00"}]);
주의: 소계와 총계도 해당 원본 값들을 모아서 중앙값을 구합니다. 하위 셀의 중앙값들을 평균내지 않습니다. 계산할 숫자가 없으면 빈 값입니다. 숫자를 모아 정렬하므로 SUM보다 메모리와 계산 시간이 더 필요합니다.
4.9 MULTIPLY — 곱
같은 필드의 값들을 차례로 곱합니다.
계산 예: 2, 3, 4의 곱은 2 × 3 × 4 = 24입니다.
// 각 기록에 있는 VALUE 값을 서로 곱합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "MULTIPLY", formatString: "#,##0"}]);
주의: 한 기록의 가격과 수량을 곱하는 기능과는 다릅니다. 기록마다 두 필드를 곱해 새 필드를 만들려면 setDerivedFields()를 사용합니다. 0이 포함되면 곱은 0이 되며, null이나 빈 문자열도 숫자 변환 시 0이 됩니다.
4.10 비율은 무엇으로 나누는지가 다릅니다
다음은 한 필드의 합계로 만든 표입니다. A행, X열의 값 10을 기준으로 세 연산을 비교해 보세요.
| 구분 | X열 | Y열 | 행 합계 |
|---|---|---|---|
| A행 | 10 | 20 | 30 |
| B행 | 30 | 40 | 70 |
| 열 합계 | 40 | 60 | 전체 100 |
| 연산 | 나누는 기준 | A행, X열 결과 |
|---|---|---|
| RATIO | X열의 합계 40 | 10 / 40 = 25% |
| ROW_RATIO | A행의 합계 30 | 10 / 30 ≈ 33.33% |
| GRAND_TOTAL_RATIO | 표 전체의 합계 100 | 10 / 100 = 10% |
합계 대신 개수로 전체 대비 비율을 구하려면 GRAND_TOTAL_COUNT_RATIO, 서로 다른 두 필드의 합계를 나누려면 RATIO_OF_SUMS를 사용합니다.
여기서 ‘전체’는 현재 필터 조건에 포함된 데이터 전체입니다. 필터를 적용하면 나누는 기준도 다시 계산됩니다. 여러 값 필드를 표시하더라도 다른 필드의 합계와 섞지 않습니다.
4.11 RATIO — 열 합계 대비 비율
현재 셀의 합계를 같은 열의 전체 합계로 나눕니다.
계산 예: 위 표에서 A행, X열은 10 / 40 = 25%입니다. B행, X열은 30 / 40 = 75%입니다.
// 같은 열의 합계를 기준으로 각 셀의 비율을 표시합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "RATIO", formatString: "0.00%"}]);
주의: X열은 40, Y열은 60으로 나눕니다. 열이 바뀌면 기준 합계도 달라집니다. 표 전체의 합계 100으로 나누려면 GRAND_TOTAL_RATIO를 사용합니다.
4.12 ROW_RATIO — 행 합계 대비 비율
현재 셀의 합계를 같은 행의 전체 합계로 나눕니다.
계산 예: 위 표에서 A행, X열은 10 / 30 ≈ 33.33%입니다. A행, Y열은 20 / 30 ≈ 66.67%입니다.
// 같은 행의 합계를 기준으로 각 셀의 비율을 표시합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "ROW_RATIO", formatString: "0.00%"}]);
주의: A행은 30, B행은 70으로 나눕니다. 행이 바뀌면 기준 합계도 달라집니다.
4.13 GRAND_TOTAL_RATIO — 전체 합계 대비 비율
현재 셀의 합계를 보고서 전체의 합계로 나눕니다. 전체에서 각 부분이 차지하는 비율을 구할 때 사용합니다.
계산 예: 위 표에서 A행, X열은 10 / 100 = 10%입니다. A행 전체는 30 / 100 = 30%, 총계는 100%입니다.
// 보고서 전체의 VALUE 합계를 기준으로 비율을 표시합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "GRAND_TOTAL_RATIO", formatString: "0.00%"}]);
주의: 각 셀과 소계가 같은 전체 합계로 나눕니다. 필터를 적용하면 분자와 분모 모두 필터에 포함된 데이터로 다시 계산됩니다. 분모가 0이거나 유한한 비율을 구할 수 없으면 빈 값입니다. 음수가 포함되면 비율도 음수이거나 100%를 넘을 수 있습니다.
4.14 GRAND_TOTAL_COUNT_RATIO — 전체 개수 대비 비율
현재 셀에서 센 개수를 보고서 전체에서 센 개수로 나눕니다. 개수는 같은 필드의 COUNT 방식으로 셉니다.
계산 예: 전체에서 센 값이 10개이고 그중 A그룹에 3개가 있으면 3 / 10 = 30%입니다.
// VALUE 값의 전체 개수 중 각 셀이 차지하는 비율을 표시합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "GRAND_TOTAL_COUNT_RATIO", formatString: "0.00%"}]);
주의: 값의 합계나 중복을 제외한 개수로 계산하지 않습니다. 수량 필드를 지정해도 수량 합계의 비율이 되지는 않습니다. 빈 값의 제외 기준은 COUNT와 같으며, 필터를 적용하면 전체 개수도 바뀝니다. 전체 개수가 0이면 빈 값입니다.
4.15 RATIO_OF_SUMS — 두 합계의 비율
같은 그룹 안에서 첫 번째 필드의 합계를 두 번째 필드의 합계로 나눕니다.
numeratorField는 분자 필드, denominatorField는 분모 필드입니다.
계산 예: 분자 값이 100, 900이고 분모 값이 2, 3이면 (100 + 900) / (2 + 3) = 200입니다. 각 기록을 먼저 나눈 50과 300의 평균 175와는 다릅니다.
// AMOUNT의 합계를 QUANTITY의 합계로 나눕니다.
AUIPivot.setValueFields(pid, [{
dataField: "AMOUNT",
operation: "RATIO_OF_SUMS",
numeratorField: "AMOUNT", // 분자로 사용할 필드입니다.
denominatorField: "QUANTITY", // 분모로 사용할 필드입니다.
customLabelText: "두 합계의 비율",
formatString: "#,##0.00"
}]);
주의: denominatorField는 반드시 지정합니다. numeratorField를 생략하면 dataField를 분자로 사용합니다. 필요한 필드가 없거나 분모 합계가 0이면 빈 값입니다. 소계와 총계도 각 범위의 두 합계를 구한 뒤 나누며, 하위 비율들을 평균내지 않습니다.
4.15.1 가중 평균과는 어떤 관계인가요?
이 연산 자체의 뜻은 ‘두 합계의 비율’입니다. 무엇을 합하여 나누는지에 따라 결과의 의미가 달라집니다.
예를 들어 금액이 단가 × 수량이라면, 금액 합계 / 수량 합계로 수량을 반영한 평균 단가를 구할 수 있습니다. 이것이 수량을 가중치로 사용한 가중 평균입니다. 100원짜리 1개와 300원짜리 3개라면 물건 네 개의 가격은 100, 300, 300, 300원입니다. 평균은 1,000 / 4 = 250원입니다.
자동차 판매 데이터에서는 AMOUNT 대신 TOTAL, QUANTITY 대신 COUNT를 사용하면 같은 방식으로 대당 매출을 구할 수
있습니다. 백분율로 보여 줄 값이라면 formatString을 0.00%로 지정하세요.
4.16 분산과 표준편차를 고르는 방법
둘 다 값들이 평균 주변에 얼마나 흩어져 있는지 나타냅니다. 값들이 비슷하면 작고, 차이가 크면 커집니다. 모든 값이 같으면 0입니다.
- 모집단: 알고 싶은 대상 전체를 계산합니다. VARIANCE, STD_DEVIATION을 사용합니다.
- 표본: 대상 전체 중 일부 자료로 전체의 흩어짐을 추정합니다. SAMPLE_VARIANCE, SAMPLE_STD_DEVIATION을 사용합니다.
자료가 많고 적음에 따라 구분하는 것이 아닙니다. 필터로 고른 자료 자체를 전체 분석 대상으로 삼을 수도 있고, 더 큰 대상의 일부 표본으로 볼 수도 있습니다.
아래 네 연산은 모두 2, 4, 6을 예로 사용합니다. 평균은 4이며, 평균과의 차이는 -2, 0, 2입니다. 차이를 제곱하여 더하면 4 + 0 + 4 = 8입니다.
| 연산 | 계산 | 결과 |
|---|---|---|
| VARIANCE | 8 / 3 | 약 2.67 |
| STD_DEVIATION | 8 / 3의 제곱근 | 약 1.63 |
| SAMPLE_VARIANCE | 8 / (3 - 1) | 4 |
| SAMPLE_STD_DEVIATION | 4의 제곱근 | 2 |
4.17 VARIANCE — 모집단 분산
각 값이 평균에서 떨어진 거리를 제곱하여 더한 뒤, 값의 개수로 나눕니다.
계산 예: 2, 4, 6의 평균은 4입니다. ((2 - 4)² + (4 - 4)² + (6 - 4)²) / 3 = 8 / 3 ≈ 2.67입니다.
// VALUE의 모든 값을 분석 대상 전체로 보고 분산을 구합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "VARIANCE", formatString: "#,##0.00"}]);
주의: 분산의 단위는 원래 값의 단위를 제곱한 것입니다. 원래 단위로 흩어짐을 읽고 싶다면 STD_DEVIATION을 사용합니다. 숫자 하나만 있으면 분산은 0입니다.
4.18 STD_DEVIATION — 모집단 표준편차
모집단 분산의 제곱근입니다. 값들이 흩어진 정도를 원래 값과 같은 단위로 표현합니다.
계산 예: 2, 4, 6의 분산은 8 / 3이므로 표준편차는 √(8 / 3) ≈ 1.63입니다. 점수를 계산했다면 표준편차의 단위도 점입니다.
// 모집단 분산의 제곱근을 구합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "STD_DEVIATION", formatString: "#,##0.00"}]);
주의: VARIANCE와 같은 자료를 사용하되 결과의 단위가 다릅니다. 값이 하나이거나 모두 같으면 0입니다.
4.19 SAMPLE_VARIANCE — 표본 분산
평균과의 차이를 제곱하여 더한 뒤, 개수에서 1을 뺀 값으로 나눕니다. 일부 자료로 전체의 흩어짐을 추정할 때 사용합니다.
계산 예: 2, 4, 6에서는 8 / (3 - 1) = 4입니다. 같은 자료의 모집단 분산 8 / 3보다 큽니다.
// VALUE를 더 큰 대상에서 뽑은 표본으로 보고 분산을 구합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "SAMPLE_VARIANCE", formatString: "#,##0.00"}]);
주의: 계산에 포함되는 값이 2개보다 적으면 빈 값입니다. 같은 값이 2개 이상이면 0입니다. 소계와 총계는 각 범위의 원본으로 다시 계산합니다.
4.20 SAMPLE_STD_DEVIATION — 표본 표준편차
표본 분산의 제곱근입니다. 표본으로 추정한 흩어짐을 원래 값과 같은 단위로 표현합니다.
계산 예: 2, 4, 6의 표본 분산은 4이므로 표본 표준편차는 √4 = 2입니다.
// 표본 분산의 제곱근을 구합니다.
AUIPivot.setValueFields(pid, [{dataField: "VALUE", operation: "SAMPLE_STD_DEVIATION", formatString: "#,##0.00"}]);
주의: SAMPLE_VARIANCE와 마찬가지로 값이 2개 이상 있어야 계산할 수 있습니다. 대상 전체의 표준편차를 구하려면 STD_DEVIATION을 사용합니다.
4.21 소계, 총계와 필터는 어떻게 반영되나요?
소계와 총계는 그 범위에 포함된 원본 기록을 기준으로 계산합니다. 화면에 있는 하위 셀들을 무조건 더하거나 평균내는 것이 아닙니다.
예를 들어 한 그룹의 값이 10이고 다른 그룹의 값이 20, 30이면 전체 평균은 (10 + 20 + 30) / 3 = 20입니다. 두 그룹의 평균 10과 25를 다시 평균낸 17.5가 아닙니다. 중복을 제외한 개수, 중앙값, 분산과 비율도 각 연산의 계산 방식을 따릅니다.
필터를 바꾸면 계산에 포함되는 원본이 달라집니다. 정렬이나 접기, 펼치기는 원본의 포함 여부를 바꾸지 않습니다. 총계 행이나 열을 화면에서 숨겨도 전체 비율을 계산하는 기준은 유지됩니다.
4.22 빈 값과 숫자가 아닌 값의 처리
위 숫자 예제는 모두 일반적인 숫자를 사용했습니다. 실제 데이터에 빈 값이나 문자열이 섞이면 연산에 따라 처리 방법이 다릅니다. 특히 화면의 빈 칸과 숫자 0은 다릅니다.
자료형별 처리와 계산할 수 없는 경우 자세히 보기
- SUM, MIN, MAX, MULTIPLY: JavaScript의 Number()로 변환하여 계산합니다. 숫자 문자열은 숫자로, null과 빈 문자열은 0으로 바뀝니다. 변환 결과가 NaN이면 숫자 계산에서 제외합니다. Infinity처럼 유한하지 않은 값이나 지나치게 큰 곱은 정상적인 숫자 결과를 만들지 못할 수 있습니다.
- COUNT, AVG와 분산 연산: COUNT는 null, undefined, 빈 문자열을 제외하지만 숫자가 아닌 문자열도 셀 수 있습니다. false와 일부 특수값은 값 필드를 하나 또는 여러 개 배치했을 때 개수 판정에 차이가 있습니다. AVG는 SUM / COUNT이며, 분산 연산도 이 개수 판정을 사용합니다. 예를 들어 개수에 포함되는 비숫자 문자열은 통계 누적에서 0으로 처리될 수 있으므로, 숫자 집계를 할 필드는 숫자형으로 일관되게 준비하세요.
- DISTINCT_COUNT: null, undefined, 빈 문자열을 제외하고 0과 false는 포함합니다. 숫자 1과 문자열 "1"은 서로 다른 값입니다. NaN은 하나로 세며 +0과 -0은 같습니다. 객체와 Date는 내용이 같아도 서로 다른 객체이면 다르게 셉니다. 날짜나 식별자는 일정한 문자열 형태로 준비하면 의도한 중복을 구분하기 쉽습니다.
- MEDIAN: null, undefined, 빈 문자열을 먼저 제외합니다. 나머지는 Number()로 변환하고 유한한 숫자만 사용합니다. 숫자 문자열, 참과 거짓 값, Date도 숫자로 변환될 수 있습니다.
- RATIO_OF_SUMS: 분자와 분모를 각각 숫자로 변환하여 합산합니다. 한 기록에서 한쪽 값이 숫자가 아니어도 반대쪽의 정상 값은 자기 합계에 포함됩니다. 분모가 0이거나 필요한 필드가 없거나 유한한 비율을 구할 수 없으면 빈 값입니다.
- 비율의 0 분모: GRAND_TOTAL_RATIO, GRAND_TOTAL_COUNT_RATIO는 빈 값을 반환합니다. 기존 RATIO, ROW_RATIO는 셀에서 NaN이나 Infinity가 나올 수 있어 같은 처리라고 가정하면 안 됩니다.
- 분산과 표준편차: 숫자가 매우 커도 오차를 줄이도록 누적 계산합니다. 예를 들어 10¹² + 1, 10¹² + 2, 10¹² + 3의 모집단 분산은 2 / 3, 표본 분산은 1입니다. 계산 가능한 개수가 부족하거나 유한한 결과를 구할 수 없는 경우는 빈 값입니다.
4.23 사용자 정의 집계 — 직접 정한 집계 방법
registerCustomAggregator()는 여러 기록을 어떻게 모아 하나의 결과로 계산할지 직접 작성하여 등록하는 메소드입니다. 등록할 때 정한 ID를 setValueFields()의 operation에 지정하면 내장 연산처럼 사용할 수 있습니다.
예를 들어 조건을 만족하는 수량의 비율, 중복 고객을 고려한 고객당 평균 등을 만들 수 있습니다. 기존 내장 연산으로 계산할 수 있으면 해당 연산을 사용하고, 별도 조건이나 계산 절차가 필요할 때 사용자 정의 집계를 사용하세요. setDerivedFields()는 기록마다 새 필드를 만드는 메소드이며, 여러 기록을 모아 계산하는 집계와 역할이 다릅니다.
4.23.1 네 함수로 계산 방법을 작성합니다
계산 도중 모아 둘 값들을 state 객체에 담습니다. 다음 네 함수가 필요합니다.
| 함수 | 작성할 내용 |
|---|---|
| create(context) | 합계 등을 담을 새 state 객체를 만듭니다. 계산 영역마다 별도 객체를 반환합니다. |
| accumulate(state, value, context) | 원본 기록 한 건을 읽어 state에 더합니다. value는 지정한 dataField의 값입니다. |
| merge(target, source, context) | 두 영역에서 모은 state를 합칩니다. target에 더하고 source는 변경하지 않습니다. |
| finalize(state, context) | state로 최종 숫자를 구해 반환합니다. state는 변경하지 않습니다. |
4.23.2 예: 수량을 반영한 평균 가격
100원짜리 1개와 300원짜리 3개를 계산해 보겠습니다. 물건 네 개의 가격을 풀어 쓰면 100, 300, 300, 300원입니다.
| 기록 | 가격 PRICE | 수량 COUNT | 가격 × 수량 |
|---|---|---|---|
| A | 100 | 1 | 100 |
| B | 300 | 3 | 900 |
전체 금액은 1,000원, 수량은 4개이므로 개당 평균은 250원입니다. PRICE의 두 값만 AVG로 계산하면 (100 + 300) / 2 = 200원입니다. 수량을 반영한 250원이 이 예제의 가중 평균입니다.
COUNT는 여기서 수량을 담은 필드 이름입니다. 개수를 세는 COUNT 연산과 구분하세요. 금액 필드가 이미 있다면 RATIO_OF_SUMS로 계산할 수 있습니다. 아래 코드는 가격 × 수량을 직접 누적하며 사용할 기록의 조건도 지정하는 예입니다.
// 수량을 반영한 평균 가격의 계산 방법을 등록합니다.
const weightedPrice = AUIPivot.registerCustomAggregator(pid, {
id: "WEIGHTED_PRICE", // 코드에서 사용할 고유한 연산 이름입니다.
version: 1, // 이 계산 정의의 버전이며 제품 버전과는 다릅니다.
labelText: "수량을 반영한 평균 가격",
defaultOptions: {weightField: "COUNT"}, // 수량을 읽을 필드입니다.
validateOptions(options, context) {
// 이 집계에 필요한 보조 필드를 선언합니다.
if (typeof options.weightField !== "string") return false;
context.requireField(options.weightField);
},
create(context) {
// 각 계산 영역의 금액 합계와 수량 합계를 담습니다.
return {amount: 0, units: 0};
},
accumulate(state, value, context) {
// value는 현재 기록의 PRICE이며, 같은 기록의 수량도 읽습니다.
const units = context.getValue(context.options.weightField);
// 이 예제에서는 숫자인 가격과 0보다 큰 숫자 수량만 사용합니다.
if (typeof value === "number" && Number.isFinite(value) &&
typeof units === "number" && Number.isFinite(units) && units > 0) {
state.amount += value * units;
state.units += units;
}
},
merge(target, source, context) {
// 다른 영역에서 구한 금액 합계와 수량 합계를 각각 더합니다.
target.amount += source.amount;
target.units += source.units;
},
finalize(state, context) {
// 금액 1,000을 수량 4로 나누면 250입니다. 수량이 없으면 빈 값입니다.
return state.units > 0 ? state.amount / state.units : null;
}
});
// 등록한 연산을 일반 평균과 나란히 배치합니다.
AUIPivot.setValueFields(pid, [
{dataField: "PRICE", operation: "AVG", customLabelText: "일반 평균"},
{dataField: "PRICE", operation: weightedPrice, formatString: "#,##0.00"}
]);
// 계산을 눈으로 확인하기 위한 작은 고정 예제입니다.
AUIPivot.setGridData(pid, [
{PRICE: 100, COUNT: 1},
{PRICE: 300, COUNT: 3}
]);
// 결과: 일반 평균 200, 수량을 반영한 평균 가격 250.00
첫 기록을 읽으면 state는 {amount: 100, units: 1}, 두 번째 기록까지 읽으면
{amount: 1000, units: 4}가 됩니다. finalize()가 1000 / 4를 계산합니다. 소계와 총계에서도
평균들을 다시 평균내지 않고, 금액과 수량을 각각 합친 뒤 나눕니다.
등록 옵션과 보고서 저장, 복원 자세히 보기
-
id는 내장 연산이나 같은 피벗에 등록한 이름과 겹칠 수 없습니다. 메소드는 이 연산 ID를 반환합니다.labelText는 필드 패널의 연산 목록에 표시할 이름입니다. -
defaultOptions는 기본 설정입니다. 값 필드에aggregatorOptions를 지정하면 그 객체를 사용하며 기본 객체와 합치지 않습니다. weightField는 이 예제에서 직접 정한 옵션 이름입니다. -
validateOptions(options, context)는 선택 함수입니다. 잘못된 옵션은 false를 반환하거나 오류를 발생시켜 거부합니다.context.requireField()는 계산에 필요한 보조 필드를 선언합니다. 이 context에는 dataField, operation, requireField가 제공되며 등록 시점에는 dataField가 빈 문자열일 수 있습니다. -
네 계산 함수의 context에는 dataField, operation, options, getValue가 제공됩니다.
getValue()는 accumulate 안에서만 사용할 수 있으며 원본 필드와 등록된 파생 필드를 읽습니다. 함수들은 동기 함수로 작성하고 누적, 병합 순서가 달라도 같은 계산 의미가 되게 합니다. - getCustomAggregators()는 ID, 버전, 표시 이름을 조회합니다. 함수나 계산 중 state를 백업하지 않습니다.
- 보고서에는 집계 ID, 버전과 값 필드의 옵션이 저장됩니다. 다른 피벗에서 복원할 때는 먼저 같은 ID와 버전의 계산 정의를 등록하고 setReportDefinition()을 호출합니다.
- 콜백 오류에는 집계 ID와 발생 단계가 포함됩니다. 실패한 계산 이전의 화면 전체를 자동 복원하는 기능은 아닙니다.
registerCustomAggregator() 상세, 판매 대수를 반영한 평균 가격 데모, 중복 고객을 고려한 고객당 평균 매출 데모
집계 후 자유 수식 (2.8.0)
수식 결과는 숫자 정렬과 기존 useHeatmap에 사용할 수 있습니다. operation으로 대상을 지정하는 setVisualAnalytics의 규칙에는 수식 값을 직접 지정할 수 없습니다.
괄호와 사칙연산을 직접 입력해 필드 합계로 계산할 수 있습니다.
enableCalculatedFieldEditor: true를 설정하고 필드 패널의 계산 필드에서 집계 후 수식을
선택하세요. 수식은 각 피벗 영역에서 필드 값을 합산한 후 계산합니다. 부분합과 총합계도 동일한 방식으로
계산합니다.
예를 들어 매출액/원가가 100/90, 900/450인 두 기록의 이익률은 각각 10%, 50%입니다. 전체 이익률은 두 값을
평균한 30%가 아닌 (1000 - 540) / 1000 * 100 = 46입니다. 원본 행마다 가격과 수량을 곱한 뒤
합산하려면 기존 setDerivedFields를 사용합니다.
// 중간 수식 이익은 숨기고 이익률만 표시합니다.
AUIPivot.setFormulaFields(myPivotID, [
{ dataField: "Profit", labelText: "이익", expression: "[TOTAL] - [COST]" },
{ dataField: "Rate", labelText: "이익률", expression: "[Profit] / [TOTAL] * 100" }
]);
AUIPivot.setValueFields(myPivotID, [
{ dataField: "Rate", calculation: { kind: "formula" }, formatString: "0.00" }
]);
AUIPivot.updatePivot(myPivotID);
수식 정의와 값 영역의 표시 여부는 별개입니다. 값에서 이익을 제거해도 이익률은 이익 정의를 참조할 수 있습니다. 정의 전체를 변경하려면 setFormulaFields, 조회하려면 getFormulaFields를 사용합니다. 다른 수식이나 값 영역이 참조하는 정의를 삭제하면 오류입니다.
| 입력 | 의미 |
|---|---|
(매출액 - 원가) / 매출액 * 100 |
현재 별칭이 고유하면 직접 입력할 수 있습니다. |
[TOTAL] * 15% |
필드 ID의 합계에 0.15를 곱합니다. |
'매출 금액' - [COST] |
공백이 있는 별칭은 작은따옴표로 감쌉니다. 작은따옴표는 두 번, ID의 ]는 ]]로 입력합니다. |
=-[COST] + [TOTAL] |
앞의 =와 단항 부호를 사용할 수 있습니다. |
곱셈과 나눗셈은 덧셈과 뺄셈보다 먼저 계산하며 같은 우선순위는 왼쪽부터 계산합니다. SUM(), IF(), 셀 주소, 범위 참조와 JavaScript 코드는 지원하지 않습니다. 필드 참조 자체가 해당 영역의 합계를 뜻합니다. 저장할 때는 별칭을 명시적인 ID 참조로 정규화합니다.
미리보기는 현재 필터와 필드 패널의 업데이트 대기 설정을 반영한 전체 총합계입니다. 원시값과 서식 적용값을 따로 보여 줍니다. 미리보기 도중 원본이나 초안이 바뀌면 결과를 폐기합니다. 이름, 수식과 표시 여부를 수정한 뒤 적용하거나 취소할 수 있습니다.
없는 필드, 중복 이름, 순환 참조와 잘못된 구문은 적용 전에 오류로 알립니다. 원본이 없는 영역, 0 나누기와 비유한 결과는 빈 값으로 표시하며 오류를 0으로 바꾸지 않습니다. 하위 영역이 0 나누기여도 상위 영역의 분모 합계가 유효하면 정상 계산합니다. 숫자 입력은 기존 SUM처럼 Number 변환을 사용하고 NaN은 누적에서 제외합니다.
수식의 필드 합계는 큰 값과 작은 값이 섞일 때의 오차를 줄이는 보정 합산을 사용합니다. 기존 SUM과 합산 방식이 달라 작은 차이가 생길 수 있습니다. 예를 들어 1e12, 0.01, -1e12의 합계는 수식에서 0.01이지만 기존 SUM에는 소수 오차가 남을 수 있습니다. 이는 임의 정밀도 십진 계산을 보장하지 않으며, 표시 자릿수는 숫자 서식으로 지정합니다. 유한한 작은 분모를 임의로 0으로 취급하지 않습니다.
수식 하나는 4096자, 구문 노드 256개, 깊이 32, 직접 참조 64개 이내입니다. 정의는 최대 64개, 의존 깊이는 32, 활성 계획의 전체 명령은 16384개 이내입니다. 수식 ID는 원본, 파생 필드 또는 다른 수식과 중복할 수 없습니다. 수식은 값 영역 전용입니다.
수식 정의가 있는 보고서는 schemaVersion: 4로 저장되며 숨긴 중간 정의도 포함합니다. 원본 데이터가 오기 전에 복원할 수 있고 첫 데이터 입력에서 필드 존재 여부를 검증합니다. 수식이 없으면 기존 schemaVersion: 3을 반환하고 1, 2, 3 입력도 계속 지원합니다. 수식 정의/보고서 검증 오류와 pivotBegin 취소 시 기존 설정을 유지합니다. 기존 사용자 집계 이름 FORMULA와 충돌하지 않습니다.
* 100으로 백분율 수치를 계산했다면 0.00 등 일반 숫자 서식을 사용하세요.
0.00%는 숫자에 다시 100을 곱해 표시하므로 수식에서 * 100을 생략합니다.
계산 중 화면 응답
원본 데이터, 수식이 참조하는 필드와 행, 열의 조합이 많으면 계산 중 클릭과 키 입력 처리가 잠시 지연될 수 있습니다. 실제 사용할 데이터와 기기에서 최초 입력, 필터 적용, 수식 변경의 응답을 확인하세요.
showAutoProcessLoader를 true로 설정하면 계산 전에 진행 표시를 볼 수 있습니다. 진행 표시는 계산
시간을 줄이거나 계산 중 입력 처리를 보장하지 않습니다. 이 설정에서는 계산을 예약하므로 호출 직후 결과가
준비됐다고 가정하지 말고 pivotComplete에서 완료를
확인하세요.