We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hey folks! I'm VERY new to Processing and p5, so please be kind. :)
I'm a data journalist working on a text and sentiment analysis of the remaining three presidential candidates' speeches. I generally make visualizations in R, but I'd like to do something a bit more interactive, which is why I've turned to p5. What I'm trying to do is load a .csv file with words and their frequencies into an array. Then, I've written a constructor for how to display the words, with sizes and shades of gray determined by their frequency. I'd like the words to then radiate out from the center to the edges and then repeat.
Here is my code:
var clinton;
var words = []; // Set up array for words in file
var frequency = []; // Set up array for frequency of each word
// Load the table of Clinton's words and frequencies
function preload() {
clinton = loadTable("cltop.csv", "header");
}
function setup() {
createCanvas(720, 400);
background(51);
// Set web font to be used
textFont("Cabin:600:latin");
// Set 'word' and 'frequency' variables
for (var i = 0; i < clinton.getRowCount(); i++) { // This was just getRowCount, but got an "undefined" error
words = [];
for(var i = 0; i < clinton.rowCount; i++) { // Same undefined error as above when using rowCount
words[i] = clinton.get(i, 2);
frequency = [];
for (var i = 0; i < clinton.rowCount; i++) {
frequency[i] = clinton.getNum(i, 3);
}
}
}
// Create link to constructor
clStream = new Words (width/2, height/2, 14)
}
function draw() {
clStream.move();
clStream.display();
}
// Create constructor function
function Words(tempX, tempY, tempSize) {
this.x = tempX;
this.y = tempY;
this.size = tempSize;
this.word = words;
this.frequency = frequency;
this.speed = 2;
this.move = function() {
this.x += random(-this.speed, this.speed); // I think this is wrong, as I want the words to radiate randomly out from the center
this.y += random(-this.speed, this.speed);
}
this.display = function() {
var freqGray = map(this.frequency, 8, 52, 102, 255);
var freqSize = map(this.frequency, 8, 52, 12, 36)
fill(freqGray);
noStroke();
textSize(freqSize);
text(this.word, 0, 0);
};
}
I'm not entirely certain where I've gone wrong, because when I try to load it in my browser, it just says "Loading," but there is no error in the javascript console. Any advice would be appreciated. Many thanks!
Answers
Those nested
var i
s look like the most likely cause to me... JS doesn't have block scope so that's equivalent to starting the setup function with:Give each nested loop a unique counter variable. The standard is i, j, k etc...
Also the triple nested loop itself looks suspect... The loadTables docs don't use a triple nested loop to iterate through content and the code doesn't appear to do what you intend: it looks like - assuming it works - what you'll get is frequency of characters and not words; though admittedly I haven't dug into the relevant docs; so may have misunderstood something.
If you're interested in counting word frequencies gut instinct says use split(" ") on each line of the CSV; iterating through the resulting array and then storing word counts in an object: JS makes this particularly simple - pseudocode:
Disclaimer: written in a hurry after drinking beer on a hot (by UK standards) day O:-)
@blindfish I think you're probably right that the triple nested loop is causing issues. The
loadTable
doc has three columns: ID, the words, their frequency count. I don't need to count the words, as that has already been done. I used a nested loop in order to match the second column (words) with the third (frequency). The doc does have headers, so I'm wondering if I should be using the header name instead of the column number. Like this:But I think there also may be issues with the
constructor
. Thethis.word = words
andthis.frequency = frequency
are supposed to call the variables defined in the nested loop insetup
. Have I done that correctly?Also, what I want is for the words to appear in the center of the sketch and then move/radiate outward, but I'm not sure how to do that. I know that what I have in my code isn't right. It's from an example in the Getting Started with p5.js book that causes a circle to jitter around the screen. I merely included it to see if I could make the words appear and move, and I was planning to try to figure out the movement pattern once it was all working.
Btw, your initial post worked insofar as the background now loads, but the words don't. And I still don't have anything appearing in the console, so I can't tell where it's getting hung up.
Looking at the docs in the cold light of day things make a little more sense; but not much:
'ID' is ambiguous here: you're trying to use the value j to iterate over the array using its index; but it's not clear whether ID and index are one and the same...
If I had your original csv data file it should be easy enough to debug this; but I'm not sure I want to dig too deeply into p5's underbelly: this is the sort of stuff I'd personally do in raw JS or using a dedicated data library...
@blindfish The reason for using p5 is that the platform I post my stories and data visualizations to accepts it and I was told that using p5 was better than using raw JS. Not sure why.
This is what my csv file looks like when I call in in R. This is just the first 10 lines of 100 total.
So what I'm think I'm doing is...
1. read in the file with
loadTable
2. pull out the individual words into one array and the frequency count for each into another array in
setup()
; I don't want to do anything with the ID column3. then the
constructor function
should dictate how the words appear in the sketch, with color and size determined by the associated frequency count4. then, when I call the
constructor function
indraw()
, the words should first appear in the center and then radiate out from there (which I've not figured out how to do yet)I've been looking all over the p5 site for guidance, but it's only making me more confused. At this point, I'm so terribly confused as to whether I'm working with an array, an object, a table, etc. I appreciate your help so far, though. Thanks!
It looks like you were over-complicating things for yourself somewhat: you only needed a single loop to iterate over the table rows. Since you only want to extract values from two known 'columns' there's no need to iterate again: instead you just reference the columns directly. Here's some working code:
https://GitHub.com/processing/p5.js/issues/1420
@ldlpdx, here's @blindfish's sketch adapted & tweaked for ES6/ECMA2015 JS, using
class
: :ar!https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
"Word Frequencies" can also be watched online at this link below: :-bd
http://p5js.SketchPad.cc/sp/pad/view/ro.90xVTuJFcVi/latest
cltop.csv:
index.html:
sketch.js:
@blindfish & @GoToLoop Thank you SO very much! Both of those were extremely helpful! I so appreciate all your effort in helping me untangle this. I've learned a ton from both of your answers.
Say, @blindfish, I have a question about these as I ended up using your solution. How could I configure 'setup' to display three 200x200 canvasses with a small margin between them in order to display the word frequency animations for the three candidates side by side? Also, if I do that, I'm not 100% certain how to change the remaining code accordingly.
Would it be easier to do the formatting of them as side by side with 'containers' in my HTML and then embed the different sketches? If so, do you have a sense of the correct syntax?
@ldlpdx: probably simplest to implement with three instances of p5 and do the positioning in the HTML. IIRC you'd need to use instance mode to keep the sketch instances independent. Only downside might be a memory/performance hit running three instances.
The alternative is building the bounds of each separate square into your Word class and having a single canvas object. Do-able but extra work...