D3.js v4
Shirley Wu
(@sxywu)
Shirley Wu
Agenda
D3 Ecosystem
Selections
Data binding
Enter-append
Scales
Shapes
Update & Exit
Transitions
Forces
d3 ecosystem
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Selection & Data
5 rectangle elements
Select all rectangle elements that exist
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
"Bind" data to the selections
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Loop through each rectangle selection
Get passed in (data, index)
Selection & Data
<svg>
<rect />
<rect />
<rect />
<rect />
<rect />
</svg>
<script>
var data = [100, 250, 175, 200, 120];
d3.selectAll('rect')
.data(data)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Selection & Data
Play with the code
(Change some numbers, add/remove a rect, console log all the things)
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Wut, no rectangle elements?!
So what are we even selecting?
A: an empty selection
So how are those bars appearing?
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Magic ✨
Enter-append
<svg></svg>
<script>
var rectWidth = 100;
var height = 300;
var data = [100, 250, 175, 200, 120];
var svg = d3.select('svg');
svg.selectAll('rect')
.data(data)
.enter().append('rect')
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('width', rectWidth)
.attr('height', d => d)
.attr('fill', 'blue')
.attr('stroke', '#fff');
</script>
Enter-append
Play with the code
(Try adding/removing some numbers, and make sure to console log all the things)
Scales & Axes
d3.scaleLinear()
.domain([min, max]) // input
.range([min, max]); // output
scale: mapping from
data attributes (domain)
to display (range)
date → x-value
value → y-value
value → opacity
etc.
Scales & Axes
// get min/max
var min = d3.min(data, d => d[city]);
var max = d3.max(data, d => d[city]);
// or use extent, which gives back [min, max]
var extent = d3.extent(data, d => d[city]);
var yScale = d3.scaleLinear()
.domain(extent)
.range([height, 0]);
// continuous
d3.scaleLinear()
d3.scaleLog()
d3.scaleTime()
// ordinal
d3.scaleBand()
Scales I use often:
Scales & Axes
var yAxis = d3.axisLeft()
.scale(yScale); // pass in a scale
d3.select('svg')
// create a group element we can translate
// so that the axis will be visible in SVG
.append('g')
.attr('transform', 'translate(40, 20)')
// selection.call(yAxis) is the same as yAxis(selection)
// and an axis will be created within the selection
.call(yAxis);
Scales & Axes
Play with the code
(Play with different scales, try different tick values and formats)
Exercise time
SVG Elements
rect
x: x-coordinate of top-left
y: y-coordinate of top-left
width
height
circle
cx: x-coordinate of center
cy: y-coordinate of center
r: radius
text
x: x-coordinate
y: y-coordinate
dx: x-coordinate offset
dy: y-coordinate offset
text-anchor: horizontal text alignment
Hi!
path
d: path to follow
Moveto, Lineto, Curveto, Arcto
SVG Elements
shapes
<path d="M0,56.17021276595736L2.191780821917808,91.91489361702125
L4.383561643835616,102.12765957446803L6.575342465753424,105.95744680851061
L8.767123287671232,107.23404255319141L10.95890410958904,128.93617021276592
L13.150684931506849,132.76595744680844L15.342465753424658,131.48936170212764
L17.534246575342465,132.76595744680844L19.726027397260275,89.3617021276595
L21.91780821917808,76.595744680851L24.10958904109589,71.48936170212761
L26.301369863013697,35.74468085106383L28.493150684931507,0L30.684931506849317,31.914893617021278
L32.87671232876712,70.21276595744675L35.06849315068493,76.595744680851
L37.26027397260274,100.85106382978714L39.45205479452055,104.68085106382978
L41.64383561643836,126.38297872340416L43.83561643835616,136.59574468085106
L46.02739726027397,81.70212765957439L48.21917808219178,25.531914893617
L50.41095890410959,79.14893617021275L52.602739726027394,140.42553191489355
L54.794520547945204,159.57446808510636L56.986301369863014,140.42553191489355
L59.178082191780824,114.89361702127653L61.369863013698634,122.55319148936167
L63.561643835616444,119.99999999999994L65.75342465753424,153.19148936170208
L67.94520547945206,117.44680851063828L70.13698630136986,31.914893617021278
L72.32876712328768,139.1489361702127L74.52054794520548,211.91489361702128
L76.71232876712328,201.7021276595745L78.9041095890411,185.1063829787234
L81.18721461187215,200.4255319148936L83.37899543378995,210.6382978723404
L85.57077625570776,159.57446808510636L87.76255707762557,146.8085106382978
L89.95433789954338,168.51063829787233L92.14611872146119,167.23404255319147
L94.337899543379,169.7872340425532L96.5296803652968,173.61702127659572
L98.72146118721462,174.8936170212766L100.91324200913243,190.21276595744675
L103.10502283105023,183.8297872340425L105.29680365296802,178.7234042553191
L107.48858447488585,231.06382978723406L109.68036529680364,213.19148936170214
L111.87214611872147,204.25531914893617L114.06392694063928,188.936170212766
L116.25570776255707,159.57446808510636L118.4474885844749,153.19148936170208
L120.63926940639269,199.14893617021275L122.83105022831052,172.34042553191486
L125.0228310502283,188.936170212766L127.21461187214611,205.531914893617
L129.40639269406392,224.68085106382978L131.59817351598173,220.85106382978728
L133.78995433789956,85.53191489361697L135.98173515981736,62.55319148936164
L138.17351598173516,112.34042553191489L140.36529680365297,183.8297872340425
L142.55707762557077,199.14893617021275L144.7488584474886,219.5744680851064
L146.9406392694064,236.17021276595744L149.13242009132418,264.2553191489362
L151.324200913242,220.85106382978728L153.5159817351598,191.4893617021276
L155.70776255707764,233.61702127659578L157.89954337899545,251.48936170212767
L160.09132420091322,254.04255319148933L162.28310502283105,268.0851063829787
L164.47488584474885,233.61702127659578L166.66666666666669,245.10638297872342
L168.85844748858446,220.85106382978728L171.05022831050226,229.78723404255317
L173.2420091324201,240.00000000000003L175.4337899543379,227.23404255319153
L177.62557077625573,238.72340425531917L179.8173515981735,176.17021276595747
L182.00913242009133,250.2127659574468L184.20091324200914,245.10638297872342
L186.39269406392694,232.34042553191492L188.58447488584474,270.63829787234044
L190.77625570776254,254.04255319148933L192.96803652968038,232.34042553191492
L195.15981735159818,206.8085106382979L197.35159817351598,181.27659574468083
L199.54337899543378,217.02127659574467L201.73515981735162,168.51063829787233
L203.92694063926942,178.7234042553191L206.11872146118722,222.12765957446805
L208.31050228310502,183.8297872340425L210.50228310502283,185.1063829787234
L212.69406392694066,231.06382978723406L214.88584474885846,205.531914893617
L217.07762557077626,131.48936170212764L219.26940639269407,188.936170212766
L221.46118721461187,197.8723404255319L223.6529680365297,220.85106382978728
L225.8447488584475,194.04255319148936L228.0365296803653,171.063829787234
L230.2283105022831,181.27659574468083L232.42009132420088,222.12765957446805
L234.61187214611874,278.29787234042556L236.80365296803654,300L238.99543378995435,282.1276595744681
L241.18721461187215,252.76595744680856L243.37899543378992,200.4255319148936
L245.57077625570778,171.063829787234L247.7625570776256,240.00000000000003
L249.9543378995434,181.27659574468083L252.14611872146116,229.78723404255317
L254.33789954337897,191.4893617021276L256.5296803652968,172.34042553191486
L258.72146118721463,213.19148936170214L260.91324200913243,214.468085106383
L263.10502283105023,169.7872340425532L265.29680365296804,194.04255319148936
L267.48858447488584,218.29787234042553L269.68036529680364,218.29787234042553
L271.87214611872145,201.7021276595745L274.06392694063925,199.14893617021275
L276.25570776255705,192.7659574468085L278.4474885844749,169.7872340425532
L280.6392694063927,159.57446808510636L282.8310502283105,163.40425531914894
L285.0228310502283,194.04255319148936L287.2146118721461,169.7872340425532
L289.4063926940639,168.51063829787233L291.5981735159817,188.936170212766
L293.78995433789953,217.02127659574467L295.98173515981733,224.68085106382978
L298.17351598173514,236.17021276595744L300.365296803653,219.5744680851064
L302.5570776255708,187.6595744680851L304.7488584474886,219.5744680851064
L306.9406392694064,197.8723404255319L309.1324200913242,246.38297872340428
L311.324200913242,234.89361702127655L313.5159817351598,222.12765957446805
L315.7077625570776,174.8936170212766L317.8995433789954,165.95744680851058
L320.0913242009133,142.9787234042553L322.2831050228311,196.595744680851
L324.4748858447489,247.65957446808508L326.6666666666667,277.0212765957447
L328.8584474885845,256.59574468085106L331.0502283105023,220.85106382978728
L333.2420091324201,232.34042553191492L335.4337899543379,242.5531914893617
L337.6255707762557,209.36170212765953L339.8173515981735,154.46808510638294
L342.00913242009136,233.61702127659578L344.20091324200916,238.72340425531917
L346.39269406392697,219.5744680851064L348.58447488584477,228.5106382978723
L350.7762557077625,196.595744680851L352.9680365296804,227.23404255319153
L355.1598173515982,218.29787234042553L357.26027397260276,236.17021276595744
L359.45205479452056,168.51063829787233L361.6438356164383,151.91489361702122
L363.83561643835617,142.9787234042553L366.027397260274,159.57446808510636
L368.2191780821918,241.27659574468083L370.4109589041096,255.3191489361702
L372.6027397260274,271.91489361702133L374.79452054794524,222.12765957446805
L376.98630136986304,200.4255319148936L379.17808219178085,200.4255319148936
L381.3698630136986,238.72340425531917L383.5616438356164,231.06382978723406
L385.75342465753425,264.2553191489362L387.94520547945206,222.12765957446805
L390.13698630136986,165.95744680851058L392.32876712328766,159.57446808510636
L394.52054794520546,188.936170212766L396.7123287671233,160.85106382978722
L398.9041095890411,139.1489361702127L401.09589041095893,204.25531914893617
L403.28767123287673,211.91489361702128L405.47945205479454,190.21276595744675
L407.67123287671234,210.6382978723404L409.86301369863014,245.10638297872342
L412.05479452054794,251.48936170212767L414.24657534246575,225.95744680851067
L416.43835616438355,218.29787234042553L418.63013698630135,201.7021276595745
L420.82191780821915,169.7872340425532L423.013698630137,181.27659574468083
L425.2054794520548,168.51063829787233L427.3972602739726,215.74468085106378
L429.5890410958904,206.8085106382979L431.7808219178082,199.14893617021275
L433.972602739726,194.04255319148936L436.16438356164383,177.44680851063822
L438.35616438356163,179.99999999999997L440.54794520547944,153.19148936170208
L442.73972602739724,144.25531914893617L444.9315068493151,116.17021276595742
L447.1232876712329,182.55319148936172L449.3150684931507,153.19148936170208
L451.5068493150685,117.44680851063828L453.6986301369863,122.55319148936167
L455.8904109589041,150.63829787234044L458.0821917808219,173.61702127659572
L460.2739726027397,158.29787234042547L462.4657534246575,167.23404255319147
L464.6575342465753,174.8936170212766L466.8493150684932,183.8297872340425
L469.041095890411,209.36170212765953L471.2328767123288,185.1063829787234
L473.4246575342466,174.8936170212766L475.6164383561644,178.7234042553191
L477.8082191780822,135.31914893617017L480,150.63829787234044L482.1917808219178,192.7659574468085
L484.3835616438356,187.6595744680851L486.57534246575347,174.8936170212766
L488.76712328767127,178.7234042553191L490.95890410958907,219.5744680851064
L493.1506849315069,192.7659574468085L495.3424657534247,141.70212765957444
L497.5342465753425,179.99999999999997L499.7260273972603,205.531914893617
L501.9178082191781,200.4255319148936L504.1095890410959,190.21276595744675
L506.30136986301363,187.6595744680851L508.49315068493155,160.85106382978722
L510.68493150684935,182.55319148936172L512.8767123287671,168.51063829787233
L515.068493150685,135.31914893617017L517.2602739726027,158.29787234042547
L519.4520547945206,186.38297872340422L521.6438356164383,191.4893617021276
L523.8356164383562,190.21276595744675L526.0273972602739,181.27659574468083
L528.2191780821918,191.4893617021276L530.4109589041096,191.4893617021276
L532.6027397260274,176.17021276595747L534.7945205479452,157.0212765957447
L536.986301369863,167.23404255319147L539.1780821917808,188.936170212766
L541.3698630136986,150.63829787234044L543.5616438356165,173.61702127659572
L545.7534246575342,165.95744680851058L547.945205479452,168.51063829787233
L550.1369863013698,162.12765957446808L552.3287671232877,154.46808510638294
L554.5205479452055,90.63829787234039L556.7123287671233,126.38297872340416
L558.9041095890411,153.19148936170208L561.0958904109589,176.17021276595747
L563.2876712328767,174.8936170212766L565.4794520547945,159.57446808510636
L567.6712328767123,128.93617021276592L569.8630136986301,146.8085106382978
L572.054794520548,186.38297872340422L574.2465753424658,168.51063829787233
L576.4383561643835,150.63829787234044L578.6301369863014,176.17021276595747
L580.8219178082192,165.95744680851058L583.013698630137,151.91489361702122
L585.2054794520548,144.25531914893617L587.3972602739726,131.48936170212764
L589.5890410958904,122.55319148936167L591.7808219178082,119.99999999999994
L593.972602739726,134.04255319148933L596.1643835616438,136.59574468085106
L598.3561643835617,111.06382978723403L600.5479452054794,105.95744680851061
L602.7397260273973,136.59574468085106L604.931506849315,135.31914893617017
L607.1232876712329,144.25531914893617L609.3150684931506,157.0212765957447
L611.5068493150685,155.74468085106383L613.6986301369863,158.29787234042547
L615.8904109589041,182.55319148936172L618.082191780822,171.063829787234
L620.2739726027397,178.7234042553191L622.4657534246576,183.8297872340425
L624.6575342465753,192.7659574468085L626.8493150684932,174.8936170212766
L629.0410958904109,167.23404255319147L631.2328767123288,167.23404255319147
L633.4246575342465,160.85106382978722L635.6164383561644,132.76595744680844
L637.8082191780821,122.55319148936167L640,127.65957446808505L642.1917808219179,114.89361702127653
L644.3835616438356,121.2765957446808L646.5753424657535,141.70212765957444
L648.7671232876712,134.04255319148933L650.9589041095891,118.72340425531917
L653.1506849315068,122.55319148936167L655.3424657534247,136.59574468085106
L657.5342465753424,150.63829787234044L659.7260273972603,154.46808510638294
L661.9178082191781,146.8085106382978L664.1095890410959,146.8085106382978
L666.3013698630137,142.9787234042553L668.4931506849315,149.36170212765956
L670.6849315068494,162.12765957446808L672.8767123287671,171.063829787234
L675.068493150685,165.95744680851058L677.2602739726027,118.72340425531917
L679.4520547945206,113.61702127659566L681.6438356164384,116.17021276595742
L683.8356164383562,128.93617021276592L686.027397260274,154.46808510638294
L688.2191780821918,157.0212765957447L690.4109589041096,179.99999999999997
L692.6027397260274,186.38297872340422L694.7945205479452,176.17021276595747
L696.986301369863,168.51063829787233L699.1780821917808,139.1489361702127
L701.3698630136986,127.65957446808505L703.5616438356165,150.63829787234044
L705.7534246575343,139.1489361702127L707.945205479452,163.40425531914894
L710.1369863013699,178.7234042553191L712.3287671232877,174.8936170212766
L714.5205479452055,160.85106382978722L716.7123287671233,145.53191489361697
L718.9041095890411,157.0212765957447L721.0958904109589,169.7872340425532
L723.2876712328766,135.31914893617017L725.4794520547946,112.34042553191489
L727.6712328767123,107.23404255319141L729.8630136986302,122.55319148936167
L732.054794520548,142.9787234042553L734.2465753424658,149.36170212765956
L736.4383561643835,145.53191489361697L738.6301369863014,178.7234042553191
L740.8219178082192,173.61702127659572L743.0136986301369,186.38297872340422
L745.2054794520548,160.85106382978722L747.3972602739726,137.87234042553192
L749.5890410958905,136.59574468085106L751.7808219178082,135.31914893617017
L753.9726027397261,136.59574468085106L756.1643835616438,149.36170212765956
L758.3561643835617,139.1489361702127L760.5479452054794,145.53191489361697
L762.7397260273972,163.40425531914894L764.931506849315,151.91489361702122
L767.1232876712328,163.40425531914894L769.3150684931508,181.27659574468083
L771.5068493150685,157.0212765957447L773.6986301369864,157.0212765957447
L775.8904109589041,131.48936170212764L778.082191780822,149.36170212765956
L780.2739726027397,144.25531914893617L782.4657534246575,142.9787234042553
L784.6575342465753,182.55319148936172L786.8493150684931,160.85106382978722
L789.0410958904109,176.17021276595747L791.2328767123288,172.34042553191486
L793.4246575342466,191.4893617021276L795.6164383561644,185.1063829787234
L797.8082191780823,168.51063829787233L800,153.19148936170208">
d3-shape calculates the path attribute so we don't have to
shapes
shapes
var data = [
{date: new Date(2007, 3, 24), value: 93.24},
{date: new Date(2007, 3, 25), value: 95.35},
{date: new Date(2007, 3, 26), value: 98.84},
{date: new Date(2007, 3, 27), value: 99.92},
{date: new Date(2007, 3, 30), value: 99.80},
{date: new Date(2007, 4, 1), value: 99.47},
…
];
var line = d3.line()
.x((d) => { return xScale(d.date); })
.y((d) => { return yScale(d.value); });
d3.select('svg')
.append('path')
.attr('d', line(data));
d3.line()
Input: array of objects
Output: path that connects each point (object) with lines or curves
shapes
d3.pie()
var pie = d3.pie();
// input
var data = [1, 1, 2, 3, 5, 8, 13, 21];
pie(data);
// output
[
{"data": 1, "value": 1, "startAngle": 6.050474740247008, "endAngle": 6.166830023713296, "padAngle": 0},
{"data": 1, "value": 1, "startAngle": 6.166830023713296, "endAngle": 6.283185307179584, "padAngle": 0},
{"data": 2, "value": 2, "startAngle": 5.817764173314431, "endAngle": 6.050474740247008, "padAngle": 0},
{"data": 3, "value": 3, "startAngle": 5.468698322915565, "endAngle": 5.817764173314431, "padAngle": 0},
{"data": 5, "value": 5, "startAngle": 4.886921905584122, "endAngle": 5.468698322915565, "padAngle": 0},
{"data": 8, "value": 8, "startAngle": 3.956079637853813, "endAngle": 4.886921905584122, "padAngle": 0},
{"data": 13, "value": 13, "startAngle": 2.443460952792061, "endAngle": 3.956079637853813, "padAngle": 0},
{"data": 21, "value": 21, "startAngle": 0.000000000000000, "endAngle": 2.443460952792061, "padAngle": 0}
]
var pie = {
"data": 1, "value": 1,
"startAngle": 6.050474740247008,
"endAngle": 6.166830023713296,
};
var arc = d3.arc()
.innerRadius(0)
.outerRadius(100)
.startAngle(d => d.startAngle)
.endAngle(d => d.endAngle);
arc(pie);
// M-23.061587074244123,-97.30448705798236A100,100,0,0,1,-11.609291412523175,-99.32383577419428L0,0Z
d3.arc()
(Code)
exercise time
Docs that may help:
update & exit
update & exit
// bars includes update selection
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff');
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('height', d => d)
.attr('fill', d => colors(d));
key function: controls which datum is assigned to which element
update & exit
// bars includes update selection
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff');
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('y', d => height - d)
.attr('height', d => d)
.attr('fill', d => colors(d));
Enter selection: chain attributes that don't depend on data
Combines 2 selections into one
Enter+update selection: chain attrs dependent on data
Exit selection
Play with the code
Update-exit
Transitions
Emphasize changes in state
(object constancy)
Transitions
var t = d3.transition()
.duration(1000);
var svg = d3.select('svg');
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().transition(t)
.attr('y', height)
.attr('height', 0)
.remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff')
.attr('y', height);
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('fill', d => colors(d))
.transition(t)
.attr('y', d => height - d)
.attr('height', d => d);
Define transition, syncs animation everywhere it's used
Animate height down to 0 before removing
Animate remaining <rect>'s height to their next state
Transitions
var t = d3.transition()
.duration(1000);
var svg = d3.select('svg');
var bars = svg.selectAll('rect')
.data(data, d => d);
// exit
bars.exit().transition(t)
.attr('y', height)
.attr('height', 0)
.remove();
// enter
var enter = bars.enter().append('rect')
.attr('width', rectWidth)
.attr('stroke', '#fff')
.attr('y', height);
// enter + update
bars = enter.merge(bars)
.attr('x', (d, i) => i * rectWidth)
.attr('fill', d => colors(d))
.transition(t)
.attr('y', d => height - d)
.attr('height', d => d);
go from state A to state B
Everything after .transition(): state B to transition to
Same attributes set before .transition(): state A to transition from
If attributes not specified for A, d3 extrapolates from default
Transitions
Play with the code
Exercise time
x: barley sites
y: yield value
animate across years
Forces
Forces
Forces
Front-end Masters: D3.js v4
By Shirley Wu
Front-end Masters: D3.js v4
- 14,294