用d3绘制轮廓(条形图)

时间:2014-12-24 16:16:39

标签: d3.js

我试图用d3绘制个人资料图表。我的数据包含一个约70项措施的小组,范围从3.5到10,每个小节都有一个标签。我想绘制一个图表,以便向上绘制大于6的值,向下绘制6以下。大于6.5的值变为绿色条,低于5.5变为红色。以下是目标图表。 enter image description here

使用以下d3代码,我设法绘制了下面的图表 - 有点像d3的新手。 enter image description here

还有几个问题,我可以向他们寻求帮助: 1.绿色条是高,我想使用3到12的固定范围,图中较高的绿色条的值为7.3,但是达到顶部(10px边距)。 2.我无法显示y轴值,我要显示3,6和9。 3. x和y轴太厚,我更喜欢1px。 4.面板中的某些条形图可能没有任何值,我希望它们显示为空条形。

从下面的代码开始,我该怎么办?我在哪里出现轴显示错误。任何帮助将不胜感激。

function draw(data) {

    data.forEach(function(d) {
            d.value = d.value - 6.0;
        });

var margin = { top: 10, right: 10, bottom: 80, left: 50 },
    padding = { top: 2, right: 2, bottom: 2, left: 2 },
    width = 960 - margin.left - margin.right - padding.left - padding.right,
    height = 240 - margin.top - margin.bottom - padding.top - padding.bottom;

var xScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], .2);

var yScale = d3.scale.linear()
    .range([height - padding.top - padding.bottom, 0])
    .domain([-3, 6]);

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(function(d) { return d.label; });

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(yScale([3,6,9]));

xScale.domain(data.map(function(d) { return d.label; }));
yScale.domain(d3.extent(data, function(d) { return d.value; }));

var svg = d3.select("#chart").append("svg:svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);

svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate("+padding.top+"," + (height-padding.bottom) + ")")
    .call(xAxis)
    .selectAll("text")
    .style("text-anchor", "end")
    .attr("dx", "-.8em")
    .attr("dy", ".15em")
    .attr("transform", "rotate(-50)" );

svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate("+padding.left+", "+padding.top+")")
    .call(yAxis)

svg.selectAll("bar")
    .data(data)
    .enter().append("rect")
    .attr("x", function(d) { return xScale(d.label); })
    .attr("y", function(d) { return yScale(d3.max([0, d.value])); })
    .attr("width", xScale.rangeBand())
    .attr("height", function(d) { return Math.abs(yScale(d.value)-yScale(0)); })
    .style("fill", function(d) {
           if(d.value < -0.5) { return "red"; }
           else if (d.value > 0.5) { return "green"; }
           else { return "lightgray"; }
        });
}

3 个答案:

答案 0 :(得分:3)

我对你的代码感到困惑。

1。)您声明要定义[3, 12]的域名,但在您的yScale中,您首先将其设置为domain([-3, 6]);,然后使用d3.extent将其重置为最小值/最大值。为什么呢?

2。)d.value = d.value - 6.0;的目的是什么,你不会更好地保留你的数据,然后对6左右的颜色进行比较。而不是试图将它映射到0并比较它?

3.。)您可以通过CSS配置轴外观。对于你想要的用途:

.axis path {
  stroke-width: 1px;
  fill: none;
  stroke: black;
}

4。)您无法看到y轴勾选,因为您只通过left.padding为它们分配了2px:

.attr("transform", "translate(" + padding.left + ", " + padding.top + ")")

事实上,我无法弄清楚填充和边距的相互作用。 classic bar chart example仅使用边距,我会简化并使用该示例。

把这些放在一起,这是一个example

enter image description here

答案 1 :(得分:2)

在Mark的帮助下,我能够实现我的目标,修正后的代码如下,并对修改提出意见。 enter image description here

D3代码:

function draw(data) {

// padding was removed
// data is no more shifted by 6, graph is centered on 6 instead.
var margin = { top: 0, right: 0, bottom: 100, left: 20 },
    width = 960 - margin.left - margin.right,
    height = 240 - margin.top - margin.bottom;

var xScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], .2);

// removed extend on scaling which forced bars to span on the whole height 
// since original data is scaled from 4 to 10, I'd like the bar reflect absolute values
var yScale = d3.scale.linear()
    .range([height, 0])
    .domain([3, 10]);

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(function(d) {
           return d.cell;
           });

// tickValues have been added to force proper ticks on y-axis
var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .tickValues([3, 6, 9]);

xScale.domain(data.map(function(d) {
                       return d.cell;
                       }));

// transform attribute was added to shift the whole graph by margin.left
// this explains why the y-axis and ticks were not visible
var svg = d3.select("#chart").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 + ")");

svg.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .selectAll("text")
    .style("text-anchor", "end")
    .attr("dx", "-.8em")
    .attr("dy", ".15em")
    .attr("transform", "rotate(-50)" );

svg.append("g")
    .attr("class", "axis")
    .call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("-log(GI50) uM");

// y-scale is now centered on 6, greater draws upward, lesser draws downward 
// color is set using a CSS with [5, 7] range set to gray
svg.selectAll("bar")
    .data(data)
    .enter().append("rect")
    .attr("class", function(d) {
      if (d.value < 5) { return "bar negative"; }
      else if (d.value > 7) { return "bar positive"; }
      else { return "bar neutral"; }
      })
    .attr("x", function(d) { return xScale(d.cell); })
    .attr("y", function(d) { return yScale(d3.max([6, d.value])); })
    .attr("width", xScale.rangeBand())
    .attr("height", function(d) { return Math.abs(yScale(d.value)-yScale(6)); });
}

使用的CSS:

.bar.positive { 
    fill: #1a9641; 
}

.bar.negative { 
    fill: #d7191c; 
}

.bar.neutral { 
    fill: #d5d5d5; 
}

.axis text { 
    font-family: sans-serif;
    font-size: 11px;
}

.axis path {
    stroke-width: 1px;
    fill: none;
    stroke: black;
}

.axis line {
    stroke-width: 1px;
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

.chart {
    padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px;
    background: transparent;
    width: 960px;
    height: 240px;
}

一些数据:

[ {
"panel" : "BRE",
"cell" : "BT-549",
"value" : 5.846
} ], [ {
"panel" : "BRE",
"cell" : "HS 578T",
"value" : 5.429
} ], [ {
"panel" : "BRE",
"cell" : "MCF7",
"value" : 5.953
} ], [ {
"panel" : "BRE",
"cell" : "MDA-MB-231/ATCC",
"value" : 5.669
} ], [ {
"panel" : "BRE",
"cell" : "MDA-MB-435",
"value" : 5.804
} ], [ {
"panel" : "BRE",
"cell" : "MDA-N",
"value" : 5.828
} ], [ {
"panel" : "BRE",
"cell" : "NCI/ADR-RES",
"value" : 5.575
} ], [ {
"panel" : "BRE",
"cell" : "T-47D",
"value" : 5.959
} ], [ {
"panel" : "CNS",
"cell" : "SF-268",
"value" : 5.556
} ], [ {
"panel" : "CNS",
"cell" : "SF-295",
"value" : 5.14
} ], [ {
"panel" : "CNS",
"cell" : "SF-539",
"value" : 5.431
} ], [ {
"panel" : "CNS",
"cell" : "SNB-19",
"value" : 4.884
} ], [ {
"panel" : "CNS",
"cell" : "SNB-75",
"value" : 5.465
} ], [ {
"panel" : "CNS",
"cell" : "U251",
"value" : 5.317
} ]

下一步将添加误差条并适当缩放,在5,7和9处添加浅灰色水平条,并在图形顶部显示带有适当颜色的标记条形组,以及活动的复合结构侧面和按需显示或隐藏轴。

enter image description here

答案 2 :(得分:0)

从上面的代码和数据开始,我试图用组标签(BRE,CNS等)绘制上部颜色条。我设法绘制了两个不同的条形图(见图片),改变代码如下:

nodeEnter
    .append("rect")
    .attr("x", function(d) { return lScale(d.cell); })
    .attr("y", 0)
    .attr("width", lScale.rangeBand())
    .attr("height", 15)
    .attr("style", function(d) { return "fill:"+color(d.panel); });

enter image description here

// create a specific scaling function for the x-axis with a reduced spacing between bars
var lScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], 1e-6);

nodeEnter
    .append("rect")
    .attr("x", function(d) { return lScale(d.cell); })
    .attr("y", 0)
    .attr("width", lScale.rangeBand())
    .attr("height", 15)
    .attr("style", function(d) { return "fill:"+color(d.panel); });

我想创建一个条形,在相同颜色的条形和组之间的分隔(不同颜色)之间没有空格,并在条形图的中心添加组名称(d.panel)。 我考虑过创建一个地图,用于从原始数据中计算最小和最大x的组。

如何为每个d.panel提取最小和最大xScale(d.cell)?我应该循环遍历数组,还是有办法使用d.panel范围对来自xScale(d.cell)的数据进行分组来计算统计数据?

enter image description here