I'm plotting the graphs with "number of clicks" as Y axis and "date" as X axis. Because of the large amount of data, the X axis is jumbled and couldn't display all the dates. I tried to use ticks(d3.time.months, 1) and tickFormat('%b %Y') to cur off some of the ticks. When I run the code, I got "getMonth() is not defined" for my data.
.tsv file:
date count
2013-01-01 4
2013-03-02 5
sample code:
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 0);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months, 1)
.tickFormat(d3.time.format('%b %Y'))
d3.tsv("data_Bar_Chart_Paino_Kristen.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.hits; })]);
var temp = height + 30; //+15
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(-8," + temp + ")")
.call(xAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.hits); })
.attr("height", function(d) {return height - y(d.hits); });
}
Is there a way to solve my problem and show ticks properly?
You'll need to tell D3 that your axis is a date. Try this:
//assumes the data is sorted chronologically
var xMin = data[0].dateFieldName;
var xMax = data[data.length-1].dateFieldName;
//set the scale for the x axis
var x = d3.time.scale().domain([xMin, xMax]).range([0, width]);
//straight from your code
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months, 1)
.tickFormat(d3.time.format('%b %Y'))
The trick here is to use the d3.time.scale() method.
If you use the d3.time.scale() you have another problem, you can't use x.rangeBand() when you draw the rect.
Related
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.
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.
So I'm dynamically creating a bunch of simple line charts with D3 and everything is going well, but for some reason charts with ranges that go from 9-10 get inverted and look absolutely terrible/do not function properly.
the first one, the values are upside down
this one is fine...
Here is some code...
var dataRange = d3.extent(quoteObjects, function(d){ return d.close });
var dateRange = d3.extent(quoteObjects, function(d){ return d.date });
// Set chart variables
var vis = d3.select("#"+type),
WIDTH = $('#chart-box').width(),
HEIGHT = $('#'+type).innerHeight(),
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 60,
},
// set scales
xScale = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]).domain(dateRange),
yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain(dataRange),
// create displayed axis
xAxis = d3.svg.axis()
.scale(xScale)
.tickValues( xScale.ticks(6) )
yAxis = d3.svg.axis()
.scale(yScale)
.tickValues( yScale.ticks(6) )
.orient("left");
console.log("WH", WIDTH, HEIGHT);
if (type == "intraday"){
xAxis.tickFormat(d3.time.format("%H"))
}
// append x axis
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.top) + ")")
.call(xAxis);
// append y axis
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
// create line based on "close" values
var lineGen2 = d3.svg.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.close);
})
.interpolate("basis");
// append "close" line
vis.append('svg:path')
.attr('d', lineGen2(quoteObjects))
.attr('stroke', '#931a28')
.attr('stroke-width', 3)
.attr('fill', '#222');
For some reason my bars (rects) are drawing really wide- I think its because the dates are not parsing correctly. here is the code
var fakeData= [
{"date":2013-10,"shortFig":10},
{"date":2013-11,"shortFig":-15},
{"date":2013-12,"shortFig":15},
{"date":2014-01,"shortFig":39},
{"date":2014-02,"shortFig":-38},
{"date":2014-03,"shortFig":33},
{"date":2014-04,"shortFig":-35},
{"date":2014-05,"shortFig":-2},
{"date":2014-06,"shortFig":-39},
{"date":2014-07,"shortFig":-46},
{"date":2014-08,"shortFig":23},
{"date":2014-09,"shortFig":45}
]
..this data becomes "thedata" in my chart building function where I try to parse the data and build the x scales and x axis:
// parse dates
var parseDate = d3.time.format("%Y–%m").parse;
thedata.forEach(function(d) {
var date = d.date.toString();
d.date = parseDate(date);
});
//The X scale
var xScale=d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(thedata.map(function(d) { return d.date; }));
//With the X scale, set up the X axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickValues([thedata[0].date]);
//Call the X axis
baseGroup.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
baseGroup.selectAll("rect")
.data(thedata)
.enter()
.append("rect")
.attr("class", function(d){ if(d.shortFig >= 0){return "green3"}else{return "red3"} })
.attr({
"x": function(d) {return xScale(d.date);},
"y": function(d) {return yScale(Math.max(0, d.shortFig));}, //Return the number with the highest value
"height": function(d) {return Math.abs(yScale(d.shortFig) - yScale(0));}, //Return the absolute value of a number, so negative numbers will be positive
"width": xScale.rangeBand()
});
Just a typo, the date parameters should be strings. Tested this, it works, just replace the fakeData array and you should be set.
var fakeData= [
{"date":"2013-10","shortFig":10},
{"date":"2013-11","shortFig":-15},
{"date":"2013-12","shortFig":15},
{"date":"2014-01","shortFig":39},
{"date":"2014-02","shortFig":-38},
{"date":"2014-03","shortFig":33},
{"date":"2014-04","shortFig":-35},
{"date":"2014-05","shortFig":-2},
{"date":"2014-06","shortFig":-39},
{"date":"2014-07","shortFig":-46},
{"date":"2014-08","shortFig":23},
{"date":"2014-09","shortFig":45}
];
var parseDate = d3.time.format("%Y-%m").parse;
fakeData.forEach(function(d){
console.log(parseDate(d.date));
});
I am trying to develop a scatterplot using d3 but the domain for y-axis is confusing me. y-axis are gonna display patient names and x-axis display their appointment dates. x-axis are working fine, but y-axis are displaying only two patient names.
function graph() {
var num_patient = Object.keys(patientList).length;
var patient_names = Object.keys(patientList);
console.log(patient_names);
var x = d3.time.scale().range([0, width]);
var y = d3.scale.ordinal().range([height, 0]);
x.domain(d3.extent(data, function(d) {return parseDate(d.dates); }));
//y.domain(patient_names.map(function(d) { return d.name;}));
y.domain(patient_names);
console.log(y.domain());
var xAxis = d3.svg.axis()
.scale(x)
.ticks(d3.time.year, 1)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#punchcard")
.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.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.value); });
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
console.log(patient_names) display the names correctly:
`["Andrew","Fred","Steve","John"]`
console.log(y.domain()) displays an extra undefined object:
["Andrew", "Fred","Steve" , "John", undefined]
But the y-axis only display Andrew at 0 and Fred at height h. How can I get to display all four names? I cannot hard code them as they are user input values. BTW: I am a beginner with d3 and js.
Thanks in advance!
With ordinal scales, you need to define the range points for the inputs explicitly (see the documentation). That is, you need to tell the scale explicitly which input value to map to which output. For example:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.range([height, height * 2/3, height * 1/3, 0]);
You probably want to use the .rangePoints() method instead, which allows you to specify an interval that D3 automatically divides based on the number of values in the domain:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.rangePoints([height, 0]);
Note that for .rangePoints() to work properly, you need to set the domain before the output range.