Filtering data based on input D3 - javascript

I'm trying to filter down data to one value based on input. If the user enters a value, I want it to be able to display only that value on the graph. However, it's not working. I believe the code is in all the right places, just the logic isn't there.
If I remove the functionallity that attempts this, everything is fine - meaning that if I click on A, only data with type A is returned, and the same with B. But this doesn't work when I implement my code that tries to fitler down to one value.
Question:
How can I filter down to one value along the x axis based on user input, but still retain the funcationality that allows the user to switch between data types.
$(document).ready(function() {
$("#input").attr("value", 0);
});
const margin = {
top: 10,
right: 30,
bottom: 70,
left: 65
},
width = 520,
height = 480;
function SVGmaker(target) {
const svg = d3.selectAll(target)
.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.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", height)
.attr("width", width)
.attr("fill", "white")
return svg;
}
const svgOne = SVGmaker("#svg");
const data = [{
x: "1",
y: "5",
type: "A"
},
{
x: "2",
y: "4",
type: "B"
},
{
x: "3",
y: "3",
type: "B"
},
{
x: "4",
y: "2",
type: "A"
},
{
x: "5",
y: "1",
type: "A"
}
]
let x = d3.scaleLinear()
.domain([1, 5])
.range([0, width]);
// DC SVG Settings
const xAxis = d3.axisBottom(x);
svgOne.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
const yAxis = d3.scaleLinear()
.domain([0, 6])
.range([height, 0])
svgOne.append("g")
.call(d3.axisLeft(yAxis));
const colors = d3.scaleOrdinal()
.domain(["A", "B"])
.range(["red", "blue"]);
function updateData(data, type, theSVG, xAxis, yAxis, colors, value) {
// Filters through the data
const changeData = data.filter(function(d) {
if (type === "A") {
return d.type === "A"
} else if (type === "B") {
return d.type === "B"
} else {
return true;
}
});
var min = d3.min(data, function(d) {
return d.x;
});
const gra = theSVG
.selectAll("circle")
.data(changeData);
gra.enter()
.append("circle")
.filter(function(d) {
if (value <= min) {
return d.x
} else {
return d.x >= value && d.x <= value
}
})
.attr("cx", function(d) {
return xAxis(d.x);
})
.attr("cy", function(d) {
return yAxis(d.y);
})
.style("fill", function(d) {
return colors(d.type)
})
.merge(gra)
.attr("r", 11)
.filter(function(d) {
if (value <= min) {
return d.x
} else {
return d.x >= value && d.x <= value
}
})
.attr("cx", function(d) {
return xAxis(d.x);
})
.attr("cy", function(d) {
return yAxis(d.y);
})
.style("fill", function(d) {
return colors(d.type)
})
gra.exit()
.remove();
}
updateData(data, "", svgOne, x, yAxis, colors, 0);
let value;
function getMessage() {
value = document.getElementById("#input").value;
}
d3.select("#a")
.on("click", function() {
updateData(data, "A", svgOne, x, yAxis, colors, value);
});
d3.select("#b")
.on("click", function() {
updateData(data, "B", svgOne, x, yAxis, colors, value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<div id="svg"></div>
<button id="a">a</button>
<button id="b">b</button>
<input type="number" id="input" value=0>
<button id="submit">submit</button>
<script type="text/javascript" src="scripts.js"></script>

I was a bit guessing what you want to do, but really the problem was that you never set'value' variable, and thus you were always passing undefined.
When you call getElementById, do not include the #.
Generally if you're not generating the tag/element with d3 or js, I'd personally attach the click listener with onclick attribute straight inside html instead of d3.on.
$(document).ready(function() {
$("#input").attr("value", 0);
});
const margin = {
top: 10,
right: 30,
bottom: 70,
left: 65
},
width = 520,
height = 480;
function SVGmaker(target) {
const svg = d3.selectAll(target)
.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.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", height)
.attr("width", width)
.attr("fill", "white")
return svg;
}
const svgOne = SVGmaker("#svg");
const data = [{
x: "1",
y: "5",
type: "A"
},
{
x: "2",
y: "4",
type: "B"
},
{
x: "3",
y: "3",
type: "B"
},
{
x: "4",
y: "2",
type: "A"
},
{
x: "5",
y: "1",
type: "A"
}
]
let x = d3.scaleLinear()
.domain([1, 5])
.range([0, width]);
// DC SVG Settings
const xAxis = d3.axisBottom(x);
svgOne.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
const yAxis = d3.scaleLinear()
.domain([0, 6])
.range([height, 0])
svgOne.append("g")
.call(d3.axisLeft(yAxis));
const colors = d3.scaleOrdinal()
.domain(["A", "B"])
.range(["red", "blue"]);
function updateData(data, type, theSVG, xAxis, yAxis, colors, value) {
// Filters through the data
var min = d3.min(data, function(d) {
return d.x;
});
const changeData = data
.filter(function(d) {
if (value >= min) {
return d.x == value;
} else {
return true;
}
})
.filter(function(d) {
if (type === "A") {
return d.type === "A"
} else if (type === "B") {
return d.type === "B"
} else {
return true;
}
});
const gra = theSVG
.selectAll("circle")
.data(changeData);
gra.enter()
.append("circle")
.attr("r", 11)
.style("fill", function(d) {
return colors(d.type)
})
.merge(gra)
.attr("cx", function(d) {
return xAxis(d.x);
})
.attr("cy", function(d) {
return yAxis(d.y);
})
.style("fill", function(d) {
return colors(d.type)
})
gra.exit()
.remove();
}
updateData(data, "", svgOne, x, yAxis, colors, 0);
d3.select("#submit")
.on("click", function() {
var value = document.getElementById("input").value;
updateData(data, "", svgOne, x, yAxis, colors, value);
});
d3.select("#a")
.on("click", function() {
updateData(data, "A", svgOne, x, yAxis, colors, 0);
});
d3.select("#b")
.on("click", function() {
updateData(data, "B", svgOne, x, yAxis, colors, 0);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<div id="svg"></div>
<button id="a">a</button>
<button id="b">b</button>
<input type="number" id="input" value=0>
<button id="submit">submit</button>
<script type="text/javascript" src="scripts.js"></script>

Related

Multiline chart x-axis ticks not aligned with data points

I am trying to draw a multi line chart with D3.js. The chart is drawn properly but the x-axis ticks are not aligned correctly with the data points circle.
Find the example code here: https://jsfiddle.net/gopal31795/qsLd7pc5/9/
I assume that there is some error in the code for creating the dots.
// Creating Dots on line
segment.selectAll("dot")
.data(function(d) {
return d.linedata;
})
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
//return x(parseDate(new Date(d.mins))) + x.rangeBand() / 2;
return x(d.mins);
})
.attr("cy", function(d) {
return y(d.value);
})
.style("stroke", "white")
.style("fill", function(d) {
return color(this.parentNode.__data__.name);
})
.on("mouseenter", function(d) {
d3.select(this).transition().style("opacity", "0.25");
tooltip.html("<span style='color:" + color(this.parentNode.__data__.name) + ";'>" + this.parentNode.__data__.name + "</span>: " + (d.value + "s")).style("visibility", "visible").style("top", (event.pageY + 10) + "px").style("left", (event.pageX) + "px");
})
.on("mouseleave", function(d) {
d3.select(this).transition().style("opacity", "1");
tooltip.style("visibility", "hidden");
});
The problem in your code (which uses the old v3) is that you're using rangeRoundBands. That would be the correct choice if you had a bar chart, for instance.
However, since you're dealing with data points, you should use rangePoints or rangeRoundPoints, which:
Sets the output range from the specified continuous interval. The array interval contains two elements representing the minimum and maximum numeric value. This interval is subdivided into n evenly-spaced points, where n is the number of (unique) values in the input domain.
So, it should be:
var x = d3.scale.ordinal()
.rangeRoundPoints([0, width]);
Here is your code with that change:
var Data = [{
"name": "R",
"linedata": [{
"mins": 0,
"value": 1120
},
{
"mins": 2,
"value": 1040
},
{
"mins": 4,
"value": 1400
},
{
"mins": 6,
"value": 1500
}
]
},
{
"name": "E",
"linedata": [{
"mins": 0,
"value": 1220
},
{
"mins": 2,
"value": 1500
},
{
"mins": 4,
"value": 1610
},
{
"mins": 6,
"value": 1700
}
]
}
];
var margin = {
top: 20,
right: 90,
bottom: 35,
left: 90
},
width = $("#lineChart").width() - margin.left - margin.right,
height = $("#lineChart").width() * 0.3745 - margin.top - margin.bottom;
//var parseDate = d3.time.format("%d-%b");
var x = d3.scale.ordinal()
.rangeRoundPoints([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var xData = Data[0].linedata.map(function(d) {
return d.mins;
});
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) {
return x(d.mins);
})
.y(function(d) {
return y(d.value);
});
function transition(path) {
path.transition()
.duration(4000)
.attrTween("stroke-dasharray", tweenDash);
}
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
var svg = d3.select("#lineChart").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 + ")");
color.domain(Data.map(function(d) {
return d.name;
}));
x.domain(xData);
var valueMax = d3.max(Data, function(r) {
return d3.max(r.linedata, function(d) {
return d.value;
})
});
var valueMin = d3.min(Data, function(r) {
return d3.min(r.linedata, function(d) {
return d.value;
})
});
y.domain([valueMin, valueMax]);
//Drawing X Axis
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "xAxisText")
.attr("x", width)
.attr("dy", "-.41em")
.style("text-anchor", "end")
.style("fill", "white")
.text("Time(m)");
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")
.style("fill", "none")
.style("stroke", "white")
.style("stroke-width", 0.8)
.text("Duration(s)");
// Drawing Lines for each segments
var segment = svg.selectAll(".segment")
.data(Data)
.enter().append("g")
.attr("class", "segment");
segment.append("path")
.attr("class", "line")
.attr("id", function(d) {
return d.name;
})
.attr("visible", 1)
.call(transition)
//.delay(750)
//.duration(1000)
//.ease('linear')
.attr("d", function(d) {
return line(d.linedata);
})
.style("stroke", function(d) {
return color(d.name);
});
// Creating Dots on line
segment.selectAll("dot")
.data(function(d) {
return d.linedata;
})
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
//return x(parseDate(new Date(d.mins))) + x.rangeBand() / 2;
return x(d.mins);
})
.attr("cy", function(d) {
return y(d.value);
})
.style("stroke", "white")
.style("fill", function(d) {
return color(this.parentNode.__data__.name);
})
.on("mouseenter", function(d) {
d3.select(this).transition().style("opacity", "0.25");
tooltip.html("<span style='color:" + color(this.parentNode.__data__.name) + ";'>" + this.parentNode.__data__.name + "</span>: " + (d.value + "s")).style("visibility", "visible").style("top", (event.pageY + 10) + "px").style("left", (event.pageX) + "px");
})
.on("mouseleave", function(d) {
d3.select(this).transition().style("opacity", "1");
tooltip.style("visibility", "hidden");
});
path {
fill: none;
stroke: black;
}
line {
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div class="card-body" id="lineChart">
</div>
Finally, as a tip: don't mix D3 and jQuery.

D3.js word cloud does not display without error message

I'm trying to make word cloud with d3.js, and when I try to call page the word cloud does not displayed.
However, there is no error message in console so I can't figure out which part is wrong.
The data set looks like this.
[{word: "happy", freq: 3}, {word: "apple", freq: 4}]
This is my code.
<div id="cloud"></div>
<style>
text:hover {
stroke: black;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://rawgit.com/emeeks/3361332/raw/61cf57523fe8cf314333e5f60cc266351fec2017/d3.layout.cloud.js"></script>
<script type="text/javascript">
var weight = 3,
width = 960,
height = 500;
var fill = d3.scale.category20();
var data = {{ data|js }};
var result = scale(data);
function scale (data) {
var result = [];
for (var k in data){
var value = data[k];
result.push({word:value['word'], weight:+value['freq'] * weight});
}
return result;
}
d3.layout.cloud().size([width, height]).words(result)
.rotate(0)
.font("Impact")
.fontSize(function(data) { return data.size; })
.on("end", draw)
.start();
function draw(words) {
d3.select("#cloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(data) { return data.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(data, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(data) {
return "translate(" + [data.x, data.y] + ")rotate(" + data.rotate + ")";
})
.text(function(data) { return data.text; });
}
I noticed two issues in your code.
1) The freq property is missing in the data set which is currently used to calculate the weight of each node. Since font size depends on the weight attribute, it becomes 0.
2) The result array contains objects having key name as word. So either you should override the text method of the cloud layout as shown below OR update the key name to text.
d3.layout.cloud()
..............
..............
.text(function(d) {
return d.word;
})
var weight = 3,
width = 960,
height = 500;
var fill = d3.scale.category20();
var data = [{
word: "happy",
weight: 10,
"freq": 8
}, {
word: "apple",
weight: 4,
"freq": 3
}, {
word: "wishes",
weight: 6,
"freq": 5
}, {
word: "sad",
weight: 5,
"freq": 2
}, {
word: "orange",
weight: 21,
"freq": 3
}, {
word: "condolence",
weight: 3,
"freq": 2
}];
var result = scale(data);
function scale(data) {
var result = [];
for (var k in data) {
var value = data[k];
result.push({
word: value['word'],
weight: +value['freq'] * weight
});
}
return result;
}
//console.log(result);
d3.layout.cloud().size([width, height]).words(result)
.rotate(0)
.font("Impact")
.text(function(d) {
return d.word;
})
.fontSize(function(data) {
return data.weight;
})
.on("end", draw)
.start();
function draw(words) {
d3.select("#cloud").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(data) {
return data.size + "px";
})
.style("font-family", "Impact")
.style("fill", function(data, i) {
return fill(i);
})
.attr("text-anchor", "middle")
.attr("transform", function(data) {
return "translate(" + [data.x, data.y] + ")rotate(" + data.rotate + ")";
})
.text(function(data) {
return data.text;
});
}
text:hover {
stroke: black;
}
<div id="cloud"></div>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://rawgit.com/emeeks/3361332/raw/61cf57523fe8cf314333e5f60cc266351fec2017/d3.layout.cloud.js"></script>

how to create dots(scatterplot) on a line in a d3 line chart

my code is working fine, but there is a issue with the first dot on the line. first dot always get the y=2 and x=1 position, but other dots are placed correctly. please help me to place the first dot in correct place.
graph:-
JSON data for the graph:-
var data = [{
"label": "Execution: 6 - defadmin#gmail.com",
"x": [1, 2, 3, 4, 5, 6],
"y": [2, 1, 1, 1, 1, 1],
"xAxisDisplayData": ["1", "2", "3", "4", "5", "6"]
}];
here is my code regarding the dot creation,
// Set the ranges
var x = d3.time.scale().range([0, innerwidth]);
var y = d3.scale.linear().range([innerheight, 0]);
// Scale the range of the data
x.domain(d3.extent(datasets[0]['x'], function (d, i) {
return datasets[0]['x'][i];
}));
y.domain([1, d3.max(datasets[0]['y'], function (d, i) {
return datasets[0]['y'][i];
})]);
// Add the scatterplot
svg.selectAll("dot")
.data(datasets[0]['x'])
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function (d, i) {
return x(datasets[0]['x'][i]);
})
.attr("cy", function (d, i) {
return y(datasets[0]['y'][i]);
});
UPDATE 1: full code
function createLineChart(data, number) {
// var data = [ { label: "Execution 1 - buddhika#gmail.com",
// x: [1,2,3,4,5,6],
// y: [2,1,1,1,1,1] }] ;
var widthForSVG;
var widthForChart;
if ((data[0]['x']).length < 13) {
widthForSVG = 1220;
widthForChart = 960;
} else {
widthForSVG = (((data[0]['x']).length - 12) * 80) + 1220;
widthForChart = (((data[0]['x']).length - 12) * 80) + 960;
}
var xy_chart = d3_xy_chart()
.width(widthForChart)
.height(500)
.xlabel("TCS")
.ylabel("STATUS");
// creating main svg
var svg = d3.select(".lineChartDiv" + number).append("svg")
.datum(data)
.call(xy_chart)
.attr("class", "lineChart" + number)
.attr('width', widthForSVG);
function d3_xy_chart() {
//1220px for 12 steps in svg
var width = widthForChart,
height = 480,
xlabel = "X Axis Label",
ylabel = "Y Axis Label";
function chart(selection, svg) {
var numberNUmber = 0;
selection.each(function (datasets) {
//
// Create the plot.
//
var margin = {top: 20, right: 80, bottom: 30, left: 50},
innerwidth = width - margin.left - margin.right,
innerheight = height - margin.top - margin.bottom;
// Set the ranges
var x_scale = d3.scale.linear()
.range([0, innerwidth])
.domain([d3.min(datasets, function (d) {
return d3.min(d.x);
}),
d3.max(datasets, function (d) {
return d3.max(d.x);
})]);
var y_scale = d3.scale.linear()
.range([innerheight, 0])
.domain([d3.min(datasets, function (d) {
return 1;
}),
d3.max(datasets, function (d) {
// d3.max(d.y)
return 3;
})]);
var color_scale = d3.scale.category10()
.domain(d3.range(datasets.length));
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickFormat(function (d, i) {
if (d % 1 == 0) {
return parseInt(datasets[0]['xAxisDisplayData'][i])
} else {
return " "
}
})
.ticks(d3.max(datasets, function (d) {
return d3.max(d.x);
}));
var y_axis = d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(d3.max(datasets, function (d) {
return d3.max(d.y);
}))
.tickFormat(function (d, i) {
if (d == "1") {
return "NOT EXECUTED"
} else if (d == "2") {
return "FAILED"
} else if (d == "3") {
return "PASSED"
} else {
return " "
}
});
var x_grid = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickSize(-innerheight)
.ticks(d3.max(datasets, function (d) {
// d3.max(d.y)
return d3.max(d.x);
}))
.tickFormat("");
var y_grid = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickSize(-innerwidth)
.tickFormat("")
.ticks(d3.max(datasets, function (d) {
return d3.max(d.y);
}));
var draw_line = d3.svg.line()
.interpolate("linear")
.x(function (d) {
return x_scale(d[0]);
})
.y(function (d) {
return y_scale(d[1]);
});
var svg = d3.select(this)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 90 + "," + margin.top + ")");
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_grid);
svg.append("g")
.attr("class", "y grid")
.call(y_grid);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_axis)
.append("text")
.attr("dy", "-.71em")
.attr("x", innerwidth)
.style("text-anchor", "end")
.text(xlabel);
svg.append("g")
.attr("class", "y axis")
.call(y_axis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text(ylabel);
var data_lines = svg.selectAll(".d3_xy_chart_line")
.data(datasets.map(function (d) {
return d3.zip(d.x, d.y);
}))
.enter().append("g")
.attr("class", "d3_xy_chart_line");
data_lines.append("path")
.attr("class", "line")
.attr("d", function (d) {
return draw_line(d);
})
.attr("stroke", function (_, i) {
return color_scale(i);
});
data_lines.append("text")
.datum(function (d, i) {
return {name: datasets[i].label, final: d[d.length - 1]};
})
.attr("transform", function (d) {
return ( "translate(" + x_scale(d.final[0]) + "," +
y_scale(d.final[1]) + ")" );
})
.attr("x", 3)
.attr("dy", ".35em")
.attr("fill", function (_, i) {
return color_scale(i);
})
.text(function (d) {
return d.name;
});
// Set the ranges
var x = d3.time.scale().range([0, innerwidth]);
var y = d3.scale.linear().range([innerheight, 0]);
// Scale the range of the data
x.domain(d3.extent(datasets[0]['x']));
y.domain([1, d3.max(datasets[0]['y'])]);
svg.selectAll("dot")
.data(d3.zip(datasets[0].x, datasets[0].y))
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function (d) {
return x(d[0]);
})
.attr("cy", function (d) {
return y(d[1]);
});
});
}
chart.width = function (value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function (value) {
if (!arguments.length) return height;
height = value;
return chart;
};
chart.xlabel = function (value) {
if (!arguments.length) return xlabel;
xlabel = value;
return chart;
};
chart.ylabel = function (value) {
if (!arguments.length) return ylabel;
ylabel = value;
return chart;
};
return chart;
}
}
UPDATE 2:
html view of created circles-(check the first circle, it always has cx=0 and cy=0 cordinates.other circles are fine)
UPDATE 3: feddle
feddle
Your use of d3.extent() as well as d3.max() is flawed. The functions provided to these methods are just accessors; there is no parameter i for an actual iteration. They are meant as a means to actually access the relevant data of the array, which was passed in as the first parameter. Because you are already passing in the flat data arrays, both accessor function can be reduced to function (d) { return d; }. These might further be omitted because this is the default behavior. Your domain setup thus becomes:
// Scale the range of the data
x.domain(d3.extent(datasets[0]['x']));
y.domain([1, d3.max(datasets[0]['y'])]);
Personally, I would also rewrite your data binding logic to improve readability:
// Add the scatterplot
svg.selectAll("dot")
.data(d3.zip(datasets[0].x, datasets[0].y))
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function (d) {
return x(d[0]);
})
.attr("cy", function (d) {
return y(d[1]);
});
Instead of doing the cumbersome deep access into the datasets array each time you need those values, this uses d3.zip() to build a new array of arrays containing the points' coordinates, which is then bound to the selection. As you can see, this leaves you with clean code for setting your cx and cy attribute values.
Besides these technical shortcomings there is a logical glitch in setting up your y scale's domain—as indicated by Andrew's comment—, where you are doing
y.domain([1, d3.max(datasets[0]['y'])]);
In the dataset you provided the maximum value for y is 2, though. This way your domain will be set to [1, 2] leaving out the 3. With this domain the point will consequently be drawn at the origin. Because your y values are categories, this is obviously not what you want. To always draw the full range of categories, you could use static values to set up your scale's domain:
y.domain([1, 3]);
You are already doing it this way—in a rather awkward way, though—for your other scale y_scale, which is why the line is drawn correctly.
Of course, you could also decide to only draw the categories contained in the dataset, in which case you would keep the d3.max() in the domain, but you will then have to also do the same for your y_scale's domain.
Have a look at the following snippet for a working example. It contains the code from your JSFiddle having just the one line changed, where the y scale's domain is set up.
var data = [{
"label": "Execution: 6 - defadmin#gmail.com",
"x": [1, 2, 3, 4, 5, 6],
"y": [2, 1, 1, 1, 1, 1],
"xAxisDisplayData": ["1", "2", "3", "4", "5", "6"]
}];
var number = 1;
var widthForSVG;
var widthForChart;
if ((data[0]['x']).length < 13) {
widthForSVG = 1220;
widthForChart = 960;
} else {
widthForSVG = (((data[0]['x']).length - 12) * 80) + 1220;
widthForChart = (((data[0]['x']).length - 12) * 80) + 960;
}
var xy_chart = d3_xy_chart()
.width(widthForChart)
.height(500)
.xlabel("TCS")
.ylabel("STATUS");
// creating main svg
var svg = d3.select(".lineChartDiv1").append("svg")
.datum(data)
.call(xy_chart)
.attr("class", "lineChartDiv1")
.attr('width', widthForSVG);
function d3_xy_chart() {
var width = widthForChart,
height = 480,
xlabel = "X Axis Label",
ylabel = "Y Axis Label";
function chart(selection, svg) {
var numberNUmber = 0;
selection.each(function(datasets) {
//
// Create the plot.
//
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
innerwidth = width - margin.left - margin.right,
innerheight = height - margin.top - margin.bottom;
// Set the ranges
var x_scale = d3.scale.linear()
.range([0, innerwidth])
.domain([d3.min(datasets, function(d) {
return d3.min(d.x);
}),
d3.max(datasets, function(d) {
return d3.max(d.x);
})
]);
var y_scale = d3.scale.linear()
.range([innerheight, 0])
.domain([d3.min(datasets, function(d) {
return 1;
}),
d3.max(datasets, function(d) {
// d3.max(d.y)
return 3;
})
]);
var color_scale = d3.scale.category10()
.domain(d3.range(datasets.length));
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickFormat(function(d, i) {
if (d % 1 == 0) {
return parseInt(datasets[0]['xAxisDisplayData'][i])
} else {
return " "
}
})
.ticks(d3.max(datasets, function(d) {
return d3.max(d.x);
}));
var y_axis = d3.svg.axis()
.scale(y_scale)
.orient("left")
.ticks(d3.max(datasets, function(d) {
return d3.max(d.y);
}))
.tickFormat(function(d, i) {
if (d == "1") {
return "NOT EXECUTED"
} else if (d == "2") {
return "FAILED"
} else if (d == "3") {
return "PASSED"
} else {
return " "
}
});
var x_grid = d3.svg.axis()
.scale(x_scale)
.orient("bottom")
.tickSize(-innerheight)
.ticks(d3.max(datasets, function(d) {
// d3.max(d.y)
return d3.max(d.x);
}))
.tickFormat("");
var y_grid = d3.svg.axis()
.scale(y_scale)
.orient("left")
.tickSize(-innerwidth)
.tickFormat("")
.ticks(d3.max(datasets, function(d) {
return d3.max(d.y);
}));
var draw_line = d3.svg.line()
.interpolate("linear")
.x(function(d) {
return x_scale(d[0]);
})
.y(function(d) {
return y_scale(d[1]);
});
var svg = d3.select(this)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 90 + "," + margin.top + ")");
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_grid);
svg.append("g")
.attr("class", "y grid")
.call(y_grid);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerheight + ")")
.call(x_axis)
.append("text")
.attr("dy", "-.71em")
.attr("x", innerwidth)
.style("text-anchor", "end")
.text(xlabel);
svg.append("g")
.attr("class", "y axis")
.call(y_axis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.style("text-anchor", "end")
.text(ylabel);
var data_lines = svg.selectAll(".d3_xy_chart_line")
.data(datasets.map(function(d) {
return d3.zip(d.x, d.y);
}))
.enter().append("g")
.attr("class", "d3_xy_chart_line");
data_lines.append("path")
.attr("class", "line")
.attr("d", function(d) {
return draw_line(d);
})
.attr("stroke", function(_, i) {
return color_scale(i);
});
data_lines.append("text")
.datum(function(d, i) {
return {
name: datasets[i].label,
final: d[d.length - 1]
};
})
.attr("transform", function(d) {
return ("translate(" + x_scale(d.final[0]) + "," +
y_scale(d.final[1]) + ")");
})
.attr("x", 3)
.attr("dy", ".35em")
.attr("fill", function(_, i) {
return color_scale(i);
})
.text(function(d) {
return d.name;
});
// Set the ranges
var x = d3.time.scale().range([0, innerwidth]);
var y = d3.scale.linear().range([innerheight, 0]);
// Scale the range of the data
x.domain(d3.extent(datasets[0]['x']));
y.domain([1, 3]);
// console.log(JSON.stringify(d3.extent(datasets[0]['x'])))
// console.log(JSON.stringify(d3.max(datasets[0]['y'])))
// Add the scatterplot
svg.selectAll("dot")
.data(d3.zip(datasets[0].x, datasets[0].y))
.enter().append("circle")
.attr("class", datasets[0]['label'])
.attr("r", 3.5)
.attr("cx", function(d) {
// console.log(JSON.stringify(d[0])+" XXXXXXXXXXx ")
return x(d[0]);
})
.attr("cy", function(d) {
//console.log(JSON.stringify(d[1])+" YYYYYYYyy ")
return y(d[1]);
});
});
}
chart.width = function(value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function(value) {
if (!arguments.length) return height;
height = value;
return chart;
};
chart.xlabel = function(value) {
if (!arguments.length) return xlabel;
xlabel = value;
return chart;
};
chart.ylabel = function(value) {
if (!arguments.length) return ylabel;
ylabel = value;
return chart;
};
return chart;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.grid path,
.grid line {
fill: none;
stroke: rgba(0, 0, 0, 0.25);
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke-width: 2.5px;
}
svg {
font: 10px sans-serif;
}
.area {
fill: lightgray;
clip-path: url(#clip);
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
clip-path: url(#clip);
}
rect.pane {
cursor: move;
fill: none;
pointer-events: all;
}
<script src="https://d3js.org/d3.v3.js"></script>
<div class="row">
<div class="col-sm-12">
<div class="lineChartDiv1" style=" overflow-x: scroll">
</div>
</div>
</div>
Try this,
if(data[0]['y'][1]==1){
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cx','0');
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cy','225');
}else if(data[0]['y'][1]==2){
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cx','0');
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cy','450');
}else{
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cx','0');
document.getElementsByClassName(data[0]['label'])[0].setAttribute('cy','0');
}
Hey I (Buddhika) improved your code below way, other wise it is useless :
var element = document.getElementsByClassName(data[0]['label']);
for (var i =0; i<element.length; i++){
if(data[0]['y'][0]==1 & data[0]['x'][i]==0){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','450');
}else if(data[0]['y'][0]==2 & data[0]['x'][i]==0){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','225');
}else if(data[0]['y'][0]==3 & data[0]['x'][i]==0){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','0');
}else if(data[0]['y'][i]==1){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','450');
}else if(data[0]['y'][i]==2){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','225');
}else if(data[0]['y'][i]==3){
document.getElementsByClassName(data[0]['label'])[i].setAttribute('cy','0');
}
}

D3 chart missing data

I have a d3 line graph which works perfectly. It maps the number of telephone calls and emails received in the current month or current year. However, the only issue is that if phone calls were only received on one day of the month, then only a dot appears at that value. What i would like to happen is that for every day there is not a value then the value is treated as 0. I have had a look around google but i can only find examples of going the other way. Is anybody able to help?
My code as it stands is:
function buildCommunicationLineChart(data, placeholder, callback, type) {
var margin = { top: 20, right: 30, bottom: 40, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
tooltipTextColour = "white";
var color = ["#FF9797", "#86BCFF", "#33FDC0", "#EFA9FE", "#7BCAE1", "#8C8CFF", "#80B584", "#C88E8E", "#DD597D", "#D8F0F8", "#DD597D", "#D6C485", "#990099", "#5B5BFF", "#1FCB4A", "#000000", "#00BFFF", "#BE81F7", "#BDBDBD", "#F79F81"];
if (type == "month") {
var x = d3.scale.linear()
.domain([1, 31])
.range([0, width]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Value:</strong> <span style='color:" + tooltipTextColour + "'>" + d.Value + "</span><br /><strong>Day of Month:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
else if (type == "year")
{
var x = d3.scale.linear()
.domain([1, 12])
.range([0, width]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Value:</strong> <span style='color:" + tooltipTextColour + "'>" + d.Value + "</span><br /><strong>Month of Year:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
var y = d3.scale.linear()
.domain([0, 60])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(true)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(true)
.orient("left");
var line = d3.svg.line()
.x(function (d) { var xTest = x(d.xValue); return x(d.xValue); })
.y(function (d) { var yTest = y(d.Value); return y(d.Value); });
var svg = placeholder.append("svg")
.attr("width", width + margin.left + margin.right + 50)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "chart")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
if (type == "year") {
svg.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "none")
.attr("y", (-margin.left) + 530)
.attr("x", -height + 860)
.text('Month');
}
else if (type == "month") {
svg.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "none")
.attr("y", (-margin.left) + 525)
.attr("x", -height + 860)
.text('Day');
}
var methods = d3.entries(data);
y.domain([
d3.min(methods, function (c) { return d3.min(c.value.DataPoints, function (v) { return v.Value; }); }) -1,
d3.max(methods, function (c) { return d3.max(c.value.DataPoints, function (v) { return v.Value; }); }) +1
]);
svg.call(tip);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 15)
.attr("x", -height / 2)
.text('Communications');
var method = svg.selectAll('.method')
.data(methods)
.enter().append('g')
.attr('class', 'method')
.style('fill',function(d,i){
return color[i];
})
.style('stroke', function (d, i) {
return color[i];
});
var m = method.append('path')
.attr('class', function (d, i) { return 'line line-' + i; })
.attr('d', function (d) { return line(d.value.DataPoints); });
method.selectAll('circle')
.data(function (d) { return d.value.DataPoints; })
.enter().append("circle")
.attr('class', function (d, i) { return 'circle circle-' + i; })
.attr("cx", function (dd) { return x(dd.xValue); })
.attr("cy", function (dd) { return y(dd.Value); })
.attr("r", 3.5)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
method.append('text')
.datum(function (d) { return { commType: d.value.Type, value: d.value.DataPoints[d.value.DataPoints.length - 1] }; })
.attr("transform", function (d) { return "translate(" + x(d.value.xValue) + "," + y(d.value.Value) + ")"; })
.attr('x', 5)
.attr('class',function(d,i){return 'text text-'+i;})
.attr('dy', '.15em')
.style('stroke','none')
.text(function (d) { return d.commType; });
if (callback) {
callback();
}
}
Many Thanks :)
EDIT
xVal is the day number of month number:
[{
"Type": "Email",
"DataPoints": [{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
I will assume some points:
You are receiving from your data source year's array of month's arrays (if you are receiving month by month you will have to make a Get request for each month, it's pretty easy, but I hope you have a route giving you data on year, would not be cool to do 12 http get for this result) data like:
[
{
month: "January",
data: [{
"Type": "Email",
"DataPoints":
[{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
}
},{
month: "February",
data: [...]
},
...
]
I will do that client side in Js:
Assuming each month is 31 days, i'm too lazy to think about doing it exactly but it shoudn't be that difficult, and xValue is the day of the month.
function completeMonths(data){
_.each(data, function(month){
_.each(month.data, function(typeSource){
for(var i = 1; i < 32; i++){
var dayOnList = _.some(typeSource.DataPoints, function(day){
return day.xValue == i;
});
if(!dayOnList)
typeSource.DataPoints.push({xValue: i, Value: 0});
}
typeSource.DataPoints = _.sortBy(typeSource.DataPoints, function(ite){
return ite.xValue;
});
});
});
return data;
}
Probably not the more optimized code, but I've tried it and it's working.
Actually I think it would be better to map the month's days present, and reject them from and 1 to 31 array.
Btw your Json isn't well formatted, you're missing a closing "]".
With JSON structured like:
[{
"Type": "Email",
"DataPoints": [{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
And assuming your DataPoints array is sorted by xValue and 31 days in the month:
var maxDaysInMonth = 31;
// for each dataset
dataset.forEach(function(d){
// loop our month
for (var i = 1; i <= maxDaysInMonth; i++){
// if there's no xValue at that location
if (!d.DataPoints.some(function(v){ return (v.xValue === i) })){
// add a zero in place
d.values.splice((i - 1), 0, {xValue: i, Value: 0});
}
}
});

How to update and draw stacked bar chart with new dataset in D3

New here. I'm working with D3 and basically I have 2 datasets in the form of arrays. What I want to achieve is upon button click, the new dataset overwrites the old one (I have achieved this much) and then the new dataset is bound and redraws the stacked bar charts. This doesn't happen for me. When the button is pressed it just deletes a couple of the bars.
Would appreciate any tips. I think it's awkward because I'm working with stacked bar charts and not normal ones.
Thanks! :)
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
// .ticks(5);
//Width and height
var w = 600;
var h = 300;
var barPadding = 50;
//Original data
var dataset = [
[
{ y: 20 }, //male
{ y: 4 },
{ y: 16},
{ y: 53},
{ y: 15 }
],
[
{ y: 12 }, //female
{ y: 4 },
{ y: 3 },
{ y: 36 },
{ y: 2 }
],
];
console.log(dataset);
// var myDataSet = dataset;
// var totalDeaths = d.y0 + d.y1;
//Set up stack method
var stack = d3.layout.stack();
//Data, stacked
stack(dataset);
//Set up scales
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset[0].length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0,
d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})
])
.range([0, h]);
//Easy colors accessible via a 10-step ordinal scale
// var colors = d3.scale.category20c();
var color = d3.scale.ordinal()
.domain(["Male", "Female"])
.range(["#00B2EE", "#FF69B4"]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Add a group for each row of data
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.style("fill", function(d, i) {
return color(i);
});
// Add a rect for each data value
var rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + xScale.rangeBand() / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) + 14;
//Create the tooltip label
svg.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("font-weight", "bold")
.attr("fill", "black")
.html("Female deaths: " + d.y + "\n" + " \nMale deaths: " + d.y0);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").remove();
})
.attr("width", xScale.rangeBand())
.attr("y", function(d) {
return h - yScale(d.y0) - yScale(d.y) ;
})
.attr("height", function(d) {
return yScale(d.y);
});
d3.select("#target")
.on("click", function() { //event listener on button click
// alert("heeeey");
//New values for dataset
dataset = [
[
{ y: 100 }, //male
{ y: 20 },
{ y: 16},
{ y: 53},
{ y: 15 }
],
[
{ y: 5 }, //female
{ y: 4 },
{ y: 3 },
{ y: 36 },
{ y: 2 }
],
];
console.log(dataset);
//Data, stacked
// stack(dataset);
//Update all rects
var gas = svg.selectAll("rect")
.data(dataset)
// .transition()
// .duration(1000)
// .ease("cubic-in-out")
.attr("width", xScale.rangeBand())
.attr("y", function(d) {
return h - yScale(d.y0) - yScale(d.y) ;
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("x", function(d, i) {
return xScale(i);
});
});

Categories