I am working on making a linegraph in D3 that displays data in JSON format that is retrieved from an API.
I found an example line graph project on D3's website and it seems relatively straight forward. The main difference between the example and my project is that the example uses data from a local csv file instead of JSON from an API request.
// This needs to be changed to my API request
d3.tsv("data.tsv", function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
I changed the csv request part to this because I'm trying to load in my JSON from the API:
d3.json(url).get(function(d) {
for(var i = 0; i < d.Data.length; i++) {
coinValue.push(d.Data[i].close);
dates.push(convertUnixTime(d.Data[i].time));
}
return d;
}, function(error, data) {
if (error) throw error;
This isn't working. I'm confused about why there's a comma and then another function right after.
What is the proper way to make the exact same line graph but with a d3.json function/API request instead?
My JSON looks like this: https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG
The difference between d3.csv and d3.json is the accessor (2nd argument).
d3.csv: d3.csv(url[[, row], callback]) in which row is nothing but each row of the data fetched i.e. if you add a function as follows:
d3.csv(url, function(d) {
// each row of the data
}, callback)
d3.json: d3.json(url[, callback]) DOES NOT provide this each row accessor. But you can do that within the callback as follows:
d3.json(url, function(err, data) {
data.forEach(function(row) {
// parse each row as required
});
})
Using the above syntax along with the provided example code and JSON URL, here's a code snippet drawing a line chart:
// This needs to be changed to my API request
d3.json("https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG", function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
//console.log(data);
if (error) throw error;
var svg = d3.select("svg"),
margin = {top: 20, right: 20, 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 + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.close); });
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
Hope this helps.
Related
I am trying to finalize a D3.js treemp using this example code: https://bl.ocks.org/mbostock/4339083
I am running a local "python -m SimpleHTTPServer" web instance to serve up my pages.
I am not proficient in HTML or JavaScript.
I have a very large local JSON file that I created in python that is referenced in the "d3.json("d3_flare.json", function(error, flare)" in the index.html code snippet.
I need assistance with two items please:
I have very long text on the 4th node down from the JSON root node that D3.js & HTML will process. The text looks like:
"name": "Some Title: Some very very long text...Some very very long text...Some very very long text...Some very very long text...Some very very long text."
but can be longer or shorter.
How can I wrap this text to be more legible and neat while viewing in the browser at this position of JSON being read by D3.js & HTML given my attached index.html code snippet?
I need to save every selected node path(s) to a local file (e.g. CSV) that the user has clicked on, remember only running a local "python -m SimpleHTTPServer" web instance to serve up my pages.
How can I write the selected node paths to a local file given my attached index.html code snippet?
Thank you
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
/*var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
*/
var margin = {top: 40, right: 120, bottom: 40, left: 500},
width = 10000 - margin.right - margin.left,
height = 775 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("d3_flare.json", function(error, flare) {
if (error) throw error;
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth original was 180
nodes.forEach(function(d) { d.y = d.depth * 900; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("y", function(d) { return d.children || d._children ? -13 : 13; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
To solve my second question I ended up adding a contextmenu to the var nodeEnter section of my previously posted code where users are directed only to right-click a certain node in the path that gets recorded. The contextmenu:
.on("contextmenu", function (d) {
d3.event.preventDefault()
sessionStorage.setItem("selected_node", JSON.stringify(d.name));
var val = sessionStorage.getItem("selected_node");
post_session(val);
});
I then passed the sessionStorage objects to this function where I have cgi/bin python writing the requests to a file:
function post_session(session_store_info)
{
var http = new XMLHttpRequest();
http.open('POST', 'cgi-bin/post_session', true);
http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
http.send(session_store_info);
return http.status!=404;
}
I am seeking assistance with my script in creating a column chart with similar string occurrences listed in a CSV file. I am creating this chart using D3.js V3. The script and technique I’m using to capture the length of the items return results, but the number count of the length is not close to the actual count, very short from the expect item count. Can someone please review my script and see what is causing my length count to be way off from the actual number count. Any help, suggestion, or techniques will be greatly appreciated.
Below is a sample of the CSV file concept and D3 script. Thank you in advance.
d3.csv("../../data/teamData.csv", function(error, csv_data) {
if (error){
alert("Data didn't load, Refresh your browser");
}else{
for (var i = 0; i < csv_data.length; i++) {
if(csv_data[i].Team == "Team 1"){
team1 = csv_data[i].Team;
}
if(csv_data[i].Team == "Team 2"){
team2 = csv_data[i].Team;
}
}
}
var teamCount1 = team1.length;
var teamCount2 = team2.length;
console.log(teamCount1);//Not showing correct number count
console.log(teamCount2);//Not showing correct number count
var margin = {top: 2, right: 2, bottom: 60, left: 2},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var barPadding = 1;
var chartdata = [teamCount1, teamCount2];
var textData = ['Team 1', 'Team 2'];
var xScale = d3.scale.ordinal()
.domain(d3.range(chartdata2.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(chartdata)])
.range([0, h]);
var svg = d3.select(".chartData")
.append("svg")
.attr("preserveAspectRatio", "xMidYMin")
.attr("viewBox", "0 0 1000 650")
.attr("width", "100%")
.attr("height", "100%")
.attr("display",'block')
.append("g").attr("class","stackChart2")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Create bars
svg.selectAll("barRect")
.data(chartdata)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
})
.on("click", function() {
sortBars();
});
//Create labels
svg.selectAll("text")
.data(chartdata)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) - 10;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "black");
//Text Label
var yTextPadding = 20;
svg.selectAll(".bartext2")
.data(textData)
.enter()
.append("text")
.attr("class", "bartext2")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.style("font-size", "16px")
.attr("x", function(d,i) {return xScale(i)+xScale.rangeBand()/2;})
.attr("y", 460)
.text(function(d){return d;});
//Define sort order flag
var sortOrder = false;
//Define sort function
var sortBars = function() {
//Flip value of sortOrder
sortOrder = !sortOrder;
svg.selectAll("rect")
.sort(function(a, b) {
if (sortOrder) {
return d3.ascending(a, b);
} else {
return d3.descending(a, b);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
});
}
});//End of D3 function
I was able to figure it out referencing this stackoverflow post: D3 Loading in CSV file then using only specific columns. I was able to filter out the needed column from my CSV file which was "Team", using d3 map as follow; var col2data = csv_data.map(function(d) { return d.Team });
Then I followed this stackoverflow post to filter my specific reoccurring item from the "Team" column: d3.js filter from csv file using multiple columns. This post made it possible to get the length of the items to build my column chart. All is working as expected. Below is the edited script. I hope this will help someone in the future encountering this issue.
d3.csv("../../data/teamData.csv", function(csv_data) {
//Filter CSV File to Specific Column
var col2data = csv_data.map(function(d) { return d.Team });
console.log(col2data);
//Filter CSV Column for Specific Column Item
var team1 = csv_data.filter(function(d){
if( d["Team"] == "Team 1"){
return d;
}
})
var team2 = csv_data.filter(function(d){
if( d["Team"] == "Team 2"){
return d;
}
})
console.log(team1);
var teamCount1 = team1.length;
var teamCount2 = team2.length;
//console.log(teamCount1);//Not showing correct number count
//console.log(teamCount2);//Not showing correct number count
var margin = {top: 2, right: 2, bottom: 60, left: 2},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var barPadding = 1;
var chartdata = [teamCount1, teamCount2];
var textData = ['Team 1', 'Team 2'];
var xScale = d3.scale.ordinal()
.domain(d3.range(chartdata.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(chartdata)])
.range([0, h]);
var svg = d3.select(".plot-div")
.append("svg")
.attr("preserveAspectRatio", "xMidYMin")
.attr("viewBox", "0 0 1000 650")
.attr("width", "100%")
.attr("height", "100%")
.attr("display",'block')
.append("g").attr("class","stackChart2")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Create bars
svg.selectAll("barRect")
.data(chartdata)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
})
.on("click", function() {
sortBars();
});
//Create labels
svg.selectAll("text")
.data(chartdata)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) - 10;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "black");
//Text Label
var yTextPadding = 20;
svg.selectAll(".bartext2")
.data(textData)
.enter()
.append("text")
.attr("class", "bartext2")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.style("font-size", "16px")
.attr("x", function(d,i) {return xScale(i)+xScale.rangeBand()/2;})
.attr("y", 460)
.text(function(d){return d;});
//Define sort order flag
var sortOrder = false;
//Define sort function
var sortBars = function() {
//Flip value of sortOrder
sortOrder = !sortOrder;
svg.selectAll("rect")
.sort(function(a, b) {
if (sortOrder) {
return d3.ascending(a, b);
} else {
return d3.descending(a, b);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
});
}
});//End of D3 function
I'm trying to duplicate this code found on bl.ocks.org, but I can't get my bar graph to render. I'm pulling from a csv instead of a tsv, but I don't think that would made a difference. There aren't any errors written to the console. Where am I going wrong?
<script type="text/javascript">
$( document ).ready(function() {
loadChart();
});
function loadChart() {
var svg = d3.select("svg");
var margin = { top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0,width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append(g).attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("Data/data.csv", function(d) {
d.NumProducts = +d.NumProducts;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Category; }));
y.domain([0, d3.max(data, function(d) { return d.NumProducts; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("Dy", "0.71em")
.attr("text-anchor", "end")
.text("Number of Products");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class" , "bar")
.attr("x", function(d) { return x(d.Category); })
.attr("y", function(d) { return y(d.NumProducts); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.NumProducts); });
});
}
</script>
The csv:
Category,NumProducts
Beverages,12
Condiments,12
Confections,13
Dairy Products,10
Grains/Cereals,7
Meat/Poultry,6
Produce,5
Seafood,12
The main issue that you have is down to missing speech marks.
var g = svg.append(g)
Should read:
var g = svg.append("g")
If you were to inspect your DOM you would see
<svg>
<undefined transform="translate(40,20)">
... (correct DOM)
</undefined>
</svg>
The reason that nothing renders after this point, is because the browser doesn't know how to render an <undefined> element, therefore it stops trying to render.
This immediately indicated that one of the append calls wasn't working because the DOM didn't understand the type of element given to it. Making the change produces this:
I'm trying to draw a Area chart . I couldn't access to the data and I don't know WHY . In the firefox console " data is not defined". This is my code and data .
This data which is JSON format is located on the server side :
{"Id":466,"Name":"korea",
"Occurrences":[
{"OccurrenceDate":"\/Date(1398207600000+0100)\/","OccurrenceFrequency":27},
{"OccurrenceDate":"\/Date(1398726000000+0100)\/","OccurrenceFrequency":1},
{"OccurrenceDate":"\/Date(1398898800000+0100)\/","OccurrenceFrequency":4},
{"OccurrenceDate":"\/Date(1399071600000+0100)\/","OccurrenceFrequency":303}
]
}
This is my code
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script>
var margin = {top: 80, right: 80, bottom: 80, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate1 = d3.time.format.iso.parse;
// Scales and axes. Note the inverted domain for the y-scale: bigger is up!
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
xAxis = d3.svg.axis().scale(x).tickSize(-height).tickSubdivide(true),
yAxis = d3.svg.axis().scale(y).ticks(4).orient("right");
// An area generator, for the light fill.
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.OccurrenceDate); })
.y0(height)
.y1(function(d) { return y(d.OccurrenceFrequency); });
// A line generator, for the dark stroke.
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.OccurrenceDate); })
.y(function(d) { return y(d.OccurrenceFrequency); });
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 strURI = "http://tr-l6/STWebService/Service.svc/session/Fetchnodehistorybyname?
strnodename=korea";
// use jquery to get the json dataset because I cannot get d3.json to work with Firefox/Chrome (but it is OK with IE)
// this is probably a jsonp / cross domain issue that requires further tweaking in the WCF web,config file
// d3.json(strURI,function(error, graph) {
$.ajax({
type: "GET",
url: strURI,
dataType: 'jsonp',
success: function (graph) {
x
data.forEach(function(d) {
// Convert unix date format to regular format
var dc
//dc = (d.date).substring(1, 26);
dc = d.OccurrenceDate.substring(6, 16)
console.log(dc);
dc = new Date(dc*1000)
console.log(dc);
d.OccurrenceDate= parseDate1(dc);
d.OccurrenceFrequency = +d.OccurrenceFrequency;
return d
});
x.domain(d3.extent(data, function(d) { return d.OccurrenceDate; }));
y.domain([0, d3.max(data, function(d) { return d.OccurrenceFrequency; })]);
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
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")
}});
I dont know if it is the reason for the Error but you missed the </script> Script at the End
I'm following a tutorial of d3 that uses tsv rather than json.
I'm more familiar with json so I wanted to apply my own json object to the example.
I found this and tried to apply it but i'm still getting errors:
How to use d3.min and d3.max within a d3.json command
Here is my code. I am getting the json object back with my first log. And the correct length with my second log.
Yet I get this error when trying to use d.value in d3.max()
Error: Invalid value for attribute width="NaN"
d3.json("sampledata.json", function(error, data) {
var maxx = d3.entries(data)
.sort(function(a,b){return d3.descending(a.value, b.value);})
console.log(maxx)
console.log(data.length);
mydata = data;
x.domain([0, d3.max(data, function(d) { return d.value; })])
chart.attr("height", barHeight * mydata.length);
var bar = chart.selectAll("g")
.data(mydata)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
// WHERE MY ERROR IS because there is no value for x
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
I think what you need to do, since x is a scale of some kind, is the following:
bar.append("rect")
.attr("width", function(d) {
return x(d.value);
})
.attr("height", barHeight - 1);
Otherwise it is just going to pass d into x, which isn't going to help you.