【问题标题】:How to make axis label of stacked bar chart in the middle of ticks chart in D3?如何在D3的刻度图中间制作堆积条形图的轴标签?
【发布时间】:2019-04-02 14:13:17
【问题描述】:

开始,x 轴标签在刻度的中间。但是,在我旋转 x 轴标签后,x 轴标签会出现一些问题。

现在,我希望 x 轴标签位于刻度的中间。我能怎么做? 如果使用transform,如何得到x轴的中点? 结果看起来像https://drive.google.com/open?id=1Fen0to5Ih86alOXu6UXeJzeicX1E1JFJ

const data = [
  {
    'group': 'G1',
    'sample': 's1',
    'Actinomyces': 12.55802794990189,
    'Alloprevotella': 0.3671446023182472,
    'Atopobium': 0.15760660109181326,
    'Anaerococcus': 0
  },
  {
    'group': 'G1',
    'sample': 's2',
    'Actinomyces': 9.55802794990189,
    'Alloprevotella': 0.3671446023182472,
    'Atopobium': 0.12760660109181326,
    'Anaerococcus': 10.0
  },
  {
    'group': 'G2',
    'sample': 's3',
    'Actinomyces': 11.55802794990189,
    'Alloprevotella': 0.3671446023182472,
    'Atopobium': 0.9760660109181326,
    'Anaerococcus': 5.0
  },
  {
    'group': 'G2',
    'sample': 's4',
    'Actinomyces': 19.55802794990189,
    'Alloprevotella': 1.3671446023182472,
    'Atopobium': 2.15760660109181326,
    'Anaerococcus': 4.0
  }
]

const w = 800
const h = 400
const margin = { top: 50, right: 50, bottom: 50, left: 150 }

const keys = Object.keys(data[0]).filter(function (val) {
  return val !== 'sample' && val !== 'group'
})

// create a stack generator
let stack = d3.stack()
  .keys(keys)

const xScale = d3.scaleBand()
  .domain(d3.range(data.length))
  .range([margin.left, w - margin.right])
  .paddingOuter(0.02)

const yScale = d3.scaleLinear()
  .domain([0,
    d3.max(data, function (d) {
      return d3.sum(keys.map(val => d[val]))
    })
  ])
  .range([h - margin.bottom, margin.top])

const colorScale = d3.scaleLinear()
  .domain([0, keys.length - 1])
  .range([0, 1])

// create svg
const svg = d3.select('#app')
  .append('svg')
  .attr('width', w)
  .attr('height', h)

const groups = svg.selectAll('g')
  .data(stack(data))
  .enter()
  .append('g')
  .style('fill', function (d, i) {
    return d3.interpolateSpectral(colorScale(i))
  })

groups.selectAll('rect')
  .data(function (d) {
    return d
  })
  .enter()
  .append('rect')
  .attr('x', (d, i) => xScale(i))
  .attr('y', d => yScale(d[1]))
  .attr('height', d => yScale(d[0]) - yScale(d[1]))
  .attr('width', xScale.bandwidth())

// add axis
const xAxis = d3.axisBottom(xScale)
  .tickFormat(d => keys[d])
svg.append('g')
  .attr('class', 'xAxis')
  .attr('transform', 'translate(0, ' + yScale(0) + ')')
  .call(xAxis)
  .selectAll('text')
  .attr('text-anchor', 'start')
  .attr('dx', '10px')
  .attr('transform', 'rotate(90)')

const yAxis = d3.axisLeft(yScale)
svg.append('g')
  .attr('class', 'yAxis')
  .attr('transform', 'translate(' + xScale(0) + ', 0)')
  .call(yAxis)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="app">

</div>

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    我的英文很差,所以建了一个transform过程图,可以在这里找到https://drive.google.com/open?id=19zmGFwivdjPqVabVGIgdNVXKRGdR8v2s

    部分代码已更改定位于

    /** change **/
    ...
    /***********/
    

    const data = [
      {
        'group': 'G1',
        'sample': 's1',
        'Actinomyces': 12.55802794990189,
        'Alloprevotella': 0.3671446023182472,
        'Atopobium': 0.15760660109181326,
        'Anaerococcus': 0
      },
      {
        'group': 'G1',
        'sample': 's2',
        'Actinomyces': 9.55802794990189,
        'Alloprevotella': 0.3671446023182472,
        'Atopobium': 0.12760660109181326,
        'Anaerococcus': 10.0
      },
      {
        'group': 'G2',
        'sample': 's3',
        'Actinomyces': 11.55802794990189,
        'Alloprevotella': 0.3671446023182472,
        'Atopobium': 0.9760660109181326,
        'Anaerococcus': 5.0
      },
      {
        'group': 'G2',
        'sample': 's4',
        'Actinomyces': 19.55802794990189,
        'Alloprevotella': 1.3671446023182472,
        'Atopobium': 2.15760660109181326,
        'Anaerococcus': 4.0
      }
    ]
    
    const w = 800
    const h = 400
    const margin = { top: 50, right: 50, bottom: 50, left: 150 }
    
    const keys = Object.keys(data[0]).filter(function (val) {
      return val !== 'sample' && val !== 'group'
    })
    
    // create a stack generator
    let stack = d3.stack()
      .keys(keys)
    
    const xScale = d3.scaleBand()
      .domain(d3.range(data.length))
      .range([margin.left, w - margin.right])
      .paddingOuter(0.02)
    
    const yScale = d3.scaleLinear()
      .domain([0,
        d3.max(data, function (d) {
          return d3.sum(keys.map(val => d[val]))
        })
      ])
      .range([h - margin.bottom, margin.top])
    
    const colorScale = d3.scaleLinear()
      .domain([0, keys.length - 1])
      .range([0, 1])
    
    // create svg
    const svg = d3.select('#app')
      .append('svg')
      .attr('width', w)
      .attr('height', h)
    
    const groups = svg.selectAll('g')
      .data(stack(data))
      .enter()
      .append('g')
      .style('fill', function (d, i) {
        return d3.interpolateSpectral(colorScale(i))
      })
    
    groups.selectAll('rect')
      .data(function (d) {
        return d
      })
      .enter()
      .append('rect')
      .attr('x', (d, i) => xScale(i))
      .attr('y', d => yScale(d[1]))
      .attr('height', d => yScale(d[0]) - yScale(d[1]))
      .attr('width', xScale.bandwidth())
    
    // add axis
    const xAxis = d3.axisBottom(xScale)
      .tickFormat(d => keys[d])
    svg.append('g')
      .attr('class', 'xAxis')
      .attr('transform', 'translate(0, ' + yScale(0) + ')')
      .call(xAxis)
      /** change **/
      .selectAll('text')
      .attr('text-anchor', 'start')
      .attr('y', 0)
      .attr('transform', 'rotate(90)')
      .attr('dx', 9) // let the text move a little to the bottom
      .attr('dy', 2) // at the beginning, the top of text is parallel with the tick. So we need move a little to the right
      /***********/
      
    
    const yAxis = d3.axisLeft(yScale)
    svg.append('g')
      .attr('class', 'yAxis')
      .attr('transform', 'translate(' + xScale(0) + ', 0)')
      .call(yAxis)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <div id="app">
    
    </div>

    【讨论】: