【问题标题】:Use the right color scale for data skewed to right对向右倾斜的数据使用正确的色标
【发布时间】:2024-04-16 03:10:01
【问题描述】:

我有数据域为 [0, 100] 但它们不均匀,它们非常向右倾斜,即它们的值大多在 80 到 100 之间。

我想用这些数据进行可视化并使用颜色来区分它们(例如:热图、地图……)。

什么是正确的色标?

我尝试了线性刻度、幂刻度和分位数刻度。 最好的似乎是分位数刻度,但仍然存在问题:

  1. 我想更好地区分最后一个值(右边的值)的颜色,它们看起来都是黑色的,可以吗?
  2. 在这个例子中我使用了灰度,如果我想使用 ViridisMagma 比例?

这是我的代码。

function sortNumber(a, b) {
  return a - b;
}

var data = [90, 95, 50, 1, 99, 89.1, 87, 94, 95, 99, 99.5, 94.3, 96, 97, 85, 74, 66, 92, 68, 91, 93, 87, 79, 86, 89, 93.5, 97, 98];
data.sort(sortNumber); // [1, 50, 66, 68, 74, 79, 85, 86, 87, 87, 89, 89.1, 90, 91, 92, 93, 93.5, 94, 94.3, 95, 95, 96, 97, 97, 98, 99, 99, 99.5]
console.log(data);

var colMin = '#ffffff';
var colMax = '#000000';

// LINEAR SCALE
var scaleLinear = d3.scaleLinear()
  .domain([0, 100])
  .range([colMin, colMax]);

var div = d3.select("#lin").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .attr("class", "dive")
  .style("background-color", d => scaleLinear(d));


// POWER SCALE
var scalePow = d3.scalePow()
  .exponent(10)
  .domain([0, 100])
  .range([colMin, colMax]);

var div = d3.select("#pow").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .attr("class", "dive")
  .style("background-color", d => scalePow(d));


// QUANTILE SCALE (using https://meyerweb.com/eric/tools/color-blend/#:::hex to find intermediate colors)
var scaleQuantile = d3.scaleQuantile()
  .domain(data)
  .range([colMin, "#E8E8E8", "#D1D1D1", "#B9B9B9", "#A2A2A2", "#8B8B8B", "#747474", "#5D5D5D", "#464646", "#2E2E2E", "#171717", colMax]);

var div = d3.select("#qua").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .attr("class", "dive")
  .style("background-color", d => scaleQuantile(d));
.dive {
  width: 20px;
  height: 20px;
  display: inline-block;
  margin: 4px;
  border: 0.5px black solid;
}
<html lang="en">

<head>
   <meta charset="utf-8">
   <title>color scheme</title>
   <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
   <script src="//d3js.org/d3-scale-chromatic.v0.3.min.js"></script> <!-- for scale color -->
   <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> <!-- for scale color -->

   <link rel="stylesheet" type="text/css" href="./style.css" media="screen"/>
</head>

<body>
   <div id="lin">
   	<p>Scale linear</p>
   </div><br>

   <div id="pow">
   	<p>Scale power</p>
   </div><br>

   <div id="qua">
   	<p>Scale quantile</p>
   </div>

   <script src="./script.js"></script>
</body>

</html>

这是结果:

【问题讨论】:

  • 分位数可能是您最好的选择。 Updated我做了一个小更新,量化了分位数上的一个比例,看起来很相似。
  • 这个问题有 24 个视图和两个答案,这很有趣(也很烦人),但 没有人 支持它。如果这不是一个问题“展示研究成果,有用且清晰”,我不知道它是什么。 +1。

标签: javascript d3.js colors data-visualization


【解决方案1】:

我想更好地区分最后一个值的颜色 (右边的值),它们看起来都是黑色的,有可能吗?

正如 Klaujesi 所建议的,制作自己的色标可能是您最好的选择。关于创建自己的配色方案的编码指导,我会参考 Klaujesi 的回答。

正如您所确定的,您有很多具有相似价值的数据点,您一直在试验的量表已经反映了这些数据点。就个人而言,我会考虑仍然让你的规模反映这一事实,而不是试图用更有吸引力的规模来隐藏它。我建议您考虑一下您的数据意味着什么,并创建一个规模来反映这一点。例如,也许 90 到 100 范围内的所有数据点应该具有比 80 到 100 范围内的数据点更相似的颜色。或者,也许您真的应该让所有颜色看起来都一样,因为所有数据都相同很重要。

在这个例子中我使用了灰度,如果我想使用 Viridis 或 Magma 规模?

感谢您整理了一个很好的示例,添加其中一种配色方案应该不会太难。这是一个线性比例的例子。

添加到script.js的底部:

// VIRIDIS
var colorScale = d3.scaleLinear()
  .domain([0, 100])
  .range([0, 1]);

var div = d3.select("#vir").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .attr("class", "dive")
  .style("background-color", d => d3.interpolateViridis(colorScale(d)));

在您的&lt;body&gt; 中添加index.html

<div id="vir">
    <p>Viridis colors</p>
</div>

【讨论】:

    【解决方案2】:

    由于您的数据偏右,这是我的建议:

    使用sequential scale,在本例中使用interpolateViridis

    var scale = d3.scaleSequential(d3.interpolateViridis);
    

    并且只需将基准的索引与插值器一起使用:

    .style("background-color", function(_, i) {
        return scale(i / (data.length - 1))
    });
    

    或者,当你使用 viridis reversed

    .style("background-color", function(_, i) {
        return scale(1 - (i / (data.length - 1)))
    });
    

    原因是,在顺序尺度下,只有数据数组中数据的索引很重要,而不是数据本身。因此,请确保数据数组已排序。

    这里是演示:

    var data = [90, 95, 50, 1, 99, 89.1, 87, 94, 95, 99, 99.5, 94.3, 96, 97, 85, 74, 66, 92, 68, 91, 93, 87, 79, 86, 89, 93.5, 97, 98];
    data.sort();
    var scale = d3.scaleSequential(d3.interpolateViridis);
    var div = d3.select("#myDiv").selectAll(null)
      .data(data)
      .enter()
      .append("div")
      .attr("class", "div")
      .style("background-color", function(_, i) {
        return scale(1 - (i / (data.length - 1)))
      });
      
    var divValue = d3.select("#myDivValue").selectAll(null)
      .data(data)
      .enter()
      .append("div")
      .attr("class", "div2")
      .html(Number)
    .div {
      width: 14px;
      height: 14px;
      display: inline-block;
      margin: 2px;
      border: 0.5px black solid;
    }
    
    .div2 {
      width: 14px;
      height: 14px;
      display: inline-block;
      margin: 2px;
      font-size: 10px;
      border: 0.5px white solid;
    }
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <div id="myDiv">
      <p>Sequential scale with Viridis</p>
    </div>
    <div id="myDivValue"></div>

    相同的代码,使用interpolateMagma:

    var data = [90, 95, 50, 1, 99, 89.1, 87, 94, 95, 99, 99.5, 94.3, 96, 97, 85, 74, 66, 92, 68, 91, 93, 87, 79, 86, 89, 93.5, 97, 98];
    data.sort();
    var scale = d3.scaleSequential(d3.interpolateMagma);
    var div = d3.select("#myDiv").selectAll(null)
      .data(data)
      .enter()
      .append("div")
      .attr("class", "div")
      .style("background-color", function(_, i) {
        return scale(1 - (i / (data.length - 1)))
      });
    var divValue = d3.select("#myDivValue").selectAll(null)
      .data(data)
      .enter()
      .append("div")
      .attr("class", "div2")
      .html(Number);
    .div {
      width: 14px;
      height: 14px;
      display: inline-block;
      margin: 2px;
      border: 0.5px black solid;
    }
    
    .div2 {
      width: 14px;
      height: 14px;
      display: inline-block;
      margin: 2px;
      font-size: 10px;
      border: 0.5px white solid;
    }
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <div id="myDiv">
      <p>Sequential scale with Magma</p>
    </div>
    <div id="myDivValue"></div>

    PS:我在这里使用的是 D3 v5。如果您使用的是 v4,则可能需要参考必要的微库。

    【讨论】:

    • 嗨 Gerardo,我有一个类似的问题,我想使用顺序缩放。你能更好地解释scale(1 - (i / (data.length - 1)))的原因吗?为什么不直接使用scale(i)
    【解决方案3】:

    什么是正确的色标?

    没有正确的色标。一切都取决于您想展示、隐藏、销售什么,您的客户、客户、用户、公众。-

    另一方面,您可以使用 D3 作为工具来展示您的需求。-

    您可以使用自定义比例,如下所示:

      // --------------------- Custom  SCALE
    var custom = d3.scaleLinear()
      .domain([0,       50,       90,        95,       100])
      .range(['#edfc1b','#ec6f3b','#bc2e67','#7c0093', '#0b0074']);
    
    var div = d3.select("#custom").selectAll(null)
      .data(data)
      .enter()
      .append("div")
      .attr("class", "dive")
      .style("background-color", d => custom(d));
    

    这给了你这个:

    还有其他的秤。

    我已经 fork 你的 Plunker 来扩展它:HERE

    我认为您遗漏了重要的一点:您正在丢弃信息。 显示这一点的正确方法是为缺乏数据留出空白空间,例如任何色标都可以。像这样:

    来自你的图片

    在“Scale Quantile”和“Scale Power”中,“value 1”与“value 50”的颜色相同。即使你用他的价值标记每个盒子,也很难注意到。-

    希望有帮助

    【讨论】:

      最近更新 更多