d3 struggling to add custom ticks to area graph for chart - d3.js

I cannot seem to get custom ticks added to an area graph, default however seem to work. I have attempted many different things I have found in examples online, but they either take no effect of result in the graph having no axis at all.
enter image description here
the code in question is as follows:
`
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.age1+d.age2+d.age3+d.age4; })])
.range([ chartHeight, 0 ]);
svg.append("g").call(d3.axisLeft(y));
if(timeline){
var x = d3
.scaleTime()
.domain(d3.extent(data, function(d) { return tParser(d.date); }))
.range([ 0, chartWidth ]);
svg.append("g")
.attr("transform", "translate(0," + chartHeight + ")")
.call(d3.axisBottom(x));
}else{
var x = d3.scaleLinear()
.domain([d3.min(data, function(d) { return d.date; }), d3.max(data, function(d) { return d.date; })])
.range([ 0, chartWidth ]);
svg.append("g")
.attr("transform", "translate(0," + chartHeight + ")")
.call(d3.axisBottom(x));
}`
I have tried adding the ticks as follows:
var x = d3 .scaleTime() .domain(d3.extent(data, function(d) { return tParser(d.date); })) .ticks(5) .range([ 0, chartWidth ]);
and this
var x = d3 .scaleTime() .domain(d3.extent(data, function(d) { return tParser(d.date); })) .range([ 0, chartWidth ]); x.ticks(5);
I have created a stack blitz with an example of my project, should that help
https://stackblitz.com/edit/angular-ivy-mrs77c?file=src%2Fapp%2FstackedArea-chart-visualization%2FstackedArea-chart-visualization.component.ts

Related

d3js stacked bar chart variable keys

I am using this example of stacked bar chart But in my data I never know keys. So I get them as a part of json data.
Data:
{"apple":6522,"cherry":4611,"orange":2004,"time":1539792000000,"keys":
["apple","cherry","orange",],"max":6522},
{"apple":6171,"melone":4348,"time":1539802800000,"keys":["apple",
"melone],"max":6171}...
I am using function to return keys list. Here is my code:
var xScale = d3.scaleBand().range([0, width]).padding(0.1);
var yScale = d3.scaleLinear().range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%b"));
var yAxis = d3.axisLeft(yScale);
var stack = d3.stack();
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.25)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal().range(['#d53e4f','#fc8d59','#fee08b']);
x.domain(data.map(function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.max;})]);
z.domain(data.map(function(d) { return d.keys; }));
g.selectAll(".serie")
.data(stack.keys(function(d) { return d.keys; })(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function(d) { return z(d.value); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.time); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
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, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks(10).pop()))
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "#000");
It doesn't show any error or anything in page :D
EDIT:
If I put an array with values here, it somehow works, but I need to change keys according the data
.data(stack.keys(["apple","orange"])(data))
In the data, I put all the keys, but I assing them to zero (instead of not having the key at all)

D3.js line chart continuous but want non continuous

Hey guys I created a time series line chart using publicly available stock data.
Where I got to is the following:
Looks like what it is doing is connecting the first datapoint with the last datapoint which is why it is creating a line across the entire chart.
I looked online and read that to create a non continuous line chart I can add
.defined(function(d) { return d; })
I did but it didn't help.
My code:
//Set dimensions and margins for the graph
var margin = {top: 20, right: 20, bottom: 100, left: 70},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Create canvas
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 + ")");
//Parse date
var parseDate = d3.timeParse("%Y-%m-%d");
//Set the ranges
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
// Define the line
var valueLine = d3.line()
.defined(function(d) { return d; })
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
//Import data from api
d3.json("api_all.php", function(error, data) {
if (error) throw error;
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
e.stockSymbol = e.stock_symbol;
});
//Create nest variable
var nest = d3.nest()
.key(function(d){ return d.stockSymbol; })
.entries(data);
console.log(nest);
//Scale the range of the data
//x axis scale for entire dataset
x.domain(d3.extent(data, function(d) { return d.date; }));
//y.domain([0, d3.max(data, function(d) { return d.value; })]);
//Add the x axis
var xaxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(d3.axisBottom(x));
//Add x axis label
svg.append("text")
.attr("transform", "translate(" + (width/2) + "," + (height + margin.top + 10) + ")")
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Date")
.attr("class", "x axis label");
//Create dropdown
var dropDown = d3.select("#dropDown")
dropDown
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){ return d.key; })
.text(function(d){ return d.key; })
// Function to create the initial graph
var initialGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock)
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
//Scale y axis
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock, function(d){
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "stockGroups")
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
console.log(selectStockUnnested);
});
var initialPath = selectStockGroups.selectAll(".line")
.data(selectStock)
.enter()
.append("path")
initialPath
.attr("d", function(d){ return valueLine(d.values) })
.attr("class", "line")
//Add the y axis
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
//Add y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 60)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Price")
.attr("class", "y axis label");
}
// Create initial graph
initialGraph("1301.T")
// Update the data
var updateGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock);
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
// Select all of the grouped elements and update the data
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock)
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
});
// Select all the lines and transition to new positions
selectStockGroups.selectAll("path.line")
.data(selectStock)
.transition()
.duration(1000)
.attr("d", function(d){
return valueLine(d.values)
})
// Update the Y-axis
d3.select(".y")
.transition()
.duration(1500)
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
}
// Run update function when dropdown selection changes
dropDown.on('change', function(){
// Find which stock was selected from the dropdown
var selectedStock = d3.select(this)
.select("select")
.property("value")
console.log(selectedStock);
// Run update function with the selected stock
updateGraph(selectedStock)
});
});
</script>
</body>

Adding axes eats up my data

I'm following the D3 tutorial but adding the axis makes half of my data disappear and I don't understand why. I thought that maybe the axis is taking up the space that's meant for the data so I added an extra 10px to the transform property, but it doesn't make any difference.
var GIST = "https://gist.githubusercontent.com/charisseysabel/f8f48fbf11b8a1b0d62cbe2d6bdc2aa6/raw/2ead1537adb822fbd59a666afd5334d525480a13/nano-2017.tsv"
var width = 1000,
height = 550,
margin = {top: 20, right: 30, bottom: 30, left: 4};
var y = d3.scaleLinear()
.range([height, 0]);
var yScale = d3.scaleLinear()
.range([height, 0]);
var xScale = d3.scaleLinear()
.range([0, width]);
var xAxis = d3.axisLeft(yScale);
var yAxis = d3.axisBottom(xScale);
var chart = d3.select(".chart")
.attr("width", width)
.attr("height", height);
chart.append("g")
.attr("transform", "translate(10, 0)")
.call(xAxis);
chart.append("g")
.attr("transform", "translate(0, 540)")
.call(yAxis);
d3.tsv(GIST, type, function(error, data) {
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + ((i * barWidth) + 10) + ",0)"; }
);
bar.append("rect")
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", (barWidth / 2) - 2)
.attr("y", function(d) { return y(d.value) + 3; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
});
function type(d) {
d.value = +d.value;
return d;
}
When you do this...
var bar = chart.selectAll("g").etc...
... you're selecting group elements that already exist in the SVG, which are the axes, and binding your data to them.
There are two easy solutions:
Move your code that creates the axes to the bottom of the d3.tsv, that is, after you have appended the bars.
Select something that doesn't exist, like
var bar = chart.selectAll(null).etc. To read more about the logic behind selectAll(null), have a look at my answer here.

d3.js multiple line chart rendering: Same dataset but different curve

I am completely new to d3.js and currently working on a multiple line chart in d3.js and therefore prepared two different datasets ("dataset", "datasetIndividual"). I noticed that the curve for the individual curve (green) differed from the points that are drawn after reload.
So in order to determine wether the problem was the curve or the dots I started using the same dataset for each curve and the problem definitely seems to be the rendering of the individual curve. After each reload sometimes the curves might completely overlap and other times differ. I am not able to determine a reason nor the solution for this particular problem. I assume it has something to do with the nesting/sequence of the code. I admit, the whole code is a bit "pedestrian".
The code for the line chart:
/*create svg element*/
var svg = d3.select('.linechart')
.append('svg')
.attr('width', w)
.attr('height', h)
.attr('id', 'chart');
/*x scale*/
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d[0];
})])
.range([padding, w - padding]);
/*y scale*/
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d[1];
})])
.range([h - padding, padding]);
/*x axis*/
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(20)
.tickSize(0, 0)
//.tickPadding(padding);
/*append x axis*/
svg.append('g')
.attr({
'class': 'xaxis',
//'transform': 'translate(0,' + (h - padding) + ')'
'transform': 'translate(0,' + 0 + ')'
})
.call(xAxis);
/*y axis*/
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(0, 0)
.tickValues([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
/*append y axis*/
svg.append('g')
.attr({
'class': 'yaxis',
'transform': 'translate(' + padding + ',0)'
})
.call(yAxis);
/*define line*/
var lines = d3.svg.line()
.x(function(d) {
return xScale(d[0])
})
.y(function(d) {
return yScale(d[1])
})
.interpolate('monotone');
/*append line*/
var path = svg.append('path')
.attr({
'd': lines(dataset),
'fill': 'none',
'class': 'lineChart'
});
/*get length*/
var length = svg.select('.lineChart').node().getTotalLength();
/*animate line chart*/
svg.select('.lineChart')
.attr("stroke-dasharray", length + " " + length)
.attr("stroke-dashoffset", length)
.transition()
.ease('linear')
.delay(function(d) {
return dataset.length * 100;
})
.duration(3000)
.attr("stroke-dashoffset", 0);
/*add points*/
var points = svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle');
/*point attributes*/
points.attr('cy', function(d) {
return yScale(d[1])
})
.style('opacity', 0)
.transition()
.duration(1000)
.ease('elastic')
.delay(function(d, i) {
return i * 100;
})
.attr({
'cx': function(d) {
return xScale(d[0]);
},
'cy': function(d) {
return yScale(d[1]);
},
'r': 5,
'class': 'datapoint',
'id': function(d, i) {
return i;
}
})
.style('opacity', 1);
// LINES INDIVIDUAL
//function drawIndividualLines (){
/*x scale*/
var xScaleIndividual = d3.scale.linear()
.domain([0, d3.max(datasetIndividual, function(d) {
return d[0];
})])
.range([padding, w - padding]);
/*y scale*/
var yScaleIndividual = d3.scale.linear()
.domain([0, d3.max(datasetIndividual, function(d) {
return d[1];
})])
.range([h - padding, padding]);
/*define line*/
var linesIndividual = d3.svg.line()
.x(function(d) {
return xScaleIndividual(d[0])
})
.y(function(d) {
return yScaleIndividual(d[1])
})
.interpolate('monotone');
/*append line*/
var pathIndividual = svg.append('path')
.attr({
//'d': linesIndividual(datasetIndividual),
'd': linesIndividual(dataset),
'fill': 'none',
'class': 'lineChartIndividual'
});
/*get length*/
var lengthIndividual = svg.select('.lineChartIndividual').node().getTotalLength();
/*animate line chart*/
svg.select('.lineChartIndividual')
.attr("stroke-dasharray", lengthIndividual + " " + lengthIndividual)
.attr("stroke-dashoffset", lengthIndividual)
.transition()
.ease('linear')
.delay(function(d) {
return datasetIndividual.length * 100;
})
.duration(3000)
.attr("stroke-dashoffset", 0);
/*add points*/
var pointsIndividual = svg.selectAll('circleIndividual')
.data(dataset)
.enter()
.append('circle');
/*point attributes*/
pointsIndividual.attr('cy', function(d) {
return yScale(d[1])
})
.style('opacity', 0)
.transition()
.duration(1000)
.ease('elastic')
.delay(function(d, i) {
return i * 100;
})
.attr({
'cx': function(d) {
return xScale(d[0]);
},
'cy': function(d) {
return yScale(d[1]);
},
'r': 5,
'class': 'datapointIndividual',
'id': function(d, i) {
return i;
}
})
.style('opacity', 1);
I prepared a fiddle in hope someone of you is able to determine the source of the problem:
https://jsfiddle.net/4vf6s6e9/
I would apreciate any help. I donĀ“t often post questions and tried to incorprate suggestions from my last experience. If you need anything from me to improve this post, feel free to tell me.
Thanks a lot in advance.
You are drawing the lines with two slightly different scales.
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d[0];
})])
.range([padding, w - padding]);
/*y scale*/
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d[1];
})])
.range([h - padding, padding]);
and
/*x scale*/
var xScaleIndividual = d3.scale.linear()
.domain([0, d3.max(datasetIndividual, function(d) {
return d[0];
})])
.range([padding, w - padding]);
/*y scale*/
var yScaleIndividual = d3.scale.linear()
.domain([0, d3.max(datasetIndividual, function(d) {
return d[1];
})])
.range([h - padding, padding]);
This makes the calculations of the position of each line just a tiny bit different and that difference depends on the random difference between the extents of the two data sets.
Passing the same data to both scales (or just using the same scale for both lines) fixes the problem. Here's an updated fiddle with that single change: https://jsfiddle.net/oa0rsvgc/

d3 center tick and x-axis label when single value

I have the following d3 code:
var json = [
{
date: "05/17",
numTags: 23
}
];
d3.select('summary-graph').selectAll('*').remove();
var svg = d3.select("summary-graph"),
margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom;
var parseTime = d3.timeParse("%m/%y");
var svg = d3.select("summary-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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function (d) {
console.log(d);
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function (d) {
return d.date;
}))
.nice();
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function (d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function (d) {
return xScale(d.date);
})
.y(function (d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) { return "blue" })
.attr("cx", function(d, i) { return xScale(d.date) })
.attr("cy", function(d, i) { return yScale(d.numTags) })
.attr("r", function(d, i) { return 10 });
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size","14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
Which produces the following visualization:
I'm trying to figure out how to center the tick mark and show the x-axis label when there is only 1 data point like this. At 2 data points, I don't love how it still sets the tick marks at the extreme ends of the x-axis. For 3 data points and above, it looks nice (thanks to .nice() it seems).
Any help?
Based on Gerado's response, I was able to get close. The last sticking point is that the left side of the X-axis now has the month (March) despite no data for that day.
Fixed:
changed .ticks(json.length) to .ticks(d3.timeMonth.every(1))
Since you are using only a single data point, your date scale has a domain in which the lower and upper values are the same:
[
Mon May 01 2017 00: 00: 00 GMT + 1000,
Mon May 01 2017 00: 00: 00 GMT + 1000
]
For putting that circle in the middle of the x axis you have to set different values for the scale's domain.
There are several ways for doing that. My proposed solution here involves verifying if the domain's values are the same...
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
... and, if they are, changing them. In this case, I'm subtracting one day from the lower limit and adding one day to the upper limit:
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
Check the result:
var json = [{
date: "05/17",
numTags: 23
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>
EDIT: As you asked in your edit, when you have two data values my solution will create additional ticks on the limits, which is the expected behaviour:
var json = [{
date: "05/17",
numTags: 23
}, {
date: "05/17",
numTags: 17
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>
There are several ways for removing those ticks. One of them is using tickValues:
.tickValues(data.map(function(d){ return d.date}))
Here is the demo:
var json = [{
date: "05/17",
numTags: 23
}, {
date: "05/17",
numTags: 17
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickValues(data.map(function(d){ return d.date}))
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>

Resources