D3(v4 / v5):将垂直小提琴图转换为水平小提琴图

时间:2020-10-16 06:56:46

标签: d3.js

按照示例https://www.d3-graph-gallery.com/graph/violin_basicHist.html,我是d3的新手,我制作了小提琴图,但找不到任何与水平小提琴图有关的示例,我试图将X轴更改为Y,将Y轴更改为X,但这没有。工作:

这是我创建的图形的示例:

var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 40
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {

  // Build and Show the Y scale
  var y = d3.scaleLinear()
    .domain([3.5, 8]) // Note that here the Y scale is set manually
    .range([height, 0])
  svg.append("g").call(d3.axisLeft(y))

  // Build and Show the X scale. It is a band scale like for a boxplot: each group has an dedicated RANGE on the axis. This range has a length of x.bandwidth
  var x = d3.scaleBand()
    .range([0, width])
    .domain(["setosa", "versicolor", "virginica"])
    .padding(0.05) // This is important: it is the space between 2 groups. 0 means no padding. 1 is the maximum.
  svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))

  // Features of the histogram
  var histogram = d3.histogram()
    .domain(y.domain())
    .thresholds(y.ticks(20)) // Important: how many bins approx are going to be made? It is the 'resolution' of the violin plot
    .value(d => d)

  // Compute the binning for each group of the dataset
  var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
    .key(function(d) {
      return d.Species;
    })
    .rollup(function(d) { // For each key..
      input = d.map(function(g) {
        return g.Sepal_Length;
      }) // Keep the variable called Sepal_Length
      bins = histogram(input) // And compute the binning on it.
      return (bins)
    })
    .entries(data)

  // What is the biggest number of value in a bin? We need it cause this value will have a width of 100% of the bandwidth.
  var maxNum = 0
  for (i in sumstat) {
    allBins = sumstat[i].value
    lengths = allBins.map(function(a) {
      return a.length;
    })
    longuest = d3.max(lengths)
    if (longuest > maxNum) {
      maxNum = longuest
    }
  }

  // The maximum width of a violin must be x.bandwidth = the width dedicated to a group
  var xNum = d3.scaleLinear()
    .range([0, x.bandwidth()])
    .domain([-maxNum, maxNum])

  // Add the shape to this svg!
  svg
    .selectAll("myViolin")
    .data(sumstat)
    .enter() // So now we are working group per group
    .append("g")
    .attr("transform", function(d) {
      return ("translate(" + x(d.key) + " ,0)")
    }) // Translation on the right to be at the group position
    .append("path")
    .datum(function(d) {
      return (d.value)
    }) // So now we are working bin per bin
    .style("stroke", "none")
    .style("fill", "#69b3a2")
    .attr("d", 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) // This makes the line smoother to give the violin appearance. Try d3.curveStep to see the difference
    )
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="my_dataviz"></div>

但是它的垂直小提琴图可以将其转换为水平小提琴图,任何帮助将不胜感激

1 个答案:

答案 0 :(得分:0)

我保持结构几乎相同,有时只是更改了x和y。我建议使用DiffChecker之类的工具来将代码并排放置并查看所有差异。

var margin = {
    top: 10,
    right: 30,
    bottom: 30,
    left: 40
  },
  width = 460 - margin.left - margin.right,
  height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {

  // Build and Show the Y scale
  var x = d3.scaleLinear()
    .domain([3.5, 8]) // Note that here the Y scale is set manually
    .range([0, width])
  svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))


  // Build and Show the X scale. It is a band scale like for a boxplot: each group has an dedicated RANGE on the axis. This range has a length of x.bandwidth
  var y = d3.scaleBand()
    .range([height, 0])
    .domain(["setosa", "versicolor", "virginica"])
    .padding(0.05) // This is important: it is the space between 2 groups. 0 means no padding. 1 is the maximum.
  svg.append("g").call(d3.axisLeft(y))

  // Features of the histogram
  var histogram = d3.histogram()
    .domain(x.domain())
    .thresholds(x.ticks(20)) // Important: how many bins approx are going to be made? It is the 'resolution' of the violin plot
    .value(d => d)

  // Compute the binning for each group of the dataset
  var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
    .key(function(d) {
      return d.Species;
    })
    .rollup(function(d) { // For each key..
      input = d.map(function(g) {
        return g.Sepal_Length;
      }) // Keep the variable called Sepal_Length
      bins = histogram(input) // And compute the binning on it.
      return (bins)
    })
    .entries(data)

  // What is the biggest number of value in a bin? We need it cause this value will have a width of 100% of the bandwidth.
  var maxNum = 0
  for (i in sumstat) {
    allBins = sumstat[i].value
    lengths = allBins.map(function(a) {
      return a.length;
    })
    longuest = d3.max(lengths)
    if (longuest > maxNum) {
      maxNum = longuest
    }
  }

  // The maximum width of a violin must be y.bandwidth = the width dedicated to a group
  var yNum = d3.scaleLinear()
    .range([0, y.bandwidth()])
    .domain([-maxNum, maxNum])

  // Add the shape to this svg!
  svg
    .selectAll("myViolin")
    .data(sumstat)
    .enter() // So now we are working group per group
    .append("g")
    .attr("transform", function(d) {
      return ("translate(0," + y(d.key) + ")")
    }) // Translation on the right to be at the group position
    .append("path")
    .datum(function(d) {
      return (d.value)
    }) // So now we are working bin per bin
    .style("stroke", "none")
    .style("fill", "#69b3a2")
    .attr("d", d3.area()
      .x(function(d) {
        return (x(d.x0))
      })
      .y0(function(d) {
        return (yNum(-d.length))
      })
      .y1(function(d) {
        return (yNum(d.length))
      })
      .curve(d3.curveCatmullRom) // This makes the line smoother to give the violin appearance. Try d3.curveStep to see the difference
    )
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="my_dataviz"></div>