- Part 1: Drawing the Data
- Part 2: Drawing the Axes
- Part 3: Interaction
- Written Questions (worth 15/80 points)
- Extra Credit
In this assignment, you will write code to build an interactive parallel coordinates visualization for multidimensional datasets such as those used in the last assignment. Your main focus will be on designing interactions that support analysis with parallel coordinates.
This assignment is designed to provide experience with alternate visualizations of multidimensional data using parallel coordinates. In addition, it offers additional practice with more advanced uses of brushes and callback handlers in d3. Specific objectives include:
- Implementing a parallel coordinates view suitable for viewing multidimensional datasets
- Experimenting with using SVG path types for drawing polylines.
- Practicing interactions with a parallel coordinates plot, particularly through implementing click events for selection and
d3.brush’s for brushing and linking.
- Designing callback handlers that support the above interactions of selection and filtering
- Understanding the types of tasks that parallel coordinates are suitable for.
In this assignment, you will create a visualization that uses a parallel coordinates display to investigate the Auto MPG dataset. This dataset has 8 attributes that describe the performance of cars. The first attribute is the name of the car and the remaining 7 attributes describe automotive characteristics such as mpg, cylinders etc. The dataset I’ve included in
.js format has 392 unique data items, as I’ve cleaned the data to remove any instance of a car with incomplete values.
Your parallel coordinates view should draw all seven numeric dimensions simultaneously, and allow the user to investigate how the data spans the seven dimensions through interactions. Specifically, your visualization must support: (1) brushing on any axis to select and filter a subset of the data and (2) reordering the axes by clicking on them which should swap them with their neighbor on the right. In addition, when you mouseover an axis label, it should bold the label and change the color of all polylines to reflect a color scale designed for that axis. To organize these interactions, you’ll implement callbacks for click and mouse events. You will also make heavy use of the
class attribute for many of the objects you create.
In this assignment, you should also get more comfortable with doing two types of selections and data joins: those that are mapped to the actual data array (called
data in the template) and those that are mapped to an array of dimensions (called
dims). The array of dimensions will be used heavily in this assignment, and we will be a one-to-one mapping between
dims and many different visual elements. You should take inspiration from A02 on how to define
dims similar to how the array
attribs was created in A02.
You’ll note in the template code provided, I’ve included code that initializes, one per each dimension, a element of the
brushRanges arrays. You are welcome to customize this initialization further if you like, but the basic version I’ve provide should be sufficient.
Part 1: Drawing the Data
The first of four selections that you need to complete involves drawing one SVG
path element per data element. These should be draw as a polyline with seven points, one point per the seven quantitative dimensions of the data element. To do so, we will use
d3.line(), which accepts an array of pairs
[[x1,y1], [x2,y2], ...] and returns the associated SVG path attribute
d. Your first task is to write the logic that makes use of the pre-defined scales to compute this parameter.
Notably, I have provided both
yScales to do this.
xScale, which is of type
d3.scalePoint(), will return for any element of
dims the appropriate \(x\)-coordinate. For example
xScale(dims) will return the \(x\)-coordinate of the first (leftmost) dimension.
yScales is a tuple of scales, one per dimension, that can be used to access the \(y\)-coordinate of specific data elements on a particular scale. For example,
yScales[dims] returns the scale associated with
dim and as a result if you call
yScales[dims](datum[dims) it will return the actual \(y\)-coordinate of a particular
datum for dimension
I recommend looking at some examples of parallel coordinates for examples of how one uses
d3.line(). For example, see Jason Davies’ Parallel Coordinate block and this example of Most basic parallel coordinates chart in d3.js. See this link for more information about how to use
After you get comfortable with how these are structured, you should take a look at Kai Chang’s Parallel Coordinates and Parallel Coordinates on Observable for some examples of how to customize parallel coordinates further. Like in A02, these examples are provided for inspiration, but I expect your implementation will result in something different.
Besides using paths, you should set the stroke color to be define by using color scales. Initially, you should draw each data element using a color scale of your choice defined by the first data attribute. As you’ll see in Part 3, by using mouseover interactions you should be able to select which data attribute defines the color scale in the plot.
You should also be sure to set the class of each path to
datapath for easy access in interactions.
Part 2: Drawing the Axes
To draw the axes, you will complete the other 3 selections. Specifically, you will need a selection that maps from
dims to SVG groups (
g) that will contain the axes. Each axis group should be given the class
To draw the axes, you will need to both appropriately transform the group as well as make sure you call the appropriate axis function that we initialized. For the transforms, you should translate each axis by an \(x\)-value corresponding to its
To call the axes functions, we will use a d3 trick, using the function
.each() so that we can call the appropriate axis function. Your
.each() functions should look something like this:
See this link for more information about the specifics of
d3.select(this) returns the current selection, and then we call the appropriate axis function
axis[d] for that selection.
.each() allows for anonymous functions so that we can select per datum
Besides calling the axes functions, you will also have to create text labels above each axis using the class
label. This should be created similarly, but you’ll transform them to be centered above each axis. The array
dimNames stores a string for each dimension’s label text. You’ll also want to make sure each text label is associated with the
Finally, for each axis we will create a brush group, given the class
brush. Like with axes, you’ll use
.each() to call the appropriate brush function that we created in the initialization. And, like with axes, you’ll have to transform this to the appropriate location to align with the axis.
For all three of this selections, you may want to do you data join using keys so that you can associate each axis/label/brush with the name of the dimension rather than the dimension’s index in
dims. This will be necessary since we will reorder the axes by reordering the array
dims, so we’ll track columns by attribute name rather than index.
Part 3: Interaction
You will implement three interactions.
First, when a text label is clicked, you should swap it with the text label that is immediately to its right. If the rightmost label is clicked, it should swap with the one immediately to its left.
To do so, you should swap the location of the dimension in the
dims array. After doing this, you can:
xScale, specifically you will change its domain
- Rebind the data for axes/labels/brushes, and redefine their transforms using the updated
- Rebind the data for the data paths, and update the positions of all polylines, again using the updated
All of the above should be transitioned using a duration of 1 second for each click.
For our second interaction, when the mouse is moved over a text label, you should update which color scale is used to draw the data items. To do this, you will use the
onMouseOver callback. Luckily, you do not have to adjust the
dims array for this. Instead, you can simply look at which
dim was passed into the callback, and you can do a data join on the datapaths and set the appropriate color scale from
colorScales. You should also bold the label of the column whose color scale is active, which can be achieved by setting all labels to be normal font weight, and then using filters to select the active label and setting its font weight to bold.
Like for mouse clicks, you should apply a transition so that the user can see the color scale updating.
For our third interaction, the user should be able to brush over a particular axis and this will select a subset of the data. If the user brushes over multiple axes, the selection must satisfy all selected ranges.
We will implement this similar to A02, by defining a function
isSelected() which should return
true for any data element that is within the range. The brushes we created are 1-dimensional, using
d3.brushY(), which means that their selected ranges are a bit simpler. Specifically,
 is the minimum of the range and
 is the maximum. So, for a given dimension
brushRanges[dim] is the minimum \(y\) (in pixels) and
brushranges[dim] is the maximum \(y\) (in pixels).
isSelected() function should check all dimensions, and for an input data element
d is within the range of each brush. If an axes’s brush is turned off,
brushRanges for that dimension will be
All selected elements should be set to have an opacity of 0.75, whereas elements that are not selected should have an opacity of 0.1. If no brushes are enabled, then all elements should have opacity of 0.75.
Written Questions (worth 15/80 points)
Each written question should be answered with a brief paragraph (approximately 100 words or less) and appear below your parallel coordinates plot in
index.html. I will not read extra material if I deem the answer too long.
What design principle(s) did you consider when choosing your color scale in this assignment?
action,targetpair to describe one possible task for each of the three interactions you were asked to implement in this assignment. As you might imagine, these interactions could achieve multiple tasks, you only need to describe one task for each interaction.
Describe one advantage of parallel coordinates over scatterplot matrices. Describe one disadvantage.
You should use git to submit all source code files. The expectation is that your code will be graded by cloning your repo and then executing it within a modern browser (Chrome, Firefox, etc.)
Please provide a
README.md file that provides a text description of how to run your program and any parameters that you used. Also document any idiosyncrasies, behaviors, or bugs of note that you want us to be aware of.
To summarize, my expectation is that your repo will contain:
d3.v5.jsplus any others you require)
.cssfiles containing style information
|Bugs or syntax errors||Up to -10 each bug at grader's discretion to fix|
Point Breakdown of Features
|External documentation (README.md) following the template provided in the base repository||5|
|Consistent modular coding style, indentation, etc.||5|
|Header documentation and internal documentation (Block for functions and Inline descriptive comments). Wherever applicable / for all files||5|
|Expected output / behavior based on the assignment specification, including|
Cumulative Relationship to Final Grade
Worth 8% of your final grade
Implementing features above and beyond the specification may result in additional extra credit, please document these in your README.md.
Notably, you may want to consider interactions that include:
- Inverting axes (e.g. switching their minimum and maximum)
- Using a drag-and-drop interface to reorder the axes instead of clicking to swap
- Augmenting the visualization to include any non-numeric attributes (e.g. the automobile name)
- Augmenting the visual interface to show other statistical information per axis
- Generally improving upon the visual look-and-feel of parallel coordinates. If you find other inspirational examples from the web, be sure to appropriate reference them in your README.