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 组数据值 的相关文章

  • 避免在 ES6 的函数内定位 this 的对象作用域

    例如 我正在使用 D3 js 运行一个项目 导入特定模块并调用它们的函数 Setup TypeScript ES6 导入特定的 D3 组件 角6 我有一个对象 在本例中是一个角度指令 并在 SVG 画布上绘制一些圆圈 并希望它们在拖动事件上
  • 如何在 d3 js 中突出显示从根到选定节点的路径?

    我使用 d3 js 创建了一棵树 现在我创建了一个下拉菜单 其中包含树中所有节点的列表 现在 从下拉菜单中选择一个节点时 我想突出显示从根到该特定节点的路径 这个怎么做 首先创建一个 flatten 函数 它将分层数据变成一个 n 数组 f
  • c3js数据标签的位置

    有没有可能的方法来更改数据上方标签的位置c3条形图 在官方文档中 很好地解释了如何通过操作 y 和 x 整数来更改 x 和 y 测量轴上标签的位置 但我没有找到任何数据标签 我试图用简单的方式指出它d3其上c3是基于但是console lo
  • 未捕获的类型错误:无法读取未定义的属性“albersUsa”

    以前可能有人问过这个问题 但我希望我能得到针对我遇到的问题的答案 我是 d3 的新手 我现在正在尝试绘制一个纬度 经度 当我成功完成第一个纬度 经度时 我将完成其余的工作 感谢专家的任何帮助 谢谢 尝试在特定区域绘制蜂窝基站 这是代码
  • D3.js - 更改鼠标悬停时元素的不透明度 IF 条件 = false

    我正在制作一个带有过滤器的交互式 D3 js 图表 当用户单击选定的复选框时 该过滤器会显示点 此外 在鼠标悬停事件上 所选点旁边将出现一个弹出窗口 其中包含一些信息 由于图表上的点数量相对较多 因此我选择在取消选中相应复选框时使相关点变得
  • 如何运行 Mike Bostock 的 D3 示例?

    我一直在尝试经营迈克博斯托克透视地球仪 http bl ocks org mbostock 6747043例如 但是如果您尝试在本地重现它 则对其 json 文件的引用是不正确的 问题来自于这行代码 d3 json mbostock raw
  • 获取现有 SVG 元素的属性并使用 d3.js 绑定为数据

    我有一个现有的 svg 元素 例如
  • 使 D3 响应式:viewBox 与 resize()?

    我必须构建在平板电脑 桌面显示器以及某些情况下非常大的 4k 高分辨率影院尺寸显示器上都能正常运行的 d3 可视化效果 因此 我试图找出使用 SVG 的 viewBox 属性和 preserveaspectratio 与调用调整大小函数以在
  • D3 js 链接在节点下面

    我创建了图形对象 稍后可以使用更多节点和链接来扩展图形对象 第一个创作看起来不错 然后 与add函数我添加了节点 4 和链接 as you can see above the link of between node 4 and 3 is
  • Javascript 将对象推送为克隆

    我将 d3 用于交互式网络应用程序 我需要绑定的数据在交互过程中发生变化 并且由 JSON 变量中的一些选定对象组成 为此 我在 JSON 变量上使用了映射 并进行了一些查询来选择适当的对象 对象被推送到列表中 并且该列表被绑定为新数据 我
  • Dimple.js - 将数据标签添加到条形图的每个条形

    我使用的是dimple js 它基于d3 js 是否可以向本示例中提到的条形图的每个条形添加数据标签http dimplejs org examples viewer html id bars vertical http dimplejs
  • 快速响应的交互式图表/图形:SVG、Canvas 还是其他?

    我正在尝试选择正确的技术来更新一个项目 该项目基本上在可缩放 可平移的图表中渲染数千个点 当前使用 Protovis 的实现性能不佳 在这里查看 http www planethunters org classify http www pl
  • D3 时间解析返回 null

    根据此页面上的说明 https github com mbostock d3 wiki Time Formatting https github com mbostock d3 wiki Time Formatting我正在尝试解析 ISO
  • LeafletJs只显示一个国家

    我使用 Leafletjs 和 D3 来显示地图 我只想在地图上显示英国 Leaflet和D3是否可以只显示英国 这当然是可能的 现在的解决方案取决于您是想使用 D3 绘制英国 还是想从 Tile Server 获取它 在后一种情况下 有一
  • 请建议 D3.js 进行 CSV 数据导入

    我正在尝试使用 D3 js 导入 CSV 数据 var englishArray d3 csv data csv function d return d value function error data var englishArray
  • Typescript 中未定义的 d3.scale

    我是 Typescript 的新手 2 周 我正在从事包装 d3 js 框架的项目 我在使用 d3 d ts 命名空间 导出模块 导入时遇到问题 我的问题 当我尝试使用 d3 scale linear 时 浏览器控制台中出现错误 TypeE
  • 时间序列折线图与轴不同步

    本实验基于这个d3官方例子 http bost ocks org mike path 我想要实现的是可视化时间序列数据的最后 x 分钟 我有这个代码的副本jsfiddle http jsfiddle net 225dC 3 单击以添加新数据
  • 在 D3 v4 中使用 Zoom.translateExtent 约束地图平移

    我正在尝试显示单个州的地图 并将缩放和平移限制在州的边界内 除了缩放状态路径以适应较小的容器时的平移约束之外 它大部分都在工作 我认为这归结于我不明白该使用什么参数zoom translateExtent 虽然我对此很陌生 所以它可能是别的
  • 需要帮助从数组中为国家/地区着色,保留其余默认颜色

    我需要一些帮助从我创建的数组中获取数据 然后仅对数组中存在的国家 地区进行着色 而不在数组中的其余国家 地区我希望保留为默认颜色 我正在使用 D3 来完成所有这些工作 并且我非常确定我可以通过 D3 实现我需要的目标 但不确定如何实现 我想
  • D3v6 嵌套图 - 嵌套 join()?

    我想可视化每个节点的 孩子 洞察力 我猜 D3v6 join 函数可以嵌套 不幸的是我找不到任何例子 下面的代码片段包含一个具有 3 个节点和子节点作为属性的outerGraph 到目前为止 这些孩子还没有被使用 相反 innerGraph

随机推荐