MICHAEL: Hello, my name is Michael. And welcome to Data Visualization in D3. Let's get started. So what is the D3? D3 stands for data-driven
documents, and it's essentially a JavaScript library-- a
collection of JavaScript functions, that's going to make it easy
to visualize and interact with data in web pages. So there are a few prominent examples
of media companies that use D3. One is The New York Times. Let me pull up a quick example of that. So this is a New York
Times D3 visualization of Obama's 2013 budget proposal. So you can see it is interactive. You can see how people
spend different ways. They have different arrangements of
the data in different sorts of charts. And it all moves pretty seamlessly,
and D3 allows us to do that. The other prominent example,
which I had included, but then took out because I
didn't really want to look at it, was the 538 projections for
the presidential election and for the Senate races. But that was also built in D3 along
with a few other JavaScript libraries. So what can we expect to achieve
during the next 40 minutes or so of this seminar? So we're going to build a visualization. We're going to look at
shots in the NBA, and we're going to build some sort
of visualization that's going to help us analyze that. But the goal of this lecture is not so
much to provide a few functions in D3 or look at a few components and just
give a basic knowledge of those. Rather, we want to provide
some sort of literacy in D3. And by this I mean that, in D3
there is a lot of creativity. We have several different ways that
we can analyze and express data to express different things. So we have to be creative, and we have
to be very versatile in how we use it. And the way that
versatility is built up is by looking at examples of
what other people have done and being able to read that code
and understand how it translates to the visual you see on the screen. After a certain amount
of time doing this, you're going to be
able to start thinking about the visuals you
want to make and the code that you're going to have to write
to make those visuals in parallel. And that's really the
goal of this lecture, is to get you started on that
path. , Well let's take a step back for a moment and review a little
basic HTML and JavaScript. So remember that HTML sort of follows
this domain object model, which is something of a tree structure. So here we have some very simple HTML
and next to it we have the DOM tree. So this is just a document
with the HTML tag. Within that HTML, there's
a head and a body. The head contains the title, and
within the title and the body, there is some text. And it's going to all be
represented as a tree. And any of the JavaScript libraries
that we use to interact with HTML are going to be about
grabbing notes from this tree and doing something with
them in some manner. And JavaScript, remember, can interact
with and change these DOM elements, so change the nodes of this tree on
the client side of the operation. So let's take a look at that quickly. So here is my Flask application,
which I have put together just so we can look at these D3 examples. And you can see it's very simple. All it does is when we go to the
site, it serves up the file we want. And this is just reinforcing
the fact that JavaScript is all being done on the client side. The server really does nothing here. And here's the HTML I have. So I can actually even take
out these scripts for now. We'll use them later. And so we just have a basic page. It's titled hello, world. And we have three paragraphs--
hello, world, hi, world, hey, world-- with three different IDs. We reload the page. That's what we have. So this is that page where
we went to javascript.html. And we have a console,
a JavaScript console. So we can type a little
bit of JavaScript directly. So if we do
document.getElementbyId("greeting") and I make sure to close my quotes, then
you can see we get that paragraph with the ID greeting. And we can edit that paragraph so we
can change the text to say hi, world. So in this manner, JavaScript
allows us to edit the text. And you can imagine that if
instead of selecting the greeting, we selected the whole body and
rather than selecting text content, we did affected the whole
HTML, we could actually add different tags to our body. We could expand our HTML
in a lot of different ways. And JavaScript allows us to do that. But it's kind of slow. We have to type a lot of code. We have to go through things one by one. So just doing it in pure JavaScript
to make these sorts of visualizations is not really feasible. So another thing that you guys can
briefly introduce to another library is jQuery. In jQuery, you can see
from its description is a JavaScript library that handles
document traversal and manipulation, event handling animation, and Ajax. And it's a very broad library. It's actually much broader than D3. Often jQuery is going to be
used in parallel with D3. So it's going to cover some
things that it's good at, and D3 is going to cover it's
much narrower focus, which is really visualizations. And I think in that area, it's going
to be a lot more effective than jQuery. So let's talk about D3, and
let's get into what D3 is. So the main interface in
D3 for traversing documents and really for doing anything
is called a selection, which is essentially a group
of HTML elements, a group of those nodes from our DOM tree. And selections are created using
this d3.select or d3.selectAll. So remember, in jQuery
we have this dollar sign which starts all of our sort of
jQuery functions, our jQuery calls. In D3, it's all started
with just lowercase d3. And we can select in a very similar
manner that we do in jQuery. So if we do d3.select
with a tag name like body, that's going to be the
same as doing dollar body in jQuery to select all of the
body tags or to select the one body tag. And select all is going to select, in
this case, all of the block classes. So remember in CSS, we have
that dot represents a class. So the d3.selectAll("block") selects all
the nodes which have the class block. And this is, again, the same
syntax that we would see in jQuery. We can also select on
ID using a hashtag. And this could be done
the same way in jQuery. It's also the same as we do in pure
HTML when we do document.getElementById. And we'll come back to
this filter in a moment. But let's look at this in action. So now I'm going to add
back in this script. And this script is going
to include the D3 library. So you can see it's hosted on de.js.org
and we're getting this JavaScript file which contains the source for D3. Now if we reload this,
we can do d3.selectAll, and if we select all
the paragraphs-- so all of the nodes with the tag paragraph,
we're going to get this sort of object back. And it's sort of a weird look
at an object, a selection, but if we look inside
this group's element, you can see it has this node list,
which is each of the paragraphs. So the question is how do we
interact with these selections? And there's a lot of different ways. So once we make a selection, so once
we do de.selectAll or d3.select, there are several functions which can
be used to manipulate that selection and to manipulate the nodes
within that selection. So a couple of examples of these are
.attr, which stands for attribute. So that's going to be changing
attributes of the HTML documents, of the HTML nodes. .style, which is going to
change the CSS for those nodes. And .append, which is actually going
to add HTML elements between the tags of those nodes. So let's do one example, one
which I didn't actually show here, which is .text, which changes
the text inside the node. So if we do d3.selectAll("p"),
and then we do .text, this will change the text of the nodes. So if we changed them all to yo, world. You can see we've selected
all the paragraphs, and we've changed the text
inside them to yo, world. So you can see that D3 allows us to act
on all the nodes at the same time, all of the nodes in our selection. We could have also selected just one
of them using d3.select on the ID. So if we just did
d3.select("#greeting"), then this is going to select
the node with the ID greeting. And if we do .text("hello,world") here,
then you can see it changes that one node back to hello, world. So why would we use select
rather than select all? There's no really specific reason why. I mean it's going to be better
style in some ways, because it's going to make it clear that
we're selecting only one element rather than selecting many. If we had done
d3.selectAll("#greeting"), of course, we're only going to
get one element back, because elements have
to have unique IDs. OK, so this is all well and good, right? But the question is
why would we ever want to change all the attributes of a
bunch of nodes to the same thing. And maybe there's a couple
instances where we would do that, but usually what would be useful
is if we could change nodes in different ways, but in one,
sort of a short period of code. And for that we're going
to have to have some reason to treat the nodes differently. And that comes back to what D3 is
really for, and D3 is for data. And how do we associate the nodes
we want to change with the data that we have. And that's done with
this idiom basically, which is going to be
d3.select.d3.selectA ll(...).data(...).enter(). So there's a bunch of functions
that we string together here. And let's explain what each
of those do in an example. So here I have a simple JS script, which
has that idiom that we talked about. And let's just explain
what each of the line does. So we first do d3.select("body"). So this is selecting
that first body tag. And now we're going to select all
the paragraphs inside the body. So you can see we can
string together selections, where we call a select
on something and that's going to select whatever
we've decided to select. Here, it's the body tag. And then if we string together a
select all or a select on that, it's going to call select
All on all the nodes below that element in the DOM tree. So here it would select all
three of these paragraphs because they're within the body tag. But suppose I comment those out. Then the selection is going to be empty. So it's a little weird that we've
done that, but let's go a bit further. Next we're going to specify some data. So .data is a function which
is going to take in an array. And that array should represent
the data that we want to visualize. Here it's just an array of greetings--
hello, hi, yo, hey, and so on. And .enter, the next function, binds
the data we've input to our selection. So every element in our
array is going to be bound to one node in our selection. But wait a second,
our selection's empty. There's nothing to bind to. In that case, if we just
add a .append below this, it's going to try to fill out our
selection so that it can bind each data point to one element of the selection. So here by doing ,append("p"), it's
going to append one paragraph per element of our array. So in this case we have
seven elements of our array, so it's going to append
seven paragraphs. So we have one last thing which we're
doing here, which is this .text. So once it's appended all
of these paragraphs, right, we now have a selection of seven
paragraphs and we're going to call .text on those paragraphs. Now remember, here we could have
put something like .text hi world or whatnot. So we could have just
typed hi world and then we would have ended up and
deleted this function here. And if we did that, then we'd
end up with seven paragraphs that say, hi, world. So let's look at that real quickly. So if we uncomment out
this script here, so we have some script which is
executed within the body. And that script is this example. And then go over and run it,
we can see that we end up with seven paragraphs called hi, world. But we can actually use the
data to make these distinct. So if we go back and put
back in this function. So it's a nameless function,
but it takes one argument d. And d is going to be the data
associated with that node. So remember, each of these
elements of the array are associated with one
node in our selection. So it's a simple function. It takes in the data d, and
it returns d plus comma world. So we're going to end up with
hello, world, hi, world, yo, world, and hey, world. And let's see if we do that, and
that's indeed what we end up with. So we can see how we've bound
data to each of these nodes. And then using that, we've been able to
treat the node differently, but still with only sort of one
writing this function once. This is beginning to
show the power of D3- not it's a pretty simple
dataset, just a list of strings. We're going to be working with a little
bit more complicated dataset today, but I think it's a fun one, which is
all the shots taken by the New York Knicks in the 2013-2014 season. So let's take a quick look
at what that looks like. And this is a little hard to see. But basically we have games,
all the game in the 2013 season specified by game ID. The period in which they
occurred, so the quarter, the score at that time, the remaining
time, the team, which is always New York Knicks, the player who took
the shot, whether the shot was made or missed, the x and
y-coordinates of the shot, the distance it was taken from, and
who, if anybody, got the assist. So we have a bunch of data here. The main data we're going
to be working with today is just these four columns,
which are the player who took the shot, the result,
and where the shot came from. And we're going try to
visualize that in some sense. So the question is how do we
actually read in data like this? So in this case all the data
is stored in a CSV format, and D3 provides some excellent
interfaces for reading in CSV data. And that's going to be done
with this function d3.csv. The first argument to
d3.csv is just going to be the file name, as to be expected. And the second argument
is a nameless function, which takes one argument data. And what's going to be passed to this
function, what's going to be executed is the data which is
contained in filename.csv. And by that I mean it's going to
be an array of JavaScript objects, where each JavaScript object has
the keys that are the column names. So these are going to
be the keys, and values, which are the values within the row. So these are going to be the values. So let's take a look
at that real quickly. So as you guys can see, I'm not
quite as confident in my ability to type code live as David. So I've typed a lot of
this code beforehand. And we'll go through and
comment it out line by line. But for the start, all we really have
here is we read in the shots.csv file. And we're going to log
what the data looks like. And we should also look at what
the actual HTML looks like here. So at shots.html, we can
ignore this SVG for now. That's not going to be important yet. And we can ignore this selector for now. But we're just running
the scripts shots.js. So if we go to shots.html,
take a look at that. Here's what our data looks like. And as you can see, it's what we said. It's an array where each
element of the array is a JavaScript object, which are
those keys are the column names and the values are the
values within the rows. OK. So the question is, how are we
actually going to visualize this? And there are maybe some limitations
with just using pure HTML elements. Like we're probably
not just going to want to put a bunch of paragraphs saying,
Carmelo Anthony made this shot here, or a div that has maybe, we fill in
blue, you know, if someone made a shot. Green, if someone made a
shot, red if someone didn't. We want to have a little bit more
freedom to draw things than that. And that's going to be allowed by this--
I really shouldn't say beyond the DOM because it still is the DOM, but
it's beyond the basic HTML elements. We're going to be dealing with
this image format called SVG. And SVG stands for
scalable vector graphics. And in SVGs, we're going to be able
to draw lines, shapes, and text. So what do I mean by
SVG follows the DOM? So let's take a look here. We have this basic HTML
file called svg.html. And you can see it looks, it is HTML. We open up an SVG with the
attributes width 600 and height 200. Within the SVG I have three tags,
each called circle, each a circle tag. And I've specified the center--
x the center, y, and the radius. So that's going to be
the x and y-coordinates of the center of the circle
and the radius of the circle. And for the last one also specified
that it should be a fill of blue. So it's going to be colored blue. So if I go to svg.html, you
can see I get this image here, which is these three
circles that I've specified. So we can see that SVGs follow the
same DOM tree that we work with HTML. So we're going to be able
to interact with them using D3 in pretty much the
same way we interact with HTML. And that's going to allow us to
create a lot of these drawings. And this is the manner in which The New
York Times created a drawing like this, by appending circles to an SVG,
and doing a lot of other stuff, but that's the starting point. So a couple important tips for
using SVGs in these visualizations. So it's going to be tempting at first
to just append a bunch of circles and specify their center x and center
y since that's what we've just seen. But this can be a little
bit annoying down the road. So take for example, if we wanted
to draw a smiley face, for instance, on an SVG. Then we might have one circle for
the head, two circles for the eyes, and an arc for the mouth. And no matter where we put
the smiley face on the SVG, the relative positions
of all of those elements are going to each other
are going to be the same. However, if we want to move
that smiley face around, then we need to change the absolute
location of each of those elements independently, right? So they each need to specify--
if we move it 500 down, 500 left, then we need to move each
element 500 down, 500 to the left. And this can get a little
bit annoying if for each one we're specifying a center. So to deal with that, we can use
this g tag, and g stands for group. And the idea is we can
specify a g tag and put all the elements that we want to group
together inside of these two tags. The g tag has this attribute transform. And transform is going to be
some transformation which we apply to all the elements of the group. So this first one we have a translation,
100 down and 50 to the left. So if we have the smiley
face within this group, then it would move all of the
elements 100 down and 50 the left. And this is clearly a lot easier
than moving them individually. We can apply other sorts
of transformations. So in the second one, you
have a rotation by 20 degrees and then a translation 100 to the right. One thing to keep in
mind, 100 to the right. One thing to keep in mind here
is that the transformations are going to occur right to left. So the rotation here is
applied before the translation. You can imagine using a
rotation if you wanted some sort of circular layout of your data. So if you wanted to draw a
bunch of circles or rectangles or text in a circle around
some center, then you might use the rotation
transform for a group. So let's see this in action. And let's get started putting
together the shots visualization. So back in shots.html, let's go
back to the SVG which we have here. So I have an SVG with the ID
canvas, a height of 600 pixels and a width of 1,200 pixels. And over in shots.js,
we've read in our data. So let's start putting a
little bit of-- let's start doing some work with this data. So first I'm going to select the SVG. And let's ignore that I've
set it equal to shots for now. We can even delete that for
now and just select the SVG and start working with that. And then I'm going to use the
same idiom which we had before, where we select all the groups. And we're going to bind the data,
so each of these shots to one group. So remember it's going to append one
group per shot in our dataset, per row in our dataset. I'm going to give each
of these groups a class. This is a very good practice in D3
when we append these general objects, with each general objects
like groups or circles. You're just going to use groups
and possibly a lot of contexts. And you're going to be
able to want to select all the groups in a certain context. And the way the best way to do that
is to say, well, if we want to say, draw all the shots and we
want to draw all the rebounds, then we might have one, which
is this class shot and one is the class rebound. And then we can just select
all the ones with class shot rather than having to
select all the groups and then do some filtering in
order to get only the shots. So this makes it a little
bit easier in the long run. And then we're going
to have our transform. So here I've done a little work already. But sort of the first
thing that makes sense is to do just the x and y-coordinates. So we're going to translate
it the x-coordinates to the left and the y-coordinates down. So we have some group here. Now what you can see here
is that at the end of this we have a selection, which is all
the groups, one group per data point. So if I go back and I put
in what I had before, where I say shots equals the
selection, what I now have is shots is a selection
of all the groups, it's going to be selection where each node
in the selection represents one shot. So this makes a lot of sense now. And to each group I'm going to
append a circle of radius 5. So for each shot, we're
going to have one circle. And let's take a look
at what we end up with. OK, so now that I've reloaded the
page, it's a little bit cramped right. So it turns out that our x and
y-coordinates are perhaps not perfectly spaced for a visualization like this. So let's expand those out a little bit. So let's multiply the x-coordinate
by 10 and the y-coordinate by 10. And we can see that now it
spaces it out a bit nicer. Still though, we can't see
the lower half of the court because our SVG is not big enough. And typically, when we're
looking at basketball courts, we want them to turned to the side here. So we can do that as well by
flipping the y and x-coordinates. And now when we run it, it's
going to look about how we expect. And yeah, it looks pretty much like
what we might expect from a shot chart, where we have some clustering
around the three point line and a lot of shots near the basket. OK, so let's start doing some work with
this data in a more interesting way. So what data do we have? We have whether the
shot was made or missed. So how about we make
the two circles green if it was a make and
red if it was a miss. So remember we have this
attribute fill for the circles. So we can edit that attribute. And we're going to have a
function here, which is again going to be a nameless function
that's going to take that data bound to that node as the input. And it's going to output
the value we want for fill. So if we uncomment each of these
lines, we have this function. And it says, if d.result equals made. So remember, the node,
the data represents is a JavaScript object, which has
the keys, which are the rows, which are the column names in our CSV file. So here we have a column name called
result, which is either made or missed. So if we do it with d.result, it equals
made, then we're going to turn green. And otherwise, we're going to turn red. So we're going to have a
green circle if it's a make, and a red circle if it's a miss. If we look at this again, then
that's what we end up with. And we can begin to see some more things
so we can see that out in this area, people are making a lot of shots. So these sort of end of
the half, half court shots are not very effective, especially
since this is the Knicks and not the Warriors. So we've began to look
at our data and really be able to pull maybe not the
most interesting thing out, but beginning to pull
something out from this data. The question is, how can we add more
and can we make this visualization interactive? So the main interface for adding
visualizations is this .on function, which again is called on selections. And the first argument to .on is going
to be a string representing an event. And this string can be click. It could be move if you move
the mouse over the selection. It can be key down if you press a key. We're going to use mouse
over and mouse out. So mouse over is going
to be an event that triggers when you move the
mouse over a node in selection. And mouse out is going to be
an event that triggers when you move the mouse away from the selection. The second argument for
on is the function that's called when that event is triggered. And again, the function is
going to take a single argument. And that argument is going to be the
data associated with the node which triggered the event. So let's look at that. So if we do .on mouseover, we are
going to have a few lines here, which we can explain. So we have first this
idiom de.selectthis. So d3.selectthis-- this
represents the actual node. So remember the argument
here is not the node itself, but the data
associated with the node. But often, when an event's
triggered on the node, we want to actually
change just that node. And the way to get just that node,
is to call this de.selectthis. And it's going to return a selection
which contains just that node which triggered the event. I'm going to call the dot raise, to
raise that note above the other ones. And let's take that off for now and
see what happens if we don't do that. But that's going to essentially move
that HTML element to the end of the SVG so it's placed above everything else. But let's see what happens
if we don't have that first. To that node, we're going
to append some text. We're going to give it a
class called player name. And again, good practice
whenever we append something to give it some sort of class. We probably should have
given these circles a class if we want to draw other circles. We're actually going to
not draw the circles, so I'm not going to bother with it now. But it is a good practice to
have that class attribute. And we're going to add
this text to this text SVG tag, which is going to be
the player name, so d.player, so we're using that argument. So now, if we run this, and we
move our mouse over some nodes, we can see that we indeed end
up with the player's name. And we have a little bit
of a problem, because when we move the mouse off the node, it
doesn't delete the player's name. So now let's go back, and we
add this mouseout function. So here we have on mouseout we're
going to trigger another function. This function is just going to select
all the text which has the class player name, and it's going to remove that. So here we wouldn't even need text. We can just do All everything
with the class player name. And we could actually even do
everything with just the tag text. However, probably the best practice
is be as specific as possible, because we may want to add
in later other sorts of text onto our visualization that
we don't want to delete every time we move off a node. So now that we've added this, let's
go back and reload our visualization, and see what happens as we move around. And now, we can indeed see that once
we move off a node, the text goes away. Maybe we can make our
nodes a little larger if we want it to be a
little easier get over them. One of the things that I
love about this dataset is that we can see J.R. Smith likes
to take a lot of shots from way out. That's an interesting point. But one thing we can see is
that once we get into here, the text is going to be added
behind a bunch of other nodes. And the reason this is
occurring is because we're adding it to the group, which
has already been placed. So if that group was placed
before another group, even when we add the text, it's
going to be behind all the circles in the group's place after it. That's why we had this .raise here, so
this selection is going to be that one group. And .raise is going to raise that group,
so it's the last group in the SVG, so it's going to be on top. And now if we look at it
again with that .raise, we can see that now the names come
up above all of the other circles. So often we're not going to have
data in necessarily the best form for the visualizations we want to make. In this case, we have rows of data. And we basically want one row for shot. So this works out pretty well. But often we are going to want to
restructure data in some manner. And there are a bunch of functions
that D3 provides in order to do that. We're going to work with
d3.nest a little bit to do some stuff with the shots data. But I'm going to talk a little bit about
d3.stratify and d3.hierarchy first, which deal with hierarchical data. So the classic example for
hierarchical data is a family tree. So suppose we have some
data, which is again represented as a table in
some sort of CSV or whatnot, where we have the name of a person
and then the name of their parent. So this could clearly be
represented with a tree structure. And a lot of D3
visualizations are going to be trees or something that represents sort
of a tree structure or a hierarchy. So we can use stratify in order
to convert this sort of CSV format into something which is easier
to work with when making trees. Where we actually want
to end up with something like this, where we have the JavaScript
object with three keys-- the ID, so the person's name, the parent
ID, so who their parent is, and their children, which is
a list of all of the nodes which have the parent of Eve. And this is going to be
sort of recursive, right? So each of these children is
going to have the same format. So it'll have their ID,
their parent ID, and then their children, which is again going to
be another array of a similar format. So if we write something like
strat equals de.stratify, so creating some stratifier. In ID and parent ID, we're going
to specify a function which is going to act on the data in that row. And it's going to return
the ID we want to use and the parent ID we want to use. So here the ID is going to
be the name of the person. And here the parent ID is
going to be the parent. So you can see that d3.stratify was sort
of designed with family trees in mind. And then if we call strat on
whatever our data is, the data we write in maybe with
d3.csv, then we'll end up with a JavaScript object
that looks something like this. And there's going to be a
lot of visualizations in D3 that are built with formats
something like this. So I just wanted to
touch on that briefly. But now let's return to
what we're going to actually work with, with just this d3.nest. And this is used to group together data. And in our case, we're going to
group together data by player. So we're going to take all the
shots taken by certain players. D3.nest has three methods
which we're going to use. The first is .entries, and the argument
to entries is just going to be the data we want to group. So in this case, we're going
to do d3.nest, entries(data). The second method we're
going to use is this key. So key is going to specify
what we want to group on. So here we're going to have a function. The function is going to
take in a row of our data. And it's going to return what
the key should be for that route. So in this case, it's going
to turn the player name. So let's take a look at that. So if we do players d3.nest,
the key is going to be a player. We're going to come back to rollup, and
the entry is just going to be our data. And let's log what that looks
like once we do this grouping. So we reload. We can see that we have an array with 17
objects where the keys are the players' names, and the values are each of
the shots taken by that player. So if we open up one
of these, we can see that this is a shot indeed taken
by Carmelo Anthony assisted by Tyson Chandler. OK. So we now have this object, which
has the shots grouped by player. The last method, which we're going
to use from d3.nest is this .rollup. So .rollup allows us, once
we've done this grouping, to do some aggregation on the rows
that have been grouped together. So rollup takes a function, which rather
than acting on the rows individually, is going to act on the
whole array of rows. So in our case, we're going to
do the simplest thing possible, which is we're just going to
return the length of that array. So the length of the array
representing the number of shots taken by the player. So here we do .rollup. Here I do V, maybe V stands
for vector or whatnot. We can change it to
A to stand for array. So it's just going to be
this function A, which takes in the array of
shots for that player and returns the length of that array. So now if we run this again. And log what we get, we
can indeed see that we get this object where we have
each object has two keys-- key-- Carmelo Anthony, value-- number of
shots taken by Carmela Anthony, 1,643. So nest has allowed us to sort of pull
some other things out from our data and group it in some manner
that we can then use. So let's use this a little bit. So we haven't actually added
it toward visualization. We've done some sort
of computations here. But let's actually add it to our
visualization and do something with it. So back in the
shots.html, you saw that I had this selector with the ID selector. And we're going to want to be able
to just look at the shots taken by a specific player. So now we can go back and start
thinking about using D3 with the HTML rather than in the SVG context. So I'm just going to
select the selector. I'm going to select-- I'm
going to use this idiom again. .data, .selectAll,
.data, .enter, .append. So I'm going to add one option for
each element of this player's array that I've just created. So one option per player where the text
of that option is going to be the key, so the player name and a colon
and then the number of shots taken by the player. And the value-- so this
attribute value is just going to be the key of that player. So if I do that, and I
look at what I end up with, you can see now I have a selector
where I have the player names and the shots taken. And I can go through and
click on that selector. And you can see the
selector isn't so pretty, but you can imagine we could
also combine this with Bootstrap, and we could have a very
nice looking selector. So we can start to use a lot of
these different CSS and JS libraries together to make a more
beautiful application. So the selector doesn't
do anything right now, but we can combine some
of the things we've learned in terms of
interaction and selections in order to make it only show
the shots for that player. So we're going to, rather
than using on click here, we're going to use on change. So that when they-- rather than just
when they click on the same thing, we don't want it to
change anything, so only when they change the value selector. We're going to select all--
well, let's leave that for now. We're going to get the
value of the selector. So what's interesting about these
.attribute and here we're using .property, and .style, is that they
can be used as accessors as well as setters, so getters
as well as setters. So if we don't provide this
second argument here to set it, then it's going to
return the current value. So here we're just getting the
current value of the selector. So whatever we set here is the value. And once we get the value, we're
going to select all the shots. We're going to apply this
filter function which is going to restrict our selection. So .filter, which I mentioned
way back in terms of selections, and said I would come back
to, well, this is the time. So once I've created a
selection I may not want all of the nodes in that selection. And I may want to be able to pick out
the nodes in a more specific sense than just class, ID, and tag. And I can do that by using
this .filter function. So the argument to filter is just
going to be a function, which takes in the data bound to that
node, and if it returns true, then we keep the node in
selection, And if returns false, we don't keep the node. So here my function takes the data, and
it returns d.player not equal to value. So it returns true if
the player for that row is not equal to the
player I've selected. So it's going to select all the
shots not taken by the player. And it's going to set
their opacity to 0.1. And so it's going to
make them transparent. Then let's see that in action. So now if I reload and click,
you can see that it only shows me the shots for Beno Udrih. Problem is, if I select
another player, then they're all going to be transparent. Because I'm only making more nodes
transparent, I'm never resetting them. So in order to change that, I go
back to the start of my function. I select all the shots. And I change their opacity to 1 before
I go and change the opacity of the shots not taken by that player. So if I do that, and reload,
and now I select Raymond Felton. And see he's spread it out,
spreads out his shots pretty well. If I go and look at Tyson Chandler,
a little bit tighter to the hoop. Metta World Peace, maybe
not taking that many shots. Amare Stoudemire, another guy who
plays tight to the hoop, and of course, Carmelo Anthony, all over the floor. So again, we can begin to pull
more things out of our data. Now the one thing which I don't
like about this is it starts out with Carmelo Anthony, right? But it's not just Carmelo
Anthony's shots here. Like nothing gets faded
until we change the selector. So to remedy that--
I'm just going to add-- remember players is just an array--
so I can just add one JavaScript object at the start of
players, one associative array at the start of players. I'm going to have my key be all, and I'm
going to have my value be this d3.sum. And we're not going to go into
too much detail in d3.sum. That's something that can
be looked at, and it's not too hard to understand given
the rest of this lecture. But it's just going to sum up the
values of the elements and players. So it's going to be the
total number of shots taken. And I'm only going to fade shots
if the value is not equal to all. So now we have this selector, where we
can select through different players. And if we go back to all,
then all the shots are shown. So now this works exactly
as we would want it to. And we have all the lines
of code uncommented, so we're done with what I had planned. So we've created this visualization. And of course, there are
a lot of ways that I'm sure you are beginning to think
about, how we can iterate on this, how we can improve on this, even
based on just the few functions that we've seen. We can think about looking at
how assists play into this. We can look at maybe coloring
the notes differently based on shot distance
or shot difficulty and whether they made or missed. And those are some great things
to think about going forward. However, as I said, really
the way to work with D3 is to look at other examples to begin
to formulate your own ideas about what visualizations you
want to create and what different kinds of code, how D3
is being used in versatile ways to create those visualizations. So I'm providing a couple of
resources here that you can look at. So we have d3js.org, which are
going to have a wide gallery of sort of complete D3 applications
as well as the code for them and a ton of great documentation. There's also this site
Blocks by this guy Mike Bostock, which has a lot of simpler
examples which sort of illustrate single aspects of D3. So here he has this
circle dragging example, which just demonstrates basically
a good way to implement circles that you can drag around. So this is a pretty simple example. It's not really a full visualization. But if you're looking
for a more specific thing you want to implement in D3,
then this blocks is a good place to look if someone has already done it. And the last thing I'm
linking is a library with 538 put together called D3-pre. As you saw, at some
points our application was a little laggy in terms of loading. It took a little while for it to render. D3-pre allows us to pre-render
a lot of our visualizations. So if we want to have a lot of
stuff going on in a visualization, we can use this D3-pre so
it renders more quickly when we want to actually show it to users. So that should complete what
I wanted to talk about D3. I hope that you guys are
inspired to look at some examples and go forward, and feel that you
were literate with the language. Thank you.