Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exp scale #48

Closed
wants to merge 4 commits into from
Closed

Exp scale #48

wants to merge 4 commits into from

Conversation

spenczar
Copy link

I've added an exponential scale. This is the inverse of the logarithmic scale. It's not sufficient to merely use scaleLog().invert because that's just a method, not a full scale object, so it doesn't work happily with many other APIs like axis generators.

I've neglected to write any documentation in this first commit because I want to get feedback first. I've also not written quite so many tests as perhaps I should, and much of the code elsewhere in this repo shows scars of having dealt with wacky inputs; I haven't tried very hard to anticipate all the possible wackiness out there and have left things pretty simple.

@mbostock
Copy link
Member

Interesting. Do you have an example of where an exponential scale is useful?

@spenczar
Copy link
Author

This came up for me while working on computing and rendering kernel density estimates in log-space.

Imagine a histogram-type data structure, with buckets spaced at logarithmic intervals. One way to represent this is explicitly in a big array:

var h = [
  {'bucket': 1e0, 'count': 100},
  {'bucket': 1e1, 'count': 200},
  {'bucket': 1e2, 'count': 250},
  {'bucket': 1e3, 'count': 120}
];

This is simple in the case of the four elements 1e0, 1e1, 1e2, and 1e3. But it gets much more tough when you want to (say) evenly-space the buckets in some other range, like [60, 15000], and you want 60 buckets.

You could work on it yourself, but the domain/range syntax provided by a scale is much nicer:

var n = 60,
    exp = d3.scale.exp().domain([0, n]).range([60, 15000]),
    h = [];
for (var i = 0; i < n; i++) {
  h.push({'bucket': exp(i), 'count': 0});
}

It's also much nicer for things like rescaling, which came up for me in writing a streaming log-space histogram.

@spenczar
Copy link
Author

The latest test fails - there is something wrong in my interpolation. Sorry for the premature PR!

@spenczar
Copy link
Author

Okay, things are back - I missed a variable which needed to update when rescaling.

@mbostock
Copy link
Member

That makes sense, though in your example you can easily replace the exp scale with log.invert:

var n = 60,
    log = d3.scaleLog().domain([60, 15000]).range([0, n]),
    h = [];
for (var i = 0; i < n; i++) {
  h.push({'bucket': log.invert(i), 'count': 0});
}

(And it feels slightly more natural to call [60, 15000] the domain, since it’s a dimension of abstract data.)

Can you expand this example, or provide another one, so that I can see how you’d use the exp scale as “a full scale object” as you suggested in the opening comment? So far, it seems like you could use log.invert to construct the histogram, and then maybe use a linear scale to display the histogram if you didn’t want to show the log transform.

@spenczar
Copy link
Author

You know, I think you're right.

Originally, I had been under the impression that I could then pass the exp scale I used to something like a d3.svg.axis() and get beautiful ticks. But this is wrong, of course, because the scale for visualization needs to be entirely different, mapping values to width on the page.

Well, at least I learned stuff by trying to write a scale. Thanks for thinking this through, @mbostock!

@spenczar spenczar closed this Jan 12, 2016
@spenczar spenczar deleted the exp-scale branch January 12, 2016 19:57
@mbostock
Copy link
Member

Thanks for your patience, and glad I could help. Plus, we’ll have a headstart if someone else discovers a use for an exp scale. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants