d3js: Hightlight bubble with specific parameters coming from input in bubble chart - javascript

I have a d3 script which has data in the following format:
var data = [{name: "A", rank: 0, student_percentile: 100.0,
admit_probability: 24},
{name: "B", rank: 45, student_percentile: 40.3,
admit_probability: 24},
{name: "C", rank: 89, student_percentile: 89.7,
admit_probability: 24},
{name: "D", rank: 23, student_percentile: 10.9,
admit_probability: 24},
{name: "E", rank: 56, student_percentile: 30.3,
admit_probability: 24}];
Initially when the page loads, I make a bubble chart with this data. After that, user can give input (from A to E). The x-axis of graph is made from _student_percentile_ and y-axis from rank. After the user input comes in, I want to highlight the bubble with this name(I have both rank and _student_percentile_ of the input that user gives)
Now, I don't understand, how do I filter the circle with cx=xscale(student_percentile), cy=yscale(rank), with student_percentile and rank received from inputs.
The script that I have is as follows:
var svg;
var margin = 40,
width = 600,
height = 400;
xscale = d3.scaleLinear()
.domain(
d3.extent(data, function(d) { return +d.student_percentile; })
)
.nice()
.range([0, width]);
yscale = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return +d.rank; }))
.nice()
.range([height, 0]);
var xAxis = d3.axisBottom().scale(xscale);
var yAxis = d3.axisLeft().scale(yscale);
svg = d3.select('.chart')
.classed("svg-container", true)
.append('svg')
.attr('class', 'chart')
.attr("viewBox", "0 0 680 490")
.attr("preserveAspectRatio", "xMinYMin meet")
.classed("svg-content-responsive", true)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// var legend = svg.append("g")
// .attr('class', 'legend')
var color = d3.scaleOrdinal(d3.schemeCategory10);
var local = d3.local();
circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("opacity", 0.3)
.attr("r", 20)
.style("fill", function(d){
if(+d.admit_probability <= 40){
return "red";
}
else if(+d.admit_probability > 40 && +d.admit_probability <= 70){
return "yellow";
}
else{
return "green";
}
})
.attr("cx", function(d) {
return xscale(+d.student_percentile);
})
.attr("cy", function(d) {
return yscale(+d.rank);
})
.on('mouseover', function(d, i) {
local.set(this, d3.select(this).style("fill"));
d3.select(this)
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
}
)
.on('mouseout', function(d, i) {
d3.select(this).style("fill", local.get(this));
d3.select(this).transition()
.style("opacity", 0.3)
.attr("r", 20)
.style("cursor", "default")
.transition()
.duration(1000)
.ease(d3.easeBounce)
});
texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("x", function(d) {
return xscale(+d.student_percentile);
})
.attr("text-anchor", "middle")
.attr("y", function(d) {
return yscale(+d.rank);
})
.text(function(d) {
return +d.admit_probability;
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "red");
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin) + ")")
.style("text-anchor", "middle")
.text("Percentile");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
Thanks in advance!

You can handle keyup input event and filter your circle selection with native d3.filter method. Look at working example in the hidden snippet below.
d3.select('#user-input').on('keyup', function() {
var value = d3.event.target.value;
circles.filter(function(circle) {
return circle.name === value.trim().toUpperCase();
})
.each(function() {
local.set(this, d3.select(this).style("fill"));
})
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
circles.filter(function(circle) {
return circle.name !== value.trim().toUpperCase();
})
.transition()
.attr("r", 20)
.style("cursor", "default")
.style("fill", function() { return local.get(this) || d3.select(this).style("fill"); })
.transition()
.duration(1000)
.ease(d3.easeBounce)
});
var svg;
var margin = 40,
width = 600,
height = 400;
var data = [{
name: "A",
rank: 0,
student_percentile: 100.0,
admit_probability: 24
}, {
name: "B",
rank: 45,
student_percentile: 40.3,
admit_probability: 24
}, {
name: "C",
rank: 89,
student_percentile: 89.7,
admit_probability: 24
}, {
name: "D",
rank: 23,
student_percentile: 10.9,
admit_probability: 24
}, {
name: "E",
rank: 56,
student_percentile: 30.3,
admit_probability: 24
}];
xscale = d3.scaleLinear()
.domain(
d3.extent(data, function(d) {
return +d.student_percentile;
})
)
.nice()
.range([0, width]);
yscale = d3.scaleLinear()
.domain(d3.extent(data, function(d) {
return +d.rank;
}))
.nice()
.range([height, 0]);
var xAxis = d3.axisBottom().scale(xscale);
var yAxis = d3.axisLeft().scale(yscale);
svg = d3.select('.chart')
.classed("svg-container", true)
.append('svg')
.attr('class', 'chart')
.attr("viewBox", "0 0 680 490")
.attr("preserveAspectRatio", "xMinYMin meet")
.classed("svg-content-responsive", true)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// var legend = svg.append("g")
// .attr('class', 'legend')
var color = d3.scaleOrdinal(d3.schemeCategory10);
var local = d3.local();
circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("opacity", 0.3)
.attr("r", 20)
.style("fill", function(d) {
if (+d.admit_probability <= 40) {
return "red";
} else if (+d.admit_probability > 40 && +d.admit_probability <= 70) {
return "yellow";
} else {
return "green";
}
})
.attr("cx", function(d) {
return xscale(+d.student_percentile);
})
.attr("cy", function(d) {
return yscale(+d.rank);
})
.on('mouseover', function(d, i) {
local.set(this, d3.select(this).style("fill"));
d3.select(this)
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
})
.on('mouseout', function(d, i) {
d3.select(this).transition()
.style("opacity", 0.3)
.style("fill", local.get(this))
.attr("r", 20)
.style("cursor", "default")
.transition()
.duration(1000)
.ease(d3.easeBounce)
});
texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("x", function(d) {
return xscale(+d.student_percentile);
})
.attr("text-anchor", "middle")
.attr("y", function(d) {
return yscale(+d.rank);
})
.text(function(d) {
return +d.admit_probability;
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "red");
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin) + ")")
.style("text-anchor", "middle")
.text("Percentile");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
d3.select('#user-input').on('keyup', function() {
var value = d3.event.target.value;
circles.filter(function(circle) {
return circle.name === value.trim().toUpperCase();
})
.each(function() {
local.set(this, d3.select(this).style("fill"));
})
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
circles.filter(function(circle) {
return circle.name !== value.trim().toUpperCase();
})
.transition()
.attr("r", 20)
.style("cursor", "default")
.style("fill", function() { return local.get(this) || d3.select(this).style("fill") })
.transition()
.duration(1000)
.ease(d3.easeBounce)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<div class="chart"></div>
<h2>Type "A", "B", "C", "D", or "E" in input below</h2>
<input type="text" id="user-input">

Related

D3 Multi Series Line chart not working with correct xAxis values

I have a D3 multi-series line chart in my application and it shows vehicle brands in x-axis and sales in y-axis. I have used ordinal scale for x-axis since it is strings that I'm dealing with in the x-axis. But the chart doesn't render correctly with the given values. Even though I have 4 car brands for x-axis values, it shows only two of them.
Can someone tell me what has happened here? Sample code is given below.
Fiddle : https://jsfiddle.net/yasirunilan/zn01cjbo/3/
var data = [{
name: "USA",
values: [{
date: "BMW",
price: "100"
},
{
date: "Nissan",
price: "110"
},
{
date: "Toyota",
price: "145"
},
{
date: "Bentley",
price: "241"
},
{
date: "Ford",
price: "101"
}
]
},
{
name: "UK",
values: [{
date: "BMW",
price: "130"
},
{
date: "Nissan",
price: "120"
},
{
date: "Toyota",
price: "115"
},
{
date: "Bentley",
price: "220"
},
{
date: "Ford",
price: "100"
}
]
}
];
const margin = 80;
const width = 1000 - 2 * margin;
const height = 550 - 2 * margin;
var duration = 250;
var lineOpacity = "0.25";
var lineOpacityHover = "0.85";
var otherLinesOpacityHover = "0.1";
var lineStroke = "1.5px";
var lineStrokeHover = "2.5px";
var circleOpacity = '0.85';
var circleOpacityOnLineHover = "0.25"
var circleRadius = 3;
var circleRadiusHover = 6;
/* Format Data */
var parseDate = d3.time.format("%B");
data.forEach(function(d) {
d.values.forEach(function(d) {
d.date = d.date;
d.price = +d.price;
});
});
/* Scale */
var xScale = d3.scale.ordinal()
.range([0, width], 0.4)
.domain(d3.extent(data[0].values, function(d) {
return d.date;
}));
var yScale = d3.scale.linear()
.domain([0, d3.max(data[0].values, d => d.price)])
.range([height - margin, 0]);
// var color = d3.scale.ordinal(d3.schemeCategory10);
var color = d3.scale.category10();
/* Add SVG */
var svg = d3.select("svg")
.attr("width", (width + margin) + "px")
.attr("height", (height + margin) + "px")
.append('g')
.attr("transform", `translate(${margin}, ${margin})`);
/* Add line into SVG */
var line = d3.svg.line()
.x(d => xScale(d.date))
.y(d => yScale(d.price));
let lines = svg.append('g')
.attr('class', 'lines');
lines.selectAll('.line-group')
.data(data).enter()
.append('g')
.attr('class', 'line-group')
.on("mouseover", function(d, i) {
svg.append("text")
.attr("class", "title-text")
.style("fill", color(i))
.text(d.name)
.attr("text-anchor", "middle")
.attr("x", (width - margin) / 2)
.attr("y", 5);
})
.on("mouseout", function(d) {
svg.select(".title-text").remove();
})
.append('path')
.attr('class', 'line')
.attr('d', d => line(d.values))
.style('stroke', (d, i) => color(i))
.style('opacity', lineOpacity)
.on("mouseover", function(d) {
d3.selectAll('.line')
.style('opacity', otherLinesOpacityHover);
d3.selectAll('.circle')
.style('opacity', circleOpacityOnLineHover);
d3.select(this)
.style('opacity', lineOpacityHover)
.style("stroke-width", lineStrokeHover)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.selectAll(".line")
.style('opacity', lineOpacity);
d3.selectAll('.circle')
.style('opacity', circleOpacity);
d3.select(this)
.style("stroke-width", lineStroke)
.style("cursor", "none");
});
/* Add circles in the line */
lines.selectAll("circle-group")
.data(data).enter()
.append("g")
.style("fill", (d, i) => color(i))
.selectAll("circle")
.data(d => d.values).enter()
.append("g")
.attr("class", "circle")
.on("mouseover", function(d) {
d3.select(this)
.style("cursor", "pointer")
.append("text")
.attr("class", "text")
.text(`${d.price}`)
.attr("x", d => xScale(d.date) + 5)
.attr("y", d => yScale(d.price) - 10);
})
.on("mouseout", function(d) {
d3.select(this)
.style("cursor", "none")
.transition()
.duration(duration)
.selectAll(".text").remove();
})
.append("circle")
.attr("cx", d => xScale(d.date))
.attr("cy", d => yScale(d.price))
.attr("r", circleRadius)
.style('opacity', circleOpacity)
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadiusHover);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(duration)
.attr("r", circleRadius);
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom").tickSize(1);
var yAxis = d3.svg.axis().scale(yScale)
.orient("left").tickSize(1);
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height-margin})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append('text')
.attr("y", 15)
.attr("transform", "rotate(-90)")
.attr("fill", "#000")
.attr('text-anchor', 'middle')
.text("No. of Employees");
You need to use Array.map in the domain and the rangePoints() to set the range
var xScale = d3.scale.ordinal()
.rangePoints([0, width], 0.1)
.domain(data[0].values.map(function(d) { return d.date; }));

d3js: Update data array and change graph

I have a d3 script for which the data that I have is as shown below:
var data = [{name: "A", rank: 0, student_percentile: 100.0},
{name: "B", rank: 45, student_percentile: 40.3},
{name: "C", rank: 89, student_percentile: 89.7},
{name: "D", rank: 23, student_percentile: 10.9},
{name: "E", rank: 56, student_percentile: 30.3}];
This data array has been fetched from the server.
I have a d3 script given below:
function d3Data(){
data = data.sort(function(x,y){
return d3.ascending(+x.rank, +y.rank);
});
var size = document.getElementById("range").value;
console.log(size);
data = data.slice(0,size);
d3(data);
}
function d3(data){
var margin = 40,
width = 600,
height = 400;
console.log(data);
var xscale = d3.scaleLinear()
.domain(
d3.extent(data, function(d) { return +d.student_percentile; })
)
.nice()
.range([0, width]);
var yscale = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return +d.rank; }))
.nice()
.range([height, 0]);
var xAxis = d3.axisBottom().scale(xscale);
var yAxis = d3.axisLeft().scale(yscale);
var svg = d3.select('.chart')
.append('svg')
.attr('class', 'chart')
.attr("width", width + margin + margin)
.attr("height", height + margin + margin + 10 )
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue")
.attr("cx", function(d) {
return xscale(+d.student_percentile);
})
.attr("cy", function(d) {
return yscale(+d.rank);
})
.on('mouseover', function(d, i) {
d3.select(this)
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("r", 32)
.style("fill", "orange")
.style("cursor", "pointer")
.attr("text-anchor", "middle");
texts.filter(function(e) {
return e.rank === d.rank;
})
.attr("font-size", "20px")
})
.on('mouseout', function(d, i) {
d3.select(this).transition()
.style("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue")
.style("cursor", "default");
texts.filter(function(e) {
return e.rank === d.rank;
})
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attr("font-size", "10px")
});
var texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("x", function(d) {
return xscale(+d.student_percentile);
})
.attr("text-anchor", "middle")
.attr("y", function(d) {
return yscale(+d.rank);
})
.text(function(d) {
return +d.student_percentile;
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "red");
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin) + ")")
.style("text-anchor", "middle")
.text("Percentile");
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
$('circle').tipsy({
console.log("tipsy");
gravity: 'w',
html: true,
title: function() {
var d = this.__data__;
return d.name + '<br/>' + d.rank;
}
});
}
$(document).ready(function(){
d3Data();
});
function rangeVal(){
d3Data();
}
function fadeOut() {
svg.selectAll("circle")
.transition()
.style("opacity", 0.3)
.attr("r", 20)
.style("fill", "blue");
}
function handleMouseOver() {
d3.select(this)
.attr("r", 32)
.style("fill", "orange");
}
I call the function d3Data when the document gets loaded and also when button is clicked (rangeVal is the function that is called on button click). On button click, I want to apply different filters on data and then make the graph again. Currently what is happening is I am getting multiple graphs on button click but the existing graph is not getting updated. The current output is as shown: multiple graphs
I just want d3Data() function to update original data array every time button is clicked and then make the graph again. How can I do that?
Each an every time depend upon the data SVG is newly created. So you have to remove the SVG Before Creation
//d3.select("Your Id Name or Your Class Name").select("svg").remove();
In Your Code, I changed follow as
d3.select('.chart').select("svg").remove();
var svg = d3.select('.chart')
I found the solution. I had to make two changes. In function d3data, I was updating the same array again and again, so the data was not getting updated correctly and before calling d3(), I had to remove existing graph.
function d3Data(){
data_sorted = data.sort(function(x,y){
return d3.ascending(+x.rank, +y.rank);
}); // update array and put it in another variable
var size = document.getElementById("range").value;
console.log(size);
data_sliced = data_sorted.slice(0,size);
d3.select('.chart').html(""); //this to remove existing graph
d3(data_sliced);
}

Realtime d3 bar chart with custom bar width

I want to add new bars to existing d3 bar chart and make it real time graph.
I can see the bars are getting updated but labels are not aligning themselves when the bars rescales.
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
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")
.ticks(10, "%");
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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
svg.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
function draw(data) {
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
var labels = svg
.selectAll(".bartext")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
svg.select(".y.axis").transition().duration(300).call(yAxis)
var bars = svg.selectAll(".bar").data(data, function(d) {
return d.letter;
})
bars.exit()
.transition()
.duration(300)
.remove();
bars.enter().append("rect")
.attr("class", "bar");
bars.transition().duration(300).attr("x", function(d) {
return x(d.letter);
})
.attr("width", 15)
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
}
var data1 = [{
"letter": 'A',
"frequency": .00167
}];
var data2 = [{
"letter": 'A',
"frequency": .01167
},{
"letter": 'I',
"frequency": .01477
}];
draw(data1);
setTimeout(function() {
draw(data2);
}, 2000);
https://jsfiddle.net/foh7cgst/
Here's the relevant part of the selection.enter() documentation:
var update_sel = svg.selectAll("circle").data(data)
update_sel.attr(/* operate on old elements only */)
update_sel.enter().append("circle").attr(/* operate on new elements
only */)
update_sel.attr(/* operate on old and new elements */)
update_sel.exit().remove() /* complete the enter-update-exit pattern
*/
As you can see, when you append to an enter selection, the operations that follow only target the new elements that were appended.
If you want to target both new and old elements, you should operate on the update selection after entering the nodes.
So, using your example code that is inside the draw function, this:
labels.enter().append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
Should be changed to this:
labels.enter().append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("y", height + 15);
labels
.attr("x", function(d) {
return x(d.letter) + 7.5;
})
.text(function(d) {
return d.letter;
});
Instead of this:
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
Do this:
labels
.enter()
.append("text")
.attr("class", "bartext");
//update all the bar text.
svg
.selectAll(".bartext").attr("text-anchor", "middle")
.transition().duration(300)
.attr("fill", "black")
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
In the first case it did not work, because the attributes will get updated only for new data, updated data will not get updated to the DOM.
working code here

D3 Scale domain does not update with selected data. I get negative values

I have a bar chart that updates based on the results selected in a drop-down menu. When I change the selcetion, I get negaitve "y" values. It seems that my domain does not get updated with the new data. When I hard code the domain, my "y" are what I expect them to be. Anyone knows why ? Any other other comments (formatting, etc) welcomed.
var new_data;
//Create SVG margins and patting for the interior
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
//Create Scale
var xScale = d3
.scale
.ordinal()
.rangeRoundBands([margin.left, width], .1);
;
var yScale = d3
.scale
.linear()
.range([height, 0])
;
var xAxis = d3
.svg
.axis()
.scale(xScale)
.orient("bottom")
.tickPadding([5])
;
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10)
;
//Create SVG with the above specs
var svg = d3.select("#container")
.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("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
;
svg
.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("frequency")
;
var temp = svg
.append("g")
.attr("class", "domx")
;
d3.csv("data3.csv", function(error, csv_data) {
// Filter the dataset to only get dept_1
var new_data = csv_data.filter(function(d) {
return d['dept'] == 'dept_1';
});
// function to handle histogram.
function histoGram(new_data){
//Create Scales
xScale
.domain(new_data.map(function(d) {return d.Pos;}))
;
yScale
// .domain([0, d3.max(new_data, function(d) { return d.Value; })])
.domain([0, d3.max(new_data, function(d) { return d.Value; })])
// .domain([0, 20])
;
svg
.select(".x.axis")
.transition()
.duration(1500)
.call(xAxis)
;
svg
.select(".y.axis")
.transition()
.duration(1500)
.call(yAxis)
;
// Data Join
var MyGroups = temp
.selectAll("g")
.data(new_data);
;
var MyGroupsEnter = MyGroups
.enter()
.append("g")
;
//Update
MyGroups
.attr("class", "update")
;
//Enter
MyGroupsEnter
.append("rect")
.attr("class", "enter")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("y", function(d) { return (yScale(d.Value));})
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.attr("fill", function(d) {return "rgb(0, 0, 0)";})
.style("fill-opacity", 0.2)
;
MyGroupsEnter
.append("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 10; })
;
//Enter + Update
MyGroups
.transition()
.duration(1500)
.select("rect")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("width", xScale.rangeBand())
.attr("y", function(d) { return (yScale(d.Value));})
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.style("fill-opacity", 1) // set the fill opacity
.attr("fill", function(d) {return "rgb(0, 0, " + (d.Value * 30) + ")";})
;
MyGroups
.transition()
.duration(1500)
.select("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 8; })
;
MyGroups
.exit()
.transition()
.duration(1500)
.remove()
;
}
histoGram(new_data);
var options = ["dept_1","dept_2","dept_3"];
var dropDown = d3
.select("#sel_button")
.append("select")
.attr("name", "options-list")
.attr("id", "id-name");
var options = dropDown
.selectAll("option")
.data(options)
.enter()
.append("option");
options
.text(function (d) { return d; })
.attr("value", function (d) { return d; });
d3.select("#id-name")
.on("change", function() {
var value = d3.select(this).property("value");
var new_data2 = csv_data.filter(function(d) {
return d['dept'] == value;
});
histoGram(new_data2);
});
});
Here is the data:
dept,Pos,Value
dept_1,d1_p1,1
dept_1,d1_p10,10
dept_1,d1_p11,11
dept_1,d1_p12,12
dept_2,d2_p1,1.5
dept_2,d2_p2,3
dept_2,d2_p3,4.5
dept_2,d2_p4,6
dept_2,d2_p5,7.5
dept_2,d2_p6,9
dept_2,d2_p7,10.5
dept_2,d2_p8,12
dept_2,d2_p9,13.5
dept_2,d2_p10,15
dept_2,d2_p11,16.5
dept_2,d2_p12,17.5
dept_2,d2_p13,18.5
dept_3,d3_p1,5
dept_3,d3_p2,7
dept_3,d3_p3,10
Firgured out what was my problem. I hadn't defined the format of the values. The max function was returning the maximum number out of character values (9). I added the following piece of code prior to the domain function and everything now works fines.
csv_data.forEach(function(d) {
d.dept = d.dept;
d.Pos = d.Pos;
d.Value = +d.Value;
});

D3: Creating scatter plot for multiple values

I am completely new to d3.
I have the following CSV:
HeaderAttempts HeaderGoals FreekickAttempts FreekickGoals Team
5 2 3 2 Team A
2 2 12 1 Team A
8 2 13 5 Team B
4 3 6 2 Team B
7 2 10 1 Team C
6 5 13 7 Team C
8 7 9 3 Team C
I am trying to create a scatter plot where x axis will have attempts and y axis will have the goals.
i have been able to create a scatter plot for the header attempts and goals by using the following code:
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
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 color = d3.scale.category10();
var axisNames = {
HeaderAttempts: 'HeaderAttempts',
HeaderGoals: 'HeaderGoals',
FreekickAttempts: 'FreekickAttempts',
FreekickGoals: 'FreekickGoals'
};
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
d3.csv("FootballData.csv", function(error, data) {
data.forEach(function(d) {
d.HeaderAttempts = +d.HeaderAttempts;
d.HeaderGoals = +d.HeaderGoals;
d.FreekickAttempts = +d.FreekickAttempts;
d.FreekickGoals = +d.FreekickGoals;
});
x.domain(d3.extent(data, function(d) { return d.HeaderAttempts; })).nice();
y.domain(d3.extent(data, function(d) { return d.HeaderGoals; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("attempts");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("goals")
var tooltip = d3.select("body").append("div") // tooltip code
.attr("class", "tooltip")
.style("opacity", 0);
var circlesH = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.HeaderAttempts); })
.attr("cy", function(d) { return y(d.HeaderGoals); })
.style("fill", function(d) { return color(d.Team); })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.HeaderAttempts+" ,"+ d.HeaderGoals)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
var circlesF = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.FreekickAttempts); })
.attr("cy", function(d) { return y(d.FreekickGoals); })
.style("fill", function(d) { return color(d.Team); })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.FreekickAttempts+" ,"+ d.FreekickGoals)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.selectAll("[name=v]")
.on("change", function() {
var selected = this.value;
display = this.checked ? "inline" : "none";
svg.selectAll(".dot")
.filter(function(d) {return selected == d.Team;})
.attr("display", display);
});
});
</script>
<div id="filter">
<b>team Filter:</b>
<br>
<input name='v' value="Team A" type="checkbox" checked>Team A
</input>
<br>
<input name="v" value="Team B" type="checkbox" checked >Team B
</input>
<br>
<input name="v" value="Team C" type="checkbox" checked >Team C
</input>
</div>
I know this wasnt going to work but had to give it a shot.
Now i have no idea how to proceed.
This is actually a truncated data and i still have 4 more columns:
PenaltyAttempts, PenaltyGoals, CornerAttempts, CornerGoals
Your approach of making separate graphs and overlapping in this is not really correct.
Better way is to make your dataset as per your requirement.
data.forEach(function(d) {
d.HeaderAttempts = +d.HeaderAttempts;
d.HeaderGoals = +d.HeaderGoals;
d.FreekickAttempts = +d.FreekickAttempts;
d.FreekickGoals = +d.FreekickGoals;
var attempts = d.HeaderAttempts + d.FreekickAttempts;//calculate all attempts of a team
var goals = d.HeaderGoals + d.FreekickGoals;//calculate all goals of a team
//make your data set like this
all.push({
team: d.Team,
attempts: attempts,
goals: goals
});
});
Then use this all object to make your domains
x.domain(d3.extent(all, function(d) { return d.attempts; })).nice();
y.domain(d3.extent(all, function(d) { return d.goals; })).nice();
Even points can be made easily like this:
svg.selectAll(".dot")
.data(all)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {console.log(d,"s");return x(d.attempts); })
.attr("cy", function(d) { return y(d.goals); })
.style("fill", function(d) { return color(d.team); })
Working code here
Hope this helps!

Categories