The code receives JSON packets through a websocket, and should use D3 to make a scatter graph of them:
<div class="container">
<div id="visualisation">
</div>
</div>
<script - websocket stuff>
// Handle incoming messages
socket.onmessage = function (message) {
// Decode the JSON
var data = JSON.parse(message.data);
var source = data.source;
if (data.type === 'Client Data') {
var value = JSON.parse(data.value);
graphData(value);
};
</script>
<script type="text/javascript">
var margin = {top:20, right: 20, bottom: 30, left:50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#visualisation").append("svg")
.attr("width", width + margin.left + margin.right);
function graphData(data) {
console.log("graphdata called")
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.value; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.time); })
.attr("cy", function(d) { return y(d.value); });
};
</script>
The javascript falls over claiming that it cannot find the attribute 'domain' of x (x.domain(d3.extent(data, function(d) { return d.time; }));. However, commenting this line out, reveals the same problem for y, on the line below, and for the following line (svg.append("g")). I have copied this example from an online tutorial, with the exception of the graphdata function. The example read from a csv, whereas this receives data from a websocket. As a result, the error is likely to be in this function, but I am not certain.
Related
First time using the d3 library, I am pulling my data from a local object structured below. Attempting to show the data but the 0 index data is not being shown (see in screenshot) despite the data array having content. I have tried many things including reading other stack questions for similar issues (such as D3 bar chart not showing first element in array), but their solutions are not working for me since my d3 is structured differently. I have been stuck on this for over 3 hours now and any help is appreciated.
Data array
roundDataArr = [...roundDataArr, {'round': roundNumber, 'data': finalRoundTime}]
1st object not displayed on graph:
Relevant code:
function displayGraph() {
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 575 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.domain([0, d3.max(roundDataArr, function(d) { return d.round; })])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(roundDataArr, function(d) { return d.data; })])
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y);
var area = d3.area()
.x(function(d) { return x(d.round); })
.y0(height)
.y1(function(d) { return y(d.data); });
var valueline = d3.line()
.x(function(d) { return x(d.round); })
.y(function(d) { return y(d.data); });
var svg = d3.select(".tfny-graphWrapper")
.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.append("path")
.datum(roundDataArr)
.attr("class", "area")
.attr("d", area);
svg.append("path")
.datum(roundDataArr)
.attr("class", "line")
.attr("d", valueline);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
}
And the wrapper div is appended to the DOM through Javascript with this code:
let graphWrapper = document.createElement("div")
graphWrapper.classList.add("tfny-graphWrapper");
resultPage.appendChild(graphWrapper);
I've been fiddling with this for a few days now and I either get the error in the title, or I get "key.call is not a function", and I don't know why... basically the point of the script is to load different csv files based on the selection made in the dropdown. Without all the dropdown stuff, the script works fine, but once I introduce the dropdown script it starts bugging out and I can't figure out why. Below is the script, and I'll attach a sample of the kind of csv I'm working with if that helps at all.
Thanks a ton in advance!
CSV example: https://www.dropbox.com/s/24zopp43r3y1ft6/PCC-edit.csv?dl=0 (QC = QuantityCharged in this example)
<svg class="chart"></svg>
<select onchange="loadData()" id="metric">
<option>A-codes</option>
<option>Anesthesia</option>
<option>C-codes</option>
<option>Clinical Components (NRV)</option>
<option>E-Codes</option>
<option>Emerging Technology</option>
<option>Evaluation & Management</option>
<option>G-codes</option>
<option>J-codes</option>
<option>L-codes</option>
<option>Medicine</option>
<option>Pathology & Laboratory</option>
<option>P-codes</option>
<option>Q-codes</option>
<option>Radiology</option>
<option>Surgery</option>
<option>V-codes</option>
</select>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<script>
var svg = d3.select("svg"),
margin = {top:10, right: 720, bottom: 5, left: 720},
width = +svg.attr("width") + margin.left + margin.right,
height = +svg.attr("height") + margin.top + margin.bottom;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear()
.range([3, width]);
var y = d3.scale.ordinal()
.range([height, 1]);
var chart = d3.select(".chart")
.attr("width", width);
var parseRow = function(row) {
return {
subcategory: row.Subcategory,
quantityCharged: parseFloat(row.QuantityCharged)
}
};
//d3
var loadData = function(data) {
var metric = document.getElementById('metric').selectedOptions[0].text;
var dataFile = 'data/' + metric + '.csv'
d3.csv(dataFile, parseRow, function(error, data) {
x.domain([1, d3.max(data, function(d) { return d.QuantityCharged; })]);
y.domain([1, d3.max(data, function(d) { return d.Charge; })])
});
chart.attr("height", height);
var bar = chart.selectAll("g")
.data("width", data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * height + ")"; });
bar.append("rect")
.attr("width", function(d) { return x(d.QuantityCharged); })
.attr("height", height - 1);
bar.append("text")
.attr("x", function(d) { return x(d.QuantityCharged) + 10; })
.attr("y", height / 2)
.attr("dy", ".35em")
.text(function(d) { return d.QuantityCharged + " -- " + d.Charge; });
var xAxis = d3.svg.axis()
.scale(x)
.ticks(20)
.orient("top")
var yAxis = d3.svg.axis()
.scale(y)
.ticks(24)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.QuantityCharged); })
.y(function(d) { return y(d.QuantityCharged); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("path")
.attr("class", "line")
.attr("data", line);
function type(d) {
d.QuantityCharged = +d.QuantityCharged; // coerce to number
d.Subcategory = d.Subcategory;
return d;
}};
function Subcategory(csv) {
d3.text(dataFile, parseRow, function(text) {
var Subcategory = d3.csv.parseRows(text),
Charge = d3.csv.parseRows(text);
})}
loadData();
</script>
I'm learning d3js using various examples found online.
I've been trying to plot a chart with dual Y axis and an X-axis. The Y axis on the left side would plot a bar chart against the X-axis and the Y-axis on the right side would plot a line chart against X-axis. The Bar graph plots as exactly as required but the line graph does not. The X-axis is date (2015-10-15 04:10). Following this example.
The code I wrote
var margin = {top: 50, right: 50, bottom: 100, left: 50},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%m/%d/%Y %H:%M:%S").parse;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var yTxnVol = d3.scale.linear().range([height, 0]);
var yResTime = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(yTxnVol)
.orient("left")
var yAxis2 = d3.svg.axis()
.scale(yResTime)
.orient("right")
.ticks(10);
var svg = d3.selectAll("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 + ")");
d3.csv("../res/data.csv", function(error, data) {
data.forEach(function(d) {
d.AVRG_RESP_TIME = +d.AVRG_RESP_TIME;
d.TXN_VOL = +d.TXN_VOL;
});
x.domain(data.map(function(d) { return d.TYM; }));
yTxnVol.domain([0, d3.max(data, function(d) { return d.TXN_VOL+50; })]);
yResTime.domain([0, d3.max(data, function(d) { return d.AVRG_RESP_TIME+50; })]);
var minDate = d3.min(data, function(d){return d.TYM});
var maxDate = d3.max(data, function(d){ return d.TYM});
var xScale = d3.time.scale().range([0,width]);//.domain([minDate, maxDate]);
xScale.domain(d3.extent(data, function(d) { return new Date(d.TYM); }));
svg.append("g")
.attr("class", "x axis")
.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")
.attr("class", "y axis")
.call(yAxis)
svg.append("g")
.attr("class","y axis")
.attr("transform","translate("+width+ ", 0)")
.call(yAxis2)
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "yhover")
.attr("x", function(d) { return x(d.TYM); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return yTxnVol(d.TXN_VOL); })
.attr("height", function(d) { return height - yTxnVol(d.TXN_VOL); })
var line = d3.svg.line()
.x(function(d) { return xScale(new Date(d.TYM));})
.y(function(d) { return d.AVRG_RESP_TIME; });
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
The Output Trying to make this to a meaningful line graph. Got NaN error while formatting the dates.
Could someone help me to make this a proper line graph ?
The csv data sample
TYM, AVRG_RESP_TIME, TXN_VOL
2015-10-15 04:00:00, 12, 170
2015-10-15 04:10:00, 18, 220
2015-10-15 04:20:00, 28, 251
2015-10-15 05:00:00, 19, 100
First, fix your csv file. It is improperly formatted and should not have spaces after the comma.
Second, You are trying to mix an ordinal scale and a time scale for you xAxis. This isn't going to work. For your use case, just stick with time.
Here's a reworking of your code with explanatory comments:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var margin = {
top: 50,
right: 50,
bottom: 100,
left: 50
},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
// x scale should be time and only time
var x = d3.time.scale().range([0, width]);
var yTxnVol = d3.scale.linear().range([height, 0]);
var yResTime = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(yTxnVol)
.orient("left")
var yAxis2 = d3.svg.axis()
.scale(yResTime)
.orient("right")
.ticks(10);
var svg = d3.selectAll("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 + ")");
//d3.csv("data.csv", function(error, data) {
var data = [{"TYM":"2015-10-15 04:00:00","AVRG_RESP_TIME":"12","TXN_VOL":"170"},{"TYM":"2015-10-15 04:10:00","AVRG_RESP_TIME":"18","TXN_VOL":"220"},{"TYM":"2015-10-15 04:20:00","AVRG_RESP_TIME":"28","TXN_VOL":"251"},{"TYM":"2015-10-15 05:00:00","AVRG_RESP_TIME":"19","TXN_VOL":"100"}];
// just make TYM a date and keep it as a date
data.forEach(function(d) {
d.TYM = parseDate(d.TYM);
d.AVRG_RESP_TIME = +d.AVRG_RESP_TIME;
d.TXN_VOL = +d.TXN_VOL;
});
// get our min and max date in milliseconds
// set a padding around our domain of 15%
var minDate = d3.min(data, function(d){
return d.TYM;
}).getTime();
var maxDate = d3.max(data, function(d){
return d.TYM;
}).getTime();
var padDate = (maxDate - minDate) * .15;
x.domain([new Date(minDate - padDate), new Date(maxDate + padDate)]);
yTxnVol.domain([0, d3.max(data, function(d) {
return d.TXN_VOL + 50;
})]);
yResTime.domain([0, d3.max(data, function(d) {
return d.AVRG_RESP_TIME + 50;
})]);
// set an intelligent bar width
var barWidth = (width / x.ticks().length) - 20;
svg.append("g")
.attr("class", "x axis")
.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")
.attr("class", "y axis")
.call(yAxis)
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis2)
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "yhover")
.attr("x", function(d) {
// center bar on time
return x(d.TYM) - (barWidth / 2);
})
.attr("width", barWidth)
.attr("y", function(d) {
return yTxnVol(d.TXN_VOL);
})
.attr("height", function(d) {
return height - yTxnVol(d.TXN_VOL);
})
.style("fill","orange");
var line = d3.svg.line()
.x(function(d) {
return x(d.TYM);
})
.y(function(d) {
return d.AVRG_RESP_TIME;
});
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line)
.style("fill","none")
.style("stroke","steelblue")
.style("stoke-width","3px");
// });
</script>
</body>
</html>
The issue with the line graph filling with black was due to improper css. The new css property.
.line {
fill: none;
stroke: darkgreen;
stroke-width: 2.5px;
}
For the dates I formatted it to (%Y-%m-%d %H:%M) format and it worked.
I have been developing an area chart for year(x axis) vs Revenue (y axis) in D3 Js.The data is as:
localData=[
{"Revenue":"4.5","Year":"2011"},
{"Revenue":"5.5","Year":"2010"},
{"Revenue":"7.0","Year":"2012"},
{"Revenue":"6.5","Year":"2013"}
]
I want year at x axis and revenue at y axis for an area chart.Currently I am using time scale for x axis but i dont know how to use it as I have not date format I have only Years to represent.
My Current code is:
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var area = d3.svg.area()
.x(function (d) { return x(d.Year); })
.y0(height)
.y1(function (d) { return y(d.Revenue); });
$("#chartArea").html("");
var svg = d3.select("#chartArea").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 + ")");
x.domain(d3.extent(localData, function (d) { return d.Year; }));
y.domain([0, d3.max(localData, function (d) { return d.Revenue; })]);
svg.append("path")
.datum(localData)
.attr("class", "area")
.attr("d", area)
.attr("fill",color);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Revenue (M)");
Currently I am getting on my X axis as .011,.012,013,.014 I need as 2011,2012,2013,2014
I am new to D3 js so dnt know much about how to use scales??Please Help anyone..Thanks in advance.
Just add tick Format to your x Axis definition:
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y")); // <-- format
In v4, it'd be something like this:
d3.axisBottom(x).ticks(d3.timeYear.every(1))
huan feng is right. The time scale is treating your year(implicitly converted to an int) as timestamps. To force the scale to operate on the year, create Date objects specifying the year in it. Basically change the following line:
x.domain(d3.extent(localData, function (d) { return d.Year; }));
to
x.domain(d3.extent(localData, function (d) { return new Date(parseInt(d.Year),0); }));
You may then use Klaujesi's solution to change the tickFormat.
I am Trying to Implement this code: http://bl.ocks.org/3883245, but instead of loading a TSV file, i am loading the data from an array.
Here is how the array looks Like:
[["2012-10-02",2],["2012-10-09", 2], ["2012-10-12", 2]]
and then I applied this function on it to get CSV Format: var data = d3.csv.format(BigWordsDates2[ArrayIndex]);
but still nothing shows up.
Here is the whole code: I think I am not that far from getting it but I have been working on it for 3 days and still cant get it to work:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 80 - margin.left - margin.right,
height = 80 - margin.top - margin.bottom;
var data = d3.csv.format(BigWordsDates2[ArrayIndex]);
var parseDate = d3.time.format("%Y-%b-%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
var svg = d3.select("#Graph").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 + ")");
d3.csv.parseRows(data, function(data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = parseInt(d.close);
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});`
I think I am also doing something wrong with the d.date and d.close but I can't figure it out either.
The chart uses data in the following format
[{ date: '...', close: '...'},
{ date: '...', close: '...'}]
So, you need to parse your array:
// fix your data parser
var parseDate = d3.time.format("%Y-%m-%d").parse;
var arrData = [
["2012-10-02",200],
["2012-10-09", 300],
["2012-10-12", 150]];
// create a new array that follows the format
var data = arrData.map(function(d) {
return {
date: parseDate(d[0]),
close: d[1]
};
});
Here's the working version: http://jsfiddle.net/jaimem/T546B/
pd: depending on the data you might have to modify your y scale's domain.