Skip to content

Instantly share code, notes, and snippets.

@kristw
Last active June 7, 2019 22:53
Show Gist options
  • Save kristw/762e219e34808e4f50a4 to your computer and use it in GitHub Desktop.
Save kristw/762e219e34808e4f50a4 to your computer and use it in GitHub Desktop.
Fetal growth chart

Babies are measured from the crown (or top) of the head to the rump (or bottom) until about 20 weeks. This is because a baby's legs are curled up against his/her torso during the first half of pregnancy and very hard to measure. After that, babies are measured from head to toe.

Please use this graph for non-serious estimation (e.g. "How big is my baby today?") and don't freak out if your real number is different. These numbers are just averages. Babies grow at different rates, so your baby's numbers may vary substantially. If you have any concern, please talk to your doctor. I am just a doctor of philosophy, not a real doctor.

Data from: www.babyyourbaby.org

Color palette "Compatible" by joy_of_summer

This chart uses the d3Kit library, so it is easy to implement auto-resizing. Try it by open in new window and resize.

[{"week":8,"length":1.6,"mass":1},
{"week":9,"length":2.3,"mass":2},
{"week":10,"length":3.1,"mass":4},
{"week":11,"length":4.1,"mass":7},
{"week":12,"length":5.4,"mass":14},
{"week":13,"length":7.4,"mass":23},
{"week":14,"length":8.7,"mass":43},
{"week":15,"length":10.1,"mass":70},
{"week":16,"length":11.6,"mass":100},
{"week":17,"length":13,"mass":140},
{"week":18,"length":14.2,"mass":190},
{"week":19,"length":15.3,"mass":240},
{"week":20,"length":16.4,"mass":300},
{"week":21,"length":26.7,"mass":360},
{"week":22,"length":27.8,"mass":430},
{"week":23,"length":28.9,"mass":501},
{"week":24,"length":30,"mass":600},
{"week":25,"length":34.6,"mass":660},
{"week":26,"length":35.6,"mass":760},
{"week":27,"length":36.6,"mass":875},
{"week":28,"length":37.6,"mass":1005},
{"week":29,"length":38.6,"mass":1153},
{"week":30,"length":39.9,"mass":1319},
{"week":31,"length":41.1,"mass":1502},
{"week":32,"length":42.4,"mass":1702},
{"week":33,"length":43.7,"mass":1918},
{"week":34,"length":45,"mass":2146},
{"week":35,"length":46.2,"mass":2383},
{"week":36,"length":47.4,"mass":2622},
{"week":37,"length":48.6,"mass":2859},
{"week":38,"length":49.8,"mass":3083},
{"week":39,"length":50.7,"mass":3288},
{"week":40,"length":51.2,"mass":3462},
{"week":41,"length":51.7,"mass":3597},
{"week":42,"length":51.5,"mass":3685},
{"week":43,"length":51.3,"mass":3717}]
<!DOCTYPE html>
<html>
<head>
<title>Fetal growth chart</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript"></script>
<script src="https://rawgit.com/kristw/75b61f9beeab9b530612/raw/389e984e4041117a9185cf6edad9f6b85a38097a/d3kit.min.js" type="text/javascript"></script>
<script src="main.js"></script>
</body>
</html>
var HEIGHT = 200;
var chart = new d3Kit.Skeleton('#chart', {
margin: {top: 30, right: 25, bottom: 30, left: 60},
initialHeight: HEIGHT*2+100
})
.autoResize('width')
.on('resize', visualize)
.on('data', visualize);
var layers = chart.getLayerOrganizer();
layers.create([
'x-axis',
{'length': ['axis','graph']},
{'mass':['axis','graph']},
{'cursor': ['flag']},
'cover'
]);
layers.get('x-axis')
.classed('axis', true)
.attr('transform', 'translate('+0+','+(HEIGHT+10)+')');
layers.get('length.axis')
.classed('axis', true);
layers.get('mass.axis')
.classed('axis', true);
layers.get('mass')
.attr('transform', 'translate('+0+','+(HEIGHT+40)+')');
layers.get('length').append('text')
.classed('chart-title', true)
.attr('x', 10)
.attr('y', 20)
.text('Length (cm)');
layers.get('mass').append('text')
.classed('chart-title', true)
.attr('x', 10)
.attr('y', 20)
.text('Weight (g)');
layers.get('x-axis').append('text')
.classed('chart-title', true)
.attr('x', chart.getInnerWidth()/2)
.attr('y', 40)
.style('text-anchor', 'middle')
.text('Age (weeks)');
layers.get('cursor')
.style('opacity', 0)
.append('line')
.classed('cursor', true)
.attr('y1', 0)
.attr('y2', chart.getInnerHeight());
layers.get('cursor.flag').append('rect')
.attr('width', 260)
.attr('height', 50);
layers.get('cursor.flag').append('text')
.attr('x', 7)
.attr('y', 20)
.classed('week', true);
layers.get('cursor.flag').append('text')
.attr('x', 7)
.attr('y', 40)
.classed('estimated', true);
var cover = layers.get('cover').append('rect')
.style('fill', 'rgba(255,255,255,0)')
.on('mouseover', showCursor)
.on('mousemove', moveCursor)
.on('mouseout', hideCursor);
var xScale = d3.scale.linear();
var yScale1 = d3.scale.linear();
var yScale2 = d3.scale.linear();
var format = d3.format(',.2f');
var xAxis = d3.svg.axis()
.scale(xScale)
.tickValues([8,12,16,20,24,28,32,36,40])
.orient('bottom');
var yAxis1 = d3.svg.axis()
.scale(yScale1)
.orient('left');
var yAxis2 = d3.svg.axis()
.scale(yScale2)
.orient('left');
var area1 = d3.svg.area()
.interpolate('basis')
.x(function(d) { return xScale(d.week); })
.y0(200)
.y1(function(d) { return yScale1(d.length); });
var area2 = d3.svg.area()
.interpolate('basis')
.x(function(d) { return xScale(d.week); })
.y0(200)
.y1(function(d) { return yScale2(d.mass); });
function visualize(){
var data = chart.data();
if(!data) return;
xScale.domain(d3.extent(data, function(d){return d.week;}))
.range([0, chart.getInnerWidth()]);
yScale1.domain(d3.extent(data, function(d){return d.length;}))
.range([200, 0]);
yScale2.domain(d3.extent(data, function(d){return d.mass;}))
.range([200, 0]);
drawLength(data);
drawMass(data);
layers.get('x-axis').select('text.chart-title').attr('x', chart.getInnerWidth()/2);
layers.get('x-axis').call(xAxis);
cover
.attr('width', chart.getInnerWidth())
.attr('height', chart.getInnerHeight());
}
layers.get('length.graph').append('path');
layers.get('mass.graph').append('path');
function drawLength(data){
var container = layers.get('length.graph');
container.select('path')
.datum(data)
.classed('area', true)
.attr('d', area1);
layers.get('length.axis')
.call(yAxis1);
var selection = container.selectAll('circle.point')
.data(data, function(d){return d.week;});
selection.enter()
.append('circle')
.classed('point', true)
.attr('r', 3)
.attr('cx', function(d){return xScale(d.week);})
.attr('cy', function(d){return yScale1(d.length);});
selection
.attr('cx', function(d){return xScale(d.week);})
.attr('cy', function(d){return yScale1(d.length);});
}
function drawMass(data){
var container = layers.get('mass.graph');
container.select('path')
.datum(data)
.classed('area', true)
.attr('d', area2);
layers.get('mass.axis')
.call(yAxis2);
var selection = container.selectAll('circle.point')
.data(data, function(d){return d.week;});
selection.enter()
.append('circle')
.classed('point', true)
.attr('r', 3)
.attr('cx', function(d){return xScale(d.week);})
.attr('cy', function(d){return yScale2(d.mass);});
selection
.attr('cx', function(d){return xScale(d.week);})
.attr('cy', function(d){return yScale2(d.mass);});
}
function showCursor(){
layers.get('cursor')
.transition()
.style('opacity', 1);
moveCursor();
}
function moveCursor(){
var pos = d3.mouse(layers.get('cover').node());
var week = xScale.invert(pos[0]);
var roundedWeek = Math.floor(week);
var estimatedDay = Math.floor((week - roundedWeek) * 7);
var estimatedWeek = roundedWeek + estimatedDay * 1/7;
var cursorX = xScale(estimatedWeek);
layers.get('cursor')
.attr('transform', 'translate('+cursorX+','+0+')');
var flagX = chart.getInnerWidth() - cursorX < 260 ? -260 : 0;
var data = chart.data();
var estimated = data ? interpolate(data, estimatedWeek) : {
week: 0,
length: 0,
mass: 0
};
layers.get('cursor.flag')
.attr('transform', 'translate('+flagX+','+(pos[1]-55)+')');
layers.get('cursor.flag').select('text.week')
.text(formatWeek(roundedWeek, estimatedDay));
layers.get('cursor.flag').select('text.estimated')
.text('Length: '+format(estimated.length)+'cm, Weight: '+format(estimated.mass)+'g');
}
function formatWeek(week, day){
return week + ' weeks' + (day>0? (' ' + day + ' day'+(day>1?'s':'')): '');
}
function hideCursor(){
layers.get('cursor')
.transition()
.style('opacity', 0);
}
function interpolate(data, week){
var prevWeek = Math.floor(week);
var nextWeek = prevWeek + 1;
if(prevWeek<8){
return data[0];
}
else if(nextWeek>data[data.length-1].week){
return data[data.length-1];
}
var prevData = data[prevWeek-8];
var nextData = data[nextWeek-8];
return {
week: week,
length: prevData.length + (week-prevWeek) * (nextData.length - prevData.length),
mass: prevData.mass + (week-prevWeek) * (nextData.mass - prevData.mass),
};
}
d3.json('fetal.json', function(error, data){
chart.data(data);
});
d3.select(self.frameElement).style('height', chart.options().defaultChartHeight);
body {
font: 11px sans-serif;
}
svg{
background: #3FB8AF;
}
text.chart-title{
fill: #DAD8A7;
font-size: 14px;
}
.axis path,
.axis line {
fill: none;
stroke: #7FC7AF;
shape-rendering: crispEdges;
}
.axis text{
fill: #DAD8A7;
}
path.area{
fill: #FF9E9D;
}
circle.point{
fill: #FF3D7F;
stroke-width: 1.5px;
stroke: #fff;
}
line.cursor {
fill: none;
stroke: #7FC7AF;
shape-rendering: crispEdges;
}
.flag-layer rect{
fill: #7FC7AF;
}
text.week{
font-size: 18px;
}
text.estimated{
font-size: 14px;
fill: #222;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment