首页 > 解决方案 > 如何在D3的刻度图中间制作堆积条形图的轴标签?

问题描述

首先,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

解决方案


我的英语很差,所以我构建了一个转换过程图,可以在这里找到它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>


推荐阅读