D3 Candlestick chart date on xaxis - javascript

I am trying to create candlestick chart using d3.js, it works great but I cannot solve a problem. I am sure it is covered somewhere but I cannot find it.
I have candlestick data, here is one record:
{
"Date":"02/01/2010",
"Time":1265004000,
"YDay":31,
"Open":1.571500,
"High":1.582100,
"Low":1.558700,
"Close":1.580500,
"SMA":1.5663,
"EMA":1.56743786563595
},
As you can see, the real xaxis is the time value which is epoch time (# of seconds since 1/1/70). I need it working with those seconds to properly format the chart, but I want the x axis labeled with "Date" value instead of the seconds. With this code the plot is great but the x axis is just a long set of numbers (the # of seconds overwriting each other).
How can I get it to do the calculations in seconds, but display the dates on the x axis.
Here is a self contained html file with embedded data. Just cut and paste into index.html on your disk and double click.
<html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var xs;
var ys;
var base_width = 960;
var base_height = 600;
var x_min;
var x_max;
var y_min;
var y_max;
var barWidth;
var candleWidth;
var xAxis;
var yAxis;
var margin = {top: 20, right: 30, bottom: 30, left: 40};
function getYLow (d)
{
return ys (d.Low);
}
function getYHigh (d)
{
return ys (d.High);
}
function getYOC (d)
{
var yOpen = ys (d.Open);
var yClose = ys (d.Close);
if (yOpen < yClose)
{
return yOpen;
}
return yClose;
}
function getYHeight (d)
{
var yHeight = ys (d.Open) - ys (d.Close);
if (yHeight < 0)
{
yHeight *= -1;
}
return yHeight;
}
function getX (d)
{
// offset by half the width of my candlestick rectangle
// but centered on the actual time
var x = xs (d.Time) - (candleWidth / 2.0);
return x;
}
function getXActual (d)
{
var x = xs (d.Time);
return x;
}
function getColor (d)
{
// I want the candlestick to be green if it closed higher
// then the open otherwise it should be red
if (d.Close > d.Open)
return "green";
return "red";
}
function load ()
{
// load the data and create the chart
width = base_width - margin.left - margin.right,
height = base_height - margin.top - margin.bottom;
ys = d3.scale.linear ().range ([height, 0]);
xs = d3.scale.linear ().range ([0, width]);
var svg = d3.select ("body").append ("svg")
.attr ("width", width + margin.left + margin.right)
.attr ("height", height + margin.top + margin.bottom)
.style ("background-color", "black")
.append ("g")
.attr ("transform", "translate(" + margin.left + "," + margin.top + ")");
xAxis = d3.svg.axis()
.scale(xs)
.orient("bottom");
yAxis = d3.svg.axis()
.scale(ys)
.orient("left");
y_min = d3.min (data, function (d) { return d.Low; });
y_max = d3.max (data, function (d) { return d.High; });
x_min = data [0].Time;
x_max = data [data.length - 1].Time;
// offset min and max by 1 day to give some margin so the candlestick
// rectangle stays on the chart and does not cross the axis
x_min = +x_min - 86400;
x_max = +x_max + 86400;
ys.domain ([y_min, y_max]);
xs.domain ([x_min, x_max]);
// this is the actual width of 1 day (1 bar)
barWidth = xs (+x_min + 86400) - xs (+x_min);
// the candle should be 75% the width of the day
candleWidth = barWidth * 0.75;
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.attr("stroke", "white")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("stroke", "white")
.call(yAxis);
svg.selectAll (".candle_bar")
.data (data)
.enter ().append ("rect")
.attr ("class", "candle_bar")
.attr ("x", getX)
.attr ("y", getYOC)
.attr ("height", getYHeight)
.attr ("width", candleWidth)
.attr ("fill", getColor);
svg.selectAll ("candle_hl")
.data (data)
.enter ().append ("svg:line")
.attr ("x1", getXActual)
.attr ("y1", getYLow)
.attr ("x2", getXActual)
.attr ("y2", getYHigh)
.style ("stroke", getColor);
// for plotting an sma and ema line
var line_sma = d3.svg.line ().x (function (d, i) { return xs (d.YDay); })
.y (function (d, i) { return ys (d.SMA); });
svg.append ("path")
.attr ("d", line_sma (data))
.attr ("stroke", "white")
.attr ("fill", "none");
var line_ema = d3.svg.line ().x (function (d, i) { return xs (d.YDay); })
.y (function (d, i) { return ys (d.EMA); });
svg.append ("path")
.attr ("d", line_ema (data))
.attr ("stroke", "orange")
.attr ("fill", "none");
}
function type (d)
{
d.Time = +d.Time;
d.Open = +d.Open;
d.High = +d.High;
d.Low = +d.Low;
d.Close = +d.Close;
d.SMA = +d.SMA;
d.EMA = +d.EMA;
return d;
}
$( document ).ready (load);
var data = [
{
"Date":"02/01/2010",
"Time":1265004000,
"YDay":31,
"Open":1.571500,
"High":1.582100,
"Low":1.558700,
"Close":1.580500,
"SMA":1.5663,
"EMA":1.56743786563595
},
{
"Date":"02/02/2010",
"Time":1265090400,
"YDay":32,
"Open":1.580500,
"High":1.586100,
"Low":1.572600,
"Close":1.576900,
"SMA":1.56758,
"EMA":1.56933029250876
},
{
"Date":"02/03/2010",
"Time":1265176800,
"YDay":33,
"Open":1.576700,
"High":1.581900,
"Low":1.571300,
"Close":1.576900,
"SMA":1.56832,
"EMA":1.57084423400701
},
{
"Date":"02/04/2010",
"Time":1265263200,
"YDay":34,
"Open":1.576900,
"High":1.595000,
"Low":1.571000,
"Close":1.580600,
"SMA":1.57017,
"EMA":1.5727953872056
},
{
"Date":"02/05/2010",
"Time":1265349600,
"YDay":35,
"Open":1.580300,
"High":1.586300,
"Low":1.573100,
"Close":1.574500,
"SMA":1.57075,
"EMA":1.57313630976448
},
{
"Date":"02/07/2010",
"Time":1265522400,
"YDay":37,
"Open":1.574300,
"High":1.576800,
"Low":1.571400,
"Close":1.575100,
"SMA":1.57224,
"EMA":1.57352904781159
},
{
"Date":"02/08/2010",
"Time":1265608800,
"YDay":38,
"Open":1.575000,
"High":1.583500,
"Low":1.572700,
"Close":1.577500,
"SMA":1.5745,
"EMA":1.57432323824927
},
{
"Date":"02/09/2010",
"Time":1265695200,
"YDay":39,
"Open":1.577400,
"High":1.578200,
"Low":1.567100,
"Close":1.571400,
"SMA":1.5753,
"EMA":1.57373859059942
},
{
"Date":"02/10/2010",
"Time":1265781600,
"YDay":40,
"Open":1.571300,
"High":1.573800,
"Low":1.549800,
"Close":1.551600,
"SMA":1.57365,
"EMA":1.56931087247953
},
{
"Date":"02/11/2010",
"Time":1265868000,
"YDay":41,
"Open":1.551700,
"High":1.552800,
"Low":1.533500,
"Close":1.537500,
"SMA":1.57025,
"EMA":1.56294869798363
},
{
"Date":"02/12/2010",
"Time":1265954400,
"YDay":42,
"Open":1.537500,
"High":1.545000,
"Low":1.526900,
"Close":1.535200,
"SMA":1.56572,
"EMA":1.5573989583869
},
{
"Date":"02/14/2010",
"Time":1266127200,
"YDay":44,
"Open":1.533200,
"High":1.536100,
"Low":1.531800,
"Close":1.533100,
"SMA":1.56134,
"EMA":1.55253916670952
},
{
"Date":"02/15/2010",
"Time":1266213600,
"YDay":45,
"Open":1.533200,
"High":1.535300,
"Low":1.525500,
"Close":1.526500,
"SMA":1.5563,
"EMA":1.54733133336762
},
{
"Date":"02/16/2010",
"Time":1266300000,
"YDay":46,
"Open":1.526500,
"High":1.529100,
"Low":1.519400,
"Close":1.528000,
"SMA":1.55104,
"EMA":1.54346506669409
},
{
"Date":"02/17/2010",
"Time":1266386400,
"YDay":47,
"Open":1.527900,
"High":1.528400,
"Low":1.511300,
"Close":1.515100,
"SMA":1.5451,
"EMA":1.53779205335528
},
{
"Date":"02/18/2010",
"Time":1266472800,
"YDay":48,
"Open":1.515000,
"High":1.516100,
"Low":1.506300,
"Close":1.513100,
"SMA":1.5389,
"EMA":1.53285364268422
},
{
"Date":"02/19/2010",
"Time":1266559200,
"YDay":49,
"Open":1.513100,
"High":1.516900,
"Low":1.509500,
"Close":1.514100,
"SMA":1.53256,
"EMA":1.52910291414738
},
{
"Date":"02/21/2010",
"Time":1266732000,
"YDay":51,
"Open":1.512400,
"High":1.514200,
"Low":1.510900,
"Close":1.513200,
"SMA":1.52674,
"EMA":1.5259223313179
},
{
"Date":"02/22/2010",
"Time":1266818400,
"YDay":52,
"Open":1.513200,
"High":1.515600,
"Low":1.508800,
"Close":1.511900,
"SMA":1.52277,
"EMA":1.52311786505432
},
{
"Date":"02/23/2010",
"Time":1266904800,
"YDay":53,
"Open":1.511700,
"High":1.522000,
"Low":1.504000,
"Close":1.515900,
"SMA":1.52061,
"EMA":1.52167429204346
},
{
"Date":"02/24/2010",
"Time":1266991200,
"YDay":54,
"Open":1.515900,
"High":1.526600,
"Low":1.511100,
"Close":1.516400,
"SMA":1.51873,
"EMA":1.52061943363477
},
{
"Date":"02/25/2010",
"Time":1267077600,
"YDay":55,
"Open":1.516200,
"High":1.532300,
"Low":1.514400,
"Close":1.526500,
"SMA":1.51807,
"EMA":1.52179554690781
},
{
"Date":"02/26/2010",
"Time":1267164000,
"YDay":56,
"Open":1.526400,
"High":1.529200,
"Low":1.517800,
"Close":1.522200,
"SMA":1.51764,
"EMA":1.52187643752625
},
{
"Date":"02/28/2010",
"Time":1267336800,
"YDay":58,
"Open":1.519800,
"High":1.520400,
"Low":1.514200,
"Close":1.515600,
"SMA":1.5164,
"EMA":1.520621150021
}
];
</script>
<body>
</body>
</html>

Oleg, was correct I can use Time Scales, I had to convert my times from seconds to miliseconds and it work. I now have a different problem with axis but I will start another question rather then chain on this one.
Thank you Oleg the part I was missing when I tried it was the milliseconds versus seconds.

Related

D3 Animate One Path Series At a Time

Learning Javascript and D3.
Trying to create a graph where it draws each line in a series, one at a time or on a delayed interval.
What I've got so far (relevant code at the end of this post)
https://jsfiddle.net/jimdholland/5n6xrLk0/180/
My data is structures with each series as one row, with 12 columns. When you read it in, the object approximately looks like
mydata = [
{NumDays01: "0", NumDays02: "0", NumDays03: "0", NumDays04: "0", NumDays05: "0",Numdays06: 30}
1: {NumDays01: "0", NumDays02: "0", NumDays03: "0", NumDays04: "0",...
]
I can get it to create line, but it draws all the paths at once. Likewise, I've tried a loop, but that still draws them all at once. Also tried d3.timer and similar results. But I'm having a hard time understanding the timer callback stuff and creating those functions correctly.
I haven't found another example to really study other than a chart from FlowingData which has a lot of bells and whistles out of my league.
https://flowingdata.com/2019/02/01/how-many-kids-we-have-and-when-we-have-them/
Any help or tips would be appreciated.
var svg = d3.select("#chart").select("svg"),
margin = { top: 30, right: 20, bottom: 10, left: 40 },
width = parseInt(d3.select("#chart").style('width'), 10) - margin.left - margin.right;
d3.tsv("https://s3.us-east-2.amazonaws.com/jamesdhollandwebfiles/data/improvementTest.tsv", function(error, data) {
if (error) throw error;
console.log(data);
myData = data;
var t = d3.timer(pathMaker);
}); // #end d3.tsv()
function pathMaker() {
var peeps = myData[rowToRun];
var coords = [];
var lineToRemove = [];
for (var nameIter in dayList) {
coords.push({ x: x(nameIter), y: y(peeps[dayList[nameIter]])})
}
var improvementPath = g.append("path")
.datum(coords)
.attr("id", "path" + peeps.caseid)
.attr("d", lineMaker)
.attr("stroke", "#90c6e4")
.attr("fill", "none")
.attr("stroke-width", 2);
var total_length = improvementPath.node().getTotalLength();
var startPoint = pathStartPoint(improvementPath);
improvementPath = improvementPath
.attr("stroke-dasharray", total_length + " " + total_length)
.attr("stroke-dashoffset", total_length)
.transition() // Call Transition Method
.duration(4000) // Set Duration timing (ms)
.ease(d3.easeLinear) // Set Easing option
.attr("stroke-dashoffset", 0); // Set final value of dash-offset for transition
rowToRun += 1;
if (rowToRun == 5) {rowToRun = 0;}
}
//Get path start point for placing marker
function pathStartPoint(Mypath) {
var d = Mypath.attr("d"),
dsplitted = d.split(/M|L/)[1];
return dsplitted;
}
there are a few problems. I tried to configure your code as little as I could to make it work. If you need further explanations, please let me know
d3.tsv("https://s3.us-east-2.amazonaws.com/jamesdhollandwebfiles/data/improvementTest.tsv", function(error, data) {
if (error) throw error;
console.log(data);
myData = data;
pathMaker()
}); // #end d3.tsv()
function pathMaker() {
var peeps = myData[rowToRun];
var coords_data = [];
var lineToRemove = [];
for (let i = 0; i < myData.length; i++) {
var coords = [];
for (var nameIter in dayList) {
coords.push({ x: x(nameIter), y: y(myData[i][dayList[nameIter]])})
}
coords_data.push(coords)
}
console.log(coords_data)
var improvementPath = g.selectAll("path")
.data(coords_data)
.enter()
.append("path")
.attr("d", lineMaker)
.attr("stroke", "#90c6e4")
.attr("fill", "none")
.attr("stroke-width", 2);
improvementPath = improvementPath.each(function (d,i) {
var total_length = this.getTotalLength();
var startPoint = pathStartPoint(improvementPath);
const path = d3.select(this)
.attr("stroke-dasharray", total_length + " " + total_length)
.attr("stroke-dashoffset", total_length)
.transition() // Call Transition Method
.duration(4000) // Set Duration timing (ms)
.delay(i*4000)
.ease(d3.easeLinear) // Set Easing option
.attr("stroke-dashoffset", 0); // Set final value of dash-offset for transition
})
rowToRun += 1;
if (rowToRun == 5) {rowToRun = 0;}
}

D3: Combine two colour scales into one

I currently have two variables that I can map to two different colours on two colours scales s1 and s2. s1 gives me the shade of red corresponding to my a value of my variable X (4 different possible colours). s1 gives me the shade of blue corresponding to my a value of my variable Y (4 different possible colours too).
Now what I would like to get is something that allows me to combine these two to get a unique colour for a combination of the variables. So for a pair (X,Y) I get a colour on the scale. So I get a scale of 16 possible colours.
Here is a legend that illustrate the kind of thing I am looking for:
I have been looking at online examples but cannot figure out how to achieve this.
You could combine two threshold scales fairly easily into a new scale function. The core of the function could look like:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
This sets the red and blue channels with the help of two d3 threshold scales. The green is simply set as the average between the two, though you could set that to whatever is desirable, say 0 or the minimum of the two other channels. My red/blue ranges are arbitrary and easily changed as well.
The above could be used as:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
// Dummy data:
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
var color = d3.scaleBivariate();
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",function(d) {
return color([d.x,d.y]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>
Of course you might want to add some flexibility by adding methods to modify what datum property sets what colors are associated with what properties, what the thresholds should be, etc. To provide a basic example, the example below has added accessors for setting what property should be mapped to blue and red channels:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(red(value));
var b = blues(blue(value));
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var red = function(d) { return d[0]; }
var blue = function(d) { return d[1];}
// Accessors:
scaleBivariate.red = function(_) {
return arguments.length ? (red = _, scaleBivariate): red;
}
scaleBivariate.blue = function(_) {
return arguments.length ? (blue = _, scaleBivariate): blue;
}
return scaleBivariate;
}
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
// set up the color scale:
var color = d3.scaleBivariate()
.red(function(d) { return d.x; })
.blue(function(d) { return d.y; });
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",color);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>

D3.js Problem rendering barchart with object data

I have the following script for rendering a simple barchart in D3.js. I have been able to render charts ok up until a point.
I have this data below which I am struggling to insert into my chart, there is no specific key I can call upon and I'm really confused how I would insert all of these into my chart.
Object { "food-environmental-science": 0, "art-media-research": 0, .....}
I have a seperate file for the HTML (only a snippet):
var barchart1 = barchart("#otherchart");
function clickScatter(d){
var unitOfAssessment = d.UoAString;
click = d.environment.topicWeights
renderTopicWeights(click)
}
function renderTopicWeights(clickedPoint){
barchart1.loadAndRenderDataset(clickedPoint)
}
When I call upon the loadAndRenderDataset function, console gives me a data.map is not a function error.
function barchart(targetDOMelement) {
//=================== PUBLIC FUNCTIONS =========================
//
barchartObject.appendedMouseOverFunction = function (callbackFunction) {
console.log("appendedMouseOverFunction called", callbackFunction)
appendedMouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.appendedMouseOutFunction = function (callbackFunction) {
appendedMouseOutFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.loadAndRenderDataset = function (data) {
dataset=data.map(d=>d); //create local copy of references so that we can sort etc.
render();
return barchartObject;
}
barchartObject.overrideDataFieldFunction = function (dataFieldFunction) {
dataField = dataFieldFunction;
return barchartObject;
}
barchartObject.overrideKeyFunction = function (keyFunction) {
//The key function is used to obtain keys for GUP rendering and
//to provide the categories for the y-axis
//These valuse should be unique
GUPkeyField = yAxisCategoryFunction = keyFunction;
return barchartObject;
}
barchartObject.overrideMouseOverFunction = function (callbackFunction) {
mouseOverFunction = callbackFunction;
render();
return barchartObject;
}
barchartObject.overrideMouseOutFunction = function (callbackFunction) {
mouseOutFunction = callbackFunction;
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.overrideTooltipFunction = function (toolTipFunction) {
tooltip = toolTipFunction;
return barchartObject;
}
barchartObject.overrideMouseClickFunction = function (fn) {
mouseClick2Function = fn;
render(); //Needed to update DOM if they exist
return barchartObject;
}
barchartObject.render = function (callbackFunction) {
render(); //Needed to update DOM
return barchartObject;
}
barchartObject.setTransform = function (t) {
//Set the transform on the svg
svg.attr("transform", t)
return barchartObject;
}
barchartObject.yAxisIndent = function (indent) {
yAxisIndent=indent;
return barchartObject;
}
//=================== PRIVATE VARIABLES ====================================
//Width and height of svg canvas
var svgWidth = 900;
var svgHeight = 450;
var dataset = [];
var xScale = d3.scaleLinear();
var yScale = d3.scaleBand(); //This is an ordinal (categorical) scale
var yAxisIndent = 400; //Space for labels
var maxValueOfDataset; //For manual setting of bar length scaling (only used if .maxValueOfDataset() public method called)
//=================== INITIALISATION CODE ====================================
//Declare and append SVG element
var svg = d3
.select(targetDOMelement)
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.classed("barchart",true);
//Declare and add group for y axis
var yAxis = svg
.append("g")
.classed("yAxis", true);
//Declare and add group for x axis
var xAxis = svg
.append("g")
.classed("xAxis", true);
//===================== ACCESSOR FUNCTIONS =========================================
var dataField = function(d){return d.datafield} //The length of the bars
var tooltip = function(d){return d.key + ": "+ d.datafield} //tooltip text for bars
var yAxisCategoryFunction = function(d){return d.key} //Categories for y-axis
var GUPkeyField = yAxisCategoryFunction; //For 'keyed' GUP rendering (set to y-axis category)
//=================== OTHER PRIVATE FUNCTIONS ====================================
var maxValueOfDataField = function(){
//Find the maximum value of the data field for the x scaling function using a handy d3 max() method
//This will be used to set (normally used )
return d3.max(dataset, dataField)
};
var appendedMouseOutFunction = function(){};
var appendedMouseOverFunction = function(){};
var mouseOverFunction = function (d,i){
d3.select(this).classed("highlight", true).classed("noHighlight", false);
appendedMouseOverFunction(d,i);
}
var mouseOutFunction = function (d,i){
d3.select(this).classed("highlight", false).classed("noHighlight", true);
appendedMouseOutFunction(d,i);
}
var mouseClick2Function = function (d,i){
console.log("barchart click function = nothing at the moment, d=",d)
};
function render () {
updateScalesAndRenderAxes();
GUP_bars();
}
function updateScalesAndRenderAxes(){
//Set scales to reflect any change in svgWidth, svgHeight or the dataset size or max value
xScale
.domain([0, maxValueOfDataField()])
.range([0, svgWidth-(yAxisIndent+10)]);
yScale
.domain(dataset.map(yAxisCategoryFunction)) //Load y-axis categories into yScale
.rangeRound([25, svgHeight-40])
.padding([.1]);
//Now render the y-axis using the new yScale
var yAxisGenerator = d3.axisLeft(yScale);
svg.select(".yAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",0)")
.call(yAxisGenerator);
//Now render the x-axis using the new xScale
var xAxisGenerator = d3.axisTop(xScale);
svg.select(".xAxis")
.transition().duration(1000).delay(1000)
.attr("transform", "translate(" + yAxisIndent + ",20)")
.call(xAxisGenerator);
};
function GUP_bars(){
//GUP = General Update Pattern to render bars
//GUP: BIND DATA to DOM placeholders
var selection = svg
.selectAll(".bars")
.data(dataset, GUPkeyField);
//GUP: ENTER SELECTION
var enterSel = selection //Create DOM rectangles, positioned # x=yAxisIndent
.enter()
.append("rect")
.attr("x", yAxisIndent)
enterSel //Add CSS classes
.attr("class", d=>("key--"+GUPkeyField(d)))
.classed("bars enterSelection", true)
.classed("highlight", d=>d.highlight)
enterSel //Size the bars
.transition()
.duration(1000)
.delay(2000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
enterSel //Add tooltip
.append("title")
.text(tooltip)
//GUP UPDATE (anything that is already on the page)
var updateSel = selection //update CSS classes
.classed("noHighlight updateSelection", true)
.classed("highlight enterSelection exitSelection", false)
.classed("highlight", d=>d.highlight)
updateSel //update bars
.transition()
.duration(1000)
.delay(1000)
.attr("width", function(d) {return xScale(dataField(d));})
.attr("y", function(d, i) {return yScale(yAxisCategoryFunction(d));})
.attr("height", function(){return yScale.bandwidth()});
updateSel //update tool tip
.select("title") //Note that we already created a <title></title> in the Enter selection
.text(tooltip)
//GUP: Merged Enter & Update selections (so we don't write these twice)
var mergedSel = enterSel.merge(selection)
.on("mouseover", mouseOverFunction)
.on("mouseout", mouseOutFunction)
.on("click", mouseClick2Function)
//GUP EXIT selection
var exitSel = selection.exit()
.classed("highlight updateSelection enterSelection", false)
.classed("exitSelection", true)
.transition()
.duration(1000)
.attr("width",0)
.remove()
};
return barchartObject;'
}
Any help would be much appreciated, and I appolguise if what I'm asking is not clear. Thanks
format your input data into object like {key:"",value:""} and pass this into d3 so that they can understand and render chart.
var input = {'a':1,"b":2};
function parseData(input){
return Object.keys(input).reduce(function(output, key){
output.push({"key":key,"value":input[key]});
return output;
},[])
}
console.log(parseData(input));
// [{"key":"a","value":1},{"key":"b","value":2}]
jsFiddle demo - https://jsfiddle.net/1kdsoyg2/1/

D3 y-axis percent display

D3 Fiddle
/**
* A bar chart. Required data format:
* [ { name : x-axis-bar-label, value : N }, ...]
*
* Sample use:
* var bargraph = d3.select('#bargraph')
* .append('svg')
* .chart('BarChart')
* .yFormat(d3.format("d"))
* .height(400)
* .width(800)
* .max(1.0);
* bargraph.draw(bardata);
*/
d3.chart('BarChart', {
initialize: function() {
var chart = this;
// chart margins to account for labels.
// we may want to have setters for this.
// not sure how necessary that is tbh.
chart.margins = {
top : 10,
bottom : 15,
left : 50,
right : 0,
padding : 10
};
// default chart ranges
chart.x = d3.scale.linear();
chart.y = d3.scale.linear();
chart.base
.classed('Barchart', true);
// non data driven areas (as in not to be independatly drawn)
chart.areas = {};
// cache for selections that are layer bases.
chart.layers = {};
// make sections for labels and main area
// _________________
// |Y| bars |
// | | |
// | | |
// | | |
// | |______________|
// | X |
// -- areas
chart.areas.ylabels = chart.base.append('g')
.classed('ylabels', true)
.attr('width', chart.margins.left)
.attr('transform',
'translate('+(chart.margins.left-1)+','+(chart.margins.top + 1)+')');
// -- actual layers
chart.layers.bars = chart.base.append('g')
.classed('bars', true)
.attr('transform',
'translate(' + chart.margins.left + ',' + (chart.margins.top + 1)+')');
chart.layers.xlabels = chart.base.append('g')
.classed('xlabels', true)
.attr('height', chart.margins.bottom);
chart.on("change:width", function() {
chart.x.range([0, chart.w - chart.margins.left]);
chart.layers.bars.attr('width', chart.w - chart.margins.left);
chart.layers.xlabels.attr('width', chart.w - chart.margins.left);
});
chart.on("change:height", function() {
chart.y.range([chart.h - chart.margins.bottom - chart.margins.top, 0]);
chart.areas.ylabels
.attr('height', chart.h - chart.margins.bottom - chart.margins.top - 1);
chart.layers.bars
.attr('height', chart.h - chart.margins.bottom - chart.margins.top);
chart.layers.xlabels
.attr('transform', 'translate(' + chart.margins.left + ',' +
(chart.h - chart.margins.bottom + 1) + ')');
});
// make actual layers
chart.layer('bars', chart.layers.bars, {
// data format:
// [ { name : x-axis-bar-label, value : N }, ...]
dataBind : function(data) {
chart.data = data;
// how many bars?
chart.bars = data.length;
// compute box size
chart.bar_width = (chart.w - chart.margins.left - ((chart.bars - 1) *
chart.margins.padding)) / chart.bars;
// adjust the x domain - the number of bars.
chart.x.domain([0, chart.bars]);
// adjust the y domain - find the max in the data.
chart.datamax = chart.usermax ||
d3.extent(data, function(d) { return d.value; })[1];
chart.y.domain([0, chart.datamax]);
// draw yaxis
var yAxis = d3.svg.axis()
.scale(chart.y)
.orient('left')
.ticks(5)
.tickFormat(chart._yformat || d3.format('.0%'));
chart.areas.ylabels
.call(yAxis);
return this.selectAll('rect')
.data(data, function(d) {
//console.log(d);
return d.name;
});
},
insert : function() {
return this.append('rect')
.classed('bar', true)
.classed('highlight', function(d) {
return d.highlight;
});
},
events: {
exit: function() {
this.remove();
}
}
});
// a layer for the x text labels.
chart.layer('xlabels', chart.layers.xlabels, {
dataBind : function(data) {
// first append a line to the top.
this.append('line')
.attr('x1', 0)
.attr('x2', chart.w - chart.margins.left)
.attr('y1', 0)
.attr('y2', 0)
.style('stroke', '#222')
.style('stroke-width', '1')
.style('shape-rendering', 'crispEdges');
return this.selectAll('text')
.data(data, function(d) { return d.name; });
},
insert : function() {
return this.append('text')
.classed('label', true)
.attr('text-anchor', 'middle')
.attr('x', function(d, i) {
return chart.x(i) - 0.5 + chart.bar_width/2;
})
.attr('dy', '1em')
.text(function(d) {
return d.name;
});
},
events: {
exit: function() {
this.remove();
}
}
});
// on new/update data
// render the bars.
var onEnter = function() {
this.attr('x', function(d, i) {
return chart.x(i) - 0.5;
})
.attr('y', function(d) {
return chart.h - chart.margins.bottom -
chart.margins.top - chart.y(chart.datamax - d.value) - 0.5;
})
.attr('val', function(d) {
return d.value;
})
.attr('width', chart.bar_width)
.attr('height', function(d) {
return chart.y(chart.datamax - d.value);
});
};
chart.layer('bars').on('enter', onEnter);
chart.layer('bars').on('update', onEnter);
},
// return or set the max of the data. otherwise
// it will use the data max.
max : function(datamax) {
if (!arguments.length) {
return this.usermax;
}
this.usermax = datamax;
if (this.data) this.draw(this.data);
return this;
},
yFormat: function(format) {
if (!arguments.length) {
return this._yformat;
}
this._yformat = format;
return this;
},
width : function(newWidth) {
if (!arguments.length) {
return this.w;
}
// save new width
this.w = newWidth;
// adjust the x scale range
this.x.range([this.margins.left, this.w - this.margins.right]);
// adjust the base width
this.base.attr('width', this.w);
this.trigger("change:width");
if (this.data) this.draw(this.data);
return this;
},
height : function(newHeight) {
if (!arguments.length) {
return this.h;
}
// save new height
this.h = newHeight;
// adjust the y scale
this.y.range([this.h - this.margins.top, this.margins.bottom]);
// adjust the base width
this.base.attr('height', this.h);
this.trigger("change:height");
if (this.data) this.draw(this.data);
return this;
}
});
var barchart = d3.select('#vis')
.append('svg')
.chart('BarChart') //**Moving transform position out of here**
.yFormat(d3.format("d"))
.height(400)
.width(800);
var data = [
{ month : 'January', temperature : 29 },
{ month : 'February', temperature : 32 },
{ month : 'March', temperature : 48 },
{ month : 'April', temperature : 49 },
{ month : 'May', temperature : 58 },
{ month : 'June', temperature : 68 },
{ month : 'July', temperature : 74 },
{ month : 'August', temperature : 73 },
{ month : 'September', temperature : 65 },
{ month : 'October', temperature : 54 },
{ month : 'November', temperature : 45 },
{ month : 'December', temperature : 35 }
];
//**Moving transform function out of barchart definition
//so it is called only once and not everytime
//the chart is redrawn**
function transform(data) {
return data.map(function(d) {
return { name : d.month, value : d.temperature };
});
}
barchart.draw(transform(data));
In this fiddle, I am trying to display the y-axis values in percentages. Not exactly calculating the percentage from the data, but just appending the "%" sign to the existing data. I am fetching the data through an array in the actual code, it is dynamic. I have tried appending using tick format but somehow it doesn't seem to work. Any help?
Do this to append % to the y tick:
var yAxis = d3.svg.axis()
.scale(chart.y)
.orient('left')
.ticks(5)
.tickFormat(function(d){return d+ "%"});
working code here

how to do a simple looping carousel of svg:g elements with D3.js

I am trying to make a scrollable list that is part of a bigger d3.js generated chart. clicking on list item acivates changes in the rest of the chart.
How would you make a simple animated looping carousel of svg:g elements with D3.js using the reusable chart pattern ?
Wired to jQuery-mousewheel or some forward and back buttons for example.
http://jsbin.com/igopix/2/edit
NameLoop = function () {
var scroll = 0;
function chart(selection) {
selection.each(function (data) {
var len = data.length,
scroll = scroll % len + len; // keep scroll between 0 <-> len
var nameNodes = d3.select(this).selectAll("g")
.data(data, function(d){return d.ID;} );
var nameNodesEnter = nameNodes.enter().append("g")
.attr("class", "nameNode")
.attr("transform", function(d, i) {
// implement scroll here ??
return "translate(30," + (i+1)*20 + ")";
})
.append("text")
.text(function(d){return d.ID;});
});
return chart;
}
chart.scroll = function(_) {
if (!arguments.length) return scroll;
scroll = _;
return chart;
};
return chart;
};
data = [{ID:"A"},{ID:"B"},{ID:"C"},{ID:"D"},{ID:"E"}];
var svg = d3.select("body").append("svg")
.attr("width", 200)
.attr("height",200)
.attr("id","svg");
var nameloop = NameLoop();
d3.select("#svg").datum(data).call(nameloop);
var body = d3.select("body").append("div");
body.append("span").on("click",function(d){ scroll(1) }).html("forward ");
body.append("span").on("click",function(d){ scroll(-1) }).html("back");

Categories