This is an exploratory visualization looking at the relationship between the total number of users and the apparent temperature - a feature that reflects the effects of temperature and humidity on perceived comfort.
This also implements the general update pattern, resizing the visualization to the size of the browser window, while maintaining a 1:1 aspect ration to optimize the interpretability of the scatter plot.
This data set contains the number of bike share system users (casual and registered) for each day (and hour) for two years (Jan 2011 - Dec 2012), as well as the day type (holiday, workingday), weather situation, temperature and humidity, apparent temperature and windspeed.
This data is from UCI Machine Learning Repository: Bike Sharing Data Set
The dataset file can be found here as a zip archive.
forked from curran's block: Stylized Scatter Plot with Color Legend
forked from sajudson's block: CS Degrees Awarded 1971-2011
forked from sajudson's block: Bike Share Users vs. Apparent Temp
xxxxxxxxxx
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<script src='https://d3js.org/d3.v4.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js'></script>
<title>DC Bike Share Test Chart</title>
<style>
#viz1 {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom:0px;
}
body {
margin: 0px;
}
.domain {
display: none;
}
.tick line {
stroke: #C0C0BB;
}
.tick text, .legendCells text {
fill: #8E8883;
font-size: 8pt;
font-family: sans-serif;
}
.axis-label, .legend-label {
fill: #635F5D;
font-size: 10pt;
font-family: sans-serif;
}
</style>
</head>
<body>
<div id="viz1"></div>
<script>
var viz1Div = document.getElementById('viz1');
var svg = d3.select(viz1Div)
.selectAll('svg')
.data([null]);
var svgEnter = svg
.enter()
.append('svg');
const localFile = 'data/day.csv'
const blocksFile = 'day.csv'
var datafile = blocksFile
const defaultOpacity =0.2;
//set x and y value pointers and axis labels
const xValue = d => d.temp;
const xLabel = 'Temperature';
const yValue = d => d.casual;
const yValue2 = d => d.registered;
const yLabel = 'Users';
const margin = { left: 120, right: 60, top: 60, bottom: 120 };
//row function to parse csv
const row = d => {
d.instant = +d.instant;
d.dteday = new Date(d.dteday); //need to parse date
d.season = +d.season;
d.yr = +d.yr;
d.mnth = +d.mnth;
d.holiday = +d.holiday; //flag
d.weekday = +d.weekday; //integer day of week (0-6)
d.workingday = +d.workingday; //flag
d.weathersit = +d.weathersit; //(1-4)
d.temp = +d.temp;
d.atemp = +d.atemp;
d.hum = +d.hum;
d.windspeed = +d.windspeed;
d.casual = +d.casual;
d.registered = +d.registered;
d.cnt = +d.cnt;
return d;
};
function scatterPlot(svg, data, xValue, yValue, width, height, margin) {
// maintain 1:1 aspect ratio for plot
const minDimension = d3.min([width, height]);
//set svg size to window
svg = svgEnter
.merge(svg)
.attr('width',minDimension)
.attr('height',minDimension);
console.log(width, height, minDimension);
console.log(svg.attr('width'), svg.attr('height'));
const innerHeight = minDimension - margin.top - margin.bottom;
const innerWidth = minDimension - margin.left - margin.right;
const xScale = d3.scaleLinear();
const yScale = d3.scaleLinear();
xScale
.domain(d3.extent(data, xValue))
.range([0, innerWidth])
.nice();
yScale
.domain(d3.extent(data, yValue))
.range([innerHeight, 0])
.nice();
const xAxis = d3.axisBottom()
.scale(xScale)
.tickPadding(10)
.tickFormat(d3.format('0'))
.tickSize(-innerHeight);
const yAxis = d3.axisLeft()
.scale(yScale)
.tickPadding(10)
.tickSize(-innerWidth);
var g = svg.selectAll('g').data([null]);
g = g.enter().append('g')
.merge(g)
.attr('transform', `translate(${margin.left},${margin.top})`);
var xAxisG = g.selectAll('#x-axis-g').data([null]);
xAxisG = xAxisG.enter().append('g').merge(xAxisG)
.attr('id','x-axis-g')
.attr('transform', `translate(0, ${innerHeight})`);
var yAxisG = g.selectAll('#y-axis-g').data([null]);
yAxisG = yAxisG.enter().append('g').merge(yAxisG)
.attr('id','y-axis-g');
console.log(innerWidth, innerHeight, margin);
var xAxisText = g.selectAll('#x-axis-label').data([null]);
xAxisText = xAxisText.enter().append('text').merge(xAxisText)
.attr('class', 'axis-label')
.attr('id', 'x-axis-label')
.attr('x', innerWidth / 2)
.attr('y', innerHeight+margin.bottom/2)
.style('text-anchor', 'middle')
.text(xLabel);
var yAxisText = g.selectAll('#y-axis-label').data([null]);
yAxisText = yAxisText.enter().append('text').merge(yAxisText)
.attr('class', 'axis-label')
.attr('id', 'y-axis-label')
.attr('x', -innerHeight / 2)
.attr('y', -margin.left/2)
.attr('transform', `rotate(-90)`)
.style('text-anchor', 'middle')
.text(yLabel);
//log domain of yValues
console.log(d3.extent(data, yValue));
//log exoected max values of x and y scale
console.log(xScale(0), xScale(1))
console.log(yScale(0), yScale(3410))
//data join
var circles = g.selectAll('circle').data(data);
//Add new elements
var circlesEnter = circles.enter().append('circle');
var t = d3.transition().duration(500);
var circlesExit = circles.exit()
.attr('class','exit')
.remove();
//UPDATE old elements present (change class)
circles
.attr('class','update')
//merge new and existing ell
circlesEnter
.attr('class','enter')
.attr('fill', 'green')
.attr('r', 5)
.merge(circles)
.attr('cx', d => xScale(xValue(d)))
.attr('cy', d => yScale(yValue(d)))
.attr('fill-opacity', d=>(d.filterOpacity));
//remove elements for which there is no data
circlesExit
//call X and Y axis
xAxisG.call(xAxis);
yAxisG.call(yAxis);
//colorLegendG.call(colorLegend)
// .selectAll('.cell text')
// .attr('dy', '0.1em');
};
console.log('join data');
d3.csv(datafile, row, data => {
const render =() => {
//get window width and height computed by CCS
const width = viz1Div.clientWidth;
const height = viz1Div.clientHeight;
data.forEach(d=>d.filterOpacity=.5);
f7 =false
filterMonth = [11,12,1,2,3,4,10]
filterYear = 0
filterHoliday =3
filterWorking =1
function applyFilter(filterMonth,filterYear,filterHoliday,filterWorking,f7){
data.forEach(d =>{
if(filterMonth.includes(d.mnth)){d.filterOpacity=0}
if(d.yr==filterYear && f7==false){d.filterOpacity=0}
if(d.holiday==filterHoliday && f7==false){d.filterOpacity=0}
if(d.workingday==filterWorking && f7==false){d.filterOpacity=0}
});
};
applyFilter(filterMonth,filterYear,filterHoliday,filterWorking,f7)
scatterPlot(svg, data, xValue, yValue, width, height, margin);
}
console.log('now render');
render();
console.log('resized and rendered...');
window.addEventListener('resize',render);
});
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js