What is the difference D3 datum vs. data?
Asked Answered
C

5

228

Can someone please explain the difference between datum() and data() in D3.js? I see both being used and I am not sure why you should choose one over the other?

Convey answered 5/12, 2012 at 16:57 Comment(0)
C
187

I found the correct answer here from Mike himself:

D3 - how to deal with JSON data structures?

If you want to bind your data to a single SVG element, use

(...).data([data])

or

(...).datum(data)

If you want to bind your data to multiple SVG elements

(...).data(data).enter().append("svg")

.....

Convey answered 5/12, 2012 at 19:33 Comment(3)
Thank you for this! the fact that you put data([data]) passing and array just helped me realize a bug I couldn't figure out for the past week! Thank you so much... always such stupid things that are wrong.Weingartner
data() performs a join, datum() does not.Wafer
Just keep in mind, in case if there are more data array elements than SVG elements when binding data with enter(), d3 will bind rest of array elements with newly created SVG elements.Guinea
B
69

After having looked into this a bit, I've found that the answers here on SO are not complete as they only cover the case when you invoke selection.data and selection.datum with an input data parameter. Even in that scenario, the two behave differently if the selection is a single element versus when it contains multiple elements. Moreover, both of these methods can also be invoked without any input arguments in order to query the bound data/datum in the selection, in which case they once again behave differently and return different things.

Edit - I posted a slightly more detailed answer to this question here, but the post below pretty much captures all the key points regarding the two methods and how they differ from each other.

When supplying data as an input argument

  • selection.data(data) will attempt to perform a data-join between the elements of the data array with the selection resulting in the creation of enter(), exit() and update() selections that you can subsequently operate on. The end result of this is if you pass in an array data = [1,2,3], an attempt is made to join each individual data element (i.e. datum) with the selection. Each element of the selection will only have a single datum element of data bound to it.

  • selection.datum(data) bypasses the data-join process altogether. This simply assigns the entirety of data to all elements in the selection as a whole without splitting it up as in the case of data-joins. So if you want to bind an entire array data = [1, 2, 3] to every DOM element in your selection, then selection.datum(data) will achieve this.

Warning: Many people believe that selection.datum(data) is equivalent to selection.data([data]) but this is only true if selection contains a single element. If selection contains multiple DOM elements, then selection.datum(data) will bind the entirety of data to every single element in the selection. In contrast, selection.data([data]) only binds the entirety of data to the first element in selection. This is consistent with the data-join behavior of selection.data.

When supplying no data input argument

  • selection.data() will take the bound datum for each element in the selection and combine them into an array that is returned. So, if your selection includes 3 DOM elements with the data "a", "b" and "c" bound to each respectively, selection.data() returns ["a", "b", "c"]. It is important to note that if selection is a single element with (by way of example) the datum "a" bound to it, then selection.data() will return ["a"] and not "a" as some may expect.

  • selection.datum() only makes sense for a single selection as it is defined as returning the datum bound to the first element of the selection. So in the example above with the selection consisting of DOM elements with bound datum of "a", "b" and "c", selection.datum() would simply return "a".

Note that even if selection has a single element, selection.datum() and selection.data() return different values. The former returns the bound datum for the selection ("a" in the example above) whereas the latter returns the bound datum within an array (["a"] in the example above).

Hopefully this helps clarify how selection.data and selection.datum() differ from each other both when providing data as an input argument and when querying for the bound datum by not providing any input arguments.

PS - The best way to understand how this works is to start with a blank HTML document in Chrome and to open up the console and try adding a few elements to the document and then start binding data using selection.data and selection.datum. Sometimes, it's a lot easier to "grok" something by doing than by reading.

Binford answered 24/1, 2017 at 2:37 Comment(1)
HamsterHuey has shown this already, but it might be a helpful reminder to recall that "datum" is singular and "data" is plural. Hence .datum applies to a single element's associated information.Viceregal
D
42

Here are some good links:

Per the latter:

# selection.data([values[, key]])

Joins the specified array of data with the current selection. The specified values is an array of data values, such as an array of numbers or objects, or a function that returns an array of values.

...

# selection.datum([value])

Gets or sets the bound data for each selected element. Unlike the selection.data method, this method does not compute a join (and thus does not compute enter and exit selections).

Drypoint answered 5/12, 2012 at 17:7 Comment(3)
given those definitions - i am still confused why you would ever need/want to use datum()Convey
One more example that might help makes things clearer: ngokevin.com/blog/d3. NOTES: 1) Kevin's definition: "The datum is the data binded to the element." 2) Note how in Kevin's examples we "incorporate" the dataset with "data()" ... but we "use" a subset by referencing a "datum()".Drypoint
@Convey Just incase anyone else comes here 8 years later. The difference between data and datum becomes obvious when dealing with a lot of nodes. Try creating a line chart using every daily close price for Amazon. With data you will end up with A LOT of path nodes (and a huge svg). With datum you only have one path node. The difference is a factor of 1000.Beria
E
7

I think the explanation given by HamsterHuey is the best so far. To expand on it and give a visual representation of the differences I created a sample document that illustrates at least part of the differences between data and datum.

The below answer is more of an opinion derived from using these methods, but I am happy to be corrected if I'm wrong.

This example can be run below or in this Fiddle.

const data = [1,2,3,4,5];
const el = d3.select('#root');

 el
  .append('div')
  .classed('a', true)
  .datum(data)
  .text(d => `node => data: ${d}`);

const join= el
.selectAll('div.b')
.data(data);

join
.enter()
.append('div')
.classed('b', true)
.text((d, i) => `node-${i + 1} => data: ${d}`)

I think that datum is simpler to grasp since it doesn't do a join, but of course this also mean it has different use cases.

To me one big difference - although there are more - is the fact that data is just the natural way of doing (live) updates on a d3 chart, as the whole enter/update/exit pattern makes it simple, once you get it.

datum on the other hand seems to me to be more suited for static representations. In the example below for example I could achieve the same result my looping on the original array and accessing the data by index like so:

data.map((n, i) => {
 el
  .append('div')
  .classed('a', true)
  .datum(data)
  .text(d => `node-${n} => data: ${d[i]}`);
});

Try it here: https://jsfiddle.net/gleezer/e4m6j2d8/6/

Again, I think this is way easier to grasp as you keep free from the mental burden coming from the enter/update/exit pattern, but as soon you need to update or change the selection you will surely be better off resorting to .data().

const data = [1,2,3,4,5];
const el = d3.select('#root');

 el
  .append('div')
  .classed('a', true)
  .datum(data)
  .text(d => `node => data: ${d}`);

const join= el
.selectAll('div.b')
.data(data);

join
.enter()
.append('div')
.classed('b', true)
.text((d, i) => `node-${i + 1} => data: ${d}`)
/* Ignore all the css */
html {
  font-family: arial;
}

.l {
  width: 20px;
  height: 20px;
  display: inline-block;
  vertical-align: middle;
  margin: 10px 0;
}
.l-a {
  background: #cf58e4;
}
.l-b {
  background:  #42e4e4;
}

.a {
  border-bottom: 2px solid #cf58e4;
}

.b {
  border-bottom: 2px solid #42e4e4;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>


<div style="margin-bottom: 20px;">
  <span class="l l-a"></span> .datum() <br />
  <span class="l l-b"></span> .data()
</div>

<div id="root"></div>
Elevated answered 21/8, 2018 at 10:18 Comment(0)
B
2

Basically, the difference between data and datum is the number of elements bound. Data is for a list of elements while datum specifies only one element. For example, d3.selectAll('div').data([1,2,3]) means to bind a list of elements (i.e., 1,2,3) to three different div. You might then chain the code with .enter().append('div') to create a new div for each new element. On the other side, d3.select('div').datum(1) means to bind element 1 to the div element.

You might note that we can also do something like d3.select(div).datum([1,2,3]). It is not the same as d3.selectAll('div').data([1,2,3]). d3.select(div).datum([1,2,3]) treats [1,2,3] as only one element to bind to the div element, just like div => [1,2,3]. While d3.selectAll('div').data([1,2,3]) treats [1,2,3] as a list of element such that it will bind each of the element inside the array to each div element, just like div => 1; div => 2; div => 3.

Burgeon answered 28/10, 2022 at 5:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.