What characterizes "scientific" visualization?
Commonly, data will be represented in "scientific visualization" through one of a few mechanisms:
For this, we will be using Python to collaboratively explore what volume-filling data is.
Install yt and ipyvolume.
conda install -c conda-forge yt ipyvolume
We will acquire a bit of data.
Go to https://yt-project.org/data/ and download "IsolatedGalaxy" and uncompress it.
import yt
ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")
ds.r[:].max("density", axis="z")
To analyze volume data, typically we conduct one or more of these operations:
This can include operations such as volume rendering, axial projection, histogramming/binning and resampling.
Let's try some of this out.
How do we deal with data that is too large to fit into memory?
Some operations we can manually cycle through, storing only reductions in memory rather than the full dataset.
Clear candidates:
Is this the same as incremental updates to a dataset?
(What about the median?)
GitHub pages is a simple, straightforward way to publish websites.
Jupyter Notebooks can be published online. The simplest way:
Widget state can be saved in many cases.
[username].github.io
gh-pages
and master
We're going to learn a very small bit of d3.js.
The easiest way to utilize D3 is through observablehq.com:
d3 = require("d3@5");
although it is straightforward to include the necessary code in an HTML page:
<script src="https://d3js.org/d3.v5.min.js"></script>
The basic concepts here we will convey, focusing on static visualizations:
.select()
and .selectAll()
.enter()
.attr()
and .style()
d3.scaleLinear()
When we manipulate items in d3, we connect the concept of a data item to an element in a document.
Typically, this will be an element in an SVG -- for instance, a line
,
circle
, rect
or text
element.
Our typical workflow:
We will very frequently run into the case where we call something, and supply a
function to it, rather than supplying a value. For instance, both of these
calls to attr
are valid in a typical d3 workflow:
.attr("cx", 1.0)
// or
.attr("cx", d => d.x)
In one, we are setting the value static; in the other, we base the value on the datapoint supplied.
For our first exploration, let's just make some circles. I will demonstrate this in observable, but here is the key snippet of code:
var dataset = [ {'x': 100, 'y': 200, 'radius': 15},
{'x': 150, 'y': 300, 'radius': 30} ];
svg.selectAll("circle").data(dataset).enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.radius)
.style("fill", "black");
What does this do?
We will mostly use the objects
rect
-- which has x
, y
, width
and height
circle
-- which has cx
, cy
, and r
line
-- which has x1
, x2
, y1
and y2
.Each of these objects can be controlled in style, appearance, position, etc, according to standard SVG and CSS rules.
Let's experiment!
To map from a given range to a different range in a linear fashion, we can construct a linear scale:
var xScale = d3.scaleLinear().range([0, 100]).domain([0.0, 1.0]);
This is now an object we can use to map the values 0 .. 1 to 0 .. 100. This is useful for, among other things, computing the position of a given value:
.attr("cx", d => xScale(d.x))
d3.axisBottom(xScale)
can be used to create ticks and axes; however, it
requires manual translation using the transform
attribute, using something
like .attr("transform", "translate(0, 30)")
. This then brings up our concept
of margins, width, height, and the like! How can we manage these?
Let's try it out!
We'll cover more d3, as well as some additional fun topics.