The layout that you have already is dependent on your data being uniform, which doesn't happen in the real world, so I found a data set and used it to create a pie chart that doesn't require perfect data.
It's a mix of the first and second charts. I have added copious comments to the code so please look through and check that you understand what is happening. I've put a demo at https://bl.ocks.org/ialarmedalien/1e453ed9b148be442f50e06ad7eb3759, so you can see the data input there.
function chart(id) { // this reads in the CSV file d3.csv('morley3.csv').then( data => { // this massages the data I'm using into a more suitable form for your chart // we have 12 runs with 6 experiments in each. // each datum is of the form // { Run: <number>, Expt: <number>, Speed: <number> } const filteredData = data .filter( d => d.Run < 13 ) .map( d => { return { Run: +d.Run, Expt: +d.Expt, Speed: +d.Speed } } ) // set up the chart const width = 800, height = 800, radius = Math.min(height, width) * 0.5 - 100, // how far away from the chart the labels should be labelOffset = 10, svg = d3.select(id).append("svg") .attr("width", width) .attr("height", height), g = svg.append("g") .attr("transform", `translate(${width/2}, ${height/2})`), // this will be used to generate the pie segments arc = d3.arc() .outerRadius(radius) .innerRadius(0), // group the data by the run number // this results in 12 groups of six experiments // the nested data has the form // [ { key: <run #>, values: [{ Run: 1, Expt: 1, Speed: 958 }, { Run: 1, Expt: 2, Speed: 869 } ... ], // { key: 2, values: [{ Run: 2, Expt: 1, Speed: 987 },{ Run: 2, Expt: 2, Speed: 809 } ... ], // etc. nested = d3.nest() .key( d => +d.Run ) .entries(filteredData), chunkSize = nested[0].values.length, // d3.pie() is the pie chart generator pie = d3.pie() // the size of each slice will be the sum of all the Speed values for each run .value( d => d3.sum( d.values, function (e) { return e.Speed } ) ) // sort by run # .sort( (a,b) => a.key - b.key ) (nested) // bind the data to the DOM. Add a `g` for each run const runs = g.selectAll(".run") .data(pie, d => d.key ) .enter() .append("g") .classed('run', true) .each( d => { // run the pie generator on the children // d.data.values is all the experiments in the run, or in pie terms, // all the experiments in this piece of the pie. We're going to use // `startAngle` and `endAngle` to specify that we're only generating // part of the pie. The values for `startAngle` and `endAngle` come // from using the pie chart generator on the run data. d.children = d3.pie() .value( e => e.Speed ) .sort( (a,b) => a.Expt - b.Expt ) .startAngle( d.startAngle ) .endAngle( d.endAngle ) ( d.data.values ) }) // we want to label each run (rather than every single segment), so // the labels get added next. runs.append('text') .classed('label', true) // if the midpoint of the segment is on the right of the pie, set the // text anchor to be at the start. If it is on the left, set the text anchor // to the end. .attr('text-anchor', d => { d.midPt = (0.5 * (d.startAngle + d.endAngle)) return d.midPt < Math.PI ? 'start' : 'end' } ) // to calculate the position of the label, I've taken the mid point of the // start and end angles for the segment. I've then used d3.pointRadial to // convert the angle (in radians) and the distance from the centre of // the circle/pie (pie radius + labelOffset) into cartesian coordinates. // d3.pointRadial returns [x, y] coordinates .attr('x', d => d3.pointRadial( d.midPt, radius + labelOffset )[0] ) .attr('y', d => d3.pointRadial( d.midPt, radius + labelOffset )[1] ) // If the segment is in the upper half of the pie, move the text up a bit // so that the label doesn't encroach on the pie itself .attr('dy', d => { let dy = 0.35; if ( d.midPt < 0.5 * Math.PI || d.midPt > 1.5 * Math.PI ) { dy -= 3.0; } return dy +'em' }) .text( d => { return 'Run '+ d.data.key +', experiments 1 - 6' }) .call(wrap, 50) // now we can get on to generating the sub segments within each main segment. // add another g for each experiment const expts = runs.selectAll('.expt') // we already have the data bound to the DOM, but we want the d.children, // which has the layout information from the pie chart generator .data( d => d.children ) .enter() .append('g') .classed('expt', true) // add the paths for each sub-segment expts.append('path') .classed('speed-segment', true) .attr('d', arc) // I simplified this slightly to use one of the built-in d3 colour schemes // my data was already numeric so it was easy to use the run # as the colour .attr('fill', (d,i) => { const c = i / chunkSize, color = d3.rgb( d3.schemeSet3[ d.data.Run - 1 ] ); return c < 1 ? color.brighter(c*0.5) : color; }) // add a title element that appears when mousing over the segment .append('title') .text(d => 'Run '+ d.data.Run +', experiment '+ d.data.Expt +', speed: '+ d.data.Speed ) // add the lines expts.append('line') .attr('y2', radius) // assign a class to each line so we can control the stroke, etc., using css .attr('class', d => { return 'run-'+ d.data.Run +' expt-'+ d.data.Expt }) // convert the angle from radians to degrees .attr("transform", d => { return "rotate("+ (180 + d.endAngle * 180 / Math.PI) +")"; }); function wrap(text, width) { text.each(function () { let text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.2, // ems tfrm = text.attr('transform') y = text.attr("y"), x = text.attr("x"), dy = parseFloat(text.attr("dy")), tspan = text.text(null).append("tspan") .attr("x", x) .attr("y", y) .attr("dy", dy +"em"); while (word = words.pop()) { line.push(word); tspan.text(line.join("")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join("")); line = [word]; tspan = text.append("tspan") .attr("x", x) .attr("y", y) .attr("dy", ++lineNumber * lineHeight + dy +"em") .text(word); } } }); } return svg; })}chart('#chart');