gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 05:12:38 26 4
gpt4 key购买 nike

我有以下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

一旦确定了解该程序的每个步骤都发生了什么,就可以开始添加其他功能,例如轴或工具提示。尽管在这种情况下,您可能希望创建一个代表每个类别的数据的子数组,并使用嵌套选择来创建矩形,但是您也将很适合使代码适用于许多类别。大多数堆叠的条形图示例都使用这种方法。使用此简化版本后,希望它们会更容易理解。

编辑

如果您知道每个堆栈中只有两个值,并且两个值的数据都来自数据表的同一行,则上述解决方案有效。如果每个堆栈中可能有很多条,和/或它们来自数据表的多行,则需要使用 nested selection将数据与各个条匹配。

为了使用嵌套选择方法,您首先必须对数据进行一些处理。您需要将其转换为嵌套数组格式。外部数组必须表示每个堆栈,每个堆栈数据对象必须包括一个表示每个条形图的子数组。

如何制作嵌套数组取决于您的原始数据格式。当您要堆叠的值位于不同的行中时, 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

});

如果您不想将某些类型的条堆叠在一起(例如,如果您想将来自不同 compid的值保留在不同的堆叠中),则可以将该参数作为嵌套函数的第二个键。如果值在所有嵌套键上都匹配,则嵌套在一起。当然,那么您还必须修改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;
});

关于d3.js-堆叠的条形图中的第2组数据值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21522825/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com