Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

0 fill null values with d3 series data

I'm working on a multi-line chart in D3 but am having some issues with the rendering. I'm trying to render two lines with data that looks like this:

[
    { key:"line 1",  values: [ {x:1, y:1}, {x:2, y:2} ] },
    { key:"line 2" , values: [ {x:1, y:1}, {x:2, y:2}, {x:3, y:3} ] }
]

"line 2" renders just fine, but "line 1" stops rendering at x = 2. I understand that my dataset might be considered incomplete, but I was curious if there is a way to set a default value of 0 where there are gaps or null values for x? Specifically in this example I want "line 1" to display y=0 where x=3.

like image 533
Dan Ramos Avatar asked Jun 11 '13 21:06

Dan Ramos


1 Answers

D3 and NVD3 don't have a function to do this. Your best bet is to insert your missing values. Some CoffeeScript that uses some Underscore utilities, but d3 had many of the same functions, so you shouldn't need to necessarily depend on Underscore if you don't need it. First you get the list of domain values, then you insert your missing values.

The method getDomainValues takes data and a dimensions (a key) and pulls all the values to get the set of domain values. If you had data like:

   key : a
    values : [ {x: 4, y:1},  {x: 5, y:1}, {x: 7, y:1} ]
   key : b
    values : [ {x: 1, y:10}, {x: 2, y:5}, {x: 3, y:0} ]

It would return:

[1,2,3,4,5,7]


getDomainValues = (data, dimension) ->
 _.uniq _.flatten _.map data, (item) -> _.pluck(item.values, dimension)

The method insertValues inserts default values (most commonly 0) for dimension values not present in the values array. It sorts by dimension. If you had data like:

  key : a
    values : [ {x: 4, y:1},  {x: 2, y:1}, {x: 1, y:1} ]

And supplied a domain [0, 1,2,3,4,5] and a value of -10 you'd get:

  key : a
    values : [ {x: 0, y:-10}, {x: 1, y:1},  {x: 2, y:1}, {x: 3, y:-10}, {x: 4, y:1}, {x: 5, y: -10}]


insertValues = (data, value, dimension, metric, domain)->    
 defaults = {} 
 for item in domain
   v = {}
   v[dimension] = item
   v[metric] = value
   defaults[item] = v

 _.each data, (item) -> 
  vals = _.groupBy( item.values, (i) -> i[dimension])
  vals = _.flatten _.values _.defaults vals, defaults
  vals = _.sortBy vals, dimension 
  item.values = vals
 data

So you might call them like this:

data = [
    { key:"line 1",  values: [ {x:1, y:1}, {x:2, y:2} ] },
    { key:"line 2" , values: [ {x:1, y:1}, {x:2, y:2}, {x:3, y:3} ] }
]

domain = getDomainValues data, 'x'
filledData = insertValues data, 0, 'x', 'y', domain

See it in JavaScript: https://gist.github.com/FaKod/e6724675e4ebaf9f8fa4

like image 164
WolfgangCodes Avatar answered Oct 06 '22 14:10

WolfgangCodes