d3.js - 堆积条形图中的第 2 组数据值

2023-11-24

我有以下 csv 数据,

date,scanned,unscanned,compid,sbu
01/2014,10,90,101,f&r
02/2014,55,40,101,f&r
03/2014,45,23,101,f&r
04/2014,65,35,101,f&r
05/2014,100,20,101,f&r
06/2014,50,30,101,f&r
07/2014,10,90,101,f&r
08/2014,22,48,101,f&r
09/2014,0,100,101,f&r
10/2014,3,97,101,f&r
11/2014,22,60,101,f&r
12/2014,57,37,101,f&r
01/2014,30,100,101,ip
02/2014,130,10,101,ip

有没有一种方法可以将 2014 年 1 月的 f&r 和 ip sbu 值的数据合并起来,并在堆积栏中显示这些值。例如,如果我选中一个复选框进行分组,我需要在 2014 年 1 月的 x 轴堆栈中显示扫描为 30+10=40,未扫描为 100+90= 190。

我构建堆栈栏的代码如下:

var w = 960,
    h = 500,
    p = [20, 50, 30, 20],

    x = d3.time.scale().range([1, 80]);
    y = d3.scale.linear().range([0, h - p[0] - p[2]]),
    z = d3.scale.ordinal().range(["#819FF7", "#CB491A"]),
    parse = d3.time.format("%m/%Y").parse,
    format = d3.time.format("%b-%y");

    var xAxis=d3.svg.axis()
          .scale(x)
          .orient("bottom")
          .ticks(d3.time.month, 1)
              //.ticks(12)



   xAxis.tickFormat(d3.time.format("%b-%y"));


    /*var yAxis = d3.svg.axis()
    .scale(y)
    .ticks(12)
    .orient("left");*/

var svg = d3.select("#container").append("svg:svg")
    .attr("width", w)
    .attr("height", h)
  .append("svg:g")
    .attr("transform", "translate(" + p[3] + "," + (h - p[2]) + ")");

d3.csv("scandata.csv", function(scan) {

  // Transpose the data into layers by cause.
  var scantypes = d3.layout.stack()(["scanned", "unscanned"].map(function(scans) {
    return scan.map(function(d) {
      return {x: parse(d.date), y: +d[scans],z:d.compid,typescan:scans};
    });
  }));




  // Compute the x-domain (by date) and y-domain (by top).
  x.domain(scantypes [0].map(function(d) { return d.x; }));
  y.domain([0, d3.max(scantypes[scantypes .length - 1], function(d) { return d.y0 + d.y; })]);

  // Add a group for each scan.
  var cause = svg.selectAll("g.scan")
      .data(scantypes)
    .enter().append("svg:g")
      .attr("class", "scan")
      .style("fill", function(d, i) { return z(i); })
      .style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });

  // Add a rect for each date.
  var rect = cause.selectAll("rect")
      .data(Object)
    .enter().append("svg:rect")
      .attr("id", function(d,i) { return i + " comp " + d.z;  })
      .attr("x", function(d,i) { 
                        if (i ==0) 
                        { 
                            return x(d.x) ;
                        } 
                        else 
                        {
                            return x(d.x);
                        }} )
      .attr("y", function(d) { return -y(d.y0) - y(d.y); })
      .attr("height", function(d) { return y(d.y); })
      .attr("width", 30)//x.rangeBand()/2
    .on("mouseover", function(d){

                   return tooltip.style("visibility", "visible")
                                   .text((d.y))//d.typescan + " -  " + 
                                   .style("left", (d3.event.pageX) + "px") 
                                   .style("top", (d3.event.pageY - 20) + "px");      ;})
      .on("mousemove", function(d){

                      return tooltip.style("visibility", "visible")
                                   .text((d.y)) //d.typescan + " -  " + 
                                   .style("left", (d3.event.pageX) + "px") 
                                   .style("top", (d3.event.pageY - 20) + "px");      ;})

      .on("mouseout", function(d){return tooltip.style("visibility", "hidden");}) 
      .on("click", function(d){});



  var tooltip = d3.select("#container")
    .append("div")
    .style("position", "absolute")
    .style("z-index", "10")
    .style("visibility", "visible")
    .text("Scanned vs UnScanned")
    .style("font", "Arial")
      .style("color", "white")
    .style("font-size", "14px");

  //Add x-Axis
    svg.append("g")
    .attr("class", "x axis")
    //.attr("transform", function(d) { return "translate(0,80)"; })
    .call(xAxis)




  // Add a label per date.
  var label = svg.selectAll("text")
      .data(x.domain())
    .enter().append("svg:text")
      .attr("x", function(d) { return x(d.x); })//x.rangeBand() / 4
      .attr("y", 6)
      .attr("text-anchor", "middle")
      .attr("dy", ".71em")
      .text(format);

  // Add y-axis rules.
  var rule = svg.selectAll("g.rule")
      .data(y.ticks(5))
    .enter().append("svg:g")
      .attr("class", "rule")
      .attr("transform", function(d) { return "translate(0," + -y(d) + ")"; });

  rule.append("svg:line")
      .attr("x2", w - p[1] - p[3])
      .style("stroke", function(d) { return d ? "#fff" : "#000"; })
      .style("stroke-opacity", function(d) { return d ? .7 : null; });

  rule.append("svg:text")
      .attr("x", -15)
      .style("font-family","Arial 12px")
      .attr("dy", ".25em")
      .text(d3.format(",d"));

您似乎对 SVG 应该是什么样子感到困惑,因此不知道如何实现它。

SVG 中的条形只是矩形。您需要告诉他们应该放置在哪里(始终由栏的左上角定义)以及它们应该有多大。

要使条形在堆叠图中对齐,您需要根据该堆叠的所有值计算出它们的位置和大小。

我创建了一个非常简化使用您的数据的堆积条形图的示例(只是扫描/未扫描的数据,我没有通过 sbu 变量将数据分开)。

这是工作中的example

这是带注释的代码:

var width = 400;
    height = 500;

var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

var xScale = d3.scale.ordinal()
               .rangeRoundBands([0,width], 0.1);
var yScale = d3.scale.linear()
               .range([height, 0]);
        //note the inverted range, so that small values
        //scale to the bottom of the SVG

var data = d3.csv.parse( d3.select("pre#data").text() );
//this just grabs the text from the preformatted block
//and parses it as if it was a csv file
//in your real code, you would use d3.csv(filename, callbackFunction) 
//and the rest would be inside your callback function:

xScale.domain( data.map(function(d){return d.date;}) );
//the xScale domain is the list of all categorical values.
//The map function grabs all the date values from the data
//and returns them as a new array.
//I'm not worrying about parsing dates, since
//strings work fine with an ordinal scale
//(although you'd want to parse them if you wanted to reformat them).

yScale.domain( [0,
                d3.max(data,
                       function(d){
                           return +d.scanned + +d.unscanned;
                       })
                ]);
//The yScale domain starts at 0 (since it's a bar chart)
//and goes to the maximum *total* value for each date.
//The d3.max function finds the maximum for an array
//based on the result returned by the function for each
//element of the array.  This function just finds the sum
//of the scanned and unscanned values 
//(after converting them from strings to numbers with "+").


var dateGroups = svg.selectAll("g") 
        //create an empty selection of groups
   .data(data); //join to the data, each row will get a group

dateGroups.enter().append("g")
    //create the actual <g> elements for each row of data
    .attr("class", "dateGroup"); 
    //give them a meaningful class

//Now, within each group create a rectangle 
//for each category (scanned and unscanned).
//If you had lots of categories, you'd want to 
//use a nested selection and a second data join.
//However, to do that you'd need to do a lot of 
//data manipulation to create an array of 
//separate data objects for each category.
//
//With only two categories, it's easier to just
//do each one separately, and let them inherit
//the data from the parent <g> element.

//For the bottom of the stack:
var bottom = dateGroups.append("rect")
    .attr("class", "data scanned");

bottom.attr("y", function(d){
        return yScale(+d.scanned);
    } )
        //y is the TOP of the rectangle
        //i.e., the position of this data value
        //on the scale
    .attr("height", function(d){
        return Math.abs( yScale(+d.scanned) - yScale(0) );
        //The height of the rectangle is the difference between
        //its data value and the zero line.
        //Note that the yScale value of zero is 
        //bigger than the yScale value of the data
        //because of the inverted scale, so we use
        //absolute value to always get a positive height.
    } );

//For the top of the stack:
var top = dateGroups.append("rect")
    .attr("class", "data unscanned");

top.attr("y", function(d){
        return yScale(+d.unscanned + +d.scanned);
    } )
        //y is the TOP of the rectangle
        //i.e., the position on the scale of 
        //the *total* of the two data categories
    .attr("height", function(d){
        return Math.abs( yScale(+d.unscanned) - yScale(0) );
        //The height of this bar is just based on 
        //its value.  However, this could also be 
        //written as
        //Math.abs(+yScale(+d.scanned + +d.unscanned) 
        //              - yScale(+d.scanned) )
        //i.e., as the difference between the total
        //(top of the bar) and the other category's 
        //value (bottom of the bar)
    } );

//The x value and width are the same for both bars
//so we can re-select all the rectangles and 
//set these attributes at the same time:
dateGroups.selectAll("rect.data")
    .attr("x", function(d){
        return xScale(d.date);
    })
    .attr("width", xScale.rangeBand() );
    //don't need a function for width,
    //since it doesn't depend on the data

一旦您确定了解该程序的每一步发生的情况,您就可以开始添加额外的功能,例如轴或工具提示。您还可以很好地调整代码以处理许多类别,尽管在这种情况下您可能需要创建一个代表每个类别数据的子数组,并使用嵌套选择来创建矩形。这是大多数堆叠条形图示例所使用的方法;在使用这个非常简化的版本后,它们有望更容易理解。

Edit

如果您满足以下条件,则上述解决方案有效know每个堆栈中只有两个值,这两个值的数据来自数据表的同一行。如果你可能有many每个堆栈中的条形,和/或如果它们来自数据表的多行,您将需要使用嵌套选择将数据与各个条形图相匹配。

为了使用嵌套选择方法,您首先必须对数据进行一些操作。您需要将其转换为嵌套数组格式。外部数组必须代表每个stack,并且每个堆栈数据对象必须包含一个代表每个堆栈数据对象的子数组bar.

如何制作嵌套数组取决于您的原始数据格式。当要堆叠的值位于不同行时,d3.nest运算符可以将它们组合成子数组。当堆叠的值与数据表同一行的不同数字时,您必须使用forEach()函数循环遍历数据的所有行并从每行构造一个数组。

在您的示例中,您想要同时执行这两项操作,因此我们将把嵌套操作与 forEach 操作结合起来。同时,我们将计算堆栈的运行总计:为了正确定位每个条形,我们不仅需要知道其自身的计数,还需要知道堆栈中其下方所有值的总计数。

这是一个工作fiddle

数据操作代码是

/*Nest data by date string */
var nestFunction = d3.nest().key( function(d){return d.date;} );
var nestedData = nestFunction.entries(data);

var maxTotal = 0; //maximum count per date, 
                  //for setting the y domain
nestedData.forEach(function(dateGroup) {
   //for each entry in the nested array,
   //each of which contains all the rows for a given date,
   //calculate the total count,
   //and the before-and-after counts for each category.  

    dateGroup.date = dateGroup.key;
    //just using the original strings here, but you could
    //parse the string date value to create a date object

    dateGroup.bars = [];
        //create an array to hold one value for each bar
        //(i.e., two values for each of the original rows)

    var total = 0; //total count per date


    dateGroup.values.forEach(function(row) {
        //the values array created by the nest function
        //contians all the original row data objects
        //that match this date (i.e., the nesting key)

        //create an object representing the bar for
        //the scanned count, and add to the bars array
        dateGroup.bars.push(
            {date:dateGroup.date,
             type: "scanned",
             count: +row.scanned,
             compid: row.compid,
             sbu: row.sbu,
             y0: total, //total value *before* this bar
             y1: (total = total + +row.scanned) //new total
            }
            );

        //create an object representing the bar for
        //the UNscanned count, and add to the bars array
        dateGroup.bars.push(
            {date:dateGroup.date,
             type: "unscanned",
             count: +row.unscanned,
             compid: row.compid,
             sbu: row.sbu,
             y0: total, //total value *before* this bar
             y1: (total = total + +row.unscanned) //new total
            }
            );
    });

    maxTotal = Math.max(maxTotal, total); //update max

});

If you didn't想要将某些类型的条形堆叠在一起 - 例如,如果您想保留不同类型的值compids 位于不同的堆栈中 - 那么您可以将该参数作为嵌套函数的第二个键。仅当值匹配时才嵌套在一起all嵌套键。当然,那么您还必须修改 x 比例以通过两个键分离堆栈。查看分组条形图的示例以了解如何执行此操作。

一旦你有了正确的嵌套数据,你就可以加入外层数组(嵌套对象数组)<g>代表每个堆栈的元素,然后在每个组中创建矩形的嵌套选择并加入内部数组(条形数据):

var dateGroups = svg.selectAll("g") 
        //create an empty selection of groups
   .data(nestedData); //join to the data, 
    //each nested object (i.e., date) will get a group

dateGroups.enter().append("g")
    //create the actual <g> elements for each row of data
    .attr("class", "dateGroup"); 
    //give them a meaningful class

//Now, within each group create a rectangle 
//for each category from the "bars" array created earlier.
//This uses a nested selection, since we don't know
//how many bars there will be for a given date.


var bars = dateGroups.selectAll("rect")
    .data( function(d) {return d.bars;})
        //the 'd' value passed in is the data for each
        //dateGroup, each of which will now have a 
        //nested selection of bars

bars.enter().append("rect"); //create the rectangles

bars.attr("class", function(d){
        //assign classes for all the categorical values
        //(after stripping out non-word characters)
        //so they can be styled with CSS

        var specialchars = /\W+/g; 
             //regular expression to match all non-letter, non-digit characters

        return ["data",
                "type-" + d.type.replace(specialchars, ""), 
                "compid-" + d.compid.replace(specialchars, ""),
                "sbu-" + d.sbu.replace(specialchars, "")
               ].join(" "); //class list is space-separated

    })
    .attr("y", function(d){
        return yScale(d.y1);
        //y is the TOP of the rectangle
        //i.e., the position of the *total* value
        //for this bar and all others under it in the stack
    } )
    .attr("height", function(d){
        return Math.abs( yScale(d.y1) - yScale(d.y0) );
        //the height of the rectangle is the difference 
        //between the total *after* 
        //this value is added to the stack
        // (top of the bar, y1)
        //and the total *before* it is added 
        // (bottom of the bar, y0)

        //Since this is a linear scale, this could also 
        //be written as
        //Math.abs( yScale(d.count) - yScale(0) )
        //i.e., as the difference between
        //its data value and zero line.

        //Note the use of absolute value to
        //compensate for a possibly inverted scale.
    } )
    .attr("x", function(d){
        return xScale(d.date);
    })
    .attr("width", xScale.rangeBand() )
    //don't need a function for width,
    //since it doesn't depend on the data
    .append("title") //add a tooltip title
        .text(function(d) {
            return d.sbu + ", " +d.type +":" + d.count;
        });
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

d3.js - 堆积条形图中的第 2 组数据值 的相关文章

  • 使用频带刻度的相同可重复字符串值

    我是 D3 的新手 想绘制带有 6 个水平条的图表 我的 xAxis 与它们的值是scaleLinear 的 所以我希望 yAxis 只是一个包含每个条形名称的列表 所以对于 yScale 我有 6 个名称 Games Apps Games
  • d3树计算所有孩子的数量

    我有一个基于以下内容的 d3 树 http bl ocks org mbostock 1093025 http bl ocks org mbostock 1093025 我如何计算所有孩子的数量 我已经尝试过这个 但是它计算了树中的所有行
  • Angular svg 或 canvas 使用颜色渐变

    我正在使用 Angular 和 d3 创建一个甜甜圈 在指令中 我可以非常简单地给填充区域指定颜色 在这个 plunker 中它是蓝色 但我想做的是让 SVG 平滑地改变其颜色 0 33 3 red 33 4 66 66 orange 66
  • d3.js:如何在圆环图中的标签下方添加值

    我是 d3 js 的新手 到目前为止我所取得的成就是 this https jsfiddle net saras arya ypdw96v9 使用教程和视频 Now I am trying to add the dataset value
  • 用渐变色绘制一个 D3 圆

    如何用渐变颜色画一个圆 比如说 从黄色到蓝色的渐变 通常 要创建黄色圆圈 我们可以使用以下代码 var cdata 50 40 var xscale 40 var xspace 50 var yscale 70 var svg d3 sel
  • d3.js:是否可以通过键而不是索引来进行转换?

    我有不同长度的数据数组 x值 年 是有限年数的一部分 例如 var data Year 2008 Value 5 Year 2009 Value 6 or var data Year 2007 Value 8 Year 2009 Value
  • 无法让 D3 .on('mouseover', ...) 工作

    我正在学习 D3 并尝试通过将鼠标悬停在 SVG 圆圈上来在散点图上显示数据信息 我从 csv 文件中获取数据 数据位于太阳系 包含行星名称 质量和半径 并且所有圆圈都正确显示 但是当我尝试在鼠标悬停时 console log 数据信息 例
  • 当节点扩展时增加d3中的连接链路长度

    我正在 d3 中研究可折叠力布局 我面临的问题是 当单击节点时 我需要增加节点之间链接的长度 以保持子节点之间的链接距离相同 当分析扩展时 如何增加分析和耀斑之间的距离 保持与子级的距离较小 是的 您可以通过定义一个函数来做到这一点力 链接
  • d3.select 不适用于特殊字符

    我正在使用 d3 js 处理简单的图表 假设下面的 div 是我计划放置 d3 的 svg 容器的地方 div 但是当我使用 d3 select my div chart 我无法选择特定的 div 但是通过使用 java 脚本选择器 它可以
  • 寻找一种重构 D3.js 风格的方法链接模式的方法

    在学习 D3 js 时 我遇到了博客文章 http bost ocks org mike chart 解释其可重用代码单元背后的主要设计模式 我复制了下面的相关代码 下面呈现的模式的方式正是在 D3 代码库和插件中使用的方式 example
  • d3.js。带条的旋转地球仪

    我正在尝试创建带有像这样的酒吧的旋转地球仪这个例子 http data arts appspot com globe 你可以看我的例子here http jsfiddle net zeleniy jrh5xucs 一切都很顺利 直到酒吧超出
  • D3:使用 d3-tile 缩放到边界框

    我已成功将 D3 矢量 地图分层到从 Mapbox 提取图块的 d3 tile 光栅 地图之上 手动缩放效果非常好 矢量和光栅都是同步的 我现在正在尝试实施迈克 博斯托克 缩放到边界框 https bl ocks org mbostock
  • 如何使用数据对象中的值指定 d3.js 选择器?

    我在 Web 应用程序中使用 d3 js 描述我想要做的事情的最简单方法是查看下面链接的 Fiddle 但基本设置是我有一个包含数据对象的数组 my data id B text I want this text in B id C tex
  • 使用 d3 进行多级/分组轴标签

    我想知道是否有一种简单的方法可以在 d3 中添加多级 分层 分组轴标签 例如 如果我有一个折线图 其中 x 轴的月份名称跨越多年 那么我还希望将年份作为月份名称下方的标签 因此它看起来像这样 Oct Nov Dec Jan Feb Mar
  • D3 旭日弧尺寸

    我正在尝试根据以下示例创建 D3 旭日图 https bl ocks org maybelinot 5552606564ef37b5de7e47ed2b7dc099 https bl ocks org maybelinot 55526065
  • D3.js - 具有多个环的圆环图

    以下示例显示了 D3 js 中的圆环图 是否可以向图表添加多个圆环 var dataset apples 53245 28479 19697 24037 40245 var width 460 height 300 radius Math
  • GeoJson 世界数据库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在开发一个项目 需要使用 d3 js 显示国家和城市的地图 实际上 D3支持GeoJson格式 通常
  • 如何将此数据编码为 JSON 中的父/子结构

    我正在使用 d3 js 将动物 有机体 家族 一次最多 4000 个 可视化为树形图 尽管数据源也可以是目录列表或命名空间对象列表 我的数据如下 json organisms name Hemiptera Miridae Kanakamir
  • 如何根据形状字段值将两个不同的形状添加到 D3 力向图?

    我是D3的新手 我正在使用力定向图 我想在节点的位置添加两种不同类型的形状 我的 json 如下 nodes name 00 00 00 00 00 00 00 01 group 0 shape 1 name 00 00 00 00 00
  • 在 Crossfilter 中使用过滤器

    我刚刚开始使用 crossfilter 和 d3 js 我正在尝试 API 参考中给出的一些片段 我有以下数据 var payments crossfilter date 2011 11 14T16 17 54Z quantity 2 to

随机推荐