본문으로 이동

차트

요약

차트는 thinkflow 원본의 chart 이름 "제목" 종류 { ... }로 숫자를 그리는 기능이다. 같은 선언을 독립 카드나 일반 카드 본문에 둘 수 있고, 독립 차트는 plot 보기로도 놓는다. 축·범례·표식·프레임·갱신 효과는 자리와 관계없이 같은 부품이다. 갱신 효과의 기하·계열 효과색·원래 표식 두께는 ThinkFlow가 전달하고 면·선·글자 후광의 표현은 디자인 정본의 fl-mark-pulse 역할이 소유한다. 가로 성장 방향은 .grow의 기본 왼쪽 시작과 .from-end의 오른쪽 시작을 사용하며, 워터폴은 시작·끝 좌표의 비교 결과만 전달한다. 밝은 계열의 점 윤곽은 --chart-boundary로 전달하며 공통 스타일을 인라인 획으로 덮어쓰지 않는다. 종류 목록은 문법 표(src/source/grammar.js)가 정본이고, 구조 그림과 같은 장면과 박자로 계열을 차례로 드러낸다. 값은 원본에 적거나 실험 결과 JSON에서 읽고, 구조 그림의 값(value)에 묶어 장면 안에서 바꿀 수도 있다.

동기

문서 스킬은 구조 그림을 D2로, 실험 차트를 Vega-Lite로 그린다. 두 도구는 글꼴, 색, 여백, 움직임이 달라서 한 문서 안의 그림이 두 모양이 된다. 차트도 같은 문법, 같은 토큰, 같은 재생기로 그리면 문서 전체가 한 모양이 되고, 구조 그림처럼 “기준선을 보인 뒤 우리 값이 자라는” 순서를 보일 수 있다.

예시

기준선에서 개선 값으로 자라는 덤벨

Plain text
thinkflow
chart tokens "작업당 토큰" dumbbell "예시 데이터. 에이전트마다 작업 61개 평균" {
  x "토큰(로그 눈금)"
  scale log
  series base "기억 없음" role=compare
  series ours "기억 붙임" role=main
  row "코딩 에이전트 A" base=120000 ours=31000
  row "코딩 에이전트 B" base=95000 ours=28000
}

scene "기억 없음"
  reveal tokens.base

scene "기억 붙임"
  reveal tokens.ours
  light tokens "코딩 에이전트 A"
  1. 첫 장면은 base 계열을 드러내고, reveal에 나오지 않은 ours 계열은 처음부터 보인다. 둘째 장면은 반대로 base가 처음부터 보이고 ours 계열을 드러낸다.
  2. 둘째 장면에서 ours의 화살표와 점, 값 글자가 나타나고, 오른쪽에 -74%처럼 바뀐 비율이 나타난다.
  3. light 박자에 첫 행이 밝아지고 둘째 행이 흐려진다.

행마다 다른 기준을 그 행에만 두는 막대

Plain text
thinkflow
chart rates "가설별 비율" bar "예시 데이터. 점선은 가설별 채택 기준" {
  x "비율(%)"
  series rate "측정 비율"
  row "H1" rate=91.6 rate.low=84.8 rate.high=95.5 rule=80
  row "H3" rate=93.8 rate.low=83.2 rate.high=97.9 rule=90
}
  1. 행 줄의 rule=값이 그 행의 기준이다. 점선은 그 행 막대 묶음에만 걸치고 위에 숫자(80, 90)가 붙는다.
  2. 다른 행에는 그 기준이 그려지지 않아, H1 행에 90의 선이 걸쳐 보이지 않는다.
  3. 모든 행에 공통인 기준은 rule 값 "라벨"을 쓴다. 둘은 함께 쓸 수 있다.
  4. 보기를 적지 않고 차트 카드 하나만 있으면 그 카드의 plot 보기가 기본으로 생긴다.

음수일 수 있는 차이와 신뢰구간

Plain text
thinkflow
chart gap "판단기 정확도 차이" difference "예시 데이터. 점은 새 판단기 − 기존 판단기, 선은 95% 신뢰구간, 점선은 허용 하한 -5%p" {
  x "정확도 차이(%p)"
  decimals 1
  series gap "정확도 차이"
  rule -5 "허용 하한"
  row "단일 세션 사실" gap=3.2 gap.low=-0.4 gap.high=6.8
  row "지식 갱신" gap=-6.4 gap.low=-9.8 gap.high=-3.0
  row "시간 추론" gap=0 gap.low=0 gap.high=0
}
  1. 행마다 차이 값에 점 하나가 놓이고, 신뢰구간이 점을 지나는 가로선으로 그려진다.
  2. 0 자리에 세로선이 서서 점이 0보다 오른쪽(+)인지 왼쪽(-)인지 한눈에 읽힌다. 값 글자는 부호를 붙인다(+3.2, -6.4, 0.0).
  3. 음수 기준선(rule -5)과 모든 값이 0인 행도 오류 없이 그린다.

값 축을 자른 선 차트

Plain text
thinkflow
chart mean "급변 전후의 평균 기준값" line "예시 데이터. 점선은 목표" {
  x "판단 수(건)"
  y "평균 기준값(비율)"
  zero off
  series mean "평균 기준값"
  rule 0.81 "목표"
  point x=1000 mean=0.76
  point x=2000 mean=0.758
  point x=3000 mean=0.812
  point x=4000 mean=0.815
}
  1. zero off로 세로 값 축이 0이 아니라 값 범위(0.74~0.82)에 맞게 시작한다. 0.76에서 0.81로 오르는 변화가 축의 5%가 아니라 화면 높이의 절반으로 읽힌다.
  2. 0에서 시작하지 않은 축은 맨 아래 격자선이 지그재그로 시작해 잘린 축임이 보인다.
  3. zero off는 선과 계단 차트에서만 쓴다. 길이로 값을 보이는 차트는 0에서 시작한다.

실험 결과에서 읽는 막대

Plain text
thinkflow
chart miss "조건별 오분류율" bar "n=500. 점선은 채택 기준 5%" {
  x "오분류율(%)"
  data "../summary.json" at "/misclassification"
  series ours "새 판단기" key="new_judge"
  rule 5 "채택 기준"
}
  1. summary.json의 /misclassification 배열이 행이 된다. 원소 키 new_judge가 계열 ours의 값이다.
  2. 행마다 막대와 신뢰구간 막대기(끝 캡이 없는 선), 5% 점선이 그려진다.

계열을 N개 겹쳐 보는 선과 기대값

Plain text
thinkflow
chart latency "리전별 지연" line "예시 데이터" {
  x "시각(시)"
  y "p95 지연(ms)"
  series seoul "서울" role=main
  series tokyo "도쿄" role=compare
  series sg "싱가포르"
  series goal "목표" role=reference
  point x=10 seoul=118 tokyo=142 sg=171 goal=150
  point x=11 seoul=126 tokyo=- sg=180 goal=150
  point x=12 seoul=171 tokyo=161 sg=204 goal=150
}
  1. 계열은 하나 이상 몇 개든 받는다. main과 compare는 각각 하나 이하이고, 셋째 계열부터는 역할이 없다.
  2. goal은 role=reference인 기대값 계열이다. 회색 점선으로 실제값과 구분하고 꼭짓점 표식은 생략한다. 선·계단·면적·누적분포(ECDF)에 같은 규칙을 적용한다.
  3. 도쿄 11시의 -는 측정이 없다는 뜻이다. 선이 그 점에서 끊기고 0으로 그리지 않는다.

상세 설계

누적 막대와 퍼센트 누적

chart stacked는 계열을 보이는 순서대로 구성값을 쌓는다. 계열은 하나 이상 몇 개든 받고 이름, 색, 범례는 공통 역할 토큰과 범주 팔레트를 쓴다. 각 구간의 시작은 앞 구성값의 합이고 길이는 해당 값이다. 0 값에는 최소 폭을 더하지 않는다. 양수는 0 오른쪽으로, 음수는 0 왼쪽으로 따로 쌓고 0선을 긋는다. 조각 값은 조각 안에 들어갈 때만 적고(면 위 글자색 paint.on), 행 순합은 막대 양수 끝 바로 옆에 굵게 적는다. 막대 아래 값 목록이나 번호 키는 없다. 막대는 테두리 없는 평평한 면이고 두께는 spacing.figure.chart.bar의 두 배라 조각 안 11px 글자가 들어간다.

chart percent는 행마다 계열 합을 100%로 놓은 몫을 쌓는다. 계열은 둘 이상이고, 조각 안에 몫(42.1%)을 적고 막대 끝 옆에 행 건수 합(합계 71)을 적는다. 반올림한 퍼센트의 합을 100으로 맞추지 않는다. 음수, 100을 넘거나 0 아래인 rule, 로그 눈금은 오류다.

두 종류 모두 -(빠진 값)를 받는다. 빠진 값이 있는 행은 막대를 그리지 않고 안내 글을 쓴다. 합이 0인 퍼센트 행은 막대 없이 합계 0 · 비율 정의 불가를 쓰고 0%로 그리지 않는다. 모든 행이 0이거나 모두 빠진 것은 오류가 아니다(값 축의 빠진 값과 0). 행의 모든 계열을 적어야 하고(없으면 -), 양수 합과 음수 합 각각이 1e15 미만이어야 한다. reference 계열은 쌓을 수 없어 오류다. 공통 rule은 합계 축에 놓고, 행별 rule=은 지원하지 않는다. data의 각 원소는 label과 선언한 계열 키를 모두 가진다.

재생에서는 앞 구간을 먼저 reveal한다. 앞 구간이 없는 상태에서 뒤 구간만 나타나는 순서는 오류다. 합계 글자는 마지막 계열과 함께 나타난다.

작은 몫 묶기. 누적·퍼센트의 계열과 원·도넛의 항목은 같은 규칙(chart/other.js)으로 작은 것을 기타 하나로 읽게 한다. 전체에서 차지하는 몫이 5% 미만인 항목과, 항목 수가 1판 팔레트 색 수(7)를 넘을 때 작은 쪽부터의 꼬리가 후보이고, 후보가 둘 이상일 때만 묶는다(하나만 묶으면 이름만 잃는다). 계열 크기는 퍼센트면 행마다 몫의 합, 누적이면 절댓값의 합이다. 묶인 항목도 자기 표식과 행·계열 묶음(cr-k, cs-i)을 그대로 가져 reveal, light, 프레임 구조가 바뀌지 않는다. 바뀌는 것은 칠(데이터 중립 역할 color.data.reference)과 이름 글뿐이고, 묶인 항목은 막대 끝 쪽(원은 마지막 각도)에 이어 놓여 기타가 한 덩어리다. 남은 항목은 순서대로 범주 색을 앞에서부터 받아 무늬 층으로 넘어가지 않는다. 범례에는 기타 칸 하나가 맨 뒤에 놓인다. 값이 0인 항목은 이름을 적지 않는다. 값에 묶인 차트는 묶지 않는다. 프레임마다 묶음이 달라지면 범례와 이름 글의 구조가 흔들리기 때문이다. 장면 직접 선택, light, 정적 SVG와 움직이는 SVG는 다른 차트의 재생 경로를 쓴다.

종류와 행 줄

종류 보여 줄 것 계열 수 행 줄
bar 조건별 값과 신뢰구간 1 이상 row "항목" 계열=값, 계열마다. 신뢰구간은 계열.low=값 계열.high=값. 행마다 다른 기준은 rule=값
stacked 구성값과 합계. 음수는 0 왼쪽에 쌓는다 1 이상 row "항목" 계열=값, 모든 구성값 필수
percent 행마다 합을 100%로 놓은 몫 2 이상 row "항목" 계열=값, 모든 구성값 필수
dumbbell 같은 입력에서 두 방식 비교 정확히 2 row "항목" 계열=값 계열=값. 신뢰구간은 막대와 같다
difference 음수일 수 있는 차이와 신뢰구간 정확히 1 row "항목" 계열=값. 신뢰구간은 막대와 같다
line 순서나 시간에 따른 변화 1 이상 point x=값 계열=값. 신뢰구간은 계열.low=값 계열.high=값
step 값이 바뀔 때만 움직이는 계단 변화 1 이상 point x=값 계열=값. 신뢰구간은 받지 않는다
area 0 기준의 양과 시간에 따른 변화. 계열은 겹쳐 그린다 1 이상 point x=값 계열=값. 서로 다른 x가 둘 이상 필요
scatter 두 변수의 관계 0 이상 point "이름" x=값 y=값, 계열이 있으면 series=계열도. link "이름" -> "이름"
histogram 관측값의 구간별 빈도 0 sample 값(빠진 표본은 -), bins 최솟값 최댓값 구간수 또는 bins auto
box 연속값 분포 0 row "항목" min=값 q1=값 median=값 q3=값 max=값(빠진 값은 -)
ecdf 표본의 누적분포 0 이상 sample 값 [series=계열]. 빠진 표본은 -
heatmap 판정 교차표 0 cell "행" "열" 값
pie, donut 전체에서 차지하는 몫 0 row "항목" value=값
waterfall 증감에서 누계로 이어지는 변화 0 row "항목" value=값(빠진 증감은 -), total "합계 이름"
  • 계열 수가 표를 벗어나면 오류다. 종류 목록과 계열 수 범위는 문법 표(src/source/grammar.js)가 정본이고, 한 종류의 입력 모양을 바꾸려면 그 표부터 고친다.
  • 막대, 덤벨, 상자, 산점도, 선, 히트맵은 문서 스킬의 Vega-Lite 차트 표와 일대일이다. 나머지 종류는 이 도구가 더한 것이다. difference는 같은 차이와 신뢰구간을 막대로 그리면 0 시작 규칙과 맞지 않아 따로 둔 종류다(아래 대안).
  • 산점도에 계열을 선언하면 모든 점에 series=가 필요하다.
  • 신뢰구간은 막대, 덤벨, 선, 차이 차트가 받는다. 산점도는 받지 않는다. 점 하나의 두 값(비용과 수정 횟수)과 link 화살표가 이미 있어, 오차 막대가 겹치면 어느 점의 구간인지 헷갈리기 때문이다. 값마다 low와 high를 함께 적고(계열.low, 계열.high), low ≤ 값 ≤ high다. 같은 값은 허용한다. 반올림한 실험 값에서 끝이 값과 같아질 수 있기 때문이다. 빠진 값에는 신뢰구간을 적을 수 없다. 둘 다 적거나 둘 다 생략하는 규칙은 네 종류가 같고, 행마다 따로 정한다. 선 차트의 띠는 신뢰구간이 연속으로 있는 점 사이에만 그려진다. 신뢰구간이 없는 점에서 띠가 끊기므로, 선 끝까지 띠가 있으려면 모든 점에 신뢰구간을 적는다.
  • 상자 그림 값은 min ≤ q1 ≤ median ≤ q3 ≤ max다. 어기면 수염과 상자가 거꾸로 그려지므로 오류다. 순서는 적힌 값끼리만 견준다.
  • 산점도의 link는 앞 점에서 뒤 점으로 화살표를 긋는다. 같은 대상의 이전과 이후를 보이기 위해서다. 두 점이 모두 보일 때 보인다. 없는 점 이름을 가리키거나, 한 점을 자기 자신과 잇거나, 같은 link를 두 번 적으면 오류다.
  • 항목, 이름, 행과 열의 짝, 선 차트의 x 값은 차트 안에서 하나다. 겹치면 오류다. light가 가리킬 대상이 하나여야 하기 때문이다. 표본(히스토그램, 누적분포)은 같은 값이 여러 번 나올 수 있다.
  • -(빠진 값)는 bar, stacked, percent, line, step, ecdf, histogram, waterfall, box의 값 자리에만 쓴다. 빠진 값은 0이 아니다. 막대는 그리지 않고 missing 문구를 쓰며, 선과 계단은 그 점에서 끊기고, 누적과 퍼센트는 그 행을 정하지 않는다. 히스토그램, 워터폴, 상자의 규칙은 아래 항목이다. 그 밖의 자리에서는 오류다.
  • 히스토그램의 sample -는 관측이 아니다. 관측만 구간에 세고 비율과 밀도의 분모도 관측 수라서, 빠진 표본이 분모에 들어가지 않는다. 빠진 수는 부제 아래에 결측 k개 제외로 알린다. 관측이 하나도 없으면 막대 없이 값 없음을 쓰고, 명시한 구간(bins 최솟값 최댓값 구간수)은 가로축만 정한다.
  • 워터폴의 value=-는 증감을 모른다는 뜻이다. 그 행은 막대 없이 값 없음을 쓰고, 그 뒤의 누계는 끝까지 알 수 없다. 누계를 모르는 뒤 증감은 막대와 점선 연결 없이 증감 글자(+5)만 쓰고, 알 수 없는 합계는 값 없음을 쓴다. 가로 값 축은 알려진 끝점만 덮는다.
  • 상자의 다섯 값이 모두 있는 행만 상자 모양을 그린다. 일부가 빠진 행은 모양 없이 − · 2 · 3 · 4 · −처럼 순서대로 적고, 빠진 값을 0이나 이웃 값으로 채우지 않는다. 접근성 이름에도 행 이름과 이 값이 붙는다.
  • 막대와 상자, 덤벨은 가로로 그린다. 항목 이름이 길어도 줄이지 않기 위해서다. 넓은 배치의 이름 칸은 가장 긴 이름에 맞춰 토큰 spacing.figure.chart.label에서 spacing.figure.chart.label-max까지 넓어지고, 이름 글자는 label-max에서 이름 앞 간격(spacing.3)을 뺀 폭 안에 들어가야 한다. 그보다 긴 이름은 그림 검사 1번 오류다. 좁은 배치(272)는 이름을 행 위에 놓고 줄을 바꾼다.

머리와 선언 줄

줄 부분 뜻
chart 이름 "제목" 종류 ["부제"] { 카드 줄 차트 카드의 이름, 제목, 종류, 부제. 부제는 표본 수와 기준선 설명(문서 스킬 규칙)이다. 문서 머리의 title, subtitle은 그림 전체의 것이다
x "축 제목", y "축 제목" 머리 축 제목. 단위는 괄호에 넣는다
scale linear|log 머리 값 축 눈금. 기본 linear
zero on|off 머리 선과 계단 차트 세로 값 축이 0에서 시작하는지. 기본 on(0과 가장 작은 값 가운데 작은 쪽에서 시작)이고, off면 값 범위(기준선 포함)에서 시작한다. 선과 계단 차트만 쓸 수 있고 다른 차트는 오류다
decimals 0~6 머리 값 글자(막대, 덤벨, 상자, 차이, 히트맵, 워터폴)의 소수 자릿수. 기본은 같은 목록에 적힌 가장 긴 소수 자릿수다. 퍼센트 누적, 원, 도넛이 계산해 적는 비율에도 쓰고, 적지 않으면 비율은 소수 한 자리다
series id "이름" [role=main|compare|reference] [key="JSON 키"] 선언 계열과 역할. main과 compare는 각각 하나 이하이고 reference는 계획이나 목표처럼 x마다 값이 있는 기대값 계열이다. 계열이 하나면 main이라 role을 생략할 수 있고(role=compare나 reference는 오류), 둘이면 role을 생략한 계열이 선언 순서대로 main, compare를 받는다(덤벨은 시작점이 compare라 첫 계열 compare). 셋 이상이면 생략한 계열은 역할이 없다. 모두 reference이면 견줄 실제 값이 없어 오류이고, stacked, percent, dumbbell에서 reference는 오류다. 실제 계열의 색은 범주 번호가 정하고 목표 계열은 공통 회색으로 표시한다(계열과 팔레트). 범례, 막대, 점 이름 칸은 main이 먼저다
rule 값 "라벨" 선언 모든 행에 걸치는 기준선. 여러 줄 가능. 보조 글자 색 점선. 행마다 다른 기준은 막대 행 줄의 rule=값이다
missing "글" 선언 막대 값이 빠진 자리 글. 기본 비교 없음. -를 받는 종류만 쓸 수 있다
data "경로" [at "/포인터"] 선언 JSON 파일의 배열에서 행을 읽는다. at을 빼면 문서 전체가 배열이다
행 줄 선언 종류와 행 줄 표
  • 카드 줄의 종류가 없거나 모르는 종류이면 그 줄의 구문 오류다. 메시지가 지원하는 종류와 적은 낱말을 알린다.
  • 자라는 시간은 문서 머리의 pace(차트 카드에서는 계열이 자라는 시간)가 정한다. 기본은 토큰 duration.reveal이다.
  • 중복은 원본에 실제로 적힌 키만 센다. 계열 이름이나 선언 낱말이 constructor처럼 JavaScript 객체가 물려받는 속성 이름과 같아도 한 번만 적었으면 오류가 아니다.
  • 축 제목 x, y는 아래 값 축 표의 가로축, 세로축 제목이다.
  • 값 축(아래 표의 값 축 칸)의 제목이 없거나 괄호 단위 (…)가 없으면 경고다. 막대, 덤벨, 상자는 x, 선은 y, 산점도는 x와 y가 대상이고 히트맵은 값 축이 없어 대상이 아니다. 단위 없는 숫자는 읽는 사람이 값을 해석할 수 없기 때문이다. 메시지는 the value axis title needs a unit in parentheses, such as x "latency(ms)"이고, 경고라 --strict에서 실패한다.
종류 가로축 세로축 값 축(scale, rule, 0 시작이 걸리는 축) 음수
bar, dumbbell, box 값 항목 가로축 오류
stacked 합계 항목 가로축 쓸 수 있다. 0 왼쪽에 쌓는다
percent 0~100 항목 가로축 오류
difference 값 항목 가로축 쓸 수 있다. 0이 가운데다
line, step, area x 값 계열 값 세로축 쓸 수 있다
scatter x 값 y 값 두 축 모두 쓸 수 있다
heatmap 열 행 없음. scale, rule은 오류 오류
  • 막대, 누적, 퍼센트에서 scale log와 막대의 음수 기준선(행의 rule=도)은 오류다. 문서 스킬이 막대 축을 0에서 시작하게 하기 때문이다. 음수 차이는 chart difference로 쓴다.
  • 차이, 면, 누적분포 차트에서 scale log는 오류다. 0이 기준이거나 0까지 채우거나 세로축이 0~1이라 로그 눈금에 놓을 수 없기 때문이다.
  • zero off인 선과 계단 차트의 값 축은 값 범위에서 시작해 0을 포함하지 않아도 된다. 0 시작은 막대, 덤벨, 상자가 길이로 값을 보이기 때문에 필요하고, 선과 계단은 위치로 보여서 끌 수 있다. 그 밖의 종류에 쓰면 오류다. 생략하면 0 시작이다.
  • scale linear의 값 축은 0과 가장 작은 값 가운데 작은 쪽에서 시작한다. scale log의 값 축은 가장 작은 값 이하의 10의 거듭제곱에서 시작한다. scale log에서 값 축의 0 이하 값과 0 이하 기준선은 오류다.
  • 빠진 값과 0. 값이 모두 0이어도 모두 빠져도 오류가 아니다. 0은 실제 값이다. 막대는 길이 0의 표식과 값 글자 0을 0 자리에 그리고, 덤벨, 상자, 누적도 값 0의 자리에 그리며 눈금은 0과 1이다. 선 차트와 산점도도 눈금이 0에서 1이다. 차이 차트는 눈금이 −1에서 1이라 0이 가운데에 선다.
  • 값이 하나도 없는(-, null, 키 없음) 차트는 틀과 축만 대체 범위로 그린다(선형 0에서 1, 로그 1에서 10). 이 범위는 축만 정하고 어떤 표식에도 값 0을 주지 않는다. 막대 행은 막대 없이 missing 문구를 쓰고, 선·계단·누적분포는 그림 영역에 값 없음을 쓴다.
  • 비율(퍼센트, 원, 도넛)은 합이 0이면 비율이 정의되지 않는다. 퍼센트는 그 행에, 원과 도넛은 가운데에 합계 0 · 비율 정의 불가를 쓰고 0%로 쓰지 않는다. 퍼센트에서 값이 빠진 행도 비율을 정하지 않는다. 음수는 계속 오류다.
  • 값에 묶인 차트는 모든 프레임의 값을 합친 한 범위를 쓰고, 첫 프레임이 모두 0이어도 오류가 아니다.
  • 선언한 계열마다 행 줄에 값이 하나씩 있어야 한다. 값이 없으면 빠진 값 -를 적는다. 빠진 값 -는 종류와 행 줄의 종류만 받고, 그 밖의 자리에서는 오류다.

값 출처

제목과 부제는 표시 글이며 언어나 접두사로 자료의 허용 여부를 바꾸지 않는다. inline 행과 외부 JSON은 같은 값 규칙을 적용한다. 측정 자료의 출처나 신뢰구간을 반드시 요구하는 문서 작성 정책은 호출하는 쪽에서 판단한다.

  • data와 row, point, cell, sample을 함께 쓰면 오류다. 값의 출처가 하나여야 하기 때문이다. link는 data와 함께 쓸 수 있다.
  • data 경로는 원본 파일 기준 상대 경로이고, at은 JSON Pointer(RFC 6901)다. 포인터는 /로 시작하고, "/"는 빈 이름 키를 가리킨다. 가리킨 값은 배열이어야 한다.
  • 계열 값은 원소의 키 key=에서 읽고, key=가 없으면 계열 이름과 같은 키에서 읽는다. 신뢰구간은 {키}.low, {키}.high 키다. 계열 키가 그 종류의 고정 원소 키(label, name, x, y, series, row, col, value)와 같으면 오류다.
  • 막대 행의 rule(숫자, 0 이상)은 행 기준이다. 계열의 이름이나 키가 rule이면 계열 값으로 읽히므로 행 기준을 쓸 수 없다.
  • 산점도 원소의 series 값은 계열의 키(key=, 없으면 계열 이름)와 맞춘다.
  • 값은 숫자나 null(빠진 값)이다. 문자열, 불리언, 객체, 배열은 오류다. 이름 키(label, name, row, col)는 글이어야 하고, 빠지면 오류다.
  • 값의 절댓값은 1e15 미만이다. 그보다 크면 12자리 십진 반올림으로 눈금과 글자를 정확히 쓸 수 없기 때문이다.
  • 0은 받고, 0이 아닌 값의 절댓값은 2.2250738585072014e-308(2⁻¹⁰²², 가장 작은 정규 수) 이상이다. 그보다 작은 비정규화 수는 간격 계산이 0으로 떨어지고 유효 자릿수도 줄어 눈금을 만들 수 없기 때문이다. 0이 아닌데 Number로 바꾸면 0이 되는 글(소수점 뒤에 0이 400개 있는 소수)도 같다. JSON의 1e-400처럼 읽는 쪽에서 이미 0이 된 수는 0으로 받는다.
  • 이 범위는 행 줄, data, rule(공통 기준선과 행 기준 rule=), cell의 모든 숫자에 같게 적용한다. 위쪽 범위를 넘으면(1e15 이상) 줄 번호와 함께 오류(values must be under 1e15 in absolute value)이고, 아래쪽 범위를 넘으면(0이 아닌데 2.2250738585072014e-308 미만) 줄 번호와 함께 오류(nonzero values must be at least 2.2250738585072014e-308 in absolute value)다. 둘 다 진단 code는 syntax다. 숫자 모양이어도 Number로 바꾸면 무한대가 되는 글(309자리가 넘는 정수)은 같은 오류다. 시간(speed, time=, for)과 비율(aspect)도 무한대가 되는 글은 구문 오류이고, 성공한 SVG에는 NaN, Infinity 좌표가 없다. 값 읽기는 parseNumber와 parseTime이 모두 유한한 값만 돌려주는 한 경로다.
  • 값에 묶은 행(ms=depth)은 장면 중에 받는 값도 같은 범위와 같은 값 규칙(막대의 음수, 로그 눈금의 0 이하, 쌓는 합계 상한, 크기 상한)을 받는다. 어기면 값이 바뀌는 시각이 아니라 묶은 행의 줄에 value-type 오류(chart "c" reads v=-5: values cannot be negative)로 알리고, 같은 숫자를 행에 직접 적었을 때 거절되는 값은 묶어도 거절된다. 값 범위를 재는 프레임과 실제 프레임이 모두 이 검사를 거치고, 값 범위(축)는 검사를 통과한 값으로만 계산한다. 그래서 로그 눈금의 선 차트에서 장면이 값을 0으로 바꿔도 축을 만들다 줄 번호 없는 내부 오류가 나지 않고 묶은 행의 줄에 log scale needs values above 0 오류가 난다. 쌓는 합계와 신뢰구간의 순서(low ≤ 값 ≤ high)처럼 값 여럿이 함께 정하는 규칙은 원본 행과 실제 프레임에서만 검사하고, 값 범위를 재려고 값 하나씩 바꿔 본 어림 프레임(가정 조합)에서는 검사하지 않는다. 그래서 값 셋(v=mid v.low=lo v.high=hi)을 같은 시각에 함께 바꾸는 장면은 통과한다. 값이 구간 밖으로 나간 채 실제로 그려지는 프레임(mid=50만 바뀐 중간 프레임 등)은 묶은 행의 줄에 value-type 오류(v needs v.low ≤ v ≤ v.high. Found 0, 50, 10)이고, 정적 행 v=50 v.low=0 v.high=10도 같은 글로 거절된다. 이 검사는 test/charts.test.js(C4)가 본다.
  • 결측값 설명과 좁은 배치의 계열 이름은 글자로 표시한다. <와 >를 포함해도 SVG나 HTML 마크업으로 해석하지 않는다.
  • JSON null은 빠진 값이며 허용하는 종류와 자리는 종류와 행 줄이 정한다. 막대·누적·퍼센트·선·계단의 계열 키를 생략하면 null로 읽는다. 다른 필수 키의 생략은 오류다.
  • data 파일을 읽지 못하면 오류 글에 파일의 내용을 싣지 않는다. 파일 시스템 오류는 코드만(ENOENT), JSON 오류는 not valid JSON과 엔진이 알린 위치만 적는다. 숫자 자리의 자료형 오류도 원래 값 대신 자료형만 알린다. 경로를 원본 폴더 안으로 제한하는 규칙은 없다.
종류 원소 키
bar label, 계열 키, {계열 키}.low, {계열 키}.high, 행 기준 rule
stacked, percent label, 계열 키
dumbbell label, 계열 키 둘, 계열 키마다 .low, .high
difference label, 계열 키, {계열 키}.low, {계열 키}.high
box label, min, q1, median, q3, max
scatter name, x, y, series
line x, 계열 키, 계열 키마다 .low, .high
step, area x, 계열 키
ecdf value, 계열이 있으면 series
pie, donut label, value
heatmap row, col, value
histogram value
waterfall 증감은 label, value, 합계는 label, total: true
  • 표에 없는 키가 있으면 경고다. 키 이름 오타를 알리기 위해서다.

시간 흐름

차트의 시간 흐름은 장면(scene) 안의 줄이다. 장면의 방식과 속도, 값 묶기는 그림 문법과 재생이 정한다.

줄 뜻
reveal 차트.계열 [차트.계열 ...] 계열을 드러낸다. 여럿을 적으면 함께 드러낸다. 막대는 값 축 시작에서 자라고, 덤벨 main 계열은 compare에서 main까지 놓인 화살표와 점이 나타나며, 선 차트의 선은 왼쪽부터 그어진다. 점과 값 글자는 흐린 상태에서 나타난다
light 차트 "항목" 막대, 덤벨, 상자, 차이, 원, 워터폴의 행이나 산점도의 점을 밝히고 나머지를 흐리게 한다
light 차트 x=값 선, 계단, 면, 누적분포, 히스토그램에서 그 x의 점이나 구간을 밝힌다
light 차트 "행" "열" 히트맵 칸 하나를 밝힌다
wait 시간 멈추는 박자. 그림 문법과 같다
  • 계열은 장면마다 따로 센다. 한 장면의 reveal에 나온 계열은 그 장면이 시작할 때 숨고 reveal 순서대로 드러나며, 나오지 않은 계열은 처음부터 보인다. 다음 장면은 이 규칙으로 새로 시작한다.
  • 계열이 없는 종류(상자, 히트맵, 원, 히스토그램, 워터폴, 계열 없는 산점도와 누적분포)에서 reveal은 오류다. 계열 이름 없이 reveal 차트만 쓰거나 없는 계열을 쓰면 오류이고, 한 장면에서 같은 계열을 두 번 드러내도 오류다.
  • 덤벨에서 main 계열을 compare 계열보다 먼저 드러내면 오류다. 화살표의 시작점(compare)이 없기 때문이다. 누적 막대는 보이는 순서대로 드러내야 한다. 앞 구간이 없는 상태에서 뒤 구간만 나타나는 순서는 오류다.
  • 자라는 시간은 pace(차트 카드에서는 계열이 자라는 시간)이고 곡선은 토큰 ease.reveal이다. 막대, 선, 띠는 그 시간 내내 자라고, 값 글자는 뒤 절반에 나타난다. 선 차트의 점(과 한 점짜리 오차 막대)은 선이 그 점에 닿는 시각에 나타난다. 선은 길이 순서로 그려지므로 그 시각은 길이 비율을 ease.reveal의 시간 비율로 거꾸로 푼 값이고, 그리기 단계가 자라는 시간 대비 비율로 계산해 요소의 data-at에 담는다. 재생기와 움직이는 SVG는 자라는 시간을 곱해 걸기만 한다. HTML 재생기와 움직이는 SVG가 같다.
  • light가 가리킬 대상은 차트 안에 있어야 한다. 종류에 맞지 않는 꼴(light x=값을 막대에, 행 이름을 선에)은 오류다. light는 그 장면 끝까지 남고 다음 장면에서 꺼진다. 같은 장면의 light는 앞의 것에 더해진다. 남아 있는 light는 장면의 상태라서 정지 장면, 길이가 0인 장면, 움직임 줄이기의 마지막 모습과 멈춘 SVG도 같은 흐림(히트맵은 굵기)을 보인다. 자라는 움직임과 펄스만 없다. 다른 장면의 light는 이어받지 않는다.
  • 범례는 계열을 드러내기 전에도 모두 보인다. 드러난 계열의 범례만 남으면 범례가 왼쪽이 빈 채 어긋나 보이기 때문이다. 범례, 막대, 점 이름 칸은 역할 차례(main 먼저)로 고정이고, 장면은 이야기 차례라 작성자가 정한다. 그래서 “기억 없음 → 기억 붙임” 같은 전 → 후 순서로 드러내도 된다. 덤벨만 화살표가 compare에서 시작하므로 compare를 먼저 드러낸다.
  • 장면이 없는 문서의 차트는 다 자란 모양으로 정적이고 움직이는 SVG도 되풀이하지 않는다. 장면의 mode=static은 reveal이 있어도 마지막 상태 하나(모든 계열이 자란 모양)를 보인다.

그리기

  • 크기와 색은 토큰이다. 차트 너비는 spacing.figure.chart.width(공통 기본값은 720)다. 본문 폭 680~760에 삽입돼도 그림이 줄지 않아 항목 이름 13px, 제목 15px, 눈금·범례·값 11px 그대로 읽히게 표준 캔버스 폭(spacing.figure.figure-canvas, 960)보다 좁게 잡았다. 보기가 모두 차트 보기(plot)나 시간 보기(time)인 그림은 캔버스 폭도 차트 너비다(canvas.js의 canvasOf). 그래서 720px 단에서 SVG가 1배로 보이고 가장 작은 글자가 11px로 남는다. 예전처럼 960 캔버스 가운데에 720 차트를 두면 양옆이 비고 720px 단에서 글자가 8.25px로 줄었다. 구조 그림과 함께 놓인 차트 카드는 그림 캔버스를 따른다. 차트는 이 폭 안에서 이름 칸, 눈금 간격, 값 글자 자리, 막대와 칸 너비, 세로 높이를 글자 폭(measure)으로 새로 계산하고 글자만 키우거나 줄이지 않는다. 캔버스보다 좁은 차트는 판 가운데에 놓이고(삽입된 재생기는 판 없이 이 폭 그대로 본문 왼쪽 선에 맞는다) width wide 같은 그림 머리의 뜻은 바뀌지 않는다. 이름 칸은 이름에 맞추고 남는 폭은 모두 그림 영역이다. 오른쪽에는 왼쪽 여백(이름과 제목이 시작하는 spacing.7)과 같은 여백을 둔다. 가장 멀리 닿는 요소(막대와 상자의 값 글자, 덤벨의 바뀐 비율 글자, 눈금 글자, 기준선 라벨, 점)가 차지하는 상자의 오른쪽 끝이 차트 너비에서 spacing.7를 뺀 자리에 놓이게 값 축 길이를 글자 폭(measure)으로 계산해 정하므로 내용이 판 가운데에 놓인다. 차지하는 상자는 글자는 잰 폭, 점은 반지름, 선 표식은 굵기의 절반까지다. 글자 뒤 바탕 면(chart-text-bg)은 보이는 먹이 없어 재지 않고, 글자 획의 보이는 가장자리도 글자마다 달라 재지 않는다. 종류가 어떤 요소보다 더 많이 비워 두면 같은 규칙을 어긴 것이다. 원과 도넛은 오른쪽 여백을 넘지 않는지만 맞춘다. 좌우 여백의 1px 안 같음을 모든 종류에서 재는 자동 시험은 현재 없다(검증 요구사항, 미완료). 선 차트와 산점도에 제목, 범례, 세로축 제목이 모두 없으면 왼쪽 끝은 세로축 눈금 글자 왼쪽 끝이고 오른쪽 여백을 그만큼 둔다. 폭을 고르는 그림 머리 선택은 없다. 또한 행 높이, 막대 두께, 점 크기도 토큰이다. 점 크기는 값과 상관없이 같다(문서 스킬 규칙).
  • 위아래 여백은 토큰 spacing.7(28px) 하나다. 위는 제목 글자 줄 윗면이 그림 윗면에서 spacing.7 아래에서 시작하고, 아래는 마지막 글자 줄(축 제목, 없으면 눈금 글자) 기준선 아래 spacing.0-75(기준선 아래로 내려오는 먹 몫)에 spacing.7를 더한 곳이 그림 아래 끝이다(히트맵은 마지막 줄이 칸이라 칸 아래 끝에서 spacing.7). 값 축 아래 글자(눈금, 축 제목)는 막대, 덤벨, 상자, 산점도, 선이 같은 규칙과 같은 간격으로 놓는다.
  • 흐림(light가 밝히지 않은 행)은 면과 글자를 따로 흐린다. 히트맵은 흐리지 않고 아래 항목이 따로 정한다. 면(막대, 원·도넛 조각과 범례 색 견본, 점, 띠)은 불투명도 토큰 opacity.dim이고, 글자(이름, 값, 점 이름)는 ink class 묶음으로 따로 담아 opacity.dim-ink로 덜 흐린다. 원과 도넛은 행 하나가 조각, 무늬, 조각 안 번호 키, 범례 색 견본, 글자를 한 묶음(cr-k)으로 가져 막대와 같은 규칙으로 강조되고, 밝힌 행과 그 글자의 굵기는 바뀌지 않는다(굵기로 구별하는 것은 히트맵뿐이다). 면과 글자가 한 묶음이면 글자가 면과 같은 비율로 흐려 바탕과 구별이 안 되기 때문이다. 막대, 누적 조각, 히스토그램 막대는 테두리가 처음부터 면과 같은 계열의 값이라 흐림을 면과 테두리가 함께 받고, 흐린 행에서만 보이는 별도의 테두리 요소는 없다. 흐려진 면과 바탕의 대비는 낮고, 이 문서는 그것을 3으로 맞추라고 요구하지 않는다. 계열 색은 정식 팔레트 값이라 대비를 맞추려고 바꾸지 않는다(노랑도 같다). 면 위 대비가 낮은 표식은 같은 계열 테두리, 직접 라벨, 번호 키, 무늬로 구분하고, 실제 면 위 대비는 그 면과 역할마다 잰다. 흐린 행의 값 글자(color.ink)와 보조 글자(color.ui-muted)는 opacity.dim-ink를 적용한 색이 라이트와 다크의 바탕과 4.5 이상이어야 한다. 이 값을 재는 자동 시험은 현재 없다(검증 요구사항, 미완료).
  • 실제 계열의 선, 계단, 범위는 모두 같은 굵기(border-width.strong)와 그 계열의 테두리 색이다. 노랑이라고 바탕 선을 따로 깔아 굵게 하지 않는다. 기대값 계열은 같은 굵기의 회색 점선이고 꼭짓점 표식이 없다. 점, 막대, 조각, 범례 칸의 면은 계열의 면색이고, 밝은 계열(노랑)의 점은 같은 계열 테두리 고리를 가진다. 라이트에서 노랑 테두리의 실제 차트 면 위 대비는 3에 못 미치고 이 그래픽 한계는 색을 바꾸지 않고 남긴다. 노랑은 계열이 둘 이상일 때만 쓰이고 범례가 늘 함께 간다. 선, 계단, 면적은 끝 이름이, 막대와 누적 막대와 산점도는 번호 키가 직접 라벨로 더해지고, 점 표식은 원으로 통일한다. 범례와 이름을 함께 표시하며 색만으로 읽어야 하는 조합의 접근성은 별도로 검수한다.
  • 히트맵은 값과 색 강도의 대응을 지키려고 light가 밝히지 않은 칸도 흐리지 않는다(칸은 직각이고 테두리가 없다). 칸 면과 글자 색은 어느 단계에서도 같아 대비가 늘 같고, 밝힌 칸의 숫자만 굵게(font-weight.semibold) 해 구분한다. 밝힌 칸이 있고 밝히지 않은 칸도 있을 때만 굵게 하고, 모든 칸을 밝히면 구별할 칸이 없어 굵기가 그대로다. 굵기는 자기 차트 카드 안에서만 정해서, 한 판의 다른 히트맵 카드를 밝혀도 이 카드의 숫자는 굵어지지 않는다. 움직이는 SVG는 굵기를 구간 시작에 한꺼번에 바꾸고 면과 글자의 불투명도, 글자색은 건드리지 않는다. 이 규칙을 직접 재는 자동 시험은 현재 없다(검증 요구사항, 미완료).
  • 세로 값 축 기준선(산점도, 선 차트)의 라벨은 오른쪽 끝 기준선 위가 기본이다. 그 자리를 데이터(선, 띠, 점, 점 이름)가 가리면 왼쪽 끝으로 옮기고, 가린 수가 같으면 오른쪽에 둔다. 점은 반지름까지 자리로 세므로 라벨 상자에 일부만 겹친 점도 가린 것이다. 가로 값 축 기준선(막대, 덤벨, 상자, 차이)의 라벨은 행 위쪽 바깥이라 옮기지 않는다.
  • 막대 행 기준(rule=값)은 그 행 막대 묶음 위아래로 spacing.1-5만 나오는 점선이고, 위에 기준 숫자(formatNumber)를 rule 라벨과 같은 글자로 붙인다. 막대 위에 얹히므로 바탕색 테두리(border-width.casing)를 점선 밑에 깔아 막대 색과 갈리게 한다. 점선과 숫자는 그 행 묶음(cr-k)에 속해 light로 밝히지 않은 행에서 막대와 함께 흐려지고, 계열 드러내기와 상관없이 처음부터 보인다. 기준 값과 숫자 폭은 값 축 범위와 길이에 들어가 그림 밖으로 나가지 않는다.
  • 차이 차트는 행마다 신뢰구간 선(border-width.strong, 끝 캡 spacing.figure.chart.cap)과 그 위의 점(반지름 spacing.figure.chart.dot, 바탕색 테두리)을 계열 색으로 그린다. 선은 막대 계열처럼 계열을 드러낼 때 왼쪽부터 자라고 점은 뒤 절반에 나타난다. 0 자리에는 보조 글자 색 세로선(border-width.tag)이 서고 눈금 0이 그 아래에 있다. 값 글자는 점과 구간 끝 가운데 더 오른쪽 옆에 두며 양수는 +, 음수는 −를 붙이고 0은 붙이지 않는다. 부호는 값의 부호이므로 소수 자릿수로 크기가 0으로 반올림되는 음수도 −0.0으로 쓴다. 기준선은 막대 계열과 같이 행 위쪽 바깥에 라벨을 둔다. 값 축 범위는 값, 구간, 기준선과 0을 모두 담는다. 0은 비교의 중심이라 축 끝에 붙지 않는다. 값이 0의 한쪽에만 있으면 반대쪽에 범위의 15%를 더해 0이 눈금 한 칸 이상 안쪽에 서고, 모두 0이면 −1에서 1이다. 값 글자는 오른쪽 여백 안에 든다.
  • zero off로 값 축이 0을 포함하지 않게 잘리면(범위가 모두 양수이면 맨 아래, 모두 음수이면 맨 위) 그 끝 격자선이 보조 글자 색 지그재그(border-width.tag)로 시작한다. 격자선만으로는 0에서 시작하는 축과 구별되지 않아, 값 차이를 크게 읽는 잘린 축이 0 시작 축으로 오해받기 때문이다. 범위가 0을 포함하면 잘린 것이 없어 표시하지 않고, 로그 눈금에도 쓰지 않는다.
  • 값 글자와 점 이름은 별도의 바탕 면을 뒤에 두어 격자와 기준선을 가린다. 글자 자체의 두꺼운 테두리는 WebKit에서 한글 획을 가릴 수 있으므로 사용하지 않는다.
  • 값 글자는 막대 끝 바깥, 덤벨 화살표 양 끝 바깥에 둔다. 두 값이 가까워도 글자가 겹치지 않게 하기 위해서다. 값 글자는 별도 바탕 면 위에 그려 획이 글자 내부를 덮지 않게 한다. 막대 값 글자가 기준선과 겹쳐도 비키지 않고 다른 막대와 같은 간격으로 막대(오차 막대) 끝 옆에 두며, 글자 뒤의 바탕 면이 그 부분의 점선을 가려 끊어 보이게 한다. 바탕 면과 받침 선(신뢰구간·기준선·바뀐 표식의 테두리)과 기대값 계열의 속 빈 점은 차트가 놓인 면의 색(--chart-ground, 새 토큰이 아니라 기존 역할을 가리키는 CSS 별칭)이다. 차트 보기는 그림 바탕(color.prose-pre-background), 차트 카드는 카드 면(--fx-face, 켜진 카드는 --fx-face-on)이다. 이 면은 차트 묶음(.fl-chart)의 일반 상속 속성 color가 싣고, 바탕 면과 받침 선과 속 빈 점은 currentColor로 읽는다(차트 안에서 color를 정하는 요소는 방향선의 역할 class뿐이고 받는 쪽을 감싸지 않는다). 움직이는 SVG는 켜진 차트 카드에서 이 묶음의 color만 켜짐 구간에 맞춰 --fx-face-on과 --fx-face 사이로 바꾸므로 정지 SVG, 움직이는 SVG, HTML 재생기 모두 켜진 카드 면을 따른다. 사용자 정의 속성(--chart-ground)을 keyframes로 바꾸지 않는 까닭은 그 값이 애니메이션 시작 때 굳어 재생 중 테마가 바뀌어도 따라가지 않기 때문이다.
  • 막대 행 이름은 모든 계열을 놓은 최종 위치에 고정한다. 계열이 나타나거나 사라져도 제목과 행 이름이 이동하지 않는다. 값이 없는 슬롯은 최종 행 중심 계산에서 제외한다.
  • 막대 신뢰구간은 막대 아래 독립된 줄에서 low부터 high까지 긋고 양끝에 짧은 수염을 단다. 막대 위에 얹지 않으므로 막대 색에 묻히거나 흰 절단선처럼 보이지 않는다. 가는 보조 선(border-width.tag, color.figure-data-line)이다. 계열 슬롯의 세로 간격(STEP)은 막대 높이에 spacing.4을 더한 값이고, 줄은 막대 아래 spacing.1-5에 놓이며, 행 높이와 이름 글자의 세로 가운데, 계열 슬롯 위치이 같은 값(src/chart/slots.js)을 쓴다. 신뢰구간이 있는 차트는 마지막 줄 아래 spacing.2를 더 비워 축과 겹치지 않는다. 값 글자는 high 바깥에 둔다. 막대 색은 계열의 범주 번호가 정한다(아래 계열과 팔레트).
  • 산점도는 신뢰구간이 없다. 점 이름과 link만 그린다. link 선은 보조 글자 색에 굵기 토큰 border-width.strong이고 끝이 둥글며, 화살촉은 흐름 그림과 같은 채운 삼각형 표식(레이아웃, draw/arrow.js의 arrowMarker)이고 선 굵기에 비례한다. 방향선의 색은 종류가 고르는 색 역할(CHART_ARROW: 덤벨은 main, 산점도는 muted)이 정한다. 역할 class(.arrow-main, .arrow-muted)의 color를 몸통 stroke와 화살촉 fill이 함께 읽고(currentColor), 표식 정의(fl-arrow-<역할>)는 쓰인 역할마다 문서에 하나다. 그래서 차트 종류가 섞여도, 선언 순서가 바뀌어도 방향선마다 자기 역할의 색 화살촉을 쓰고 context-stroke 지원에 기대지 않는다. 선은 화살촉 꼭지에서 몸통 굵기의 절반만큼 앞에서 끝나 둥근 끝이 꼭지 밖으로 나오지 않는다. 방향선(산점도 link, 덤벨 화살표)은 몸통과 화살촉이 같은 자리에서 한꺼번에 나타나고(pop, 계열이 자라는 시간의 뒤 절반에 페이드) 선만 그려지며 머리가 목적지에 먼저 떠 있지 않는다. 선 차트의 선만 왼쪽부터 그려진다. 선이 가늘면 화질이 거칠어 보이기 때문이다.
  • 상자 차트의 수염, 상자, 가운데 값 선은 한 묶음(pop)으로 같은 좌표에서 함께 나타난다. 상자만 자라면 q1~q3 폭만 커져 가운데 값과 수염이 고정된 채 통계가 틀려 보이므로 어느 요소도 크기를 바꾸지 않는다. 정지한 차트는 움직이지 않는다.
  • 막대, 누적 조각, 원·도넛 조각, 워터폴 막대, 범례 칸은 테두리 없는 평평한 면이다. 조각 사이는 바탕 색 틈으로 가른다. 소유자 결정으로 "모양은 평평하고 그림자가 없다"는 규칙은 차트 표식에 대해 폐기했다. 표식과 판의 옅은 깊이(Things 같은 부드러운 띄움)는 디자인 정본의 역할 토큰으로만 넣고, 그 역할이 생기기 전에는 차트가 그림자 값을 직접 만들지 않는다(토큰 요청: shadow.chart-mark, color.data.other).
  • 상자 차트의 상자(q1~q3)는 첫 범주 계열의 옅은 면(tint)과 그 계열 면색의 가는 테두리이고, 중앙선은 같은 계열 테두리 색의 굵은 선(border-width.strong)이다. 수염은 흐린 글자색의 가는 선이고 양 끝에 막대 두께만 한 눈금이 있다. 다섯 값이 있는 행의 중앙값 n 글자는 상자 위 한 줄에서 중앙선 바로 오른쪽에 붙는다. 다섯 값 가운데 빠진 값이 있는 행은 모양 없이 수염 끝 자리에 다섯 값을 적는다.
  • 선 차트 신뢰구간은 계열 색의 옅은 띠(opacity.band)다. 선과 점은 띠 위에 그린다. 회색 그림 바탕 위에서 탁해 보이지 않을 만큼 색이 살고 두 계열 띠가 겹쳐도 읽히게 불투명도를 0.22로 둔다. 띠는 계열이 자랄 때 선을 따라 왼쪽부터 드러난다. 신뢰구간이 있는 점이 이어지지 않고 하나뿐이면 면이 안 되므로 세로 오차 막대(보조 글자 색, 굵기 border-width.edge, 끝 캡 spacing.figure.chart.cap)로 그린다. 선은 stroke-dashoffset으로 그리고(.draw가 stroke-dasharray: 1을 가진다), 띠는 같은 시간 동안 왼쪽부터 clip-path로 드러난다. 둘이 같은 시간에 끝나서 끝 상태에서는 띠가 선 끝까지 맞는다.
  • 덤벨 신뢰구간은 점 뒤에 같은 줄로 깔리는 옅은 둥근 막대기(spacing.figure.chart.range, opacity.range)다. 점, 화살표, 값 글자보다 뒤 레이어이고, 값 글자는 두 점과 두 범위를 모두 덮는 구간의 바깥에 둬서 막대기, 점, 화살표와 겹치지 않는다.
  • 덤벨 끝점은 거리와 상관없이 모든 행에서 같은 모양이다. 시작점은 노랑 면과 같은 노란 계열 경계를 가진 사각형 점(둘째 범주의 모양), 끝점은 main 색 채운 원 점(바탕색 테두리, 반지름 spacing.figure.chart.dot)이고 둘 다 항상 그린다. 두 점의 넓이는 같고 모양만 다르다. 두 점을 잇는 화살표는 두 점 가장자리(점 반지름과 spacing.0-5 간격 밖) 사이가 토큰 spacing.figure.chart.arrow-min(16px) 이상일 때만 그린다. 모자라면 화살표만 빼고 점은 그대로 둔다. 화살촉이 점에 가려지는 일을 막기 위해서다. 두 점이 점 지름보다 가까우면 채운 끝점이 빈 시작점 위에 겹쳐 그려져 시작점 테두리가 가장자리로 비친다. 모양이 행마다 다르면 한 행만 다른 뜻으로 읽히기 때문에 공간이 모자랄 때 줄이는 것은 연결선뿐이다. 기준선은 점선이고, 기준선 값은 값 축 범위에 넣는다. 기준선이 그림 밖에 그려지지 않게 하기 위해서다. 히트맵 칸 안에는 값을 적는다. 칸 너비는 이름 칸 오른쪽에서 내용의 오른쪽 끝(차트 너비에서 28을 뺀 자리)까지 남은 폭을 열 수로 나눈 값이고 상한이 없으며, 칸 높이는 spacing.figure.chart.cell이다. 열이 적으면 칸이 가로로 길어져 정사각 비율에서 벗어난다. 그래도 정사각 칸을 가운데에 두지 않고 폭을 채우는 쪽을 골랐다. 모든 차트가 자기 차트 너비를 채우게 하고, 정사각 칸은 열이 셋일 때 내용이 차트 너비의 일부에 그쳐 왼쪽에 쏠리며, 가운데 배치는 다른 차트의 왼쪽 여백 28과 어긋나기 때문이다. 칸 색은 값 0의 color.data.heat-low에서 최댓값의 color.data.heat-high까지 OKLab으로 보간한다. 라이트는 옅은 파랑(꾸밈 요소, 색 역할 표)에서 진한 파랑으로, 다크는 바탕보다 조금 밝은 낮은 채도의 남청(color.palette.blue.850)에서 파랑(color.palette.blue.600)으로 가서 값이 클수록 밝아진다. 칸마다 강도(0~1, 소수 셋째 자리까지)를 --s 변수로 두고 CSS color-mix가 색 변수로 계산하므로 다크 모드의 color.data.heat-low를 따라가고, color-mix를 모르는 뷰어는 같은 OKLab 보간(src/chart/oklab.js)으로 라이트 값을 구운 fill 속성을 쓴다. 두 계산이 브라우저의 color-mix(in oklab, ...) 결과와 채널까지 같다는 것은 자동 테스트로 확인하지 않았다(검증 요구사항, 미완료). 칸 안 값 글자는 칸마다 color.data.heat-ink(어두운 글자)와 color.data.heat-ink-on(밝은 글자) 가운데 칸 색과 대비가 큰 쪽을 빌드 때 고르고(src/contrast.js) 어느 칸에서나 4.5 이상이다. 칸 색과 글자색은 --s에 적는 강도 한 값에서 같은 함수(heatLook)로 정하고, 강도 0~1 전체에서 4.5 이상인지 라이트와 다크로 재는 자동 시험은 현재 없다(검증 요구사항, 미완료). 라이트는 큰 칸에 밝은 글자 후보(heat-ink-on), 작은 칸에 어두운 글자 후보(heat-ink)다. 다크는 두 글자가 같은 흰색이고, 칸 색 범위(color.palette.blue.850에서 color.palette.blue.600)가 흰 글자와 4.5 이상이 되게 정했다. 색을 고르는 계산은 라이트 값으로만 하므로 다크 범위를 바꾸면 이 조건을 사람이 다시 잰다. 열 이름은 칸 가운데에 놓이므로 쓸 수 있는 폭은 칸 너비에서 spacing.2를 뺀 값 하나(columnRoom)이고, 이웃 열 이름과 spacing.2 이상 떨어진다. 줄바꿈과 그림 검사 1번이 이 폭을 함께 읽어, 이름이 넘으면 오류다. 좁은 배치(컨테이너 폭 이하)에서는 열 이름이 이 폭에 맞게 글자 단위로 줄을 바꾸고(글자를 줄이거나 돌리지 않는다), 행 이름 칸은 가장 긴 행 이름이 한 줄로 들어갈 폭까지만 얻어 남는 폭을 열이 가져간다. 줄바꿈으로 이름의 글자가 한 글자만 둘째 줄에 남을 수 있다. 산점도 점 이름은 점 오른쪽에 두고, 내용의 오른쪽 끝을 넘으면 점 왼쪽에 둔다. 계열이 둘 이상이면 이름 앞에 그 점 계열의 번호 키가 붙고(3 검색 전), 점 이름은 이 번호 키를 포함한 글로 재고 놓는다. 그림 검사와 오류 메시지가 가리키는 점 이름은 번호 키 없는 원래 이름이다. 이름 상자의 높이는 줄 수에 한 걸음(글자 높이에 spacing.0-5)을 곱한 값이고 한 줄이 기본이다. 먼저 적은 점부터 이름을 놓는데, 이미 놓인 이름이나 점과 겹치면 반대쪽, 위아래로 한 걸음(글자 높이에 spacing.0-5)씩 최대 네 걸음 옮긴 자리 순으로 찾는다. 이름 자리는 그 이름 상자에서 자기 점까지의 거리가 다른 어느 점까지의 거리보다 spacing.3 이상 짧은 자리를 먼저 쓰고(이름이 이웃 점 쪽으로 가서 어느 점의 이름인지 헷갈리지 않게 하려는 것이다), 그런 자리가 없으면 그 조건 없이 이름과 점을 모두 피하는 자리를 쓴다. 점을 피하는 자리가 없으면 이름끼리만 겹치지 않는 자리를 쓰고, 그것도 없으면 기본 자리에 두고 그림 검사 2번이 나중에 적은 점의 줄에 오류로 알린다(같은 좌표에 열 점 넘게 몰린 경우). 점이 몰린 그림에서 모든 이름이 자기 점에 가장 가깝다고 보장하지 않는다. link 화살표의 끝은 점 가장자리에서 spacing.1만큼 바깥에서 시작해, 화살촉(굵기 곱 spacing.figure.arrow.head)이 어느 점의 이름 글자 상자(spacing.1 여유 포함)와도 겹치지 않을 때까지 시작 점 쪽으로 더 당긴다. 어느 쪽에도 들어가지 않으면 1번 오류다. 선 차트는 점을 표시한다.
  • 덤벨의 시작점은 노랑 면과 같은 노란 계열 경계를 가진 사각형 점, 끝점은 main 색 채운 원 점이다. 범례는 main, compare 순이다. 범례 색과 같게 하기 위해서다. 값 글자와 바뀐 비율 글자는 계열과 상관없이 글자색(color.ink)이다. 계열 색(특히 라이트의 파랑, 노랑)은 글자로 쓰면 대비가 4.5에 못 미쳐서 막대, 점, 띠로만 전한다. 강조 계열(main)의 값 글자는 굵기(semibold)로만 구분한다.
  • 덤벨은 오른쪽에 바뀐 비율을 적는다. 비율은 (둘째 / 첫째 − 1) × 100을 정수로 반올림한 값이고(절반은 0에서 먼 쪽), 줄면 −, 늘면 +를 붙이고 같으면 부호 없이 0%다. 부호는 반올림 전 비율로 정하므로 0으로 반올림되는 감소는 −0%, 증가는 +0%다. 첫 값이 0이거나 비율이 숫자 범위를 넘으면(가장 작은 정규 수에서 1로 가는 값) 적지 않는다. 그래서 출력에 Infinity%가 나오지 않는다.
  • 상태 색(성공 초록, 실패 빨강)은 쓰지 않는다. 판정은 글자로 적는다(문서 스킬 규칙).
  • 눈금과 바뀐 비율 글자의 숫자는 1000 이상이면 k, 1000000 이상이면 M을 붙이고, 소수 한 자리로 반올림(절반은 0에서 먼 쪽)한 뒤 끝의 .0은 지운다(120000은 120k, 1250은 1.3k). 반올림한 값이 1000k가 되면 1M으로 쓴다. 1000 미만은 가장 짧은 십진 표기로 쓴다(0.012, 91.4).
  • 눈금 값은 간격(1, 2, 5, 10 × 10ⁿ)의 정수배를 십진 글자로 이어 만든다. 값을 고정 자릿수로 반올림하지 않으므로 간격이 1e-12처럼 작아도 눈금이 서로 다른 값이고 순서대로 늘어난다. 0.1, 0.2, 0.3 같은 눈금에 이진 오차(0.30000000000000004)도 끼지 않는다. 1000 미만의 아주 작은 값은 JavaScript의 가장 짧은 표기를 따라 5e-13, 1.5e-12처럼 지수 표기가 된다.
  • 축은 값 범위를 덮는 눈금이 모두 유한하고 엄격히 늘며 눈금 글자가 서로 다를 때만 쓴다(makeScale). 범위의 두 값이 이웃한 부동소수점 수(1과 1.0000000000000002)처럼 가까우면 처음 간격의 눈금이 겹치므로 간격을 한 단계씩(1, 2, 5, 10 × 10ⁿ) 최대 일곱 번 키워 구분되는 축을 먼저 찾는다. 찾으면 그 축을 그리고, 값을 합치거나 반올림하지 않는다. 끝내 못 찾으면 줄 번호와 함께 오류(values are too close to tell apart on an axis (최솟값 to 최댓값). Move them further apart, code는 syntax)로 거부한다. 줄은 그 축 범위의 가장 큰 값이 적힌 줄이고(행, data 줄, rule), 큰 값이 없으면 가장 작은 값이 적힌 줄, 그것도 없으면 그림 첫 줄이다. scale log 축도 같은 검사를 받고, 아래·위 10의 거듭제곱은 Math.log10의 반올림 대신 십진 글로 만든 거듭제곱과 견주어 정해 값이 축 밖에 놓이지 않는다(999999999999999).
  • 축과 장면 좌표를 다 계산한 뒤 다시 유한한지 확인한다. 좌표 속성이나 숫자 글(눈금, 값, 바뀐 비율)에 NaN이나 Infinity가 있으면 결과 파일을 쓰지 않고 internal 오류로 끝낸다. 위 입력 진단이 먼저 막으므로 이 확인은 이 도구의 버그를 잡는 마지막 방어선이다.
  • 반올림한 k, M 글자가 서로 다른 눈금을 같은 글자로 만들면(1e14 위의 작은 간격) 그 축의 눈금 글자는 모두 15자리 유효숫자의 십진 표기로 쓴다. 한 축에 같은 글자가 두 번 나오지 않는다. 눈금 글자는 눈금 값과 같은 순서의 labels로 한 곳(makeScale)에서 정해 그리기와 폭 계산이 같은 글자를 쓴다.
  • 값 글자(막대, 덤벨, 상자, 차이, 히트맵)는 소수 자릿수 때문에 0이 아닌 값이 0으로 지워지면(머리 줄 decimals를 쓰지 않았을 때) 눈금과 같은 지수 표기로 쓴다. decimals를 적었으면 적은 자릿수를 지킨다.
  • 값 글자(막대와 덤벨 계열 값, 상자 가운데 값, 히트맵 칸 숫자)는 같은 목록 안에서 소수 자릿수가 같다. 목록은 막대와 덤벨은 계열, 상자는 가운데 값 열, 히트맵은 모든 칸이다. 자릿수는 머리 줄 decimals N(0~6)이고, 없으면 그 목록에 적힌 가장 긴 소수 자릿수(최대 6)다. 그래서 0.6, 0.05, 1이 0.60, 0.05, 1.00으로, 한 계열의 85, 91.4가 85.0, 91.4로 쓰인다. 모두 정수면 소수점이 없다. 1000 이상 값은 위 k, M 표기다.
  • 상자 가운데 값 글자는 중앙값 230처럼 무엇의 값인지 붙여 쓴다. 글자가 수염 오른쪽 끝 옆에 있어 최댓값으로 읽혔기 때문이다.
  • 반올림은 십진 계산으로 한다. 이진 부동소수점 오차로 −70.5가 −70이 되는 일을 막기 위해서다.

요구사항

시험은 공개 진입점(buildFigure, toSvg, toHtml, 명령)으로 종류만의 수학과 의미(길이 비례, 부호, 비율 글자, 위치 순서)와 거절을 확인한다. 카드 틀, 색, 두께처럼 공통 부품이 소유한 모양은 차트 시험이 다시 보지 않는다(시험 번호). 표는 시험이 맡은 계약의 범위를 보일 뿐 차트 전체를 확인했다는 근거가 아니다. 통과 수는 이 문서가 아니라 CI와 GitHub 이슈의 기록이 맡는다. 화면에서 눈으로 보는 일(색, 여백, 좁은 배치)은 시험이 대신하지 않는다.

요구사항 시험
차트 종류 열여섯이 문법 표와 일대일이고, 예제가 종류마다 하나다. test/charts.test.js(K1), test/examples.test.js
차트를 행 줄과 data JSON에서 같은 결과로 그린다. test/charts.test.js(C5)
계열 수와 역할(main, compare, reference), 결측 -, 음수와 퍼센트 규칙을 줄 번호와 함께 지킨다. test/charts.test.js(K4, K5)
그릴 수 없는 값(1e15 이상, 비정규화 수, 근접한 값)을 줄 번호와 함께 막고, 그린 결과에 비유한 좌표나 숫자 글이 있으면 결과 파일을 쓰지 않는다. test/charts.test.js(K7, K8)
값에 묶은 차트의 프레임이 같은 표식 목록과 같은 축을 쓴다. 장면 중에 받은 값도 정적 행과 같은 값 규칙을 받고(#171), 숫자가 아닌 값과 묶을 수 없는 차트는 줄 번호와 함께 알린다. test/charts.test.js(C1~C4)
길이는 값에 비례하고 위치는 값을 따른다. 원·도넛은 합이 0이면 0%로 그리지 않고 말한다. test/charts.test.js(K2, K3, K6)
좁은 폭(reflowFigure의 chartWidth 224~358)에서 히트맵 열 이름이 이웃 이름과 6px 이상 떨어지고, 줄바꿈을 이어 붙이면 원래 이름이다. 간격은 잰 글 폭으로 센다. test/charts.test.js(K9). 이 폭에서 보기에 좋은지는 직접 확인하는 시험이 없다(검증 요구사항, 미완료)
좁은 폭에서 면적, 계단, 히스토그램, 누적분포의 가로 눈금 글자가 양 끝을 지키고 같은 걸음이며 이웃과 spacing.3 이상 떨어져 그림 안에 든다. test/charts.test.js(K9)
산점도 점 이름이 자기 점에서 다른 어느 점보다 spacing.3 이상 가깝다(이름 자리가 있는 폭만. 점이 몰려 그런 자리가 없으면 예전 자리로 돌아가므로 보지 않는다). test/charts.test.js(K9)
차트의 글자 뒤 바탕 면과 받침 선, 기대값 계열의 속 빈 점이 차트 묶음의 color로 실린 면의 색(--chart-ground)을 currentColor로 읽는다: 그림 바탕, 카드 면. 켜진 차트 카드는 정지 SVG, 움직이는 SVG(keyframes는 color만 바꾸고 사용자 정의 속성을 선언하지 않는다), HTML 재생기가 같은 켜진 면을 읽는다. test/component-state.test.js(S3)가 묶음의 color, 받는 쪽이 currentColor를 읽는 CSS, keyframes의 선언을 읽고, test/components.test.js(U10)는 그려진 형상(묶음 안의 위치, 속 빈 표식, 별칭이 속성에 새지 않음)만 읽는다. 둘 다 선언과 형상을 읽을 뿐 계산된 색을 보지 않는다. 실제 Chrome에서 계산된 색을 본 범위(켜진 카드의 움직이는 SVG, 정지 SVG, HTML 재생기, 재생 중 라이트·다크 전환 양방향, 막대의 다크 바탕 면과 기준선 받침 선, 선 차트 기대값 계열의 속 빈 점)는 표현 범위의 “차트 바탕” 행이 정하고, 그 밖의 소비자와 상태, 픽셀 일치는 검증 요구사항, 미완료다
히스토그램은 빠진 표본을 관측에서 빼고 분모를 관측 수로 둔다. 워터폴은 빠진 증감 뒤의 누계를 알 수 없는 값으로 두고, 상자는 다섯 값이 모두 있는 행만 모양을 그린다. 이 항목을 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료)
정지·움직임 줄이기·멈춘 SVG의 마지막 모습이 남아 있는 light를 같게 보인다. 원·도넛은 막대와 같은 행 강조를, 히트맵은 굵기만 쓴다. 자동 시험은 없다. 실제 화면 검수로 확인한다(검증 요구사항, 미완료)
숫자와 비율 글자가 반올림 규칙을 따른다. 읽는 글은 소수 여섯 자리 이하이고 좌표와 크기는 다섯 자리 이하이며, 원자료 비율과 히스토그램 높이 메타데이터만 전체 정밀도다. 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료)
범주 색과 무늬, 모양, 직접 라벨이 계열 번호로 정해진다. 자동 시험은 없다. 실제 화면 검수로 확인한다(검증 요구사항, 미완료)
좁은 배치(컨테이너 폭 이하)의 차트가 같은 사건 시각과 프레임 순서로 다시 그려진다. 자동 시험은 없다. 실제 화면 검수로 확인한다(검증 요구사항, 미완료)
제목과 부제의 언어가 inline 자료의 허용 여부를 바꾸지 않는다. test/charts.test.js의 buildFigure_localized_chart_labels_accept_same_inline_data
차트 재생이 장면의 시계를 따르고 마지막 모습이 다 자란 상태다. 자동 시험은 없다. 실제 화면 검수로 확인한다(검증 요구사항, 미완료)

단점

  • Vega-Lite가 그리는 그 밖의 차트(누적 면적, 지도)는 그릴 수 없다.
  • 실험 결과 JSON의 키를 원소 키 표에 맞춰야 한다.

대안

  • Vega-Lite 유지: 구조 그림과 모양, 움직임이 두 벌이 되어 버렸다.
  • 세로 막대: 항목 이름이 길면 기울이거나 줄여야 해서 버렸다.
  • 막대에 음수를 허용하는 발산형 모드: 기존 bar는 막대 길이를 0에서 잰다는 뜻을 사용자가 정했고, 그 뜻을 바꾸지 않는다. 차이 값을 막대로 그리면 길이와 신뢰구간 선이 같은 정보를 두 번 보이고 값이 모두 0이면 길이가 없어 그릴 것이 사라진다. 그래서 위치와 구간으로 보이는 종류를 따로 두었다.
  • 행 기준의 라벨 글: 행마다 rule.label="..."을 받으면 문법과 글꼴 조각이 늘고 행 이름과 중복된다. 숫자만 붙이고 뜻은 부제나 행 이름에 맡긴다.
  • 모든 선 차트의 0 시작 해제: 길이로 값을 보이는 차트와 같은 규칙을 흐리게 한다. 기본은 0 시작이고 zero off로 끈다.

상자 그림 예제는 캐시 상태별 응답 시간의 분포를 비교한다. 상자는 가운데 50%, 양끝 선은 최소·최대이며 중앙값은 이름을 붙여 표시한다. 일반적인 평균 비교만 필요하면 막대 차트를 쓴다.

면적 차트

chart area는 선 차트의 축·기준선·범례·점·드러내기를 공유하고, 각 계열의 점을 x 오름차순으로 이어 0 기준선까지 면을 채운다. 원본 행의 입력 순서는 보존한다. 음수와 모두 0인 값도 받는다. 서로 다른 x가 둘 이상 필요하며 중복 x, 결측, 신뢰구간, scale log, zero off는 오류다. 면으로 양을 비교하므로 0 기준을 자르지 않는다.

두 계열은 같은 기준선에 겹쳐 그리며 합산하지 않는다. 누적 면적과 백분율 면적은 아직 지원하지 않는다. 선은 계열의 의미색, 면은 같은 계열의 채움색과 공통 띠 불투명도를 사용한다. reveal 계열은 면·선·점을 함께 드러내고 light x=값은 해당 점을 밝힌다. JSON 입력도 같은 x와 계열 값 계약을 따른다.

재현 원본은 examples/area.thinkflow, 한 x뿐인 면의 거절은 test/charts.test.js(K5), JSON은 C5가 공개 출력으로 확인한다. 0 기준과 행 순서를 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료).

히스토그램

chart histogram은 원시 관측값을 같은 폭의 구간으로 나눠 건수를 센다. bins 최솟값 최댓값 구간수 또는 bins auto를 한 번 선언하고 sample 숫자를 반복하거나 data의 객체 배열에서 value를 읽는다. 중복 관측값과 입력 순서를 보존하고, 구간 밖 값을 버리거나 끝 구간에 몰아넣지 않고 오류로 알린다. 음수·0·같은 값만 있는 관측도 명시한 범위 안이면 유효하다.

  • 최솟값은 최댓값보다 작고 구간 수는 1~100의 정수다. 경계는 범위를 균등 분할한다. 내부 경계를 15자리 유효숫자로 정리하는 차이가 구간 폭 × 기계 엡실론 × 16 이하일 때만 정리해, 큰 기준값 위의 작은 구간 폭을 반올림으로 바꾸지 않는다. 경계가 겹치거나 기존 숫자 범위를 벗어나면 오류다.
  • bins auto는 관측 최솟값·최댓값을 범위로 삼고 Sturges 식 ceil(log2(n)) + 1로 구간 수를 정한다(최대 100). 같은 값만 있으면 한 구간으로 표시하고 양쪽 범위를 0.5씩 확장한다. 기존 숫자 한계를 넘는 쪽은 원래 값에 둔다. 이 확장은 실제 관측값의 분산을 뜻하지 않는다. 부동소수점 경계가 겹치거나 지원 범위보다 작은 수가 되면 구간 수를 줄여 다시 계산한다. 입력은 버리거나 바꾸지 않는다.
  • 이 추정은 Sturges 방식의 단순한 시작점이다. 비정규·대규모 데이터에 항상 적합하지 않으며 NumPy의 혼합 auto 방식과 같지 않다. 비교용 공통 경계가 필요하면 수동 구간을 선언한다.
  • 구간은 [왼쪽, 오른쪽)이고 마지막 구간만 [왼쪽, 오른쪽]이다. 내부 경계와 같은 값은 다음 구간에 들어간다. 빈 구간은 건수 0으로 남고 집계 합은 입력 관측 수와 같다.
  • x축은 관측값이고 기본 y축은 건수다. 건수 모드의 세로 눈금은 정수만 표시한다. 막대는 빈틈 없이 맞닿고 높이가 건수에 비례한다. 기준선 rule은 y축의 음수가 아닌 건수에 놓인다. 축과 제목은 고정하고 막대만 0에서 위로 자란다.
  • light x=값은 그 값이 왼쪽 경계인 구간을 강조한다. 입력 관측값 자체를 선택하는 문법이 아니다.
  • 계열·reveal·로그 축·zero off는 지원하지 않는다. 빠진 표본(sample -)은 받아서 관측에서 뺀다(종류와 행 줄). Sturges 외 구간 추정, 가중치, 여러 분포 비교, 바이올린·밀도 곡선은 아직 없다.
  • SVG의 각 막대는 접근성 이름과 제목에 구간과 건수를 포함한다.

구현은 src/histogram.js, src/source/histogram.js, src/chart/histogram.js다. 원본은 examples/histogram.thinkflow(같은 표본을 수동·자동 구간으로 보인다), 구간 길이 비례(K2), 거꾸로 된 bins의 거절(K5), JSON(C5)은 test/charts.test.js가 공개 출력으로 확인한다. 경계와 집계의 값을 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료).

bins 끝에 measure=count|probability|density를 지정한다. 생략하면 기존 count와 같다. probability의 높이는 구간 건수 ÷ 전체 관측 수로 높이 합이 1이다. density는 이 비율을 실제 구간 폭으로 나누며 면적 합이 1이다. 밀도 높이는 1을 넘을 수 있다. 자동 구간과 JSON 입력에도 같은 계산을 사용한다. 원시 관측값과 구간 건수는 변경하지 않는다.

정규화 모드의 y축에는 소수 눈금도 표시하며 rule은 선택한 높이 단위를 따른다. 축 이름은 작성자가 y "비율(0~1)", y "확률밀도(1/ms)"처럼 입력 단위에 맞춰 쓴다. 면적과 높이는 반올림 전 값으로 계산하고, 보이는 글만 한 함수(histogramLabels)가 구간 범위와 높이를 같은 규칙으로 쓴다. SVG 막대의 접근성 이름과 <title>이 이 한 규칙을 읽어 같은 글이다. SVG 막대의 접근성 이름도 모드·계산값·건수를 구분한다. 계산된 밀도가 기존 차트 숫자 범위를 넘으면 출력을 거부한다.

  • 높이 글: 건수 모드는 정수이고, 비율과 밀도 모드는 값 글자와 같은 decimals 규칙이다(적으면 그 자릿수, 아니면 목록에서 필요한 만큼 최대 6자리). 원시 소수 전체(0.003220858895705515)를 읽는 글에 내보내지 않는다.
  • 구간 범위 글: 경계는 k, M으로 줄이지 않는다(1250을 1.3k로 쓰면 경계가 달라진다). decimals를 적으면 그 자릿수로 맞추고, 아니면 목록에서 가장 긴 소수 자릿수(최대 6)로 반올림한 가장 짧은 글이다([22, 161.714286), [0, 100)).
  • 경계 정체: 반올림이 서로 다른 두 경계를 같은 글로 만들거나 0이 아닌 경계를 0으로 만들면, 그 구간 목록의 경계를 모두 15자리 유효숫자 글로 쓴다. 글이 같아져 구간이 합쳐 보이는 일이 없고, 이 경우 경계에 한해 적은 decimals보다 정체가 앞선다. 구간 자체는 글로 합치지 않는다.
  • 전체 정밀도로 남는 것: 막대의 data-value(반올림하지 않은 높이), 구간 행 키(x= 뒤 bin.lower), 구간표 칸의 data-value. 계산, 경계, 밀도는 그대로다.

비율 합·밀도 면적·빈 구간·단위 변경·JSON·오류 범위를 직접 확인하는 시험은 test/charts.test.js에서 확인되지 않았다(검증 요구사항, 미완료).

워터폴

chart waterfall은 위에서 아래로 항목을 읽으며 증감이 최종 누계에 기여하는 과정을 그린다. row "항목" value=숫자는 직전 누계에서 시작하고 total "합계 이름"는 0에서 현재 누계까지 표시한다. 합계는 값을 다시 입력하지 않으며, 누계를 초기화하거나 다시 더하지 않는다. 시작 누계는 0이다. 최초 잔액이 필요하면 첫 증감 행으로 적는다.

  • 음수·0·0을 가로지르는 변화·음수 합계·중간 합계를 지원한다. 행 이름은 유일하며 입력 순서를 유지한다. 점선은 직전 행의 누계를 다음 행에 연결한다.
  • 증가 막대는 main 색, 감소 막대는 compare의 노란 면과 대비 경계다. 합계는 main 색과 굵은 이름, = 누계로 구분한다. 증감 옆에는 직전 누계 + 증감 = 새 누계 또는 뺄셈식을 적어 색만으로 방향을 구분하지 않는다.
  • 누계는 입력 숫자가 보존한 십진 표현으로 합산한다. 모든 중간 누계가 공통 숫자 범위 안에 있어야 하고, 0이 아닌 변화가 부동소수점 누계에서 사라지면 오류다. 범위를 넘었다가 돌아오는 입력도 중간 값에서 오류다.
  • x축은 누계 값이고 0을 포함한다. rule은 같은 값 축에 놓이며 음수도 받는다. 합계는 0에서, 증감은 직전 누계에서 자란다. 감소는 오른쪽에서 왼쪽으로 자라며 이름과 계산식의 자리는 변하지 않는다.
  • light "항목"으로 행을 강조하고 공통 장면과 박자를 쓴다. 계열·reveal·신뢰구간·로그 축·zero off는 없다. 빠진 증감(value=-)은 받고, 그 뒤의 누계는 알 수 없다(종류와 행 줄). 여러 계열 워터폴, 누계 초기화 문법, 세로 방향과 실시간 갱신은 미지원이다.
  • JSON은 { "label": "항목", "value": 20 }과 { "label": "합계", "total": true }를 같은 배열에 적는다. total: false는 증감 행이다. 합계에 value를 함께 적을 수 없다.

구현은 src/source/waterfall.js, src/chart/waterfall.js다. 원본은 examples/waterfall.thinkflow, 누계와 부호의 읽는 글은 test/charts.test.js(K1)가 공개 출력으로 확인한다. 0과 오류 범위를 직접 확인하는 시험은 확인되지 않았다(검증 요구사항, 미완료).

신뢰구간의 면은 옅은 배경 보조 정보로 그리며 별도 외곽선을 두지 않는다. 실제 계열 선과 점의 경계만 유지해 추정 범위가 데이터보다 강하게 보이지 않게 한다.

차트 그리기 v2

이 절은 src/chart/**의 그리기 계층이 하는 일이다. 원본을 읽는 쪽(문법, 계열 수와 역할 규칙, 결측 허용 범위)은 종류와 행 줄과 문법 표가 정한다. 자동 시험은 차트 입력(IR) 같은 내부 모양을 직접 만들지 않고, 실제 원본에서 그림까지의 흐름을 공개 진입점(buildFigure, toSvg, toHtml)으로 확인한다.

종류와 공통 틀. 막대(묶음 N), 누적(부호 있음), 퍼센트 누적(percent), 선 N, 계단(step, post 방식), 면 N, 산점도(계열 N), 히스토그램, 상자, 누적분포(ecdf), 히트맵, 원·도넛, 덤벨, 차이, 워터폴을 같은 틀(제목, 범례, 그림 영역, 축, 표식)과 같은 팔레트 도우미로 그린다. 순수 계산은 src/chart/data.js(결측 묶음, 계단·누적분포 꼭짓점, 퍼센트 몫, 부호 누적 조각과 범위, 누적분포 집계)이고 그림 좌표와 무관하다.

계열과 팔레트. 범주 번호는 main이 0번, 나머지는 계열 순서대로 1번부터다(main이 없으면 계열 번호 그대로). 역할은 색을 정하지 않는다. 색 수(1판은 7)를 넘는 계열은 층이 올라 막대, 누적 조각, 원 조각, 범례 칸 위에 무늬(해치, 점, 격자)가 덮인다. 점의 모양(원, 사각형, 마름모, 삼각형)은 층이 아니라 범주 번호를 모양 개수(1판은 4)로 나눈 나머지가 정하므로 첫 바퀴 안에서도 이웃한 계열의 점 모양이 다르고, 범례 칸과 그림 안 점이 같은 규칙을 읽는다. 무늬는 층마다 바뀌어 첫 바퀴의 면은 단색이다. 무늬 정의는 그림 하나에 한 번만 있으면 되도록 drawChart가 patternKeys(정렬한 id)와 defs(<pattern> 글)로 돌려주고 본문은 url(#id)로 가리킨다. id는 무늬 종류, 간격, 선 색에서 만든 해시라 같은 무늬는 어느 판에서 그려도 같은 id와 같은 내용이다. 정의를 SVG마다 한 번 넣는 조립은 이 계층의 일이 아니다. 선에는 무늬를 쓰지 않는다.

색 면적. 의미 아이콘·이동 점·막대·누적·원형 조각·히스토그램·워터폴·추적 구간과 범례는 chart-palette.js의 categoryPaint 하나를 사용한다. 채운 면은 color.data.category.N, 면 위 번호와 무늬는 category-on.N이다. 면적에 따라 별도 색을 고르거나 채도를 낮추지 않는다. 상태 글자, 옅은 도형 면과 경계도 같은 범주 계열의 역할 토큰을 쓴다. 기준은 Things 공식 기능 아이콘의 팔레트이고, 차트 자체는 ThinkFlow의 문서 표현 확장이다.

입력값·히스토그램·누적분포 표는 공통 Table을 조립한다. 본문 표와 같은 카드 면·글자색·구분선·안쪽 여백을 사용하고 수치 열의 정렬만 바꾼다. 넓은 표의 가로 이동은 표 영역 안에서 하고 고정 높이나 세로 스크롤은 만들지 않는다.

기대값(reference). 계획이나 목표처럼 x마다 값이 있는 기대값 계열이다. 색은 자기 범주의 값 그대로이고, 막대 차트에서는 슬롯을 차지하지 않고 행 막대 묶음을 가로지르는 흐린 글자색의 세로 눈금과 그 위 값 글자이며(행 기준 rule=과 같은 자리 규칙. 행 기준 글자와 겹치면 눈금 왼쪽에 적는다), 선과 계단과 누적분포는 점선(chart-dashed)과 속이 빈 꼭짓점이며, 실제값은 늘 채우고 실선이다. 점선은 길이로 드러낼 수 없어 x 위치로 닦아 낸다(wipe). 누적, 퍼센트, 덤벨, 원에서 기대값을 막는 것은 해석기의 일이다.

결측, 0. 선과 계단은 결측(-)에서 끊어 M으로 다시 시작하고, 결측 점에는 표식도 data-at도 없고 선이 자라는 시간도 쓰지 않는다. 계단은 값이 다음 점까지 수평으로 이어지며(H, V만 쓴다) 묶음 마지막 점에서 선을 늘이지 않는다. 막대의 0은 가장 짧은 막대와 값 글자 0이고 결측은 막대 없이 안내 글이다. 값이 모두 0이거나 모두 빠진 차트도 그린다: 모두 빠졌으면 틀과 축만 대체 범위(valueRange, 선형 0~0이라 눈금 0과 1, 로그 1~10)로 그리고 어떤 표식에도 값 0을 주지 않는다. 누적과 퍼센트에서 결측이 있는 행은 막대를 그리지 않고 안내 글과 값 목록의 −만 둔다(합계는 생략). 퍼센트에서 행 합이 0이면 막대 없이 합계 0 · 비율 정의 불가를 쓰고 0%로 그리지 않는다. 퍼센트 글자는 1: 42.1% (30)처럼 비율과 원값을 함께 적고 행 끝에 합계 71을 쓴다. 반올림한 퍼센트의 합을 100으로 맞추지 않는다. 설명을 따로 풀어 쓰는 글(꼬리말)은 두지 않는다. 히스토그램, 워터폴, 상자의 빠진 값은 종류와 행 줄이 정한다. 빠진 값은 0이나 이웃 값으로 채우지 않는다.

축. 누적은 양수 합과 음수 합을 모두 덮고 0선을 긋는다. 퍼센트는 0~100, 누적분포는 0~1에 고정한다. 값에 묶인 차트가 모든 프레임에서 같은 축과 같은 카드 크기를 쓰도록 하는 범위 계산은 src/chart/extent.js(extentOfFrames)이고, 누적 값 목록의 줄 수는 값 범위 끝의 글자 폭으로 미리 잡는다.

누적분포. 결측과 유한하지 않은 표본은 세지 않고 정렬해 같은 값을 묶고 고유값마다 count(≤v) / n이다. 곡선은 그림 영역 왼쪽 끝 0에서 시작해 마지막 값에서 1에 닿은 뒤 오른쪽 끝까지 가고 표식은 고유값마다 하나다. 결측을 뺀 개수가 있으면 부제 아래에 결측 k개 제외, 표본이 없는 계열은 곡선 없이 범례에 표본 없음을 쓴다.

누적분포의 계열 구분. 누적분포는 모든 곡선이 100%에서 끝나므로 끝 이름과 안내선을 그리지 않는다. 계열 이름은 공통 범례에서 표시하고 실제 곡선·측정점·기대값 점선은 유지한다. 끝 이름을 위한 가로·세로 공간도 예약하지 않는다. 색이 비슷한 실제 계열끼리의 식별성은 범례만으로 보장하지 않으며 별도 검수 대상이다.

색 없이 구분하기. 파랑과 보라, 빨강과 초록처럼 색각 이상에서 가까운 쌍이 첫 판 안에도 있으므로 선, 계단, 면적이 두 계열 이상이면 색 수와 상관없이 계열마다 끝 이름을 그림 영역 오른쪽의 한 줄 열에 직접 적고, 끝 점(마지막 정의 점)에서 이름까지 안내선을 긋는다. 안내선은 같은 계열의 테두리 색이고 모든 계열이 같은 가는 굵기다. 이름 상자의 줄 수와 높이는 계열 이름과 폭만으로 정해 프레임마다 같고, 그림 영역의 높이는 필요하면 모든 이름 높이의 합까지 자라서 이름이 몇이든 서로 겹치지 않는다. 글자를 줄이거나 이름을 숨기지 않는다. 자리는 끝 점 순서를 지키면서 끝 점 높이에 가장 가깝게(제곱 거리 최소, 인접 위반 병합) 정해 결정적이다. 끝 점이 이름과 같은 높이에 있어 이을 거리가 없거나 끝 점이 없는 계열은 안내선이 숨는다. 값이 하나도 없는 계열은 이름 뒤에 값 없음이 붙는다. 막대, 누적, 퍼센트, 산점도는 범례에 계열 이름만 적는다(번호 키가 없다). 범례 순서는 막대 종류와 산점도가 선언 순서, 그 밖은 main이 먼저다. 누적 조각 사이에는 바탕이 드러나는 틈을 둔다. 산점도 점 이름은 이름만 적고 계열은 색과 모양, 범례가 가른다. 원과 도넛은 조각 바깥 왼쪽·오른쪽 열에 이름과 값 · 비율을 직접 적고 조각 가장자리에서 안내선(조각 테두리 색, 가는 굵기)으로 잇는다. 열 안의 자리는 끝 이름과 같은 배치(placeEndLabels)로 조각 높이에 가장 가깝게 겹치지 않게 정한다. 바깥 열이 들어갈 폭이 아니면(좁은 배치) 원 아래 목록(색 칸, 이름, 값과 비율)으로 내려간다. 조각은 12시에서 시계 방향으로 원본 순서이고 기타 묶음은 끝에 이어진다. 조각 사이 틈은 차트가 놓인 면 색의 가장자리다. 도넛은 구멍 가운데에 큰 합계(text.figure.22)와 그 아래 합계를, 원은 원 아래에 합계 n을 적는다. 덤벨은 끝 이름이 없고 계열이 정확히 둘이라 범주 0번(파랑 원)과 1번(노랑 사각형)만 쓴다. 두 점의 모양이 달라 색 없이도 구분하고, 같은 모양을 쓰는 쌍은 덤벨에 나오지 않는다. 산점도의 점 모양도 같은 범주 번호를 따른다. 첫 바퀴에서 같은 모양을 쓰는 쌍은 파랑과 주황, 노랑과 보라, 빨강과 청록이고 이 쌍은 색으로 갈린다. 산점도의 이 쌍은 범례와 점 이름이 가른다. 범례 칸은 한 함수(legendSwatch)가 막대(속이 찬 칸, 기대값은 세로 눈금, 무늬), 선(선분, 점선, 모양 표식), 점(모양 표식)으로 그린다. 면적의 범례 칸은 면 칸이고, 면적의 꼭짓점 모양은 선과 같은 규칙을 읽는다. 색각 이상 수치는 위험을 고르는 용도일 뿐이다. 실제 화면으로는 차트 여섯 종류를 라이트와 다크에서 정상, 적록 색각 이상(deuteranopia), 무색각(achromatopsia) 에뮬레이션으로 보며 끝 이름, 점 모양을 대조한 범위만 확인했다. 직접 라벨로 바꾼 뒤의 색각 이상 에뮬레이션 화면은 다시 보지 않았다(검증 요구사항, 미완료). 색각 이상이 있는 실제 사용자를 대상으로 한 시험은 하지 않았다(검증 요구사항, 미완료).

프레임 칸. 모든 칸은 값이 0이거나 없어도 표식 요소를 하나씩 늘 가진다(0인 원 조각은 한 점짜리 빈 경로, 0인 막대는 길이 0, 필요 없는 화살표나 번호 키는 visibility="hidden"). 그래서 값이 0에서 양수로 바뀌어도 그림 구조가 같다. chart.markIds가 참이면 요소에 data-mark 또는 data-mark-text(이름 계열:행번호, 같은 칸의 다른 요소는 .p 무늬, .ci 신뢰구간, .t 글자, .k 번호)와 원자료 data-raw가 붙는다. 강조는 원자료(data-raw)가 달라진 표식만 받으므로(src/chart/frames.js의 changedBetween) 퍼센트에서 위치만 밀린 다른 조각은 조용히 옮겨진다. src/chart-frames.js가 이 모듈의 프레임 묶음(frameSet, changedBetween)과 범위(extentOfFrames)를 써서 장면의 프레임 목록과 바뀐 표식을 만든다. 바뀐 표식의 겹침 효과는 재생이 정한다. 값에 묶인 표식(원·도넛 조각 등)은 마우스를 올릴 때 보이는 <title>을 두지 않는다. 프레임 사이에 바뀌지 않는 <title>이 처음 값에 굳기 때문이다. 현재 값은 aria-label이 알리고, 프레임마다 달라지는 <title>이나 표식이 아닌 요소의 달라지는 aria-label은 프레임 묶음을 만들 때 거부한다. 움직이는 SVG는 SMIL이 속성 글을 바꾸지 못하므로 프레임마다 달라지는 aria-label을 요소에서 뺀다.

좁은 화면

넓은 화면의 차트는 논리 폭 spacing.figure.chart.width(720) 그대로 그려진다. 좁은 화면(컨테이너 폭 272, 재생)에서는 HTML이 차트 그림 폭(layout.width)을 그 폭 이하로 잡고 같은 시간표로 차트를 다시 그린 좁은 배치를 함께 싣는다. 차트 보기(plot) 안의 차트와 그래프 카드 안의 차트가 모두 해당한다.

  • 좁은 배치는 항목 이름, 값 글자, 기준선 라벨, 축 제목, 범례를 그 폭 안에서 줄 바꿈해 다시 놓는다. 글자 크기를 줄이거나 이름·점을 생략하거나 차트 보기를 숨기지 않는다. 사건 시각, 프레임 순서, 값 범위는 넓은 배치와 같고 표식의 기하만 다르다.
  • 좁은 배치의 값 축 눈금 글자는 있는 눈금을 같은 걸음으로 건너뛰어 줄이고, 양 끝 눈금은 늘 남긴다. 이웃 글자 사이에 spacing.3이 남는 가장 촘촘한 걸음을 쓴다. 첫 글자는 눈금에서 시작해 세로 축 맨 아래 글자와 모서리에서 닿지 않게 하고, 나머지는 눈금 가운데에 둔다(마지막 글자의 오른쪽 절반은 축 길이를 잴 때 비워 둔다). 어느 걸음도 안 들어가면 첫 글자와 한 줄 아래의 마지막 글자만 쓴다.
  • 넓은 배치와 좁은 배치는 따로 만든 SVG 두 벌이고 화면 폭에 따라 하나만 문서에 있다.
  • 좁은 배치가 판을 실제로 좁히지 못하면 싣지 않고 판 안 가로 이동을 쓴다.
  • 이 배치를 눈으로 본 범위는 표현 범위가 정한다. 거기에는 폭 390 라이트에서 본 면적, 지표, 상태, 워터폴, 산점도, 막대 같은 차트 화면이 있고, 눈금 솎기와 점 라벨 자리를 고친 뒤 다시 봤다. 폭 320·430의 읽힘, 본 범위 밖의 다크 화면, 모든 종류·장면의 읽힘은 검증 요구사항, 미완료다.

신뢰구간의 가는 선과 목표는 공통 중립 회색이다. 히스토그램은 구간 폭과 기준선을 유지하고 위쪽 모서리에만 공통 작은 곡률을 적용한다.