Inversion with ordinal scale

匿名 (未验证) 提交于 2019-12-03 01:59:02

问题:

Is there any way to find inversion of ordinal scale?

I am using string value on x axis which is using ordinal scale and i on mouse move i want to find inversion with x axis to find which string is there at mouse position?

Is there any way to find this?

var barLabels = dataset.map(function(datum) {     return datum.image; }); console.log(barLabels); var imageScale = d3.scale.ordinal()         .domain(barLabels)         .rangeRoundBands([0, w], 0.1); // divides bands equally among total width, with 10% spacing. console.log("imageScale...................."); console.log(imageScale.domain());   .  . var xPos = d3.mouse(this)[0]; xScale.invert(xPos); 

回答1:

I actually think it doesn't make sense that there isn't an invert method for ordinal scales, but you can figure it out using the ordinal.range() method, which will give you back the start values for each bar, and the ordinal.rangeBand() method for their width.

Example here: http://fiddle.jshell.net/dMpbh/2/

The relevant code is

    .on("click", function(d,i) {         var xPos = d3.mouse(this)[0];         console.log("Clicked at " + xPos);         //console.log(imageScale.invert(xPos));         var leftEdges = imageScale.range();         var width = imageScale.rangeBand();         var j;         for(j=0; xPos > (leftEdges[j] + width); j++) {}             //do nothing, just increment j until case fails         console.log("Clicked on " + imageScale.domain()[j]);     }); 


回答2:

I found a shorter implementation here in this rejected pull request which worked perfectly.

var ypos = domain[d3.bisect(range, xpos) - 1]; 

where domain and range are scale domain and range:

var domain = x.domain(),     range = x.range(); 


回答3:

I have in the past reversed the domain and range when this is needed

> var a = d3.scale.linear().domain([0,100]).range([0, w]); > var b = d3.scale.linear().domain([0,w]).range([0, 100]);  > b(a(5)); 5 

However with ordinal the answer is not as simple. I have checked the documentation & code and it does not seem to be a simple way. I would start by mapping the items from the domain and working out the start and stop point. Here is a start.

imageScale.domain().map(function(d){     return {         'item':d,         'start':imageScale(d)     }; }) 

Consider posting your question as a feature request at https://github.com/mbostock/d3/issues?state=open in case

  1. There is sufficient demand for such feature
  2. That I haven't overlooked anything or that there is something more hidden below the documentation that would help in this case


回答4:

I recently found myself in the same situation as OP.

I needed to get the inverse of a categorical scale for a slider. The slider has 3 discrete values and looks and behaves like a three-way toggle switch. It changes the blending mode on some SVG elements. I created an inverse scale with scaleQuantize() as follows:

var modeArray = ["normal", "multiply", "screen"]; var modeScale = d3.scalePoint()   .domain(modeArray)   .range([0, 120]); var inverseModeScale = d3.scaleQuantize()   .domain(modeScale.range())   .range(modeScale.domain()); 

I feed this inverseModeScale the mouse x-position (d3.mouse(this)[0]) on drag:

.call( d3.drag()     .on("start.interrupt", function() { modeSlider.interrupt(); })     .on("start drag", function() { inverseModeScale(d3.mouse(this)[0]); }) ) 

It returns the element from modeArray that is closest to the mouse's x-position. Even if that value is out of bounds (-400 or 940), it returns the correct element.

Answer may seem a bit specific to sliders but posting anyway because it's valid (I think) and this question is in the top results for " d3 invert ordinal " on Google.

Note: This answer uses d3 v4.



回答5:

I understand why Mike Bostock may be reluctant to include invert on ordinal scales since you can't return a singular true value. However, here is my version of it.
The function takes a position and returns the surrounding datums. Maybe I'll follow up with a binary search version later :-)

function ordinalInvert(pos, scale) {     var previous = null     var domain = scale.domain()     for(idx in domain) {         if(scale(datum[idx]) > pos) {             return [previous, datum[idx]];         }         previous = datum[idx];     }     return [previous, null]; } 


回答6:

I solved it by constructing a second linear scale with the same domain and range, and then calling invert on that.

  var scale = d3.scale.ordinal()     .domain(domain)     .range(range);   var continousScale = d3.scale.linear()     .domain(domain)     .range(range)    var data = _.map(range, function(i) {     return continousScale.invert(i);   }); 


回答7:

If you just want to know which mouse position corresponds to which data, then d3 is already doing that for you.

.on("click", function(d,i) {     console.log("Clicked on " + d); }); 

I have updated the Fiddle from @AmeliaBR http://fiddle.jshell.net/dMpbh/17/



回答8:

You can easily get the object's index/data in callback

.on("click", function(d,i) {     console.log("Clicked on index = " + i);      console.log("Clicked on data  = " + d);      // d == imageScale.domain()[1] });  

d is the invert value itself.
You don't need to use obj.domain()[index] .



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!