function drawViolinPlot() {
const colors = ["264653", "2a9d8f", "843B62"].map((d) => "#" + d);
const pathToCSV = "./iris.csv";
const target = "Sepal_Length";
const targetAccessor = (d) => +d[target];
const styleAccessor = (d) => d.Species;
const species = ["setosa", "versicolor", "virginica"];
const speciesDisplay = {
setosa: "setosa",
versicolor: "versicolor",
virginica: "virginica"
};
let data = [];
rawData.forEach((d) => {
if (species.indexOf(d.Species) < 0) {
return;
} else {
data.push(d);
}
});
const bounds = wrapper.append("g").style(
"transform",
`translate(${dimensions.violin.margin.left}px,
${dimensions.violin.margin.top}px)`
);
const rc = rough.svg(bounds);
bounds
.append("text")
.attr("class", "title")
.text(target)
.style(
"transform",
`translate(${dimensions.violin.boundedWidth / 2 - 10}px, -15px)`
)
.style("font-size", "20px")
.style("text-anchor", "middle")
.style("font-weight", "bold");
var y = d3
.scaleLinear()
.domain(d3.extent(data, targetAccessor))
.range([dimensions.violin.boundedHeight, 0]);
bounds
.append("g")
.attr("id", "axisLeft")
.style("font-size", "10px")
.call(d3.axisLeft(y).ticks(20))
.attr("stroke-opacity", 0);
const leftAxisPath = d3.select("#axisLeft > path").attr("d");
bounds.append("g").each(function (datum, idx) {
const aPath = d3.select(this);
const container = d3.select(this.parentNode);
const child = rc.path(leftAxisPath);
container.node().appendChild(child);
aPath.remove();
});
var x = d3
.scaleBand()
.range([0, dimensions.violin.boundedWidth])
.domain(species)
.padding(0.05);
bounds
.append("g")
.attr("id", "axisBottom")
.attr("transform", "translate(0," + dimensions.violin.boundedHeight + ")")
.style("font-size", "16px")
.call(d3.axisBottom(x).tickFormat((d) => speciesDisplay[d]))
.attr("stroke-opacity", 0)
.selectAll("text");
const bottomAxisPath = d3.select("#axisBottom > path").attr("d");
bounds.append("g").each(function (datum, idx) {
const aPath = d3.select(this);
const container = d3.select(this.parentNode);
const child = rc.path(bottomAxisPath);
child.setAttribute(
"transform",
"translate(0," + dimensions.violin.boundedHeight + ")"
);
container.node().appendChild(child);
aPath.remove();
});
var histogram = d3
.histogram()
.domain(y.domain())
.thresholds(y.ticks(20))
.value((d) => d);
var sumstat = d3
.nest()
.key(styleAccessor)
.rollup(function (d) {
const input = d.map(targetAccessor);
const bins = histogram(input);
return bins;
})
.entries(data);
var maxNum = 0;
for (let i in sumstat) {
const allBins = sumstat[i].value;
const lengths = allBins.map(function (a) {
return a.length;
});
const longuest = d3.max(lengths);
if (longuest > maxNum) {
maxNum = longuest;
}
}
var xNum = d3
.scaleLinear()
.range([0, x.bandwidth()])
.domain([-maxNum, maxNum]);
const eachViolin = bounds
.selectAll("violin")
.data(sumstat)
.enter()
.append("g")
.attr("transform", function (d) {
return "translate(" + x(d.key) + " ,0)";
});
eachViolin
.append("path")
.datum(function (d) {
return d.value;
})
.each(function (datum, idx) {
const aPath = d3.select(this);
const container = d3.select(this.parentNode);
const violinRoughAttr = {
stroke: "gray",
strokeWidth: "0.8",
fill: colors[idx],
roughness: 1,
hachureGap: 3,
fillWeight: 1
};
const path = d3
.area()
.x0(function (d) {
return xNum(-d.length);
})
.x1(function (d) {
return xNum(d.length);
})
.y(function (d) {
return y(d.x0);
})
.curve(d3.curveCatmullRom)(datum);
const child = rc.path(path, violinRoughAttr);
container.node().appendChild(child);
aPath.remove();
});
return wrapper;
}