Related
I have created a D3 chart where I would like the "x" coordinate (rect) to be set via the "start_time" property of my data. The "x" axis scale is set to the last six hours. The "start_time" is within the six hour range so I am trying to get my data to match up to the time range on the x axis. (currently "start_time" is 5:30am and I would like my rectangle to start at 5:30am on the x axis). My code is below, thanks!
I'm using moment.js to create the time range so it's possible that something is wrong there as far as the two data properties talking to each other.
Fiddle: http://jsfiddle.net/dtepdc/bmry9f04/4/
const data = [
{
"start_time": "2019-01-15T05:30:40",
"end_time": "2019-01-15T04:32:25",
"elapsed_time": 89,
"mynum":"CO12345"
}
];
const svgWidth = 700, svgHeight = 300;
const svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
const start = moment().format('LLL');
const end = moment().subtract(12, 'hours').format('LLL');
const dataFormat = moment(data[0].start_time).format('LLL');
console.log('start: ', dataFormat)
const xScale = d3.scaleTime()
.domain([new Date(start), new Date(end)])
.range([0, svgWidth]);
const x_axis = d3.axisBottom()
.scale(xScale);
svg.append("rect")
.attr("width", "100%")
.attr("height", "86%")
.attr("fill", "#000");
svg.append("rect")
.attr('x', function(d) { return d ; })
.attr("y", 120)
.attr("width", data[0].elapsed_time)
.attr("height", 40)
.attr("fill", "green");
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.mynum;
})
.attr("x", 120)
.attr("y", 148)
.attr("width", data[0].elapsed_time)
.attr("height", 50)
.attr("font-size", 14)
.attr("fill", "#fff");
const xAxisTranslate = svgHeight - 40;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate +")")
.call(x_axis)
.selectAll("text")
I'm no d3 hero but the way I do it is to create my time scale instance and then use that to translate a date to a number.
x = d3.scaleTime().range([0, axisWidth]);
var xPos = x(moment(d.positionDate, "MM/D/YYYY h:mm a"));
So in your case it would be something like:
svg.append("rect")
.attr('x', function(d) {
return moment(d.start_time, "MM/D/YYYY h:mm a") // not correct formatting
})
.attr("y", 120)
.attr("width", data[0].elapsed_time)
.attr("height", 40)
.attr("fill", "green");
You should look up the moment formatting flags for your ""2019-01-15T05:30:40" data
I have a data that is filtered by year then nested by group. The first few years there is one group, while the remaining years have seven to eight groups for which the lines are plotted. Along with lines I also plot points of actual data.
here is the plot for the year 2007 that has only one group:
2007 line plot
Notice the y-axis is missing and only the points are showing but no lines.
Here is the plot for the year 2010 that has eight groups:
2010 lines plot
Notice the y-axis, all points and lines are showing.
Here is the associated code:
Defining line
var line = d3.svg.line()
.x(function(d) { return LoanAmount_scale(d['amount_mean']); })
.y(function(d) { return Investor_scale(d['Investors_mean']); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right )
.attr("height", height + margin.top + margin.bottom);
var plot = svg.append("g")
.attr("class", "chart")
.attr("width" , width- margin.left)
.attr("height", height)
.attr("transform","translate ("+ margin.left + "," + margin.top + ")");
menu selection
// setting stage for dropdown menu
// years
var years = ['Select Year'];
var i= 2006;
for( i = 2006; i<= 2014; i++){
years.push(i);
}
// selecting an option
var select = d3.select('body')
.append('select')
.attr('class','select')
.attr('id','select_id')
.on('change',change);
// available options on dropdown menu
var option = select.selectAll('option')
.data(years)
.enter()
.append('option')
.text(function(d){ return d});
// menu selection of year
function change(){
var select_year = d3.select('select')
.property('value')
// reset chart before updating year
update(data,'Select Year');
// update year
update(data,select_year);
}
Update function filters year and nests by group, then plots lines and points
function update(data, year){
var Filtered = data.filter(function(d){ return d["LoanOrig_Yr"] == year});
var nested_by_rating = d3.nest()
.key(function(d) {
return d['ProsperRating']
}).sortKeys(d3.ascending)
.entries(Filtered);
// add lines for each rating
var lines = plot.selectAll("path")
.data(nested_by_rating)
// remove old lines
lines.exit().remove();
plot.selectAll("circle")
.data(nested_by_rating)
.exit().remove();
nested_by_rating.forEach( function(d,i){
var prosperR = d.key
// entering data
lines.enter()
.append("path")
.attr("class", "line")
.attr("d", line(d.values))
.attr("stroke", RatingColor(prosperR))
.attr("stroke-width", 3)
.attr("stroke-opacity",0.3)
.attr("fill","none");
debugger;
plot.selectAll("path.dot")
.data(d.values)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5)
.attr("fill","Black");
debugger;
// dynamic legend
svg.append("text")
.attr("x", 1300)
.attr("y", 100 + i*20)
.attr("class", "legend") // style the legend
.style("fill", function() { // Add the colours dynamically
return d.color = RatingColor(prosperR); })
.text(prosperR);
Reset chart between year selection
// reseting chart between years
if (year == 'Select Year'){
gX.call(xAxis);
gY.call(yAxis);
svg.append("text")
.attr("x", 1300)
.attr("y", 100)
.attr("class", "legend") // style the legend
.style("fill", "white")
.text(["NR"]);
svg.selectAll("legend_text")
.data(["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"])
.enter()
.append("text")
.attr("class","legend")
.attr("x", 1300)
.attr("y", function(d,i){return 100 + i*20;
})
.attr("class", "legend") // style the legend
.style("fill", "white")
.style("stroke","white")
.text(function(d,i){return d;});
} \\ end if statement
} \\end .forEach statement
When tracking data entry and exit using the browser inspector, I find the data in both cases move in and out as expected. Observe the points data are passed using line.x() and line.y(), so it is very strange that the line does not show in the year 2007 and other similar years with one group.
I would appreciate any help fixing this bug. Thank you.
You delete the lines by selecting all paths, and the axis line is a path.
Also the update of the legend was adding each time a lot of entries.
Black legend text is hidden/shown by setting fill to white or black.
The axis do not need update drawing for the Select Year, they stay the same for all graphs.
var data = d3.csv.parse(d3.select("#dataset").text());
data.forEach(function(d){
d.LoanOrig_Yr = parseInt(+d.LoanOrig_Yr);
d.amount_median = parseFloat(+d.amount_median);
d.Investors_median = parseInt(+d.Investors_median);
});
function draw(data) {
var margin = {top: 5, right: 100, bottom: 150, left: 80},
width = 1460 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var margin2 = {top: 430, right: 200, bottom: 110, left:80},
height2 = 600 - margin2.top - margin2.bottom;
var margin3 = {top: 40, right:20, bottom: 80, left:180},
width2 = 1300 - margin3.left - margin3.right;
// display title
d3.select("body")
.append("h2")
.text("Funding low risk loans is shared by more investors than high risk loans");
// guiding text
d3.select("body")
.append("p")
.text("An investor may fund the whole loan or fractions of several loans with minimum"+
" investment amount of $25."+
" Prosper rating started in July 2009." +
" Prosper Rating (ordered): AA (lowest risk), A, B, C, D, E, HR (high risk), NR (not rated)." +
" Points on the line reflect loan amount average computed by "+
" agregating loans over brackets of size $1500, starting at $1000." +
" Data for 2014 is only for the first quarter."
);
var Investor_extent = d3.extent(data, function(d){
return d.Investors_median;
});
var Investor_scale = d3.scale.linear()
.domain(Investor_extent)
.range([height,margin.top]);
var LoanAmount_extent = d3.extent(data, function(d){
return d.amount_median;
});
var LoanAmount_scale = d3.scale.linear()
.range([margin.left, width])
.domain(LoanAmount_extent);
var ProsperRating = ["1.AA","2.A","3.B","4.C","5.D","6.E","7.HR","NR"];
var colors = ['Brown','Red','Orange','Yellow','Green','Blue','Purple','Gray'];
var RatingColor = d3.scale.ordinal()
.domain(ProsperRating)
.range(colors);
var xAxis = d3.svg.axis()
.scale(LoanAmount_scale)
.orient("bottom")
.ticks(12);
var yAxis = d3.svg.axis()
.scale(Investor_scale)
.orient("left")
.ticks(10);
var line = d3.svg.line()
.x(function(d) { return LoanAmount_scale(d.amount_median); })
.y(function(d) { return Investor_scale(d.Investors_median); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right )
.attr("height", height + margin.top + margin.bottom);
var plot = svg.append("g")
.attr("class", "chart")
.attr("width" , width- margin.left)
.attr("height", height)
.attr("transform","translate ("+ margin.left + "," + margin.top + ")");
var div = d3.select("body")
.append("div")
.attr("class","tooltip")
.style("opacity",0);
// setting stage for dropdown menu
// years
var years = ['Select Year', 2007, 2010];
// selecting an option
var select = d3.select('body')
.append('select')
.attr('class','select')
.attr('id','select_id')
.on('change',change);
// available options on dropdown menu
var option = select.selectAll('option')
.data(years)
.enter()
.append('option')
.text(function(d){ return d});
// menu selection of year
function change(){
var select_year = d3.select('select')
.property('value')
// reset chart before updating year
update(data,'Select Year');
// update year
update(data,select_year);
}
// add x axis
var gX = plot.append("g")
.attr("class","x axis")
.attr("transform","translate (0 " + height + ")")
.call(xAxis);
// add y axis
var gY = plot.append("g")
.attr("class", "y axis")
.attr("transform" , "translate( " + margin.left + " ,0)")
.call(yAxis);
//add x axis label
plot.append("text")
.attr("class", "x label")
.attr("text-anchor", "middle")
.attr("x", width/2 )
.attr("y", height + 40)
.text("Loan Amount median (US dollars)");
//add y axis label
plot.append("text")
.attr("class", "y label")
.attr("text-anchor", "middle")
.attr("x",0-(height/2))
.attr("y", 20)
.attr("dy", "1em")
.attr("transform", "rotate(-90)")
.text("Median number of investors who share funding one loan");
// legend title
svg.append("text")
.attr("x",1300)
.attr("y",70)
.attr("class","legend_title")
.style("fill","white")
.text("Legend: Prosper Rating");
svg.append("text")
.attr("x",1300)
.attr("y",30)
.attr("class","legend_title")
.style("fill","white")
.text("point mouse at line to determine rating");
function update(data, year){
var Filtered = data.filter(function(d){ return d.LoanOrig_Yr == year});
var nested_by_rating = d3.nest()
.key(function(d) { return d.ProsperRating })
.sortKeys(d3.ascending)
.entries(Filtered);
// add lines for each rating
var lines = plot.selectAll(".line")
.data(nested_by_rating)
// remove old lines
lines.exit().remove();
plot.selectAll("circle")
.data(nested_by_rating)
.exit().remove();
nested_by_rating.forEach( function(d,i){
var prosperR = d.key;
// entering data
lines.enter()
.append("path")
.attr("class", "line")
.attr("d", line(d.values))
.attr("stroke", RatingColor(prosperR))
.attr("stroke-width", 3)
.attr("stroke-opacity",0.3)
.attr("fill","none");
plot.selectAll("path.dot")
.data(d.values)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", line.x())
.attr("cy", line.y())
.attr("r", 3.5)
.attr("fill","Black");
// dynamic legend
svg.append("text")
.attr("x", 1300)
.attr("y", 100 + i*20)
.attr("class", "legend") // style the legend
.style("fill", function() { return RatingColor(prosperR); }) // Add the colours dynamically
.text(prosperR);
// mouse hover tip tool
lines.on("mouseover", function(d){
div.transition()
.duration(200)
.style("opacity", 0.9);
div.html("Prosper Rating : " + prosperR)
.style("left", (d3.event.pageX) + "px")
style("top", (d3.event.pageY - 2) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
});
// reseting chart between years
if (year == 'Select Year'){
svg.selectAll(".legend_title").style("fill","white");
svg.selectAll(".legend").remove();
} else {
svg.selectAll(".legend_title").style("fill","black");
}
}
}
draw(data);
I have created a waterfall chart using D3 (V4) with three values (ticks) for the y axis.
The x axis tick values are automatically calculated.
How can I add an additional tick value (today's date) on the x axis (date values)?
function risklevels(d) {
if (d <= 25 && d >= 13.5) {
return "High";
} else if (d <= 13.5 && d > 7) {
return "Med";
}
return "Low";
}
function drawWaterfall(){
var margin = {top: 20, right: 20, bottom: 30, left: 50};
var width = 800 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
dt = new Date();
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 1]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y).tickFormat(risklevels).tickValues([4, 10.25, 19.125]);
var parseDate = d3.timeParse("%Y-%m-%d");
var riskwaterfall = d3.select('#riskwaterfall').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+')');
riskwaterfall.append('rect')
.attr('class', 'high')
.attr("x", 0) // start rectangle on the good position
.attr("y", 0) // no vertical translate
.attr("width", width) // correct size
.attr("height", height*((25.0-13.5)/25.0) + height*0.5/25)
.attr("fill", "#ee0000"); // full height
riskwaterfall.append('rect')
.attr('class', 'high')
.attr("x", 0) // start rectangle on the good position
.attr("y", height*((25.0-13.5)/25.0) + height*0.5/25.0) // no vertical translate
.attr("width", width) // correct size
.attr("height", height*((13.5-7.0)/25.0) + height*0.5/25.0)
.attr("fill", "#eeee00"); // full height
riskwaterfall.append('rect')
.attr('class', 'high')
.attr("x", 0) // start rectangle on the good position
.attr("y", (25-7)*height/25 + height*0.5/25.0)// no vertical translate
.attr("width", width) // correct size
.attr("height", 7*height/25 - height*0.5/25.0)
.attr("fill", "#00ee00"); // full height
var line = d3.line()
.curve(d3.curveStepAfter)
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.risk); });
line('step-after');
risk.forEach(function(d) {
d.date = parseDate(d.date);
d.risk = +d.risk;
});
x.domain(d3.extent(risk, function(d) { return d.date; }));
y.domain(d3.extent(risk, function(d) { return d.risk; }));
riskwaterfall.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,'+height+')')
.call(xAxis);
riskwaterfall.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
riskwaterfall.append('path')
.datum(risk)
.attr('d', line(risk));
for (var i = 0; i < risk.length; i++)
riskwaterfall.append('circle')
.datum(risk[i])
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.risk); })
.attr("stroke-width", "2px")
.attr("fill", "black" )
//.attr("fill-opacity", .5)
//.attr("visibility", "hidden")
.attr("r", 5);
}
Right now, you're creating a new date for today:
dt = new Date();
But this has no effect on the x scale (which is used by the axis generator). So, instead of:
x.domain(d3.extent(risk, function(d) { return d.date; }));
Which only goes to the maximum date in the risk data, it should be:
x.domain([d3.min(risk, function(d) { return d.date; }), dt]);
After that, to make sure that the last tick shows up, you can use nice() or concat the end domain in your tick values.
I have data like the following
date,values
2016-10-01,10
2016-10-02,20
2016-10-03,30
2016-10-04,5
2016-10-05,50
2016-10-06,2
2016-10-07,7
2016-10-08,17
and am generating a bar chart using the following code
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 800 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleBand().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Month of " + d.date + ":</strong> <span style='color:red'>" + d.value + " sales</span>";
})
var svg = d3.select("#barg").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.call(tip);
data = d3.csvParse(d3.select("pre#data2").text());
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" )
svg.append("g")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value ($)");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth() - 5)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
So the problem I am having is that I have ordinal data, but for large cardinality (for instance, 120 data points) The x axis has way too many ticks. I have tried a few things like tickValues, but when I use this, my x axis tick points all show up on top of each other. Ideally I would like 10 tick points or so, when the cardinality is high. Any ideas?
This can be done using tickValues indeed. For instance, in this demo, we have 200 values, so the axis is absolutely crowded:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 100);
var data = d3.range(200);
var xScale = d3.scaleBand()
.domain(data.map(function(d){ return d}))
.range([10, 490]);
var xAxis = d3.axisBottom(xScale);
var gX = svg.append("g").call(xAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
Now, the same code using tickValues:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 100);
var data = d3.range(200);
var xScale = d3.scaleBand()
.domain(data.map(function(d){ return d}))
.range([10, 490]);
var xAxis = d3.axisBottom(xScale)
.tickValues(xScale.domain().filter(function(d,i){ return !(i%10)}));
var gX = svg.append("g").call(xAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
In this last snippet, tickValues uses the remainder operator to show only 1 in every 10 ticks:
.tickValues(xScale.domain().filter(function(d,i){
return !(i%10)
}));
Here is a general solution to this problem using tickFormat(...). We can define a minimum acceptable width for our ticks, then skip every nth tick based on this minimum.
d3
.axisBottom(xScale)
.tickFormat((t, i) => {
const MIN_WIDTH = 30;
let skip = Math.round(MIN_WIDTH * data.length / chartWidth);
skip = Math.max(1, skip);
return (i % skip === 0) ? t : null;
});
let skip = ... is a rearrangement of the inequality ChartWidth / (NumTicks / N) > MinWidth. Here N represents the tick "step size", so we are asserting that the width of every nth tick is greater than the minimum acceptable width. If we rearrange the inequality to solve for N, we can determine how many ticks to skip to achieve our desired width.
I have a barchart which is populated by values from a JSON variable. The chart is dynamic because the values are retrieved from an SQL query by doing a count. So my data gets fed back in like :
[{"Fruit":"Apple","COUNT( Fruit )":"12"},{"Fruit":"Orange","COUNT( Fruit )":"6"},{"Fruit":"Pear","COUNT( Fruit )":"1"},{"Fruit":"Blank","COUNT( Fruit )":"1"},{"Fruit":"Pineapple","COUNT( Fruit )":"1"},{"Fruit":"Kiwi","COUNT( Fruit )":"1"}]
For the most part my graphs seem to be displaying properly. However some are returning values that exceed the Y Axis, I dont think it's the values that are causing the issues I believe its the axis that isnt calculating the right height. For instance
If Orange count is 14, sometimes the Y axis stops at a number less than this and that column extends the graph.
By viewing it in google chrome developer console, I can see the height of the bar is
<rect id="Orange" y="-520px" x="94px" height="1040px" width="162"></rect>
which far extends my SVG height of 600px - Margins(top + bottom) of 80px!
Does anyone know why my Y Axis isn't getting the right Max value?
Code here:
var canv = document.getElementById("exportCanvas");
canv.width = screen.width;
var margin ={top:40, right:0, bottom:40, left:40},
width=screen.width - 250,
height=600-margin.top-margin.bottom;
var jsplit = jdata.split('"');
var keyX = jsplit[1];
var keyY = "";
var data = JSON.parse(jdata);
for (k in data[0]) {
if (k!=keyX) keyY=k;
}
console.log("keyX = " + keyX);
console.log(keyY);
console.log(data[0]);
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal()
.domain(['Orange','Apple','Pear']) //Added this in temporarilly. this should be calculated from the data.
.rangeRoundBands([0, width], 0.25,0.25);
//scale to numerical value by height
// var y = d3.scale.linear().range([height, 0]);
var y = d3.scale.linear()
.range([height, 0]);
console.log(data);
x.domain(data.map(function(d){ return d[keyX]}));
y.domain([0, d3.max(data, function(d){return d[keyY]})]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width+ margin.left+margin.right) //set width
// .attr("width", width+(2*margin.left)+margin.right) //set width
.attr("height", height+margin.top+margin.bottom); //set height
// .attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10).tickFormat(function(d) {
if (d % 1 == 0) {
return d3.format('.f')(d)
} else {
return ""
}
});
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g");
//you're moving the group and then moving the rect below as well
//one or the other not both need to be moved.
//.attr("transform", function(d, i){
// return "translate("+x(d[keyX])+", 0)";
//});
bar.append("rect")
.attr("id", function(d) {
return d[keyX];
})
.attr("y", function(d) {
return y(d[keyY]) + "px";
})
.attr("x", function(d,i){
//AB - Adjusted this so it correcly places the bar along the X
//x.range is an array of x values for each bar
//calculated in the var x = line above , with the .rangeRoundBands([0, width], 0.25,0.25);
//setting the width of the bars (an equal division of width) with margins of 0.25 at the start
//and end of the graph and 0.25 between each bar.
return x.range()[i] + margin.left + "px";
})
.attr("height", function(d) {
return height - y(d[keyY]) +"px";
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x",function(d,i){
//similar to above but adding half the width of the bar to the x position
//to roughly center it on the bar. only rough as doesnt take account of length of text.
return x.range()[i] + margin.left + (x.rangeBand()/2)+ "px";
})
.attr("y", function(d) { return y(d[keyY]) +20; })
.attr("dy", ".75em")
.style("fill","white")
.style("font-weight", "bold")
.text(function(d) { return d[keyY]; });
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+margin.left+","+ height+")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate("+margin.left+",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(keyY);
Apologies for commented out code, I have been playing with it alot to try and suss this out.
You need to recalculate y.domain() when your dataset refreshes. So when you update your data, you can try something like:
y.domain([0, d3.max(data, function(d){return d[keyY]})]);
chart.select(".y.axis")
.call(yAxis.scale(y));