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.
Related
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!
I'm using d3js to build a bar chart from a .csv file, and as my programming knowledge is not good, I get an example from the web and start to working in. I can get a specific value from the .csv and draw the chart, but I would like to implement a dropdown list to select a specific value from the .csv as showed in the image below.
Imagem:
I'm unable to load this data from .csv. It doesn't seem complicated, but I tried it in some ways and it didn't work out. Could someone give me any tips on how to do this?
Best regards!
.csv file
country,year,hdi,gdp,unemp
Australia,2000,45,5.6,9
Austria,2000,54,6.1,8
Canada,2000,23,4.5,6
...
...
HTML + JS
<html>
<head>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
</style>
<script src="//d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div>
<select id="S_indicators"></select>
</div>
<svg width="560" height="300"></svg>
<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.scaleBand()
.rangeRound([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
d3.csv("output.csv").then(function (data) {
var allGroup = ["gdp","hdi","unemp"]
d3.select("#S_indicators")
.selectAll('myOptions')
.data(allGroup)
.enter()
.append('option')
.text(function (d) { return d; })
.attr("value", function (d) { return d; })
x.domain(data.map(function (d) {
return d.year;
}));
y.domain([0, d3.max(data, function (d) {
return Math.max(d.unemp);
})]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
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")
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return x(d.year);
})
.attr("y", function (d) {
return y(Number(d.unemp));
})
.attr("width", x.bandwidth())
.attr("height", function (d) {
return height - y(Number(d.unemp));
});
});
</script>
</body>
</html>
-Add a dropdown to the html file (I named it dd):
<!DOCTYPE html>
<html lang="en">
<title>My Scatter2</title>
<body>
<select id="dd"></select>
<div id="wrap"></div>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="scatterplot.js"></script>
<link rel='stylesheet' href='styles.css'>
</body>
</html>
Populate it in D3 via:
var specs = ['A','B','C']
var dropdown = d3.select('#dd')
dropdown.selectAll('option')
.data(specs)
.enter().append("option")
.attr("value",d=>d)
.text(d => d)
Or if you want an observeable example:
https://observablehq.com/#kickout/basic-scatterplot
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/
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>
In the following example I mapped histograms on letter vs frequency. Now, I want a line chart also for the same data without making much change. This means just a red line joining top of histograms. Can someone help me out?
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
data.forEach(function(){
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
});
</script>
</body>
</html>
HISTOGRAM:
First, get rid of that data.forEach: why do you want to paint everything 3 times?
After that, define your line generator:
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);;
Here, x.bandwidth()/2 will put the line in the middle of the top of each bar. I'm using d3.curveCardinal, but you have other options for the curve.
Then, append the line:
g.append("path")
.datum(data)
.attr("d", line);
Here is the demo:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
g.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "red")
.attr("fill", "none");
</script>