yooper/php-text-analysis

Build Word Cloud

nickescobedo opened this issue · 3 comments

Saw this package and noticed on the wiki page it mentions building a word cloud, but the page is empty. https://github.com/yooper/php-text-analysis/wiki/PHP-Keyword-Phrases-Word-Cloud

How could I potentially go about building a word cloud with this package?

Thanks!

Hello, sorry for my delayed response. To make the word cloud you will need D3 word cloud, https://github.com/jasondavies/d3-cloud

As for the PHP code, here is what I have done in the past ...

use TextAnalysis\Analysis\Keywords\Rake;
use TextAnalysis\Documents\TokensDocument;
use TextAnalysis\Tokenizers\WhitespaceTokenizer;
use StopWordFactory;
use TextAnalysis\Filters;

class WordCloud
{
    const NGRAM_SIZE = 3;
    
    /**
     * @var \TextAnalysis\Interfaces\ITokenTransformation[]
     */
    protected $tokenFilters = [];
    
    /**
     * @var \TextAnalysis\Interfaces\ITokenTransformation[]
     */    
    protected $contentFilters = [];    

    /**
     * The keyword scores are not setup in a compatible way with
     * what D3 cloud expects
     * @param array $keywordScores
     */
    public function getScaledScores($keywordScores)
    {
        $scaleFactor = 1 / array_sum(array_values($keywordScores));
        
        array_walk($keywordScores, 
            function(&$value, $key) use ($scaleFactor){                 
                $value = round($value * $scaleFactor, 5);
            });            
        return $keywordScores;
    }
    
    /**
     * 
     * @return \TextAnalysis\Interfaces\ITokenTransformation[]
     */
    public function getContentFilters()
    {
        if(empty($this->contentFilters)) {
            
            $lambdaFunc = function($word){
                return  preg_replace('/[^[:print:]]/', ' ', $word);
            };
            
            $this->contentFilters = [
                new Filters\StripTagsFilter(),
                new Filters\LowerCaseFilter(),
                new Filters\NumbersFilter(),           
                new Filters\EmailFilter(),
                new Filters\UrlFilter(),
                new Filters\PossessiveNounFilter(),
                new Filters\QuotesFilter(),
                new Filters\PunctuationFilter(),
                new Filters\CharFilter(),
                new Filters\LambdaFilter($lambdaFunc),
                new Filters\WhitespaceFilter()     
            ];
        }
        return $this->contentFilters;
    }
    
    /**
     * 
     * @return \TextAnalysis\Interfaces\ITokenTransformation[]
     */
    public function getTokenFilters()
    {
        if(empty($this->tokenFilters)) {
            $stopwords = StopWordFactory::get('stop-words-fox.txt');
            $this->tokenFilters = [              
                new Filters\StopWordsFilter($stopwords),
            ];
        }        
        return $this->tokenFilters;
    }
    
    /**
     * 
     * @param string $content
     * @return array
     */
    public function getKeywordScores($content)
    {        
        $tokens = (new WhitespaceTokenizer())->tokenize($content);       
        $tokenDoc = new TokensDocument(array_map('strval', $tokens));
        unset($tokens);
                
        foreach($this->getTokenFilters() as $filter)
        {
            $tokenDoc->applyTransformation($filter, false);
        }        
        
        // will return null values in an array
          
        $size = count($tokenDoc->toArray());
        if($size < self::NGRAM_SIZE || !array_filter($tokenDoc->toArray())) {
            return [];
        }           
        
        $rake = new Rake($tokenDoc, self::NGRAM_SIZE);
        return $rake->getKeywordScores();
    }

}

$cloud = new WordCloud();
$scores = $cloud->getKeywordScores("YOUR CONTENT GOES HERE")
// scales the scores for the D3 cloud library
$scaledScores = $cloud->getScaledScores($scores);

You must use $scaledScores with the D3 cloud library. Sorry for the incomplete example. Please post your completed solution and I will use it to update the documentation.

No problem, thank you for this! I'll report back after I try this.

I did get a working prototype with jQCloud and the getKeyValuesByWeight from the FreqDist class.

Sounds good. I am closing this issue.