【问题标题】:d3.js conditional word color fill in wordcloudd3.js条件词颜色填充wordcloud
【发布时间】:2017-04-11 21:11:37
【问题描述】:

我创建了一个基于 csv 的 d3.js 词云。

在 csv 我有三列:

  • 单词(单词 - 唯一)
  • count(单词在文本中出现的次数)
  • 索引(这是计数高于平均值与低于平均值的值)

words.csv:

word    count   index
word1   457      239
word2   373      155
word3   76      -142
word4   345      127
word5   12      -206
word6   46      -172

我编写了以下代码(在 youtube 和 jason davies 的世界云包的帮助下),它可以创建词云,但我不知道如何根据索引更改词的颜色。

    <script>

    var width = 1500,
        height =400;

    var wordScale = d3.scale.linear().range([10,100]);

    var fill = d3.scale.category20c();
    d3.csv("words.csv", function(data) {  
        var subjects = data
            .map(function(d) {return {text: d.word, size: +d.count} })
            .sort(function(a,b) {return d3.descending (a.size, b.size); })
            .slice(0,100);

        wordScale.domain([
            d3.min(subjects, function(d) {return d.size; }),
            d3.max(subjects, function(d) {return d.size; }),
        ]);    

    // var layout = cloud()
        d3.layout.cloud().size([width, height])
        .words(subjects)
        .padding(1)
        .rotate(function() { return ~~(Math.random() * 2) * 0; })
        .font("Impact")
        .fontSize(function(d) { return wordScale(d.size); })
        .on("end", draw)
        .start();

    });

        function draw(words) {
          d3.select("#word-cloud").append("svg")
              .attr("width",width)
              .attr("height",height)
            .append("g")
              .attr("transform", "translate("+(width /2)+","+(height /2)+")")
              //where the center is
            .selectAll("text")
              .data(words)
            .enter().append("text")
              .style("font-size", function(d) { return d.size + "px"; })
              .style("font-family", "Impact")
              .style("fill", function(d, i) { return fill(i); })
              .attr("text-anchor", "middle")
              .attr("transform", function(d) {
                return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
              })
              .text(function(d) { return d.text; });
    }


        </script>


I need to change this line in the code `var fill = d3.scale.category20c();`

The goal would be that all words above the average (so positive index) are green and all words below the average (negative index) are grey. If  the transparency of the color/fill could be dependent on how much above or below the average the count is that would be amazing - but not necessary. 

    word    count   index   color
    word1   457      239    green
    word2   373      155    green
    word3   76      -142    grey
    word4   345      127    green
    word5   12      -206    grey
    word6   46      -172    grey

谢谢!

更新:

根据 Dan 的回答尝试了以下方法,但带有索引:

    mycolor = d3.rgb("#00cc66");

var width = 1500,
    height =400;

var wordScale = d3.scale.linear().range([10,90]);
var colorScale = d3.scale.linear().range([10,90]);

// var fill = d3.scale.category20c();
d3.csv("words.csv", function(data) {  
    var subjects = data
        .map(function(d) {return {text: d.word, size: +d.count, clr: d.index } })
        .sort(function(a,b) {return d3.descending (a.size, b.size); })
        .slice(0,100);

    wordScale.domain([
        d3.min(subjects, function(d) {return d.size; }),
        d3.max(subjects, function(d) {return d.size; }),
    ]);   

    colorScale.domain([
        d3.min(subjects, function(d) {return d.clr; }),
        d3.max(subjects, function(d) {return d.clr; }),
    ]);  

// var layout = cloud()
    d3.layout.cloud().size([width, height])
    .words(subjects)
    .padding(1)
    .rotate(function() { return ~~(Math.random() * 2) * 0; })
    .font("Impact")
    .fontSize(function(d) { return wordScale(d.size); })
    .on("end", draw)
    .start();

});

    function draw(words) {
      d3.select("#word-cloud").append("svg")
          .attr("width",width)
          .attr("height",height)
        .append("g")
          .attr("transform", "translate("+(width /2)+","+(height /2)+")")
          //where the center is
        .selectAll("text")
          .data(words)
        .enter().append("text")
          .style("fill", function(d) { return d.clr >= 50 ? mycolor : "grey";})
          .style("opacity", function(d) { return d.clr / 100 })
          .style("font-size", function(d) { return d.size + "px"; })
          .style("font-family", "Impact")
          // .style("fill", function(d, i) { return fill(i); })
          .attr("text-anchor", "middle")
          .attr("transform", function(d) {
            return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
          })
          .text(function(d) { return d.text; });
}

不幸的是,当我运行它时什么都没有显示(也没有错误消息)。

【问题讨论】:

    标签: javascript csv d3.js scale


    【解决方案1】:

    对于您上面的代码,即;假设 wordScale 在 10 到 100 之间:

    在draw函数中,在.append("text")部分代码中,

    如下编辑填充函数。这将根据大小高于/低于中点 (55) 变为绿色/灰色。随意使用比下面硬编码的颜色值更好的颜色值:

    .style("fill", function(d) { return d.size >= 55 ? "green" : "grey";})
    

    要满足您的其他要求,请在 fill 正下方添加以下样式。这将设置相对于大小的不透明度(介于 0.1 和 1 之间)。同样,您可以根据需要调整这些值。

    .style("opacity", function(d) { return d.size / 100 })
    

    如果您进行这些编辑,您还可以将var fill 放在代码顶部。

    更新答案:

    请注意,如果没有脚本错误,我无法让 Jason Davies 远程加载,但没有时间进行调试。在他的脚本之后向下滚动以获得实际答案。我也没有为此添加任何旋转元素。

    !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b=b.d3||(b.d3={}),b=b.layout||(b.layout={}),b.cloud=a()}}(function(){var a;return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c||a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function h(a){return a.text}function i(){return"serif"}function j(){return"normal"}function k(a){return Math.sqrt(a.value)}function l(){return 30*(~~(6*Math.random())-3)}function m(){return 1}function n(a,b,c,d){if(!b.sprite){var h=a.context,i=a.ratio;h.clearRect(0,0,(f<<5)/i,g/i);var j=0,k=0,l=0,m=c.length;for(--d;++d<m;){b=c[d],h.save(),h.font=b.style+" "+b.weight+" "+~~((b.size+1)/i)+"px "+b.font;var n=h.measureText(b.text+"m").width*i,o=b.size<<1;if(b.rotate){var p=Math.sin(b.rotate*e),q=Math.cos(b.rotate*e),r=n*q,s=n*p,t=o*q,u=o*p;n=Math.max(Math.abs(r+u),Math.abs(r-u))+31>>5<<5,o=~~Math.max(Math.abs(s+t),Math.abs(s-t))}else n=n+31>>5<<5;if(o>l&&(l=o),j+n>=f<<5&&(j=0,k+=l,l=0),k+o>=g)break;h.translate((j+(n>>1))/i,(k+(o>>1))/i),b.rotate&&h.rotate(b.rotate*e),h.fillText(b.text,0,0),b.padding&&(h.lineWidth=2*b.padding,h.strokeText(b.text,0,0)),h.restore(),b.width=n,b.height=o,b.xoff=j,b.yoff=k,b.x1=n>>1,b.y1=o>>1,b.x0=-b.x1,b.y0=-b.y1,b.hasText=!0,j+=n}for(var v=h.getImageData(0,0,(f<<5)/i,g/i).data,w=[];--d>=0;)if(b=c[d],b.hasText){for(var n=b.width,x=n>>5,o=b.y1-b.y0,y=0;y<o*x;y++)w[y]=0;if(null==(j=b.xoff))return;k=b.yoff;for(var z=0,A=-1,B=0;B<o;B++){for(var y=0;y<n;y++){var C=x*B+(y>>5),D=v[(k+B)*(f<<5)+(j+y)<<2]?1<<31-y%32:0;w[C]|=D,z|=D}z?A=B:(b.y0++,o--,B--,k++)}b.y1=b.y0+A,b.sprite=w.slice(0,(b.y1-b.y0)*x)}}}function o(a,b,c){c>>=5;for(var k,d=a.sprite,e=a.width>>5,f=a.x-(e<<4),g=127&f,h=32-g,i=a.y1-a.y0,j=(a.y+a.y0)*c+(f>>5),l=0;l<i;l++){k=0;for(var m=0;m<=e;m++)if((k<<h|(m<e?(k=d[l*e+m])>>>g:0))&b[j+m])return!0;j+=c}return!1}function p(a,b){var c=a[0],d=a[1];b.x+b.x0<c.x&&(c.x=b.x+b.x0),b.y+b.y0<c.y&&(c.y=b.y+b.y0),b.x+b.x1>d.x&&(d.x=b.x+b.x1),b.y+b.y1>d.y&&(d.y=b.y+b.y1)}function q(a,b){return a.x+a.x1>b[0].x&&a.x+a.x0<b[1].x&&a.y+a.y1>b[0].y&&a.y+a.y0<b[1].y}function r(a){var b=a[0]/a[1];return function(a){return[b*(a*=.1)*Math.cos(a),a*Math.sin(a)]}}function s(a){var b=4,c=b*a[0]/a[1],d=0,e=0;return function(a){var f=a<0?-1:1;switch(Math.sqrt(1+4*f*a)-f&3){case 0:d+=c;break;case 1:e+=b;break;case 2:d-=c;break;default:e-=b}return[d,e]}}function t(a){for(var b=[],c=-1;++c<a;)b[c]=0;return b}function u(){return document.createElement("canvas")}function v(a){return"function"==typeof a?a:function(){return a}}var d=a("d3-dispatch").dispatch,e=Math.PI/180,f=64,g=2048;b.exports=function(){function I(a){a.width=a.height=1;var b=Math.sqrt(a.getContext("2d").getImageData(0,0,1,1).data.length>>2);a.width=(f<<5)/b,a.height=g/b;var c=a.getContext("2d");return c.fillStyle=c.strokeStyle="red",c.textAlign="center",{context:c,ratio:b}}function J(b,c,d){for(var l,m,n,f=(a[0],a[1],c.x),g=c.y,h=Math.sqrt(a[0]*a[0]+a[1]*a[1]),i=A(a),j=F()<.5?1:-1,k=-j;(l=i(k+=j))&&(m=~~l[0],n=~~l[1],!(Math.min(Math.abs(m),Math.abs(n))>=h));)if(c.x=f+m,c.y=g+n,!(c.x+c.x0<0||c.y+c.y0<0||c.x+c.x1>a[0]||c.y+c.y1>a[1])&&(!d||!o(c,b,a[0]))&&(!d||q(c,d))){for(var y,p=c.sprite,r=c.width>>5,s=a[0]>>5,t=c.x-(r<<4),u=127&t,v=32-u,w=c.y1-c.y0,x=(c.y+c.y0)*s+(t>>5),z=0;z<w;z++){y=0;for(var B=0;B<=r;B++)b[x+B]|=y<<v|(B<r?(y=p[z*r+B])>>>u:0);x+=s}return delete c.sprite,!0}return!1}var a=[256,256],b=h,c=i,e=k,s=j,x=j,y=l,z=m,A=r,B=[],C=1/0,D=d("word","end"),E=null,F=Math.random,G={},H=u;return G.canvas=function(a){return arguments.length?(H=v(a),G):H},G.start=function(){function l(){for(var b=Date.now();Date.now()-b<C&&++i<h&&E;){var c=k[i];c.x=a[0]*(F()+.5)>>1,c.y=a[1]*(F()+.5)>>1,n(d,c,k,i),c.hasText&&J(f,c,g)&&(j.push(c),D.call("word",G,c),g?p(g,c):g=[{x:c.x+c.x0,y:c.y+c.y0},{x:c.x+c.x1,y:c.y+c.y1}],c.x-=a[0]>>1,c.y-=a[1]>>1)}i>=h&&(G.stop(),D.call("end",G,j,g))}var d=I(H()),f=t((a[0]>>5)*a[1]),g=null,h=B.length,i=-1,j=[],k=B.map(function(a,d){return a.text=b.call(this,a,d),a.font=c.call(this,a,d),a.style=s.call(this,a,d),a.weight=x.call(this,a,d),a.rotate=y.call(this,a,d),a.size=~~e.call(this,a,d),a.padding=z.call(this,a,d),a}).sort(function(a,b){return b.size-a.size});return E&&clearInterval(E),E=setInterval(l,0),l(),G},G.stop=function(){return E&&(clearInterval(E),E=null),G},G.timeInterval=function(a){return arguments.length?(C=null==a?1/0:a,G):C},G.words=function(a){return arguments.length?(B=a,G):B},G.size=function(b){return arguments.length?(a=[+b[0],+b[1]],G):a},G.font=function(a){return arguments.length?(c=v(a),G):c},G.fontStyle=function(a){return arguments.length?(s=v(a),G):s},G.fontWeight=function(a){return arguments.length?(x=v(a),G):x},G.rotate=function(a){return arguments.length?(y=v(a),G):y},G.text=function(a){return arguments.length?(b=v(a),G):b},G.spiral=function(a){return arguments.length?(A=w[a]||a,G):A},G.fontSize=function(a){return arguments.length?(e=v(a),G):e},G.padding=function(a){return arguments.length?(z=v(a),G):z},G.random=function(a){return arguments.length?(F=a,G):F},G.on=function(){var a=D.on.apply(D,arguments);return a===D?G:a},G};var w={archimedean:r,rectangular:s}},{"d3-dispatch":2}],2:[function(b,c,d){!function(b,e){"object"==typeof d&&void 0!==c?e(d):"function"==typeof a&&a.amd?a(["exports"],e):e(b.d3=b.d3||{})}(this,function(a){"use strict";function c(){for(var e,a=0,b=arguments.length,c={};a<b;++a){if(!(e=arguments[a]+"")||e in c)throw new Error("illegal type: "+e);c[e]=[]}return new d(c)}function d(a){this._=a}function e(a,b){return a.trim().split(/^|\s+/).map(function(a){var c="",d=a.indexOf(".");if(d>=0&&(c=a.slice(d+1),a=a.slice(0,d)),a&&!b.hasOwnProperty(a))throw new Error("unknown type: "+a);return{type:a,name:c}})}function f(a,b){for(var e,c=0,d=a.length;c<d;++c)if((e=a[c]).name===b)return e.value}function g(a,c,d){for(var e=0,f=a.length;e<f;++e)if(a[e].name===c){a[e]=b,a=a.slice(0,e).concat(a.slice(e+1));break}return null!=d&&a.push({name:c,value:d}),a}var b={value:function(){}};d.prototype=c.prototype={constructor:d,on:function(a,b){var h,c=this._,d=e(a+"",c),i=-1,j=d.length;{if(!(arguments.length<2)){if(null!=b&&"function"!=typeof b)throw new Error("invalid callback: "+b);for(;++i<j;)if(h=(a=d[i]).type)c[h]=g(c[h],a.name,b);else if(null==b)for(h in c)c[h]=g(c[h],a.name,null);return this}for(;++i<j;)if((h=(a=d[i]).type)&&(h=f(c[h],a.name)))return h}},copy:function(){var a={},b=this._;for(var c in b)a[c]=b[c].slice();return new d(a)},call:function(a,b){if((e=arguments.length-2)>0)for(var e,f,c=new Array(e),d=0;d<e;++d)c[d]=arguments[d+2];if(!this._.hasOwnProperty(a))throw new Error("unknown type: "+a);for(f=this._[a],d=0,e=f.length;d<e;++d)f[d].value.apply(b,c)},apply:function(a,b,c){if(!this._.hasOwnProperty(a))throw new Error("unknown type: "+a);for(var d=this._[a],e=0,f=d.length;e<f;++e)d[e].value.apply(b,c)}},a.dispatch=c,Object.defineProperty(a,"__esModule",{value:!0})})},{}]},{},[1])(1)});
    
    /// ACTUAL START OF ANSWER ///
    
    // fake JSON data in place of CSV file.
    
    data = [{
        word: "word1",
        count: 457,
        index: 239
      },
      {
        word: "word2",
        count: 373,
        index: 155
      },
      {
        word: "word3",
        count: 76,
        index: -142
      },
      {
        word: "word4",
        count: 345,
        index: 127
      },
      {
        word: "word5",
        count: 12,
        index: -206
      },
      {
        word: "word6",
        count: 46,
        index: -172
      }
    ]
    
    //  the script
    
    
        var width = 700, // amended to make result easier to see
            height =400;
    
        var wordScale = d3.scale.linear().range([10,100]);
    
    //    d3.csv("words.csv", function(data) {  // uncomment when using CSV
            var subjects = data
                .map(function(d) {return {text: d.word, size: +d.count, in: +d.index} }) // note use of +d.index, to ensure it's held as a number when read from csv, same as +d.count
                .sort(function(a,b) {return d3.descending (a.size, b.size); })
                .slice(0,100);
    
            wordScale.domain([
                d3.min(subjects, function(d) {return d.size; }),
                d3.max(subjects, function(d) {return d.size; }),
            ]);    
    
        // var layout = cloud()
            d3.layout.cloud().size([width, height])
            .words(subjects)
            .padding(1)
            .rotate(function() { return ~~(Math.random() * 2) * 0; })
            .font("Impact")
            .fontSize(function(d) { return wordScale(d.size); })
            .on("end", draw)
            .start();
    
     //   }); uncomment when using csv
    
            function draw(words) {
              d3.select("#word-cloud").append("svg")
                  .attr("width",width)
                  .attr("height",height)
                .append("g")
                  .attr("transform", "translate("+(width /2)+","+(height /2)+")")
                  //where the center is
                .selectAll("text")
                  .data(words)
                .enter().append("text")
                  .style("font-size", function(d) { return d.size + "px"; })
                  .style("font-family", "Impact")
                  .style("fill", function(d, i) { return d.in > 0 ? "#00cc66" : "grey" })
                  .style("opacity", function(d) { return d.in > 0 ? d.size/100 : (50 - d.size) / 100 } ) // bit of a hack to get furthest from index to become darker
                  .attr("text-anchor", "middle")
                  .attr("transform", function(d) {
                    return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                  })
                  .text(function(d) { return d.text; });
        }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <!--
    word    count   index
    word1   457      239
    word2   373      155
    word3   76      -142
    word4   345      127
    word5   12      -206
    word6   46      -172
    -->
    <div id="word-cloud"></div>

    【讨论】:

    • 谢谢你的回答;在 10 到 100 的范围内,55 是否等于 0?因为这不是平均值;正确的?上面的代码是按字数着色,而我需要按索引着色;如果我想按索引来做,我应该做以下 .map(function(d) {return {text: d.word, size: +d.rate, color: d.index} }) 然后在 .append ("text") .style("fill", function(d) { return d.color >= 0 ? "green" : "grey";}) .style("opacity", function(d) { return d.颜色 / 100 })
    • 我尝试了代码,但它并没有使所有高于平均水平的单词(如此积极的索引)变成绿色;只有一部分
    • 抱歉,我花了一段时间才回复你,我忙于工作!更新了上面的答案,您应该可以运行。它类似于您上面的编辑。
    • 非常感谢您在忙于工作的情况下回复我 - 我非常感谢。我现在正在测试代码。
    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2015-04-04
    • 2014-04-27
    • 1970-01-01
    • 1970-01-01
    • 2016-01-11
    相关资源
    最近更新 更多