d3.js appending two labels from a dataset on the Y axis - d3.js

My data for a horizontal bar graph is an array of objects that look like this:
{value: -10, dataset:"Corvette", year: "1975"}. The "dataset" labels are on the y axis. I would like to append the "year" label to the "dataset" label, so the labels on the y axis would look like this:
Corvette 1975
So far I can add one or the other to the Y axis but not both. Here is the code I have:
var margin = {top: 30, right: 10, bottom: 50, left: 50},
width = 500,
height = 300;
var data = [{value: -10, dataset:"Corvette", year: "1975"},
{value: 40, dataset:"Lumina", year: "1975"},
{value: -10, dataset:"Gran Torino", year: "1971"},
{value: -50, dataset:"Pomtiac GTO", year: "1964"},
{value: 30, dataset:"Mustang", year: "19655"},
{value: -20, dataset:"Camaro", year: "1973"},
{value: -70, dataset:"Firebird", year: "1975"}];
// Add svg to
var svg = d3.select('body').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 + ')');
// set the ranges
var y = d3.scaleBand()
.range([height, 0])
.padding(0.1);
var x = d3.scaleLinear()
.range([0, width]);
// Scale the range of the data in the domains
x.domain(d3.extent(data, function (d) {
return d.value;
}));
y.domain(data.map(function (d) {
return d.dataset;
}));
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function (d) {
return "bar bar--" + (d.value < 0 ? "negative" : "positive");
})
.attr("x", function (d) {
return x(Math.min(0, d.value));
})
.attr("y", function (d) {
return y(d.dataset);
})
.attr("width", function (d) {
return Math.abs(x(d.value) - x(0));
})
.attr("height", y.bandwidth());
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
let yAxisGroup = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisRight(y));
yAxisGroup.selectAll('.tick')
.data(data)
.select('text')
.attr('x', function(d,i){return d.value<0?9:-9})
.style('text-anchor', function(d,i){return d.value<0?'start':'end'})
Here is the fiddle:
https://jsfiddle.net/Kavitha_2817/2e1xLxLc/

You could map a concatenated string of your d.dataset and d.year to the y scale, and then use the same concatenated string when positioning your rects using that y scale.
The y axis will then use that concatenated string.
Example:
https://jsfiddle.net/2e1xLxLc/4/
Relevant code:
//create a reusable function to concatenate the values you want to use
function yValue(d) { return d.dataset + " " + d.year }
// Scale the range of the data in the domains
x.domain(d3.extent(data, function (d) {
return d.value;
}));
y.domain(data.map(function(d){ return yValue(d) }));
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function (d) {
return "bar bar--" + (d.value < 0 ? "negative" : "positive");
})
.attr("x", function (d) {
return x(Math.min(0, d.value));
})
.attr("y", function (d) {
return y(yValue(d));
})
.attr("width", function (d) {
return Math.abs(x(d.value) - x(0));
})
.attr("height", y.bandwidth());

If you (for any reason) want to keep the same domain, get the year using tickFormat:
.call(d3.axisRight(y)
.tickFormat(function(d) {
//filter the data array according to 'd', which is 'dataset'
var filtered = data.filter(function(e) {
return e.dataset === d;
})[0];
//get the year in the 'filtered' object using 'filtered.year'
return d + " " + filtered.year
})
);
Here is your code with that change:
var margin = {
top: 30,
right: 10,
bottom: 50,
left: 50
},
width = 500,
height = 300;
var data = [{
value: -10,
dataset: "Corvette",
year: "1975"
}, {
value: 40,
dataset: "Lumina",
year: "1975"
}, {
value: -10,
dataset: "Gran Torino",
year: "1971"
}, {
value: -50,
dataset: "Pomtiac GTO",
year: "1964"
}, {
value: 30,
dataset: "Mustang",
year: "19655"
}, {
value: -20,
dataset: "Camaro",
year: "1973"
}, {
value: -70,
dataset: "Firebird",
year: "1975"
}];
// Add svg to
var svg = d3.select('body').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 + ')');
// set the ranges
var y = d3.scaleBand()
.range([height, 0])
.padding(0.1);
var x = d3.scaleLinear()
.range([0, width]);
// Scale the range of the data in the domains
x.domain(d3.extent(data, function(d) {
return d.value;
}));
y.domain(data.map(function(d) {
return d.dataset;
}));
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) {
return "bar bar--" + (d.value < 0 ? "negative" : "positive");
})
.attr("x", function(d) {
return x(Math.min(0, d.value));
})
.attr("y", function(d) {
return y(d.dataset);
})
.attr("width", function(d) {
return Math.abs(x(d.value) - x(0));
})
.attr("height", y.bandwidth());
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
let yAxisGroup = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisRight(y)
.tickFormat(function(d) {
var filtered = data.filter(function(e) {
return e.dataset === d;
})[0];
return d + " " + filtered.year
})
);
yAxisGroup.selectAll('.tick')
.data(data)
.select('text')
.attr('x', function(d, i) {
return d.value < 0 ? 9 : -9
})
.style('text-anchor', function(d, i) {
return d.value < 0 ? 'start' : 'end'
})
<style> .bar--positive {
fill: steelblue;
}
.bar--negative {
fill: darkorange;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>

Related

Stacked bar chart, y domain

I have a plunker here https://plnkr.co/edit/hBWoIIyzcHELGyewOyZE?p=preview
I'm trying to create a simple stacked bar chart.
The bars go above the top of the chart which I think is a problem with the domain
I also need a scale on the y axis which I think is to do with the y domain.
Is it the y domain that controls the height of the bars and scales shown on the y axis
y.domain([0, d3.max(data, (d)=>{
return d
})])
This is a list of the issues so far:
First, your y domain is not correctly set. It should use the stacked data:
y.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, function(d) {
return d[0] + d[1];
});
})])
Second, the math for the y and height of the rectangles is wrong. It should be:
.attr('height', (d) => {
return y(d[0]) - y(d[0] + d[1]);
})
.attr('y', (d) => {
return y(d[0] + d[1]);
})
Finally, use the x scale for the x position:
.attr('x', (d, i) => {
return x(d.data.day)
})
Here is the code with those changes:
var margin = {
top: 40,
right: 20,
bottom: 40,
left: 40
}
var width = 400 - margin.left - margin.right
var height = 500 - margin.top - margin.bottom
var data = [{
day: 'Mon',
apricots: 120,
blueberries: 180,
cherries: 100
},
{
day: 'Tue',
apricots: 60,
blueberries: 185,
cherries: 105
},
{
day: 'Wed',
apricots: 100,
blueberries: 215,
cherries: 110
},
{
day: 'Thu',
apricots: 150,
blueberries: 330,
cherries: 105
},
{
day: 'Fri',
apricots: 120,
blueberries: 240,
cherries: 105
}
];
var svg = d3.select('body')
.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 + ')')
var colors = ['#FBB65B', '#513551', '#de3163'];
var stack = d3.stack()
.keys(['apricots', 'blueberries', 'cherries']);
var stackedSeries = stack(data);
// Create a g element for each series
var g = d3.select('g')
.selectAll('g.series')
.data(stackedSeries)
.enter()
.append('g')
.classed('series', true)
.style('fill', (d, i) => {
return colors[i];
});
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
var y = d3.scaleLinear()
.range([height, 0])
x.domain(data.map((d) => {
return d.day
}))
y.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, function(d) {
return d[0] + d[1];
});
})])
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(d3.axisBottom(x))
svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(y))
// For each series create a rect element for each day
g.selectAll('rect')
.data((d) => {
return d;
})
.enter()
.append('rect')
.attr('height', (d) => {
return y(d[0]) - y(d[0] + d[1]);
})
.attr('y', (d) => {
return y(d[0] + d[1]);
})
.attr('x', (d, i) => {
return x(d.data.day)
})
.attr('width', x.bandwidth())
.style("stroke", "#ccc");
<script src="https://d3js.org/d3.v4.min.js"></script>

trouble accessing max of nested object data for use in a domain in D3

I have csv data with a column of years and then columns of age groups with rates:
year,15-19,20-24,25-29,...
1999,0,1.3,8.1,...
2000,0,1.3,8.3,...
.
.
I've mapped the data as shown below. Once mapped the data is in the form of:
>Array[Object, Object, ...]
Each Object is of the form:
>id:"age group"
values:Array[]
>Object
>rate: ""
>year: ""
I'm having trouble with the max value for the y.domain. It seems to be pulling the max value from my 4th object and stopping there. Any help figuring out what's wrong with the max call would be appreciated.
var svg = d3.select("svg"),
margin = {top: 20, right: 80, bottom: 30, left: 50},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// define scales
var x = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
//define color scale
z = d3.scaleOrdinal(d3.schemeCategory10);
//define line generator
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.rate); });
//load data
d3.csv("breaseByageSpread.csv", function(error, data) {
if(error) throw error;
var ages = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d) {
return {year: d.year, rate: d[id]};
})
};
});
//define x axis
x.domain(d3.extent(data, function(d) { return d.year; }));
//define y axis
y.domain([
d3.min(ages, function(c) { return d3.min(c.values, function(d) { return d.rate; }); }),
d3.max(ages, function(c) { return d3.max(c.values, function(d) { return d.rate; }); })
]);
// define color scale
z.domain(ages.map(function(c) { return c.id; }));
//append x axis
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.ticks(15)
.tickFormat(d3.format('d')));
//append y axis
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -50)
.attr('x', -125)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Rate per 100,000 ppl");
//append rate data to svg
var rate = g.selectAll(".rate")
.data(ages)
.enter().append("g")
.attr("class", "age");
//append rate path to svg
rate.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return z(d.id); });
rate.append("text")
.datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.year) + "," + y(d.value.rate) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.id; });
function type(d, _, columns) {
//d.date = parseTime(d.date);
for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c];
return d;
};
});
I found the problem. The rates data was coming in as strings. To convert it to a numeric I changed the following code when loading the data:
Old:
values: data.map(function(d) {
return {year: d.year, rate: d[id]};
New:
values: data.map(function(d) {
return {year: d.year, rate: +d[id]};

Conditionally position labels on y axis

I have a horizontal bar graph with positive and negative values. I would like to position the labels on the y axis to the left of the axis when the value is positive and to the right of the axis when the value is negative.
{value: -70, dataset:"foo"},
{value: -20, dataset:"bar"},
{value: 30, dataset:"baz"}
etc...
The value of the first bar is negative and the label foo is on the right side of the axis, which is where I want it. But the value of the third bar is positive, so I would like to see that label on the left of the y axis. Is this possible?
Here is the JSFiddle: https://jsfiddle.net/Kavitha_2817/fmr1x1gu/18/
A simple solution (out of many) is calling two axis generators, axisLeft and axisRight, and formatting the ticks according to the dataset values:
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisRight(y).tickFormat(function(d) {
var value = data.filter(function(e) {
return e.dataset === d
})[0].value;
return value > 0 ? null : d;
}));
svg.append("g")
.attr("class", "y axis left")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisLeft(y).tickFormat(function(d) {
var value = data.filter(function(e) {
return e.dataset === d
})[0].value;
return value < 0 ? null : d;
}));
Here is a demo:
var margin = {
top: 30,
right: 10,
bottom: 50,
left: 50
},
width = 500,
height = 300;
var data = [{
value: -10,
dataset: "barbaz"
}, {
value: 40,
dataset: "barbar"
}, {
value: -10,
dataset: "foobaz"
}, {
value: -50,
dataset: "foobar"
}, {
value: 30,
dataset: "baz"
}, {
value: -20,
dataset: "bar"
}, {
value: -70,
dataset: "foo"
}];
// Add svg to
var svg = d3.select('body').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 + ')');
// set the ranges
var y = d3.scaleBand()
.range([height, 0])
.padding(0.1);
var x = d3.scaleLinear()
.range([0, width]);
// Scale the range of the data in the domains
x.domain(d3.extent(data, function(d) {
return d.value;
}));
y.domain(data.map(function(d) {
return d.dataset;
}));
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) {
return "bar bar--" + (d.value < 0 ? "negative" : "positive");
})
.attr("x", function(d) {
return x(Math.min(0, d.value));
})
.attr("y", function(d) {
return y(d.dataset);
})
.attr("width", function(d) {
return Math.abs(x(d.value) - x(0));
})
.attr("height", y.bandwidth());
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisRight(y).tickFormat(function(d) {
var value = data.filter(function(e) {
return e.dataset === d
})[0].value;
return value > 0 ? null : d;
}));
svg.append("g")
.attr("class", "y axis left")
.attr("transform", "translate(" + x(0) + ",0)")
.call(d3.axisLeft(y).tickFormat(function(d) {
var value = data.filter(function(e) {
return e.dataset === d
})[0].value;
return value < 0 ? null : d;
}));
.bar--positive {
fill: steelblue;
}
.bar--negative {
fill: darkorange;
}
.y.axis.left {
fill: none;
stroke: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

D3 How to update the chart after selection from drop down menu with new data

I'm building a waterfall chart in D3. When the page will load, it will render the default page but user will have choice to select different
'Company' and 'Year' from the drop down menu. I have been able to create the chart what I want. But when I select any different Company or Year, D3 adds another chart on top of the existing instead of replacing it and thats because I'm targeting a particular div / svg from the HTML. How can I use D3 to update the chart with new data instead add another one of top? And if I can have that movement of chart bars with transition, that will be awesome.
HTML is a simple svg:
<svg class="chart"></svg>
Here is the function to create the chart which I call when Ajax call is successful:
function waterfallChart (dataset) {
var data = [];
for (var key in dataset[0]) {
data.push({
name: key,
value: dataset[0][key]
})
}
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 0.3;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width])
.padding(padding);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x)
var yAxis = d3.axisLeft(y)
.tickFormat(function(d) {
return dollarFormatter(d);
});
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cumulative = 0;
for (var i = 0; i < data.length; i++) {
data[i].start = cumulative;
cumulative += data[i].value;
data[i].end = cumulative;
data[i].class = (data[i].value >= 0) ? 'positive' : 'negative'
}
data.push({
name: 'Total',
end: cumulative,
start: 0,
class: 'total'
});
x.domain(data.map(function(d) {
return d.name;
}));
y.domain([0, d3.max(data, function(d) {
return d.end;
})]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return "bar " + d.class
})
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end));
})
.attr("width", x.bandwidth());
bar.append("text")
.attr("x", x.bandwidth() / 2)
.attr("y", function(d) {
return y(d.end) + 5;
})
.attr("dy", function(d) {
return ((d.class == 'negative') ? '-' : '') + ".75em"
})
.text(function(d) {
return dollarFormatter(d.end - d.start);
});
bar.filter(function(d) {
return d.class != "total"
}).append("line")
.attr("class", "connector")
.attr("x1", x.bandwidth() + 5)
.attr("y1", function(d) {
return y(d.end)
})
.attr("x2", x.bandwidth() / (1 - padding) - 5)
.attr("y2", function(d) {
return y(d.end)
})
function dollarFormatter(n) {
n = Math.round(n);
var result = n;
if (Math.abs(n) > 1000) {
result = Math.round(n/1000) + 'B';
}
return '$ ' + result;
}
}
Here is code where I have event listener and on selection it will run the above function:
$("#airline-selected, #year-selected").change(function chartsData(event) {
event.preventDefault();
var airlineSelected = $('#airline-selected').find(":selected").val();
var yearSelected = $('#year-selected').find(":selected").val();
$.ajax({
url: "{% url 'airline_specific_filtered' %}",
method: 'GET',
data : {
airline_category: airlineSelected,
year_category: yearSelected
},
success: function(dataset){
waterfallChart(dataset)
},
error: function(error_data){
console.log("error")
console.log(error_data)
}
})
});
You are missing some pretty important things here. If you are going to do updates on your data you need to do a couple things.
Give a key to the data() function. You need to give D3 a way to identify data when you update it so it knows if it should add, remove, or leave existing data. The key does this. For instance you might do something like this:
.data(data, function(d) { return d.name })
Now d3 will be able to tell you data items apart assuming d.name is a unique identifier.
You need an exit() for data that is removed during update. You need to save the data joined selection so you can call enter and exit on it:
var bar = chart.selectAll(".bar")
.data(data, function(d) { return d.name})
now you can call: bar.exit().remove() to get rid of deleted items and bar.enter() to add items.
You need to make a selection that hasn't had enter() called on it to update attributes.
Probably more a matter of style, but you should set up the SVG and margins outside the update function since they state the same. You can still update the axis and scales by calling the appropriate functions in the update.
The code you posted is a little hard for other people to run — you'll always get better faster answers if you post code that has been reduced to the main problem and that others can run without needing access to offsite data or apis.
Here's an example that updates on a setInterval between two data sets based on your code. But you should also look at the General Update Patterns - they are very simple but have almost everything you need to know. (https://bl.ocks.org/mbostock/3808234)
dataset = [
{name: "Albert", start: 0, end:220},
{name: "Mark", start: 0, end:200},
{name: "Søren", start: 0, end:100},
{name: "Immanuel", start: 0, end:60},
{name: "Michel", start: 0, end:90},
{name: "Jean Paul", start: 0, end: 80}
]
dataset2 = [
{name: "Albert", start: 0, end:20},
{name: "Immanuel", start:0, end:220},
{name: "Jaques", start: 0, end:100},
{name: "Gerhard", start:0 , end:50},
{name: "Søren", start: 0, end:150},
{name: "William", start: 0, end: 180}
]
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 400 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom,
padding = 0.3;
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.range([0, width])
.padding(padding);
var y = d3.scaleLinear()
.range([height, 0])
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
chart.append("g")
.attr("class", "y axis")
var currentData = dataset
waterfallChart(currentData)
setInterval(function() {
currentData = currentData === dataset ? dataset2 : dataset
waterfallChart(currentData)
}, 3000)
function waterfallChart(data) {
var t = d3.transition()
.duration(750)
x.domain(data.map(function(d) {
return d.name
}))
y.domain([0, d3.max(data, function(d) {
return d.end
})])
var xAxis = d3.axisBottom(x)
var yAxis = d3.axisLeft(y)
d3.select('g.x').transition(t).call(xAxis)
d3.select('g.y').call(yAxis)
var bar = chart.selectAll(".bar")
.data(data, function(d) {
return d.name
})
// ENTER -- ADD ITEMS THAT ARE NEW IN DATA
bar.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)"
})
.attr("class", 'bar')
.append("rect")
.attr("y", function(d) {
return y(Math.max(d.start, d.end));
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end));
})
.attr("width", x.bandwidth())
// UPDATE EXISTING ITEMS
chart.selectAll(".bar")
.transition(t)
.attr("transform", function(d) {
return "translate(" + x(d.name) + ",0)"
})
.select('rect')
.attr("y", function(d) {
return y(Math.max(d.start, d.end))
})
.attr("height", function(d) {
return Math.abs(y(d.start) - y(d.end))
})
.attr("width", x.bandwidth())
// REMOVE ITEMS DELETED FROM DATA
bar.exit().remove()
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg class="chart"></svg>

getting error with d3 (V4) Stacked Bar Chart

Getting error while creating Stacked Bar Chart using D3 JS in a Angular 2 application,
here is the code,
//data
var data = [
{ month: 'Jan', A: 20, B: 5, C: 10 },
{ month: 'Feb', A: 30, B: 10, C: 20 }
];
var xData = ["A", "B", "C"];
var margin = { top: 20, right: 50, bottom: 30, left: 0 },
width = 350 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.35);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var xAxis = d3.axisBottom(x);
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 + ")");
var dataIntermediate = xData.map(function (c) {
return data.map(function (d) {
return { x: d.month, y: d[c] };
});
});
var dataStackLayout = d3.stack().keys([dataIntermediate]);
x.domain(dataStackLayout[0].map(function (d) {
return d.x;
}));
y.domain([0,
d3.max(dataStackLayout[dataStackLayout.length - 1],
function (d) { return d.y0 + d.y; })
])
.nice();
var layer = svg.selectAll(".stack")
.data(dataStackLayout)
.enter().append("g")
.attr("class", "stack")
.style("fill", function (d, i) {
return color(i);
});
layer.selectAll("rect")
.data(function (d) {
return d;
})
.enter().append("rect")
.attr("x", function (d) {
return x(d.x);
})
.attr("y", function (d) {
return y(d.y + d.y0);
})
.attr("height", function (d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.range());
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
Error are,
(51,41): error TS7017: Index signature of object type implicitly has an 'any' type.
# below line,
return { x: d.month, y: d[c] };
(76,19): error TS2345: Argument of type '(this: BaseType, d: {}) => {}' is not assignable to parameter of type 'ValueFn'.
Type '{}' is not assignable to type '{}[]'.
Property 'find' is missing in type '{}'.
# below line,
var dataStackLayout = d3.stack().keys([dataIntermediate]);
im using same example to implement stacked barchart in angular2.
i think your issue is
var dataStackLayout = d3.stack().keys([dataIntermediate]);
dataStackedLayout should be array instead of function.
were you able to resolve this issue yet?

Resources