BoxPlot
Data Visualization
visx 기반 상자 수염 그림.
Usage
import
API Reference
예제
Usage
여러 집단의 중앙값·사분위·이상치 분포를 함께 비교할 때 사용한다.
import
import
import { BoxPlot } from '@mildang/design-system/Chart';예제를 복사해 쓸 때 필요한 준비
• @mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.
API Reference
BoxPlot Props
Prop
Type
Default
data
필수
TDatum[]
지정 안 함
valueKey
필수
string
지정 안 함
categoryKey
string
지정 안 함
className
string
지정 안 함
colors
string[]
지정 안 함
labels
Record<string, string>
지정 안 함
layout
"horizontal" | "vertical"
vertical
legendPosition
"bottom" | "left" | "right" | "top"
bottom
margin
Partial<ChartMargin>
지정 안 함
paints
ChartPaint[]
지정 안 함
showGrid
boolean
true
showLegend
boolean
false
showOutliers
boolean
true
showTooltip
boolean
false
showXAxis
boolean
true
showYAxis
boolean
true
statLabels
Partial<BoxPlotStatLabels>
지정 안 함
예제
세로 배치 vs 가로 배치
layout 으로 상자를 세로로 나열할지 가로로 나열할지 고른다 — 카테고리 라벨이 길면 가로가 읽기 편하다.
코드
'use client';
import { BoxPlot } from '@mildang/design-system/Chart';
import { Text } from '@mildang/design-system/Text';
import { HStack, VStack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
/**
* `layout` 으로 상자를 세로(카테고리가 x축)로 나열할지 가로(카테고리가 y축)로 나열할지
* 고른다. 카테고리 라벨이 길면 가로 배치가 겹침 없이 읽기 편하다.
*/
const data = [
{ class: '1반', score: 62 },
{ class: '1반', score: 78 },
{ class: '1반', score: 85 },
{ class: '1반', score: 91 },
{ class: '2반', score: 55 },
{ class: '2반', score: 70 },
{ class: '2반', score: 88 },
{ class: '2반', score: 95 },
];
const frame = css({ width: '100%', height: '[220px]' });
export default function BoxPlotLayoutExample() {
return (
<HStack gap="6" alignItems="flex-start" flexWrap="wrap">
<VStack gap="2" alignItems="stretch" flex="1" minWidth="[220px]">
<Text variant="caption-md" color="neutral.text.low">
vertical(기본) — 카테고리가 x축
</Text>
<div className={frame}>
<BoxPlot data={data} valueKey="score" categoryKey="class" layout="vertical" />
</div>
</VStack>
<VStack gap="2" alignItems="stretch" flex="1" minWidth="[220px]">
<Text variant="caption-md" color="neutral.text.low">
horizontal — 카테고리가 y축
</Text>
<div className={frame}>
<BoxPlot data={data} valueKey="score" categoryKey="class" layout="horizontal" />
</div>
</VStack>
</HStack>
);
}
기본
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotDefaultExample = () => <BoxPlot data={scoreData} valueKey="score" categoryKey="className" />;
export default BoxPlotDefaultExample;
Hover Tooltip
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotHoverTooltipExample = () => <BoxPlot data={scoreData} valueKey="score" categoryKey="className" showTooltip />;
export default BoxPlotHoverTooltipExample;
가로 레이아웃
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotHorizontalExample = () => <BoxPlot data={scoreData} valueKey="score" categoryKey="className" layout="horizontal" showTooltip />;
export default BoxPlotHorizontalExample;
단일 박스 (카테고리 없음)
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotSingleBoxExample = () => <BoxPlot data={scoreData} valueKey="score" labels={{ score: '전체 점수' }} showTooltip />;
export default BoxPlotSingleBoxExample;
이상치 숨김
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotWithoutOutliersExample = () => <BoxPlot data={scoreData} valueKey="score" categoryKey="className" showOutliers={false} />;
export default BoxPlotWithoutOutliersExample;
범례 표시
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotWithLegendExample = () => <BoxPlot data={scoreData} valueKey="score" categoryKey="className" showLegend />;
export default BoxPlotWithLegendExample;
빈 데이터
import { BoxPlot } from '@mildang/design-system/Chart';
const BoxPlotEmptyDataExample = () => <BoxPlot data={[]} valueKey="score" categoryKey="className" />;
export default BoxPlotEmptyDataExample;
데이터 1개
import { BoxPlot } from '@mildang/design-system/Chart';
const BoxPlotSingleDatumExample = () => <BoxPlot data={[{ className: '1반', score: 85 }]} valueKey="score" categoryKey="className" showTooltip />;
export default BoxPlotSingleDatumExample;
반응형 (너비 자동 조정)
import { BoxPlot } from '@mildang/design-system/Chart';
// 반별 수학 시험 점수 — 반마다 13명 (사분위가 정수로 떨어지는 크기)
// 2반의 32점은 Tukey fence(1.5×IQR) 밖 이상치
const classScores: Record<string, number[]> = {
'1반': [81, 62, 74, 96, 68, 84, 76, 88, 71, 90, 79, 93, 86],
'2반': [80, 32, 75, 91, 70, 83, 77, 87, 72, 89, 78, 85, 82],
'3반': [65, 58, 72, 78, 61, 70, 55, 74, 68, 80, 63, 76, 59],
'4반': [88, 79, 92, 85, 95, 82, 90, 76, 97, 84, 93, 87, 81],
};
const scoreData = Object.entries(classScores).flatMap(([className, scores]) =>
scores.map((score) => ({ className, score })),
);
const BoxPlotResponsiveExample = () => (
<div style={{ display: 'flex', flexDirection: 'column', gap: 32, width: '100%' }}>
{([280, 480, 720] as const).map((w) => (
<div key={w} style={{ width: w, height: 240 }}>
<BoxPlot data={scoreData} valueKey="score" categoryKey="className" />
</div>
))}
</div>
);
export default BoxPlotResponsiveExample;