d3-cloud icon indicating copy to clipboard operation
d3-cloud copied to clipboard

Most important words (highest counts) are excluded if they don't fit

Open john-guerra opened this issue 10 years ago • 25 comments

Right now the algorithm leaves out words that are too big to fit on the available width/height. This is OK for smaller words that aren't that important, but it also leaves out the most important words on a cloud without any warning.

http://jsfiddle.net/duto_guerra/VNurQ/

I can think of two solutions:

  1. If the word doesn't fit, draw it at least partially.
  2. Adjust the size range to guarantee that the biggest word will always fit

I was planning on trying to do this myself and send you a pull request, but I would like to hear your preferences and opinion

John

john-guerra avatar Apr 22 '14 18:04 john-guerra

We are running into this problem too. I think either solution suggested above would work. Has anyone implemented one of them? Although these words are often the highest ranking ones, they could occur anywhere in the list.

lauvil avatar Sep 25 '14 18:09 lauvil

I implemented an overflow option here https://github.com/john-guerra/d3-cloud

john-guerra avatar Sep 26 '14 17:09 john-guerra

@john-guerra I tried to replace my version of the layout with your version, and while it does work the scaling and arrangement seem a bit off.

It does not seem to take all available width into account.

See pictures below to see what I mean.

image

image

tribe84 avatar Oct 31 '14 12:10 tribe84

@tribe84 your solution looks cool, have you posted your code somewhere?

john-guerra avatar Oct 31 '14 17:10 john-guerra

@john-guerra nice job on the overflow code.

sedenardi avatar Jan 26 '15 20:01 sedenardi

Thanks a lot!

On Mon Jan 26 2015 at 12:54:19 PM Sanders DeNardi [email protected] wrote:

@john-guerra https://github.com/john-guerra nice job on the overflow code.

— Reply to this email directly or view it on GitHub https://github.com/jasondavies/d3-cloud/issues/36#issuecomment-71536165.

john-guerra avatar Jan 26 '15 23:01 john-guerra

Hi, i've runned into this problem at first, too. But i noticed when you set a domain, the words won't get drawn bigger than your range value:

var fontScale = d3.scale.linear() .domain([words min size, words max size]) .range([min font size, max font size]);

See this fiddle for an example: http://jsfiddle.net/ndenmhhw/3/

While this doesn't change the Issue, that the words won't get drawn when they are bigger than the height/width, it let's you controll it through the size parameters.

hope this helps someone

xellos86 avatar Mar 05 '15 20:03 xellos86

I ran into the same problem as @tribe84. That forced me to rethink my approach to this issue. I ended up increasing the layout space in the place function, such that it is no longer checks sizing bounds but enlarging sizing bounds. Of course I scale my entire SVG on the subsequent draw method. I understand that this solution is not ideal but it seemed to work for me.

vvitvits avatar Jun 25 '15 12:06 vvitvits

+1 this feature is super needed

ghostsquad avatar Jul 15 '15 08:07 ghostsquad

My fix was to create a maximum font size based on the largest size and scale all other words point size. I found that the words are always visible. If you want a larger size simply adjust the maxFontSize variable...

Example:

//add a variable or dynamically control this value from an input box var maxFontSize = 36;

d3.layout.cloud().size([width, height]) .words(tags.map(function (d) { return { text: d.text, size: d.size }; })) .padding(1) //.rotate(function () { return ~~(Math.random() * 2) * 60; }) .rotate(function (d) { return 0; }) .text(function (d) { return d.text; }) // THE SOLUTION .font("Impact") .fontSize(function (d) { //THE SOLUTION PART II //restrict font size to max of 36 //largest size / word current size * maxFontSize //Example: 1st word is Canada and size is 444. Calculation is 444/444 = 1 * 36 sets largest word to 36 (points) //Example: 2nd word is item and size is 327. Calculation is 327/444 = 0.734 * 36 = 26.5 (points) return eval(d.size / largest_size * maxFontSize); }) .on("end", draw) .start();

eformx avatar Jul 18 '15 23:07 eformx

Having the same issue. If I have long words they are just left out depending on the position of the other words. Specifying a max font size is not really helping because short words are then very small even they could be a lot bigger...

+1

supaMUC avatar Aug 26 '15 19:08 supaMUC

+1 @supaMUC

@eformx I don't see how Canada would get a size of 444 in your example. If you set the largest font size to 36, how does your solution prevent that a really long word like blablablablablablabla would fall off?

bartaelterman avatar Sep 11 '15 07:09 bartaelterman

You need one more calculation in there.

Max size of div / # characters = max pixels per character then you would need to figure out the size/character for the font you are using at each font size.

This might help. http://websemantics.co.uk/resources/font_size_conversion_chart/

So, the above calculation will actually give you the max font sized based on the longest word with the highest value.

ghostsquad avatar Sep 13 '15 20:09 ghostsquad

I found a way of fixing this issue.

I confirm that all words are rendered by the recursive strategy.

  • set domain and range
  • compare between the length of input and output
  • if the length(s) are not equal, call the function recursively decreasing font size.

edited http://jsfiddle.net/ndenmhhw/3/

var words = [
        "Hello", "world", "normally", "you", "want", "more", "words",
        "than", "this"].map(function(d) {
        return {text: d, size: 10 + ~~(Math.random() * 90)};
        }).concat([{text:"OneBigWord", size: 150}]);

maxSize = d3.max(words, function(d) { return d.size; });
minSize = d3.min(words, function(d) { return d.size; });

function drawcloud (range_max) { // declare the function
var fontScale = d3.scale.linear()
  .domain([minSize, maxSize]) 
  .range([5, range_max]); // the argument here 

var fill = d3.scale.category20();
d3.layout.cloud().size([300, 300])
  .words(words)
  .padding(5)
  .rotate(function() { return ~~(Math.random() * 2) * 90; })
  .font("Impact")
  .fontSize(function(d) { return fontScale(d.size) }) 
  .on("end", function(output) {
      if (word.length !== output.length) {  // compare between input ant output
           drawcloud ( range_max - 5 ); // call the function recursively
           return undefined;  
      }
      else { draw(output); }     // when all words are included, start rendering
   })
  .start();


function draw(words) {
    d3.select("body").append("svg")
////
//// please refer http://jsfiddle.net/ndenmhhw/3/
////
}

Although it does not always fit your situations, it might help some people annoyed with this issue And I hope that this issue is solved in the d3-cloud.

satotake avatar Oct 17 '15 11:10 satotake

@satotake that looks like a decent idea for a solution. Although the jsfiddle you link is currently broken and doesn't render anything. Probably best to load d3-cloud from github rather than jasondavies.com.

axelson avatar Oct 29 '15 00:10 axelson

Building on examples above, I've successfully implemented sorting and sizing of the words by frequency of appearance with duplicate removal: http://codepen.io/znak/details/rOgXoV/

As for the max font size for the biggest word, it could be done with measureText() method.

fallenartist avatar Nov 25 '15 15:11 fallenartist

hi @john-guerra i used your algorithm but its performance is not good. Kindly help me in that.

BhumikaSarswat avatar Dec 10 '15 11:12 BhumikaSarswat

@BhumikaSarswat I'm sorry but your request is too vague

john-guerra avatar Dec 10 '15 22:12 john-guerra

Any update on this?

Frozenfire92 avatar Jun 23 '16 19:06 Frozenfire92

@satotake @axelson I fix satotake's broken jsfiddle http://jsfiddle.net/rnz0khfx/

samkugji avatar Dec 27 '16 07:12 samkugji

@jasondavies I try to make that word size is bigger proportionately word frequency like cool, nice and fabulous jasondavies's (https://www.jasondavies.com/wordcloud/)

I really wonder how do I make it? Is it possible with this d3-cloud api?

Here is my best to make now below based on satotake's

function wordFrequency(words){
  var newArray = [];
  var wordObj;
  words.forEach(function(word) {
    wordObj = newArray.filter(function (w){
      return w.text == word;
    });
    if (wordObj.length) { wordObj[0].size += 1; }
    else { newArray.push({text: word, size: 1}); }
  });
  return newArray;
};

var words = $("#data").data("words");
var regExp = /[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]/gi;
words = words.replace(regExp, '').split(' ');
words = wordFrequency(words).map(function(d) {
      return {text: d.text, size:50 * ( 1+Math.sqrt(d.size) )};
    })

samkugji avatar Dec 27 '16 09:12 samkugji

Satotakes and samkugij jsfiddle did not include his proposed workaround using recursive calls. Here is a enhanced jsfiddle http://jsfiddle.net/ymmh9dLq/

wildgarden avatar Jan 09 '17 09:01 wildgarden

Wow, I just stumbled on the same issue in 2018. Thanks for your posts... :-)

owendall avatar Feb 28 '18 02:02 owendall

I implemented an overflow option here https://github.com/john-guerra/d3-cloud

hello @john-guerra can you provide this expmple in d3 v5? if I am try to run this exaplme in d3 v5 its noy working

khushbu-maurya avatar Oct 08 '19 14:10 khushbu-maurya

Sorry I don't have the cycles right now

John. From my phone, brevity and all http://johnguerra.co

On Tue, Oct 8, 2019, 9:24 AM khushbu [email protected] wrote:

I implemented an overflow option here https://github.com/john-guerra/d3-cloud

hello @john-guerra https://github.com/john-guerra can you provide this expmple in d3 v5? if I am try to run this exaplme in d3 v5 its noy working

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jasondavies/d3-cloud/issues/36?email_source=notifications&email_token=AAJJCS5O4IC7QWSHBPUFBDTQNSJZZA5CNFSM4AOSCJC2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAULIPQ#issuecomment-539538494, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJJCS5WBUMTPILEFE6IZNLQNSJZZANCNFSM4AOSCJCQ .

john-guerra avatar Oct 09 '19 17:10 john-guerra