How to do line graph from CSV into HTML - javascript

My code now looks like this:
<!DOCTYPE html>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
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.date); })
.y(function(d) { return y(d.close); });
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);
});
</script>
Now I would like to redo it to upload CSV file right there on this page and to read data from there.
The file is going to be a table with two columns: first one is going to be a column with dates, but the second one - with numbers, with which the graph will be built.
Now I also tried doing it like this:
<!DOCTYPE html>
<html lang="lv"><head>
<title>HTML formas</title>
<meta charset="UTF-8">
</head>
<body>
<p>Norādi datni</p>
<input type="file" id="datne" onchange="datnesApstrade()">
<p>Ielasītie dati</p>
<pre id="rezultats"></pre>
<script>
function datnesApstrade(){
var fr=new FileReader();
fr.onload=function(){
apstradat( fr.result );
}
f = document.getElementById("datne");
fr.readAsText(f.files[0]);
}
function apstradat( dati ) {
document.getElementById('rezultats')
.textContent=dati;
var rindas = dati.split("\n");
//document.getElementById('rezultats').textContent="Rindas:"+rindas.length;
//alert( "In file is " + rindas.length + " lines" );
var r, i, c, co2 = [];
// pirmā rindā ir tabulas galva, tāpēc to nelasām
for( r=1; r<rindas.length; r=r+1 ) {
c = rindas[r].split(",");
co2.push( Number(c[1]) );
}
var rl = rindas.length-2;
for (i=1; i<rl; i=i+1){
document.getElementById('rezultats').textContent=co2[i];
}
}
</script>
</body></html>
But I don't know how to modify it to achieve my goal. Please help!

Related

Scale d3 x axis(time) to zero

I am making a chart using d3 and time data.
However, its scale is wrong.
It is a simple example.
Code is below:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = svg.attr("width"),
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%H:%M:%S");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
d3.csv("data.csv", function(d) {
d.date = parseTime(d.date);
d.close = +parseFloat(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");
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("dy", "0.71em")
.attr("y",2)
.attr("text-anchor", "end");
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);
});
Data is:
date,close
00:00:00,0.00
03:00:00,10
06:00:00,20
09:00:00,30
12:00:00,40
15:00:00,50
18:00:00,60
21:00:00,70
The point midnight,0 is wrong. It should be at 0,0.
Instead, it generates this:
How can it fit the chart correctly (starting at 0,0)?
Link to the Codepen:
https://codepen.io/jaqueline-passos/pen/mGpexG/

how to code according to json data from api in d3.js?

I am a beginner, I am learning d3.js but I am unable to figure out on how to draw a graph or code according to the data is in json format from api.
Here is one of the things I have tried but I am unable to change it according to the new data in another api. Can anybody help me?
where do I make the changes?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
</head>
<style>
body {
text-align: center;
margin-top: 5em;
background-color: #74b9ff;
}
h1 {
color: snow;
}
</style>
<body>
<h1>Bitcoin Prices in U.S. Dollars</h1>
<script>
var url = "https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD&limit=200&aggregate=3&e=CCCAGG";
d3.json(url).get(function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
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 x = d3.scaleTime()
.range([0, width])
var y = d3.scaleLinear()
.range([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))
.attr("stroke-width", 2)
.attr("fill", "none")
.style("font-size",".8em");
g.append("g")
.call(d3.axisLeft(y))
.attr("stroke-width", 2)
.style("font-size",".8em")
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 20)
.attr("text-anchor", "end")
.attr("font-size", "1.2em")
.text("Price ($)")
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "#ffeaa7")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("d", line);
});
</script>
</body>
</html>
I get the correct output for the above mentioned code but I want to change the api to https://blockchain.info/ticker
where could I make the changes to make it work?
Because it is the convertion rate of bitcoin compared to other coins it does not make sense to plot them all in one graph. The JPY would crush all the other bars.
Why buy/sell bitcoin using different coins if they just are conversions given the current rate.
If you draw a single coin values you get the following graph
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
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.json("https://blockchain.info/ticker", function(error, data) {
if (error) throw error;
data = data.EUR;
var keys = ["15m", "last", "buy", "sell"];
x.domain(keys);
y.domain([0, d3.max(keys, function(k) { return data[k]; })]);
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))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("price");
g.selectAll(".bar")
.data(keys)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(k) { return x(k); })
.attr("y", function(k) { return y(data[k]); })
.attr("width", x.bandwidth())
.attr("height", function(k) { return height - y(data[k]); });
});
</script>
Also not useful. Because all the variation is in the top few pixels.
What you need is a way to record the values from the JSON file over time for a particular coin and graph that like your original dataset.
Can be done by fetching the data every x minutes and then modify the graph with the enter/exit/remove data() call or just redraw the graph like this.
Place comments before the DEBUG sections for the real data. And uncomment the following line
//setInterval(getData, 5 * 60 * 1000);
Here I generate dummy data every 5 seconds for the demo.
To prevent out of memory data length is limited to 1000 samples.
Edit
It now shows the date of the sample on the x-axis.
<!DOCTYPE html>
<style>
.p15m { stroke: steelblue;}
.pbuy { stroke: red;}
.plast { stroke: green;}
.psell { stroke: orange;}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
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;
var data = [];
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
function getData() {
//DEBUG
data.push( {"15m": Math.random()*100 + 100, "last": Math.random()*100 + 100, "buy": Math.random()*100 + 100, "sell": Math.random()*100 + 100, "date": new Date() } );
updateGraph();
return;
// DEBUG END
d3.json("https://blockchain.info/ticker", function(error, dataNew) {
if (error) throw error;
var d = dataNew.EUR;
d.date = new Date();
data.push();
if (data.length > 1000) data = data.shift();
updateGraph();
});
}
getData();
setTimeout(getData, 5000);
//DEBUG
setInterval(getData, 5 * 1000);
//DEBUG END
//setInterval(getData, 5 * 60 * 1000);
function updateGraph() {
if (data.length < 2) return;
svg.select("g").remove(); // clean the graph
var keys = ["15m", "last", "buy", "sell"];
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(data, d => d.date));
var flat = [];
data.map( d => keys.map(k => d[k]) ).forEach(e => { flat = flat.concat(e); });
y.domain(d3.extent(flat , function(d) { return d; }));
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 (EUR)");
g.selectAll("g.key")
.data(keys)
.enter()
.append("g")
.attr("class", d => "key p" + d )
.append("path")
.datum(k => data.map( (d, i) => [d.date, d[k]]))
.attr("fill", "none")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
}
</script>

How to put different D3.js scripts on the same page without overlapping?

I am using D3.js to showcase my data. However, I am unable to get two different objects to not overlap. For example, the code below shows a line graph and a bar graph. I am using code from https://github.com/d3/d3/wiki/Gallery for this example to show my issue. The line graph code is from https://bl.ocks.org/mbostock/3883245. The bar graph code is from https://bl.ocks.org/mbostock/3885304. I tried using as shown in this example http://www.d3noob.org/2013/07/arranging-more-than-one-d3js-graph-on.html, but it did not work. I also made sure they both used the same version of d3.js. The data is from two tsv files that are on the links above for the line and bar graph. Any help would be appreciated!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineg"></div>
<div id="barg"></div>
<lineg width="960" height="500"></lineg>
<barg width="960" height="500"></barg>
<script>
var svga = d3.select("#lineg"),
margina = {top: 20, right: 20, bottom: 30, left: 40},
widtha = +svga.attr("width") - margina.left - margina.right,
heighta = +svga.attr("height") - margina.top - margina.bottom;
var xa = d3.scaleBand().rangeRound([0, widtha]).padding(0.1),
ya = d3.scaleLinear().rangeRound([heighta, 0]);
var ga = svga.append("g")
.attr("transform", "translate(" + margina.left + "," + margina.top + ")");
d3.tsv("bardata.tsv", function(d) {
d.frequency = +d.frequency;
return d;
}, function(error, data) {
if (error) throw error;
xa.domain(data.map(function(d) { return d.letter; }));
ya.domain([0, d3.max(data, function(d) { return d.frequency; })]);
ga.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + heighta + ")")
.call(d3.axisBottom(xa));
ga.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(ya).ticks(10, "%"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");
ga.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xa(d.letter); })
.attr("y", function(d) { return ya(d.frequency); })
.attr("width", xa.bandwidth())
.attr("height", function(d) { return heighta - ya(d.frequency); });
});
</script>
<script>
var svgb = d3.select("barg"),
marginb = {top: 20, right: 20, bottom: 30, left: 50},
widthb = +svgb.attr("width") - marginb.left - marginb.right,
heightb = +svgb.attr("height") - marginb.top - marginb.bottom,
gb = svgb.append("g").attr("transform", "translate(" + marginb.left + "," + marginb.top + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var xb = d3.scaleTime()
.rangeRound([0, widthb]);
var yb = d3.scaleLinear()
.rangeRound([heightb, 0]);
var line = d3.line()
.x(function(d) { return xb(d.date); })
.y(function(d) { return yb(d.close); });
d3.tsv("linedata.tsv", function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
xb.domain(d3.extent(data, function(d) { return d.date; }));
yb.domain(d3.extent(data, function(d) { return d.close; }));
gb.append("g")
.attr("transform", "translate(0," + heightb + ")")
.call(d3.axisBottom(xb))
.select(".domain")
.remove();
gb.append("g")
.call(d3.axisLeft(yb))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
gb.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>
In your code barg and lineg are just divs:
<div id="lineg"></div>
<div id="barg"></div>
Instead of that, they should be SVG elements, like this:
<svg id="barg" width="960" height="500"></svg>
<svg id="lineg" width="960" height="500"></svg>
Or, alternatively, append an SVG in your selection:
var svga = d3.select("#lineg").append("svg");
var svgb = d3.select("#barg").append("svg");
However, in that case, you cannot use the getters to get the width and height of the SVGs.
Finally, there is no HTML tag named <lineg> or <barg>.

d3 line chart, line not drawing

I'm trying to mimic the simple blocks example here, however I only seem to get the x and y axis to draw, no sign of the line. I'm loading data differently, via an array rather than a tsv file, though I've combed through a few times and I think everything is feeding into the scales and the line the same way. Non-working JSBin here:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
var data = [["2014-12-31", 0.31999999999999],["2014-11-30", 2.71],["2014-10-31", -4.05],["2014-09-30", 4.22],["2014-08-31", 2.17],["2014-07-31", 5.36],["2014-06-30", 3.99],["2014-05-31", 3.52],["2014-04-30", -.46],["2014-03-31", -8.22],["2014-02-28", 5.89]]
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("%Y-%m-%d");
var x = d3.scaleTime().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var data = data.map(function(d){
return [parseTime(d[0]), +d[1]]
});
var line = d3.line()
.x(function(d){
return d[0];
})
.y(function(d){
return d[1];
});
x.domain(d3.extent(data, function(d) { return d[0]; }));
y.domain(d3.extent(data, function(d) { return d[1]; }));
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", 50)
.attr("dy", "0.9em")
.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", 3)
.attr("d", line);
</script>
</body>
</html>
anything obvious that I'm doing wrong?
The issue is that you are not scaling your data to fit properly within your svg coordinate space:
var line = d3.line()
.x(function(d){
return d[0];
})
.y(function(d){
return d[1];
});
This section of code sets the plotted coordinates of your data, and you are currently using your data values as your x and y svg values without any scaling. You need to scale it according to your scale:
var line = d3.line()
.x(function(d){
console.log(x(d[0])); return x(d[0]);
})
.y(function(d){
return y(d[1]);
});
This will allow your graph to draw as intended.

d3 tsv not working with django

I know this question is asked multiple times, but my problem seems to be unique:
my django view:
def data(request):
event = Event.objects.get(pk=request.GET.get('epk'))
occurrences = event.occurrences.all()
response = HttpResponse(content_type='text/tsv')
response['Content-Disposition'] = 'filename="data.tsv"'
response.write('date\tclose\n')
for occ in occurrences:
response.write('%s\t%s\n' % (occ.timestamp.strftime('%Y-%m-%d %H:%M:%S'), occ.counter))
return response
and js:
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
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("%Y-%m-%d %H:%M:%S");
var x = d3.scaleTime().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var line = d3.line().x(function (d) {
return x(d.date);
}).y(function (d) {
return y(d.close);
});
d3.tsv("{% url 'data' %}?epk={{ event.id }}", function (d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function (error, data) {
console.log(data); // ----> the screenshot at the bottom
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," + 1 + ")")
.call(d3.axisBottom(x)) // <---- error place
.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("Occurs");
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>
content of tsv file I get from my django view is:
date close
2017-02-12 14:07:14 1
2017-02-12 14:07:35 1
2017-02-12 17:12:14 1
2017-02-12 17:23:40 1
2017-02-12 17:51:52 1
2017-02-13 04:15:58 1
the worst part is that it is working on plunker: http://plnkr.co/edit/Moyyawptt8lY0pLBDQDr?p=preview
but in my code, something is wrong. I just dont know where to debug, since the error message is so horribly unclear.
can someone please help me out? it would be the greatest help to humanity!
this is the screenshot from the console.log(data);

Categories