Getting to know... D3

Today I was looking into ObservableHQ and their Quickstart page had many suggested resources. The second of these resources was titled "Learn D3." Since it's been several years since I've worked with D3, I felt it would be appropriate to go through it. In addition, at the time I thought the resource would lean heavily towards learning D3 within the context of an Observable notebook. Instead, the series was dominantly a comprehensive introduction on D3, delivered via a series of Observables, and occasionally touched on some of the benefits of using an Observable notebook. In hindsight, I should have expected the relationship between an Observable notebook and D3 to be more like that between a Jupyter notebook and a Python module: learning to use the features of Jupyter and learning to use a particular Python module are mutually exclusive activities.

In any case, below you'll find my notes to each of the sections in the Observable series "Learn D3" by Mike Bostock.


- Reasons for learning D3: popular, flexible, and renowned for animation and interaction.
- Vega-Lite mentioned as a tool "expressly intended for exploratory visualization."
- Observable as a bridge across the complexity of D3 in order for the user to access the power of D3.

By Example

- Another example of importing an object (in this case a histogram example someone has already built) and then substituting values in for the data.


- First talks about bringing in data via a file attachment
- Some methods on parsing a CSV file and it's contents
- An interesting note concerns named cells. Only named cells can be referred to in other places. Only the final value is referenced. Local variables inside the cell are not visible to the rest of the notebook.
- Due to the previous bullet, typically a cell should define a named value to be referenced by other cells or produce a piece of information.
- There's a suggestion to avoid implicit dependencies; an example is given where one cell defines an array and another modifies it in-place


Ah, yes. Scales. Essentially a scale is a mapping of the data. I really like scales because they place the power of adjusting the display of the data in the hands of the developer. In particular, it separates the data from the visualization. For example, when making a graph in Python, there are times where I have to prep the data so that it can be graphed appropriately on a log scale.

- One convention mentioned by example is that the scale returns the value of the point within the canvas.
- Another convention is that the xy-position of a bar is it's top-right corner.


- Lines: d3.line()
- Area Graphs: d3.area()
- Arcs (i.e., annulus sector): d3.arc()


- transition()
- d3.interpolate()
- timing can be done using D3, Observable and a scrubber, or Observable and generators.


The overall concept of this section is easy to understand but the code takes more time to digest. With that being said, I might not have properly learned to distinguish among "enter," "update," and "exit" (or "join") when I first learned about selections. In short, comparing the data that was part of the selection ("old data") and the new data that is being associated ("new data"), then "enter" consist of elements in "new data" not found in "old data" (i.e., data which needs to "enter"), "update" is the overlap (i.e., data whose attributes might need to be updated), and "exit" consists of elements in "old data" not found in "new data" (i.e., data which needs to "exit").

- First graphic is static. 1) Create an svg. 2) Make a selection of all "text" elements (it is empty), associate the `alphabet` data to it, and join that data. In this case, all the letters are "new data." As such, each of these elements is given an x-position based on it's index (`i * 17`) and the text attribute being associated will be the letter itself.
- The second graphic continues to change as the variable `randomLetters` is being changed every 3 seconds. 1) Create an svg. 2) Make a selection stored to `text`. 3) Create a function that when called with an array as an argument will update the graphic. With respect to code, the body of the function is the same as the step in the static graphic. The distinction is that it receives a set of letters that may or may not have some overlap with the letters being displayed. Now, there is a set of enter and updated letters which will have their attributes reassigned and the set of exit letters which will silently be removed.
- The third graphic is similar to the second graphic but specifies different functions to enter, update, and exit during the join in order to reduce the number of operations. Specifically, the second graphic was reassigning all attributes to every element in both enter and update. However, it is only necessary to reassign the "x" attribute to every element in both enter and update; separately, during the join, we only need to assign the other attributes to the new elements.
- The fourth graphic is similar to the third graphic but adds transitions. 1) Create an svg. 2) Make a selection of all "text elements." 3) Create an update function which starts by creating a transition. Separately create a selection. During the join, the exit elements are assigned a transition to be removed, where their removal occurs by a shift down (an increase in their "y" position from 17 to 41). Meanwhile, the enter elements are given a starting "y" position of -7 as well as a starting "x" position. After the join, all elements in enter and update are moved to their appropriate positions. This gives the effect of elements in enter to be shifting down into position and elements in update to be shifting left or right towards their appropriate place.


Briefly talks about Tooltips and some other controls. By this point, I'm ready to take a break.

Further Topics


Popular posts from this blog

PySpark + Anaconda + Jupyter (Windows)

Using tensorflowjs_converter