admin管理员组

文章数量:1279086

Can anybody explain to me why these two expressions return different values...

log1.text(c20(1)); // "#aec7e8"
log2.text(d3.scale.category20()(1)); // "#1f77b4"

... in the following context


Working example...

    var c20 = d3.scale.category20(),
      col = d3.range(20).map(function(c) {
        return c20(c).replace("#", "0x")
      }),
      log1 = d3.select("#log1"),
      log2 = d3.select("#log2");

    log1.text(c20(1)); // "#aec7e8"
    log2.text(d3.scale.category20()(1)); // "#1f77b4"
    $("#user-agent").text(navigator.userAgent);
#log div {
  display: inline-block;
  margin: 0 0 0 10px;
  background: #ccc;
}
<script src=".5.6/d3.min.js"></script>

<div id="log">
  <div id="log1"></div>
  <div id="log2"></div>
</div>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="container"></div>
  <p id="user-agent"></p>
  <script src="//ajax.googleapis/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script src=".5.6/d3.min.js"></script>
  <div id="log1"></div>
  <div id="log2"></div>
</body>

</html>

Can anybody explain to me why these two expressions return different values...

log1.text(c20(1)); // "#aec7e8"
log2.text(d3.scale.category20()(1)); // "#1f77b4"

... in the following context


Working example...

    var c20 = d3.scale.category20(),
      col = d3.range(20).map(function(c) {
        return c20(c).replace("#", "0x")
      }),
      log1 = d3.select("#log1"),
      log2 = d3.select("#log2");

    log1.text(c20(1)); // "#aec7e8"
    log2.text(d3.scale.category20()(1)); // "#1f77b4"
    $("#user-agent").text(navigator.userAgent);
#log div {
  display: inline-block;
  margin: 0 0 0 10px;
  background: #ccc;
}
<script src="https://cdnjs.cloudflare./ajax/libs/d3/3.5.6/d3.min.js"></script>

<div id="log">
  <div id="log1"></div>
  <div id="log2"></div>
</div>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="container"></div>
  <p id="user-agent"></p>
  <script src="//ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare./ajax/libs/d3/3.5.6/d3.min.js"></script>
  <div id="log1"></div>
  <div id="log2"></div>
</body>

</html>

The user agent reported in my system is

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36


I kind of get the above behaviour but this is very strange...

Why is this...

// method 1
d3.range(20).map(d3.scale.category20())

0   #1f77b4
1   #aec7e8
2   #ff7f0e
3   #ffbb78
4   #2ca02c
5   #98df8a
6   #d62728
7   #ff9896
8   #9467bd
9   #c5b0d5
10  #8c564b
11  #c49c94
12  #e377c2
13  #f7b6d2
14  #7f7f7f
15  #c7c7c7
16  #bcbd22
17  #dbdb8d
18  #17becf
19  #9edae5

different from this...

// method 2
d3.range(20).map(function(d, i) {
    return d3.scale.category20()(i);
})  

0   #1f77b4
1   #1f77b4
2   #1f77b4
3   #1f77b4
4   #1f77b4
5   #1f77b4
6   #1f77b4
7   #1f77b4
8   #1f77b4
9   #1f77b4
10  #1f77b4
11  #1f77b4
12  #1f77b4
13  #1f77b4
14  #1f77b4
15  #1f77b4
16  #1f77b4
17  #1f77b4
18  #1f77b4
19  #1f77b4

var c20 = d3.scale.category20(),
  log1 = d3.select("#log1"),
  log2 = d3.select("#log2");

log1.text(c20(1)); // "#aec7e8"
log2.text(d3.scale.category20()(1)); // "#1f77b4"

d3.select("#t1").selectAll(".logs")
  .data(d3.range(20).map(d3.scale.category20()))
  .enter().append("tr").selectAll("td").data(function(d) {
    return [d]
  })
  .enter().append("td")
  .attr("class", "logs")
  .text(function(d, i, j) {
    return [j, d].join("\t")
  })

d3.select("#t2").selectAll(".logs")
  .data(d3.range(20).map(function(d, i) {
    return d3.scale.category20()(i);
  }))
  .enter().append("tr").selectAll("td").data(function(d) {
    return [d]
  })
  .enter().append("td")
  .attr("class", "logs")
  .text(function(d, i, j) {
    return [j, d].join("\t")
  })
#log div {
  display: inline-block;
  margin: 0 0 10px 10px;
  background: #ccc;
}
#t1,
#t2 {
  background: #ccc;
  display: inline-block;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/d3/3.5.6/d3.min.js"></script>

<div id="log">
  <div id="log1"></div>
  <div id="log2"></div>
</div>
<div id="t1"></div>
<div id="t2"></div>

Just to explain, the reason I wanted to use method 2 above was because I needed to convert the hex strings into properly formatted hex numbers so I had to process the domain values on the way through. The actual use case is this:

var col = d3.range(20).map(function(c){
        return d3.scale.category20()(c).replace("#", "0x")
    });  

which doesn't work (and I still don't get why not), which is why I had to do this:

var c20 = d3.scale.category20(),
    col = d3.range(20).map(function(c){
        return c20(c).replace("#", "0x")
    });
Share Improve this question edited Sep 6, 2015 at 16:04 Cool Blue asked Sep 6, 2015 at 10:05 Cool BlueCool Blue 6,4767 gold badges30 silver badges69 bronze badges 12
  • 1 It would be good if the drive-by anon down-voter + close-voter could put me out of my misery as to why this is a bad question... – Cool Blue Commented Sep 6, 2015 at 10:23
  • Not my downvote, but you'll have to show an example that reproduces the issue. I get "#1f77b4" both ways: jsfiddle/pr9num7d – JJJ Commented Sep 6, 2015 at 10:25
  • I do get #aec7e8 #1f77b4 when running the code snippet on Chrome 47.0.2502.0 canary. – widged Commented Sep 6, 2015 at 10:44
  • Same value for both the cases jsfiddle/4b6duqdg – Tarang Commented Sep 6, 2015 at 10:44
  • @Tarang, yes, your right, I get the same as you when I run your fiddle. What happens when you run the snippet I added? I get different values. – Cool Blue Commented Sep 6, 2015 at 10:48
 |  Show 7 more ments

2 Answers 2

Reset to default 5

You can think of the palette 'building up' as it's being used. If you create the palette on the top, e.g.

var palette = d3.scale.category20();

and apply the palette different values in an iteration (e.g.

selection.style('fill', function(d, i) {return palette(i);});

then on each invocation, the palette checks if it already assigned a color for that value; if not, it'll attempt to give a new color (or recycle if you run out of colors).

In contrast, if you apply the value to a fresh palette in your iteration, it'll always just pull one value from that one specific palette:

selection.style('fill', function(d, i) {return d3.scale.category20()(i);});

The undesirable result is that all colors will be the same.

In other words, the d3.scale.category20 isn't a pure function; it implicitly keeps track of its state. It's similar to e.g. using a random number generation that accepts a seed, i.e. deterministic: you don't want to recreate it in an iteration, otherwise the random number you pull will always be the same.

This issue (pre D3v4) speaks to the general value of functional programming, as there's anticipation that a function called with some values will always depend on just the supplied arguments, making testing easier too.

It seems to be an issue with the d3js code for the ordinal scale function. When running this on Chrome 47.0.2502.0 canary, d3.scale.category20()(c) always returns #1f77b4, whatever the value of c is. #1f77b4 is the first value in the list of 20 colors

Updated snippet.

var c20 = d3.scale.category20(),
      col = d3.range(20).map(function(c){
        console.log(c20(c), d3.scale.category20()(c))
        // d3.scale.category20()(c) is always #1f77b4
        return c20(c).replace("#", "0x")
      }),
      log1 = d3.select("#log1"),
      log2 = d3.select("#log2");

    log1.text(c20(1)); // "#aec7e8"
    log2.text(d3.scale.category20()(1)); // "#1f77b4"
    $("#user-agent").text(navigator.userAgent);
#log div {
  display: inline-block;
  margin: 0 0 0 10px;
  background: #ccc;
}
<script src="https://cdnjs.cloudflare./ajax/libs/d3/3.5.6/d3.min.js"></script>

<div id="log">
  <div id="log1"></div>
  <div id="log2"></div>
</div>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="container"></div>
  <p id="user-agent"></p>
  <script src="//ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare./ajax/libs/d3/3.5.6/d3.min.js"></script>
  <div id="log1"></div>
  <div id="log2"></div>
</body>

</html>

本文标签: javascriptd3scalecategory20 is too smart for meStack Overflow