Create a gist now

Instantly share code, notes, and snippets.

@mansweet /README.md
Last active Dec 11, 2015

Civic Impact through Data Visualization: Exercise 1

Join the chat at https://gitter.im/Jay-Oh-eN/data-scientists-guide-apache-spark

These are the materials for my workshop on creating interactive data visualizations with D3! We will be using the following two tools to works through these exercises:

And please do not hesitate to reach out to me directly via email at jondinu@gmail.com or over twitter @clearspandex

Throughout this workshop, you will learn how to make an interactive map of AirBnB listings in SF to better understand the companies impact on the city.

Exercises

  • Part 1 (you are here 👇): simple bubble chart visualizing 3 dimensions of the data.
  • Part 2: thematic map of AirBnB listings distribution across SF neighborhoods.
  • Final: interactive linked map and line plot of AirBnB activity per each neighborhood in SF.

The Data

The data comes from Inside AirBnB and it is available below under a Creative Commons CC0 1.0 Universal (CC0 1.0) "Public Domain Dedication" license.

Inside AirBnB is an independent, non-commercial set of tools and data that allows you to explore how Airbnb is really being used in cities around the world.


Github repo: https://github.com/Jay-Oh-eN/interactive-data-viz

Archival event link: Open Data Science Conference SF 2015

forked from Jay-Oh-eN's block: Civic Impact through Data Visualization: Exercise 1

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.8/d3.min.js"></script>
<style>
// the above is an import of the d3.js library
circle {
opacity: 0.408576;
}
body {
font-family: futura;
}
h2.title {
color: black;
text-align: center;
}
.axis {
font-family: arial;
font-size: 0.7em;
}
text {
fill: black;
}
.label {
font-size: 2em;
}
path {
fill: none;
stroke: black;
stroke-width: 1px;
}
.tick {
fill: none;
stroke: black;
}
.line {
fill: none;
stroke: #4eb0bb;
stroke-width: 1px;
}
</style>
<script>
function draw(data) {
"use strict";
/*
D3.js setup code
*/
// set margins according to Mike Bostock's margin conventions
// http://bl.ocks.org/mbostock/3019563
// We set it accordinly so we can acually see the axis labels
var margin = {top: 25, right: 40, bottom: 150, left: 75};
// set height and width of chart
var width = 1400 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
// so we are subtracting a small margin to make like a little window within the other window
// specify the radius of our circles and the
// column we want to plot
var radius = 3,
field = 'San Francisco',
y_field = "price",
x_field = "number_of_reviews",
review_rate = 'reviews_per_month';
// Append the title for the graph
d3.select("body")
.append("h2")
.text(field + " Listings")
.attr('class', 'title');
// append the SVG tag with height and width to accommodate for margins
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append('g')
.attr('class','chart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// the 'g' tag is a way to group elements
// remove missing values
data = data.filter(function(d) {
return d[y_field];
});
// for each element, pass it through our function. If it is true, keep it, if no then throw it away. (eg if it has a nan in that column, through the whole observation away.)
// bind our data to svg circles for the scatter plot
svg.selectAll("circle") // there are not any circles yet..but this still works for some odd reason
.data(data) //using .data here, instead of .datum(). This is a big array of objects so we can bind them all at once. You could do this with a for-loop, but this is a more d3.way (akin to pythonic vs numpy). This does a join() function of sorts
.enter() // this is specifying the TYPE of join (inner vs outer)
.append("circle")
// maximum price
var max_y = d3.max(data, function(d) {
return +d[y_field]; // the + turns a str to an int/float (no diff in JS)
});
// get min/max review count
var review_extent = d3.extent(data, function(d){
return +d[x_field]; // returns the min and the max in an array (no tuples)
});
var month_review_extent = d3.extent(data, function(d) {
return +d[review_rate];
});
// Create x-axis scale mapping dates -> pixels
var review_scale = d3.scale.linear()// this is the created object
.range([0, width])
.domain(review_extent); // these things are just mutating it
// Create y-axis scale mapping price -> pixels
var measure_scale = d3.scale.linear()
.range([height, 0])
.domain([0, 1200]);
// // Create a scale for monthly reviews
var avg_review_scale = d3.scale.log()
.range([1, 5])
.domain([0,4]);
// Create D3 axis object from time_scale for the x-axis
var x_axis = d3.svg.axis()
.scale(review_scale);
// Create D3 axis object from measure_scale for the y-axis. This is needed for the axis to appear
var measure_axis = d3.svg.axis()
.scale(measure_scale)
.orient("left");
// Append SVG to page corresponding to the D3 x-axis
svg.append('g')
.attr('class', 'x axis')
.attr('transform', "translate(0," + height + ")")
.call(x_axis);
// Append SVG to page corresponding to the D3 y-axis
svg.append('g')
.attr('class', 'y axis')
.call(measure_axis);
// add label to y-axis
d3.select(".y.axis")
.append("text")
.attr('class', 'label')
.text("Price (dollar/sq-ft)")
.attr("transform", "rotate(-90, -49, 0) translate(-400, 0)");
//not sure what the transform actually does??????
// add label to x-axis
d3.select(".x.axis")
.append("text")
.attr('class', 'label')
.text("Number of Reviews")
.attr("transform", "rotate(0) translate(400, 48)");
// based on the data bound to each svg circle,
// change its center-x (cx) and center-y (cy)
// coordinates
d3.selectAll('circle')
.attr('cx', function(d) {
return review_scale(+d[x_field]);
})
.attr('cy', function(d) {
return measure_scale(+d[y_field]);
}) // this is d3 magic. Notices that we're plugging elements into a variable...
.attr('r', function(d) {
return d[review_rate]; // return avg_review_scale(d+[review_rate]);
})
.style('fill', function(d) {
switch (d['room_type']) {
case 'Entire home/apt':
return 'red';
case 'Private room':
return 'green'
case 'Shared room':
return 'blue';
default:
return 'gray';
}
});
}
</script>
</head>
<body>
<script>
/*
Use D3 to load the CSV file and pass
the contents of it to the draw function.
*/
d3.csv("http://jay-oh-en.github.io/interactive-data-viz/data/airbnb/listings.csv", draw); // draw is the callback func
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment