【问题标题】:D3 Inverting Color Scale Map to get amplitudeD3 反转色标图以获取幅度
【发布时间】:2018-07-19 16:31:55
【问题描述】:

我有一个 colorscaleMap 让我们假设是这样的

d3.scale.linear()
 .domain([0.01,0.02,0.03,0.04,0.05])
 .range(["#5100ff", "#00f1ff", "#00ff30", "#fcff00", "#ff0000"])

现在我想使用反转功能

假设 invert("#5100ff") 我期望输出为 0.01,但 invert 功能不适用于非数字值。如https://github.com/d3/d3-scale 中所述 仅当范围是数字时才支持此方法。如果范围不是数字,则返回 NaN

问题:- 这与具有超过 10000 个数据点的大型数据频谱图有关。 Y - 轴是频率,X 轴是时间,颜色代表幅度。我想写索引函数会效率低下

有没有人能解决这个问题?

【问题讨论】:

  • 有什么理由不能自己制作索引还是必须通过缩放功能?
  • 我应该详细说明这个问题与具有超过 10000 个数据点的大数据频谱图有关。 Y - 轴是频率,X 轴是时间,颜色代表幅度。我想编写索引函数会效率低下。
  • 我认为拥有多少数据点并不重要。如果你有一个域和对应的范围,它应该不是一个大的索引问题。

标签: javascript d3.js charts


【解决方案1】:

这不是一件容易的事,如果可能的话,最好通过使用绑定数据来避免这个问题。

但是,这是一个非常有趣的问题。当反转数字时,您至少绑定到一个维度,数字将落入可插值的域内(有一些特定的例外)。使用三维色彩空间,这会变得有点困难,尤其是对于分段域和范围。但是,即使我们将颜色提供给无法由刻度产生的反转功能,事情仍然很困难。

如前所述,d3-scale 不支持非数值反转,d3-interpolate 也不提供反转支持。我们也不能有连续尺度的非数字域(这将提供最简单的解决方案)。在不重写每个颜色插值器的情况下,任何解决方案都可能特定于某个插值(默认是每个通道上的线性插值,所以我将在下面使用它)

如果我们能够解决上述问题,最后一个挑战是您不能拥有大量的精度:RGB 通道值被四舍五入为从 0 到 255 的 8 位数字。反转这个数字会产生舍入错误。

这种舍入问题通常会因许多色标在任何给定通道上不使用 0 - 255 的完整范围这一事实而更加复杂。如果从红色到橙色,变化最大的通道是绿色,变化165。这肯定会降低任何反演函数的精度。

也就是说,我们可以为具有颜色空间范围的比例制作简单的反转函数。这可能需要几个步骤:

  1. 找到在该范围的最小值和最大值之间具有最大范围的颜色通道(最小值和最大值分别为:a,b)。
  2. 从需要反转的颜色中提取相同的颜色通道:y
  3. ab 之间对y 进行反插值,得到t
  4. t 并在刻度域的最小值和最大值之间进行插值

线性插值器看起来像:y = a + t * (b - a)

我们可以使用以下代码进行反插值:t = (y - a)/(b - a)

t 是一个介于 0 和 1 之间的数字,其中(当应用于插值器时)0 将产生域中的最小值,而 1 将产生域中的最大值。 p>

这可能是这样的:

var scale = d3.scaleLinear()
  .domain([0,100])
  .range(["orange","red"])


 scale.invertColor = function(x) {
    // get the color into a common form
    var color = d3.color(x); 
    
    // do the same for the range:
    var min = d3.color(this.range()[0]);
    var max = d3.color(this.range()[1]);
	
    // find out which channel offerrs the greatest spread between min and max:
    var spreads = [max.r-min.r,max.g-min.g,max.b-min.g].map(function(d) { return Math.abs(d); });
    var i = spreads.indexOf(Math.max(...spreads));
    var channel = d3.keys(color)[i];
	
    // The deinterpolation function	
    var deinterpolate = function (value,a,b) {
      if(b-a==0) return 0;  
      return Math.abs((value - a) / (b - a))
    }

	// Get t
	var t = deinterpolate(color[channel],min[channel],max[channel]);

    // Interpolate between domain values with t to get the inverted value:
    return d3.interpolate(...this.domain())(t)

};
  

  
var test = [0,10,50,90,100];
console.log("Test values:");
console.log(test)
console.log("Scaling and then Inverting values:");
console.log(test.map(function(d) { return scale.invertColor(scale(d)); }));
console.log("Original values minus scaled and inverted values:");
console.log(test.map(function(d) { return (d - scale.invertColor(scale(d))); }));
<script src="https://d3js.org/d3.v5.min.js"></script>

上面有限制,你可以喂这个任何颜色。它会为您插入一个值,即使该比例无法产生该值。

通过多次使用上述函数可以很容易地为多个段修改它。我们可以检查每个段,以查看一个值一旦反转,是否会针对每个段缩回至自身。这样做只能反转刻度产生的颜色:

var scale = d3.scaleLinear()
  .domain([0,100,200])
  .range(["orange","red","purple"])


scale.invertColor = function(x) {
  var domain = this.domain();
  var range = this.range();
		
  // check each segment to see if the inverted value scales back to the original value:
  for(var i = 0; i < domain.length - 1; i++) {
    var d = [domain[i], domain[i+1]];
    var r = [range[i], range[i+1]];
		  
    var inverted = (invert(x,d,r))
    if (d3.color(this(inverted)).toString() == d3.color(x).toString()) return inverted;		
  }
  return NaN;  // if nothing matched
		
		
  function invert(x,d,r) {
    // get the color into a common form
    var color = d3.color(x); 
        
    // do the same for the range:
    var min = d3.color(r[0]);
    var max = d3.color(r[1]);
    	
    // find out which channel offerrs the greatest spread between min and max:
    var spreads = [max.r-min.r,max.g-min.g,max.b-min.g].map(function(d) { return Math.abs(d); });
    var i = spreads.indexOf(Math.max(...spreads));
    var channel = d3.keys(color)[i];
    	
    // The deinterpolation function	
    var deinterpolate = function (value,a,b) {
      if(b-a==0) return 0;  
      return Math.abs((value - a) / (b - a))
    }

    // Get t
    var t = deinterpolate(color[channel],min[channel],max[channel]);

    // Interpolate between domain values with t to get the inverted value:
    return d3.interpolate(...d)(t)	
  }
		
};
      

      
    var test = [0,10,50,90,100,110,150,190,200];
    console.log("Test values:");
    console.log(test)
    console.log("Scaling and then Inverting values:");
    console.log(test.map(function(d) { return scale.invertColor(scale(d)); }));
    console.log("Original values minus scaled and inverted values:");
    console.log(test.map(function(d) { return (d - scale.invertColor(scale(d))); }));
&lt;script src="https://d3js.org/d3.v5.min.js"&gt;&lt;/script&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 2019-09-29
    • 2016-12-02
    • 2023-03-16
    • 2017-08-29
    • 1970-01-01
    • 2023-04-07
    相关资源
    最近更新 更多