2017-04-11 37 views
1

我創建了一個基於csv的d​​3.js文字雲。d3.js條件詞的顏色填寫wordcloud

在CSV我有三列:

  • 字(詞 - 獨一無二的)
  • 計數(這個詞在文中有多少次來到了)
  • 指數(這是什麼樣的值許多數是高於平均水平與它低於多少)

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 

謝謝!

UPDATE:

嘗試下面的基礎上丹的答案,但搭配指數:

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; }); 
} 

不幸的是,沒有什麼是當我運行它顯示(也沒有錯誤消息)。

回答

2

對於上面的代碼,即;假設wordScale是10和100之間:填充功能如下編輯

在繪製函數,在代碼的.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

修訂答:

注意,我不能讓傑森戴維斯在沒有腳本錯誤的遠程加載,但沒有時間來調試。在他的腳本之後向下滾動以獲得實際答案。我也沒有添加任何旋轉元素。

!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>

+0

謝謝你的答案;在10到100的範圍內,55是否等於0?因爲這不是平均水平;正確?上面的代碼是由字數着色,而我需要按索引着色;如果我想通過索引做到這一點,我應該做以下.map(function(d){return {text:d.word,size:+ d.rate,color:d.index}}),然後在.append (「文本」)。style(「fill」,function(d){return d.color> = 0?「green」:「gray」;}) .style(「opacity」,function(d){return d.color/100}) – jeangelj

+0

我試過了代碼,但它並沒有讓所有的單詞都高於平均水平(所以積極的指數)綠色;只有一部分 – jeangelj

+0

道歉,花了一段時間回到你身邊,我一直在工作中淹沒!更新上面的答案,你應該可以運行。它與上面的編輯類似。 –