*! version 1.0.0 09jul2026

Title

chord -- Stata package to draw chord diagrams

Syntax

chord
fromvar tovar [valuevar]
[if] [in]
[, options]

chord
rowvar numvar1 numvar2 [numvar3 ...]
[if] [in]
, adjmatrix [options]

In the option table below, [s] marks options whose items are
space-separated and [c] marks options whose items are
comma-separated; see Separator rules
for the one-line rule behind this and a quick-reference table with examples.

Data input
adjmatrix interpret the variable list as an adjacency matrix (wide format)

colsectors(names) [s] sector names for the numeric columns in adjmatrix mode

Sector layout
sectororder(names) [s] order of sectors around the circle, e.g. sectororder(East South North)

sectorgroup(pairs) [s] assign sectors to groups, e.g. sectorgroup(East-G1 South-G1 North-G2)

groupgap(#) gap in degrees between groups; default is 4 x gap()

gap(#) gap in degrees between adjacent sectors; default is gap(3)

withingapoverride(pairs) [s] per-group within-group gap, e.g. withingapoverride(G1-0.5)

scale give every sector an equal angular span instead of a flow-proportional span

sectorscaleoverride(pairs) [s] multiply the angular span of individual sectors, e.g. sectorscaleoverride(East-1.5)

startangle(#) rotate the whole diagram by # degrees

splithorizontal with exactly two groups, place them top/bottom instead of left/right

Ribbon ordering and placement
linksort(method) ribbon-endpoint ordering: circular (default), asis, value, or minimize

niter(#) refinement iterations for linksort(minimize); default is niter(6)

ribbonposition(spec) [c] pin ribbon endpoints to fixed slots within the whole sector, e.g. ribbonposition(East-South: from(1) to(2), North-South: to(1))

ribbonzorder(pairs) [s] draw specific ribbons on top, e.g. ribbonzorder(East-South North-West); first listed is topmost

Ribbon appearance
colorlist(colors) [s] one color per sector, e.g. colorlist(navy #FF7F0E 128,0,200)

ribbontransparency(#) ribbon transparency, 0-100; default is ribbontransparency(30)

ribboncoloroverride(spec) [s] recolor individual ribbons, e.g. ribboncoloroverride(East-South-red)

bulge(#) curvature of ribbons toward the center, 0-1; default is bulge(0.85)

ribbonbulgeoverride(spec) [s] per-ribbon curvature, e.g. ribbonbulgeoverride(East-South-0.4)

arrow draw the "to" end of each ribbon as an arrowhead to show direction

arrowgap(#) radial depth reserved for the arrowheads; default is arrowgap(0.05)

ribbonborder draw an outline around every ribbon

ribbonborderopts(line_options) default outline style; default is lwidth(thin) lpattern(solid) lcolor(black)

ribbonborderoverride(spec) [c] per-ribbon outline, e.g. ribbonborderoverride(East-South: lcolor(red), North-West: lpattern(dash))

ribbongap(#) gap between ribbons and the sector ring

Sector ring
ringwidth(#) radial thickness of the outer sector ring, 0-1; default is ringwidth(0.06)

ringcolorlist(colors) [s] one ring color per sector; default is the sector color

ringtransparency(#) ring transparency, 0-100; default is ringtransparency(0)

interseg draw a thin inner "target segment" arc, colored by destination sector, under each ribbon's from-end

intersegwidth(#) radial thickness of the target segments; default is intersegwidth(0.01)

intersegoutgap(#) gap between the ring and the target segments; default is intersegoutgap(0.03)

intersegingap(#) gap between the target segments and the ribbons; default is intersegingap(0.01)

Sector labels
labelsize(size) label size; a number or a textsizestyle keyword; default is labelsize(2.2)

labeldir(dir) label orientation: curved (default), curvedwestern, radial, or horizontal

labelradius(#) radius at which labels are placed; default is labelradius(1.12)

labelinside place labels inside the sector ring instead of outside

labelcolor(color) default label color; default is black

labelcoloroverride(pairs) [s] per-sector label color, e.g. labelcoloroverride(East-red)

labelsizeoverride(pairs) [s] per-sector label size, e.g. labelsizeoverride(East-3.2)

sectorlabeloverride(spec) [c] display-text override, e.g. sectorlabeloverride(East-East Region, North-North Region)

labelfont(fontname) font used for sector labels

curvechargap(#) angular spacing per character in curved labels; default is curvechargap(3)

narrowcharwidth(#) relative width of narrow (Latin) characters in curved labels; default is narrowcharwidth(0.55)

curvedlabeladjust(#) radial fine-tuning of curved labels to compensate upper/lower asymmetry; default is curvedlabeladjust(-0.01)

Value axis (outer scale)
ticks draw a circular value axis with tick marks and numeric labels around each sector

tickdir(dir) direction in which values increase: clockwise (default) or counterclockwise

tickstep(#) distance between major ticks in data units; default is chosen automatically per sector

tickstepoverride(pairs) [s] per-sector major tick step, e.g. tickstepoverride(East-25)

minorticks(#) number of intervals between major ticks; default is minorticks(5)

minorticksoverride(pairs) [s] per-sector number of minor intervals

ticklen(#) major tick length; default is ticklen(0.02)

minorlen(#) minor tick length; default is half of ticklen()

tickside(outside|inside) side on which minor ticks are drawn; default is outside

tickgap(#) gap between the ring and the axis line; default is tickgap(0.01)

ticklabgap(#) gap between the tick tips and the tick labels; default is ticklabgap(0.025)

ticklabdir(dir) tick-label orientation: curved (default), radial, or horizontal

ticklabsize(size) tick-label size; default is ticklabsize(1.4)

tickcolor(color) default axis/tick color; default is gs8

ticklabcolor(color) default tick-label color; default is gs6

tickcoloroverride(pairs) [s] per-sector axis/tick color

ticklabcoloroverride(pairs) [s] per-sector tick-label color

ticklabsizeoverride(pairs) [s] per-sector tick-label size

axislwidth(linewidth) axis line width; default is thin

ticklwidth(linewidth) major tick line width; default is thin

minorlwidth(linewidth) minor tick line width; default is vthin

ticklpattern(linepattern) line pattern for the whole value axis; default is solid

ticklabfont(fontname) font used for tick labels

Percentage axis
pctticks draw a second axis showing 0-100% of each sector's total flow

pcttickstep(#) major tick step in percentage points; default is pcttickstep(20)

pctminorticks(#) minor intervals between major ticks; see Options for the default rule

pctaxisgap(#) radial gap between the value axis and the percentage axis; default is pctaxisgap(0.05)

pctticklen(#) major tick length; default is ticklen()

pctminorlen(#) minor tick length; default is half of pctticklen()

pctticklabgap(#) gap between tick tips and labels; default is ticklabgap()

pcttickcolor(color) default axis/tick color; default is tickcolor()

pctticklabcolor(color) default label color; default is ticklabcolor()

pctticklabsize(size) label size; default is ticklabsize()

pcttickcoloroverride(pairs) [s] per-sector axis color

pctticklabcoloroverride(pairs) [s] per-sector label color

pctticklabsizeoverride(pairs) [s] per-sector label size

pctaxislwidth(linewidth) axis line width; default is axislwidth()

pctticklwidth(linewidth) major tick line width; default is ticklwidth()

pctminorlwidth(linewidth) minor tick line width; default is minorlwidth()

pctticklpattern(linepattern) line pattern for the whole percentage axis; default is solid

pctticklabfont(fontname) label font; default is ticklabfont()

Center-to-center link lines
linkchords(spec) [c] overlay curved link lines, e.g. linkchords(East-South: arrow(double) lcolor(red), North-West)

Rendering resolution
nrim(#) points per ribbon end arc; default is nrim(15)

nconn(#) points per ribbon side (Bezier) curve; default is nconn(20)

nring(#) points per sector-ring arc; default is nring(60)

axisarcres(#) points per axis arc line; default is axisarcres(40)

linkres(#) points per link curve; default is linkres(60)

intersegres(#) arc resolution of each target segment; default is intersegres(15)

Titles and overall graph
title(text [, textbox_options]) overall title; passed to twoway title()

subtitle(text [, textbox_options]) subtitle

note(text [, textbox_options]) note

caption(text [, textbox_options]) caption

plotmargin(#) extra margin around the diagram as a fraction of its radius; default is plotmargin(0)

name(name) name of the graph window

scheme(schemename) graph scheme; also supplies the default sector palette

graphregion(suboptions) passed to twoway graphregion(), e.g. graphregion(color(white))

plotregion(suboptions) passed to twoway plotregion()

Description

chord is a Stata command to draw chord diagrams. A chord diagram represents
directed (or undirected) flows between categories: each category is represented by a
sector on the circumference of a circle, and each flow is represented by a
ribbon whose width at each end is proportional to the flow value. The package is
built based on native Stata graphics (help twoway scatter, twoway line, twoway area, twoway pcarrow). The
functionality is inspired by the R package circlize.

The command supports flow-proportional or equal sector spans, sector
grouping, several ribbon-sorting algorithms (including a
crossing-minimization heuristic), directional arrowheads, inner
destination-colored segments, a numeric value axis and a percentage axis
around the circle, curved/radial/horizontal labels with full CJK
(Chinese/Japanese/Korean) support, per-sector and per-ribbon style
overrides, and overlaid center link lines. It can be combined with graph export,
graph combine, schemes, and standard title/textbox options.

The user's dataset is preserved and restored automatically; running
chord does not modify the data in memory.

Data formats

Two input formats are supported.

1. Long (edge-list) format -- the default. Supply two or three
variables:

fromvar origin category (string or numeric; numeric values are converted to strings)

tovar destination category

valuevar (optional) flow value; if omitted, each observation counts as 1 (frequency weights)

Multiple observations with the same from/to pair are summed automatically.
Rows with missing values or zero/negative aggregated flows are dropped.
Self-loops (from = to) are allowed and drawn as ribbons that start and end
in the same sector.

2. Adjacency-matrix (wide) format -- specify the adjmatrix
option. The first variable must be a string variable holding the row
(origin) category names; every remaining variable must be numeric and
represents one destination category. By default each numeric variable's own
name is used as the destination sector name, which works directly with
Unicode variable names (Stata 14+ allows Chinese variable names, for
example). Use colsectors() when the variable names cannot serve as
display names (spaces, length limits, names starting with digits, etc.).
Missing cells are treated as "no flow" and skipped.

Separator rules: space-separated vs comma-separated options

Many options accept a list of items (one per sector, or one per
ribbon). There are exactly two flavors, and one simple rule tells you which
is which:

The rule. If each item's value can never contain a space (a color
name, a hex code, a number, a hyphen-free sector name), the items are
separated by spaces. If an item's value may itself contain spaces
(a combination of sub-options like lcolor(red) lwidth(thick), a
multi-word display text like East Region, or a font name like
Times New Roman), the items are separated by commas.

Quick-reference table.

Space-separated [s] Comma-separated [c]

colsectors(), sectororder() ribbonposition()

sectorgroup(), sectorscaleoverride() ribbonborderoverride()

withingapoverride() linkchords()

tickstepoverride(), minorticksoverride() sectorlabeloverride()

labelcoloroverride(), labelsizeoverride()

tickcoloroverride(), ticklabcoloroverride()

ticklabsizeoverride()

ribboncoloroverride(), ribbonbulgeoverride()

ribbonzorder()

colorlist(), ringcolorlist()

pcttickcoloroverride(), pctticklabcoloroverride()

pctticklabsizeoverride()

One example of each flavor side by side:

space-separated (each item is one "word"):

ribboncoloroverride(East-South-red North-West-#1F77B4)

comma-separated (each item may contain spaces):

ribbonborderoverride(East-South: lcolor(red) lwidth(medthick), North-West: lpattern(dash))

In color lists, RGB triples would break the space rule (128 0 200 is
three words), so write them in the compact comma form 128,0,200
(one word) instead.

Safety net. If chord detects a comma inside a
space-separated option, it prints a bilingual note reminding you that the
option uses spaces, so accidental commas are flagged rather than silently
misparsed.

Options

Data input

adjmatrix declares that the variable list is a wide adjacency matrix
rather than a from/to/value edge list; see Data formats above. At
least one row variable plus two numeric columns are required.

colsectors(names) supplies, in order, one sector name per numeric
column in adjmatrix mode (space-separated). The number of names must
equal the number of numeric columns. If omitted, the variable names
themselves are used.

Sector layout

sectororder(names) fixes the order of sectors around the circle
(space-separated), e.g. sectororder(East South North Southwest Northeast).
Sectors are laid out in the listed order, proceeding clockwise from
the 12 o'clock position. The list must contain exactly the set of
categories that occur in the data, each exactly once; otherwise an error is
issued that also displays the categories actually found. If omitted,
sectors appear in order of first appearance in the data (from-values first,
then to-values).

sectorgroup(pairs) assigns sectors to named groups using
space-separated sector-group pairs, for example
sectorgroup(East-Coastal South-Coastal North-Inland). Sectors of the
same group are rearranged to be contiguous on the circle. Sectors not
mentioned form their own single-member groups. When the result is exactly
two groups, the diagram is automatically rotated so that the two group
centers sit exactly left and right (or top and bottom with
splithorizontal); a note is displayed when this happens, and
startangle() can still be used for fine adjustment.

groupgap(#) sets the gap in degrees inserted between different groups.
The default is 4 times gap().

gap(#) sets the gap in degrees between adjacent sectors (within a
group, or everywhere when no groups are defined). Default is 3.

withingapoverride(pairs) overrides the within-group gap for particular
groups using space-separated group-# pairs, e.g.
withingapoverride(Coastal-0.5).

scale makes all sectors span the same angle regardless of their total
flow, mimicking scale = TRUE in circlize. Without it, each sector's
span is proportional to its total (incoming + outgoing) flow.

sectorscaleoverride(pairs) multiplies the angular weight of individual
sectors by a positive factor, using space-separated sector-# pairs;
e.g. sectorscaleoverride(East-1.5 North-0.8) enlarges East by 50% and
shrinks North by 20%. It can be combined with or without scale.

startangle(#) rotates the whole diagram. The first sector starts at
the 12 o'clock position and # rotates it by # degrees.

splithorizontal only takes effect when sectorgroup() yields
exactly two groups; the two groups are then centered at the top and bottom
of the circle instead of left and right.

Ribbon ordering and placement

linksort(method) chooses how ribbon endpoints are stacked within each
sector:

circular (default) sorts by circular distance between origin
and destination sectors, which usually produces a tidy, circlize-like
layout.

asis keeps the original data order.

value sorts by descending flow value.

minimize starts from the circular layout and iteratively
reorders endpoints to reduce the number of ribbon crossings. The command
reports the crossing counts of the seed layout and the refined layout and
automatically keeps whichever is better.

niter(#) sets the number of refinement iterations used by
linksort(minimize); default is 6.

ribbonposition(spec) pins the endpoint of specific ribbons to a fixed
physical slot within a sector's whole arc. Within each sector, all
ribbon endpoints that touch it (both from-ends and to-ends) share one
sequence of slots numbered 1, 2, ..., m, where m is the total
number of ribbons touching that sector; slot 1 is the first position in the
stacking direction. The specification is a comma-separated list of
A-B: from(#) to(#) items — the comma goes
between items, i.e. after one ribbon's sub-options and before the next
A-B pair:

ribbonposition(East-South: from(1) to(2), North-South: to(1))

Here A-B identifies a directed ribbon (from A to B),
from(#) pins its endpoint within sector A's arc, and
to(#) pins its endpoint within sector B's arc. Either
suboption may be given alone. Because from- and to-endpoints share the
same slot numbering inside a sector, an endpoint can be pinned anywhere in
the sector, not just inside its own from- or to-block — and two pins in
the same sector may not claim the same slot. Unpinned endpoints fill the
remaining slots in their natural order. Out-of-range or duplicated slots
are reported as errors.

ribbonzorder(pairs) raises specific directed ribbons above all
others, using space-separated A-B pairs:

ribbonzorder(East-South North-West)

Ribbons are drawn bottom-up: unlisted ribbons first, then the listed pairs
so that the first listed pair ends up on top (in the example,
East-South is topmost, North-West second). Each pair must match an
existing directed edge (direction matters) and may not be repeated.

Ribbon appearance

colorlist(colors) supplies one color per sector, space-separated, in
sector order, e.g. colorlist(navy #FF7F0E 128,0,200 green%60).
Ribbons take the color of their origin sector and are drawn
semi-transparent by default; append %alpha to a color to set an
individual item's opacity. Any color may use the extended color syntax
described under Common syntax conventions below. If fewer colors than
sectors are given, the remaining sectors fall back to the default palette:
the scheme palette when scheme() is specified, otherwise evenly
spaced HSV hues.

ribbontransparency(#) sets the global ribbon transparency (0 = opaque,
100 = invisible). Default is 30. A per-color %alpha suffix in
colorlist() or ribboncoloroverride() takes precedence for that
item.

ribboncoloroverride(spec) recolors individual directed ribbons with
space-separated A-B-color triples, e.g.
ribboncoloroverride(East-South-red East-North-#1F77B4%60). Direction
matters.

bulge(#) controls how strongly ribbon sides bend toward the center
(0 = straight chords, 1 = pulled fully to the center). Default is 0.85.

ribbonbulgeoverride(spec) sets the curvature of individual directed
ribbons with space-separated A-B-# triples (# between 0 and 1), e.g.
ribbonbulgeoverride(East-South-0.4 North-West-1). Unlisted ribbons
keep the global bulge().

arrow replaces the smooth arc at each ribbon's "to" end with a
triangular arrowhead pointing at the destination sector, making flow
direction visible. arrowgap(#) reserves the radial depth of the
arrowheads (default 0.05) and slightly shrinks the ring accordingly.

ribbonborder outlines every ribbon using ribbonborderopts()
(default lwidth(thin) lpattern(solid) lcolor(black)).

ribbonborderoverride(spec) restyles the outlines of individual
ribbons and also works without ribbonborder (only the listed
ribbons then get outlines). It is a comma-separated list of
A-B: line_options items — the comma sits between one
ribbon's options and the next pair:

ribbonborderoverride(East-South: lcolor(red) lwidth(medthick), North-West: lpattern(dash))

ribbongap(#) sets the gap between the ribbons and the inner edge of
the sector ring (or, with interseg, between the ribbons and the
target segments). When omitted, a sensible default is used (0.04 without
interseg).

Sector ring

ringwidth(#) sets the radial thickness of the outer sector arcs,
strictly between 0 and 1 (fraction of the unit radius). Default is 0.06.

ringcolorlist(colors) (space-separated, one per sector) styles the
ring independently of the ribbons; by default the ring reuses the sector
colors.

interseg adds, just inside the ring, a thin arc under each ribbon's
from-end colored by the destination sector (using the destination's
ring color), so that a sector's outgoing composition can be read at a
glance -- similar to the "diffHeight + target track" style of
circlize. intersegwidth(), intersegoutgap(),
intersegingap(), and intersegres() control its thickness,
outer gap, inner gap, and arc resolution.

Sector labels

labelsize(size) accepts a number (in Stata text-size units) or one of
tiny, vsmall, small, medsmall, medium,
medlarge, large, vlarge, huge. Default is 2.2.

labeldir(dir) chooses the label orientation:

curved (default) bends the label along the circle. Wide
(CJK/full-width) characters are placed one by one; runs of narrow
(Latin/digit) characters are kept together as one segment and rendered
natively, so Western text keeps natural letter spacing.

curvedwestern is like curved but also places narrow
(Western) characters one by one along the arc, so long Latin labels hug the
circle more tightly.

radial points the label along the radius.

horizontal keeps the label upright.

In curved and radial modes, labels on the appropriate half of
the circle are flipped automatically so that text never appears upside
down.

labelradius(#) places labels at radius # (default 1.12, i.e. just
outside the ring). labelinside instead centers labels within the
ring itself; a note suggests enlarging ringwidth() if the ring is
thin. With labelinside, Western segments are automatically switched
to per-character curved placement (as in labeldir(curvedwestern)) so
they follow the narrow arc band; a note is displayed when this happens.

curvechargap(#) is the angular spacing per (wide) character in curved
labels, in degrees; default is 3. narrowcharwidth(#) (default 0.55)
is the assumed width of a narrow character relative to a wide one when
allocating arc length. curvedlabeladjust(#) (default -0.01) is a
small radial offset that compensates the slight vertical asymmetry of
curved labels between the upper and lower halves of the circle; set it to 0
to disable.

labelfont(fontname) sets the label font. Inline
{fontface "name":...} tags inside sector display texts
are also honored, including in curved mode, where each character keeps the
font of its enclosing tag.

sectorlabeloverride(spec) replaces the displayed text of individual
sectors without affecting matching elsewhere. It is a comma-separated
list of sector-text items (the text may contain spaces):

sectorlabeloverride(East-East Region, North-North Region)

To render a replacement text in a specific font, embed a
{fontface "name":...} tag in the text part; font names
with spaces are fine because this option is comma-separated:

sectorlabeloverride(East-{fontface "Times New Roman":East Region}, North-North Region)

All other options (sectorgroup(), the various overrides, the data
itself) keep referring to the original sector name.

labelcolor(color) sets the default label color;
labelcoloroverride(pairs) and labelsizeoverride(pairs) adjust
individual sectors with space-separated sector-value pairs, e.g.
labelcoloroverride(East-red) and labelsizeoverride(East-3.2).
Precedence follows the common rules described below.

Value axis (ticks)

ticks draws, outside each sector, a continuous arc-shaped axis with
major ticks (numbered in data units, starting at 0) and unnumbered minor
ticks. Values increase in the tickdir() direction along each
sector.

When tickstep() is not given, the major step for each sector is
chosen automatically as a "nice" number (1, 2, or 5 times a power of 10)
that yields roughly five major intervals over the sector's total flow.
tickstepoverride() and minorticksoverride() adjust individual
sectors via space-separated sector-# pairs, e.g.
tickstepoverride(East-25) and minorticksoverride(East-2).

tickside(outside|inside) determines whether minor ticks protrude
outward or inward from the axis line. Major ticks always point outward,
and tick labels always sit outside the major tick tips at distance
ticklabgap().

ticklpattern(linepattern) sets a global line pattern (e.g.
dash, dot) for the whole value axis: main arc, major ticks, and
minor ticks; default is solid.

The color/size options (tickcolor(), ticklabcolor(),
ticklabsize(), and their per-sector ...override() variants)
follow the common precedence rules described below.

Percentage axis (pctticks)

pctticks draws a second axis on which every sector runs from 0% to
100% of its own total flow, regardless of absolute size. It can be used
alone (it then occupies the position of the value axis) or together with
ticks (it is then placed pctaxisgap() outside the value-axis
labels). All styling defaults fall back to the corresponding value-axis
settings unless overridden by the pct... options; a distinct line
pattern via pctticklpattern() (e.g. dot) is handy for visually
distinguishing the two axes.

Default minor-tick rule: when ticks and pctticks are both on,
the percentage axis draws no minor ticks (to reduce clutter); when only
pctticks is on, it inherits minorticks(). Override with
pctminorticks(#).

The pct...override() options are space-separated sector-value
pairs — exactly the same syntax as their value-axis counterparts.

Center-to-center link lines

linkchords(spec) overlays curved lines connecting the midpoints of a
ribbon's two ends, optionally with arrowheads -- useful for calling
out specific flows on a busy diagram. The specification is a
comma-separated list of A-B pairs, each optionally
followed by : and per-link suboptions; the comma sits between one
link's suboptions and the next pair:

linkchords(East-South: arrow(double) lcolor(red) lwidth(medthick) bulge(0.6), Southwest-Northeast: arrow(none) lpattern(dash), North-West)

Recognized per-link suboptions:

arrow(single|double|none) arrowhead style for this link:
one arrowhead at the destination end, arrowheads at both ends, or a plain
line; default is single;

lcolor(color) line and arrowhead color; default is
black;

lwidth(linewidth) line width; default is
thin;

lpattern(linepattern) line pattern (e.g. solid,
dash, dot); default is solid;

msize(marker_size) arrowhead size; default is
1pt;

mangle(#) arrowhead angle, passed to
twoway pcarrow; default is the scheme default;

barbsize(#) arrowhead barb size, passed to
twoway pcarrow; default is the scheme default;

radiusfrom(#) and radiusto(#) radii at which
this link starts and ends (both must be positive); default is the ribbon
rim radius at each end;

bulge(#) curvature of this link, 0-1 (0 = straight
chord, 1 = pulled fully to the center); default is the value of
bulge();

res(#) number of points used to draw this link's curve
(>= 2); default is linkres();

any remaining text is appended verbatim to the underlying
twoway line call.

Rendering resolution

nrim(#) sets the number of points used to approximate each ribbon's
end arc; default is 15.

nconn(#) sets the number of points per ribbon side (Bezier) curve;
default is 20.

nring(#) sets the number of points per sector-ring arc; default
is 60.

axisarcres(#) sets the number of points used to draw each sector's
continuous axis arc line (the value axis and the percentage axis both use
it); default is 40.

linkres(#) sets the default number of points per linkchords()
curve; default is 60. Individual links can override it with the per-link
res() suboption.

The defaults render smoothly at ordinary sizes; increase these values for
large-format export, decrease them to speed up drawing of very dense
diagrams. intersegres(#) (documented under Sector ring) plays
the same role for the inner target segments.

Common syntax conventions

Colors. Wherever a color is expected, you may use:

- any Stata colorstyle name, e.g. navy;

- an RGB triple in the compact comma form 128,0,200 (required inside space-separated lists, where the spaced form "128 0 200" would split into three items);

- a hex code, #RRGGBB or #RGB, e.g. #1F77B4;

- any of the above followed by %alpha (0-100 opacity), e.g. #E24A33%60, which sets that item's opacity. Color opacity requires Stata 15 or newer.

Pair lists. Space-separated pair options expect items of the form
name-value (or name1-name2-value for per-ribbon items), where
name is a sector name exactly as it appears in the data. Because
- is the separator, sector names containing hyphens cannot be
addressed by these options; rename such categories first (or use
sectorlabeloverride() to display a hyphenated label while keeping a
hyphen-free internal name).

Comma-separated options. In ribbonposition(),
ribbonborderoverride(), linkchords(), and
sectorlabeloverride(), the comma separates whole items (pair +
its sub-options/text). See
Separator rules above.

Style precedence. For any styled element (sector labels, ticks, tick
labels, percentage-axis items), the effective style of sector i is
resolved as: ...override() entry for that sector, if any; otherwise
the global option (e.g. labelcolor(), tickcolor()); otherwise
the built-in default.

Remarks and notes

1. Stata version and fonts. chord requires Stata 14.0
or newer; the %alpha opacity suffix in colors additionally requires
Stata 15. For CJK labels, make sure the graph font (or labelfont())
covers the required glyphs.

2. Data are never modified. The command runs under
preserve/restore; your dataset is intact afterward. Results of
interest are returned in r().

3. Direction and self-loops. Ribbons are directed (from origin to
destination). Use arrow to visualize direction. Self-loops are
drawn within their own sector.

4. Angles vs. radii. gap(), groupgap(),
startangle(), and curvechargap() are in degrees; radii and
radial gaps (ringwidth(), ticklen(), ribbongap(),
labelradius(), etc.) are fractions of the unit circle radius.

5. Too many categories. If the total of all gaps reaches 360 degrees
the command exits with an error; reduce gap()/groupgap() or the
number of categories.

6. Legibility of dense axes. With ticks, sectors with very
large totals may receive many major ticks; use tickstep() or
tickstepoverride() to thin them.

7. Quoting. Sector names in the pair-list options should be written
without quotes; stray quotes and backticks are stripped defensively, but it
is best not to include them. If a comma is detected inside a
space-separated option, a warning note is printed.

8. Exporting. For publication-quality output, export with
graph export to PDF/SVG/PNG at the desired size; consider raising
nconn()/nring() for very large formats.

Examples

Setup: a 5-region flow dataset (long format)

. clear

. input str10 from str10 to value

"South" "East" 19

"North" "East" 18

"Southwest" "East" 7

"Northeast" "East" 46

"East" "South" 47

"North" "South" 18

"Southwest" "South" 29

"Northeast" "South" 30

"East" "North" 31

"South" "North" 9

"Southwest" "North" 32

"Northeast" "North" 13

"East" "Southwest" 33

"South" "Southwest" 39

"North" "Southwest" 12

"Northeast" "Southwest" 11

"East" "Northeast" 46

"South" "Northeast" 13

"North" "Northeast" 23

"Southwest" "Northeast" 40

"North" "North" 15

"Southwest" "Southwest" 30

end

Basic chord diagram

. chord from to value

Directional arrows, custom colors, and a value axis

. chord from to value, arrow ticks colorlist(#1F77B4 #FF7F0E #2CA02C #D62728 #9467BD) graphregion(color(white))

Fixed sector order (clockwise from 12 o'clock), larger gaps, rotated start

. chord from to value, sectororder(East South North Southwest Northeast) gap(5) startangle(15)

Two groups, auto-centered left/right, group gap widened

. chord from to value, sectorgroup(East-Coastal South-Coastal Northeast-Inland North-Inland Southwest-Inland) groupgap(15)

Crossing minimization

. chord from to value, linksort(minimize)

Per-ribbon curvature and outline (note: one space-separated, one comma-separated)

. chord from to value, ribbonbulgeoverride(East-South-0.4) ribbonborderoverride(East-South: lcolor(red) lwidth(medthick))

Inner destination segments with a value axis

. chord from to value, arrow interseg ticks

Value axis plus dotted percentage axis

. chord from to value, ticks pctticks pctticklpattern(dot) pcttickstep(25) labelradius(1.25) gap(6)

Overlay a double-headed link line on one pair and put that ribbon on top

. chord from to value, linkchords(East-South: arrow(single) lcolor(red) lwidth(medthick)) ribbonzorder(East-South)

Titles with textbox options and display-name overrides

. chord from to value, title("Regional trade flows", size(large) color(navy)) note("Source: simulated data") sectorlabeloverride(East-East Region, North-North Region) arrow

Adjacency-matrix (wide) input

. clear

. input str10 region double South double East double North double Southwest double Northeast

"South" 0 19 9 39 13

"East" 47 0 31 33 46

"North" 18 18 15 12 23

"Southwest" 29 7 32 30 40

"Northeast" 30 46 13 11 0

end

. chord region South East North Southwest Northeast, adjmatrix arrow

Chinese labels (Stata 14+ Unicode variable names work directly)

. clear

. input str10 区域 double 华南 double 华东 double 华北 double 西南 double 东北

"华南" 0 19 9 39 13

"华东" 47 0 31 33 46

"华北" 18 18 15 12 23

"西南" 29 7 32 30 40

"东北" 30 46 13 11 0

end

. chord 区域 华南 华东 华北 西南 东北, adjmatrix labeldir(curved) labelfont(SimSun) //宋体

Stored results

chord stores the following in r():

Scalars

r(sector_count) number of sectors drawn

r(edge_count) number of aggregated edges (ribbons)

r(total_flow) sum of all flow values

Macros

r(sectors) sector names in the order drawn

Authors

Jiajun Zhou (周家俊)

Technical University of Munich (TUM)

zhoujiajun_06@163.com

De Zhou (周德)

Nanjing Agricultural University

zhou-de@hotmail.com

The design of chord is inspired by the R package
circlize (Gu et al., 2014, Bioinformatics 30:2811-2812).
Bug reports and feature requests are welcome by email or on the project's GitHub repository issues.