[Stata] Sankey Diagrams: sankey
A Sankey diagram shows how a population divides as it passes through a sequence of categories, with the width of each band proportional to the number of people in it. It suits questions about pathways: how a cohort moves through stages of care, how latent classes at one wave map onto classes at the next, or how one characteristic splits by another. Asjad Naqvi’s sankey draws these diagrams in Stata from a small dataset of flows.
The example shows how California adults divide by nativity, then by English proficiency, then by whether they have a usual source of care.
Installation
ssc install sankey
ssc install palettes
ssc install colrspace
ssc install graphfunctionsThe current version is 1.91 (May 2026), and it requires Stata 15 or newer. The other three packages are dependencies for colors and drawing.
Data
The examples use the 2024 California Health Interview Survey (CHIS) adult public use file, which has 24,810 adult respondents. The CHIS public use files are free and available through a brief application on the UCLA Center for Health Policy Research site. Every post in this series builds its variables with the same short do-file, 00_chis_prep.do:
use "chis_adult_2024.dta", clear
* outcomes
gen k6 = distress if distress >= 0 // K6 score, past month (0-24)
gen nousual = usual == 2 // no usual source of care
gen delay = ah22 == 1 // delayed or did not get medical care, past 12 months
gen erv = er == 1 // any ER visit, past 12 months
gen uninsured = ins == 2
gen needhelp = af81 == 1 if af81 > 0 // needed help for emotional/mental or substance problem
gen fairpoor = ab1 >= 4 // fair or poor self-rated health
gen spd = dstrs12 == 1 // likely serious psychological distress, past year
* groups
gen lep = spk_eng == 3 // speaks English not well or not at all
gen foreign = ah33new == 2
gen female = srsex == 2
* covariates
gen age = srage_p1
recode ahedc_p1 (1 2 = 1 "Less than high school") (3 = 2 "High school") ///
(4 5 6 = 3 "Some college") (7 8 9 = 4 "Bachelor's or more"), gen(educ)
gen pov = povll
label define povllf 4 "300%+ FPL", modify
label values pov povllf
recode ombsrr_p1 (1 = 1 "Latino") (2 = 2 "White") (3 = 3 "Black") (5 = 4 "Asian") ///
(4 6 = 5 "Other or multiracial"), gen(race)
* CHIS design: final weight plus 80 jackknife replicate weights
svyset [pw=rakedw0], jkrweight(rakedw1-rakedw80, multiplier(1)) vce(jackknife) mseLimited English proficiency (lep) means the respondent reported speaking English not well or not at all; 1,520 respondents (6.1 percent) meet that definition. Commands that accept the svy prefix use the jackknife replicate weights; commands that do not accept it use the final weight rakedw0 as a probability weight, which gives the right point estimates and robust standard errors that ignore the replicate design.
Data in flow form
sankey does not take individual records. It takes one row per flow, with a source (from), a destination (to), a size (value), and a step number (by()) that says which part of the diagram the flow belongs to. Weighted counts come from collapse (sum) on the survey weight, built once per step and then appended.
gen str20 s_nat = cond(foreign, "Foreign-born", "US-born")
gen str24 s_eng = cond(spk_eng == 1, "English only", cond(spk_eng == 2, "Very well", "Not well"))
gen str24 s_care = cond(nousual, "None", "Usual source")
* layer 0: nativity -> English proficiency
preserve
collapse (sum) value = rakedw0, by(s_nat s_eng)
gen str24 from = s_nat
gen str24 to = s_eng
gen layer = 0
tempfile l0
save `l0'
restore
* layer 1: English proficiency -> usual source of care
preserve
collapse (sum) value = rakedw0, by(s_eng s_care)
gen str24 from = s_eng
gen str24 to = s_care
gen layer = 1
append using `l0'
replace value = value / 1e6
format value %9.1f
list layer from to value, sepby(layer) noobs +------------------------------------------------+
| layer from to value |
|------------------------------------------------|
| 1 English only None 1.8 |
| 1 English only Usual source 14.3 |
| 1 Not well None 0.8 |
| 1 Not well Usual source 2.5 |
| 1 Very well None 2.0 |
| 1 Very well Usual source 8.3 |
|------------------------------------------------|
| 0 Foreign-born English only 1.5 |
| 0 Foreign-born Not well 3.1 |
| 0 Foreign-born Very well 5.3 |
| 0 US-born English only 14.6 |
| 0 US-born Not well 0.2 |
| 0 US-born Very well 4.9 |
+------------------------------------------------+The values are weighted counts in millions of adults. “Very well” combines respondents who speak English very well or well, and “Not well” combines not well and not at all, following the CHIS variable spk_eng; “None” means no usual source of care.
The diagram
sankey value, from(from) to(to) by(layer) palette(tableau) sort1(value, reverse) ///
valcondition(0.3) format(%9.1f) labsize(3.4) valsize(2.8) gap(6)
valcondition(0.3) hides the value labels of flows smaller than 0.3 million so tiny bands are not crowded with numbers, format() sets the number format, and labsize() and valsize() set the sizes of the category and value labels. gap() sets the space between boxes in a column, and sort1() orders the boxes within each column.
The diagram shows that most of the 3.3 million adults who speak English not well are foreign-born (3.1 million), and that about one in four of them (0.8 of 3.3 million) has no usual source of care, compared with about one in nine of adults who speak only English (1.8 of 16.1 million).
Practical notes
Keep labels short. Category labels are drawn vertically on the boxes, and a long label on a small box spills over its neighbors. Short labels in the diagram with the full definitions in the caption or text work better than wrapped labels.
Color. palette() takes any palette name from colorpalette, and colorvar() colors flows by a variable you define, for example to highlight one pathway and gray out the rest.
Comparisons. A Sankey diagram shows sizes, not rates. For a statement such as “one in four,” compute the proportion from the flow table and report it in the text, as above.
Reference
Naqvi, A. (2026). sankey: A Stata package for Sankey diagrams (version 1.91). https://github.com/asjadnaqvi/stata-sankey
