你真的会用wordcloud制作词云图吗?

共 1194字,需浏览 3分钟

 ·

2021-02-04 02:15

前言

对于文本分析而言,大家都绕不开词云图,而python中制作词云图,又绕不开wordcloud,但我想说的是,你真的会用吗?你可能已经按照网上的教程,做出来了一张好看的词云图,但是我想今天这篇文章,绝对让你明白wordcloud背后的原理。

小试牛刀

首先你需要使用pip安装这个第三方库。接着我们简单看一下中英文制作词云有什么不同。

from matplotlib import pyplot as plt
from wordcloud import WordCloud

text = 'my is luopan. he is zhangshan'

wc = WordCloud()
wc.generate(text)

plt.imshow(wc)
from matplotlib import pyplot as plt
from wordcloud import WordCloud

text = '我叫罗攀,他叫张三,我叫罗攀'

wc = WordCloud(font_path = r'/System/Library/Fonts/Supplemental/Songti.ttc'#设置中文字体
wc.generate(text)

plt.imshow(wc)

聪明的你会发现,中文的词云图并不是我们想要的,那是因为wordcloud并不能成功为中文进行分词。通过下面wordcloud的源代码分析,我想你就应该能弄明白了。

WordCloud源码分析

我们主要是要看WordCloud类,这里我不会把全部源代码打上来,而是主要分析制作词云的整个流程。

class WordCloud(object):
    
    def __init__(self,):
        '''这个主要是初始化一些参数
        '
''
        pass

    def fit_words(self, frequencies):
        return self.generate_from_frequencies(frequencies)

    def generate_from_frequencies(self, frequencies, max_font_size=None):
        '''词频归一化,创建绘图对象 
        '
''
        pass

    def process_text(self, text):
        """对文本进行分词,预处理
        "
""

        flags = (re.UNICODE if sys.version < '3' and type(text) is unicode  # noqa: F821
                 else 0)
        pattern = r"\w[\w']*" if self.min_word_length <= 1 else r"\w[\w']+"
        regexp = self.regexp if self.regexp is not None else pattern

        words = re.findall(regexp, text, flags)
        # remove 's
        words = [word[:-2] if word.lower().endswith("'s"else word
                 for word in words]
        # remove numbers
        if not self.include_numbers:
            words = [word for word in words if not word.isdigit()]
        # remove short words
        if self.min_word_length:
            words = [word for word in words if len(word) >= self.min_word_length]

        stopwords = set([i.lower() for i in self.stopwords])
        if self.collocations:
            word_counts = unigrams_and_bigrams(words, stopwords, self.normalize_plurals, self.collocation_threshold)
        else:
            # remove stopwords
            words = [word for word in words if word.lower() not in stopwords]
            word_counts, _ = process_tokens(words, self.normalize_plurals)

        return word_counts

    def generate_from_text(self, text):
        words = self.process_text(text)
        self.generate_from_frequencies(words)
        return self

    def generate(self, text):
        return self.generate_from_text(text)

当我们使用generate方法时,其调用顺序是:

generate_from_text
process_text  #对文本预处理
generate_from_frequencies #词频归一化,创建绘图对象

备注:所以制作词云时,不管你使用generate还是generate_from_text方法,其实最终都是会调用generate_from_text方法。

所以,这里最重要的就是process_text 和generate_from_frequencies函数。接下来我们就来一一讲解。

process_text函数

process_text函数其实就是对文本进行分词,然后清洗,最好返回一个分词计数的字典。我们可以尝试使用一下:

text = 'my is luopan. he is zhangshan'

wc = WordCloud()
cut_word = wc.process_text(text)
print(cut_word)
#  {'luopan': 1, 'zhangshan': 1}

text = '我叫罗攀,他叫张三,我叫罗攀'

wc = WordCloud()
cut_word = wc.process_text(text)
print(cut_word)
# {'我叫罗攀': 2, '他叫张三': 1}

所以可以看出process_text函数是没法对中文进行好分词的。我们先不管process_text函数是怎么清洗分词的,我们就着重看看是怎么对文本进行分词的。

def process_text(self, text):
    """对文本进行分词,预处理
    "
""

    flags = (re.UNICODE if sys.version < '3' and type(text) is unicode  # noqa: F821
             else 0)
    pattern = r"\w[\w']*" if self.min_word_length <= 1 else r"\w[\w']+"
    regexp = self.regexp if self.regexp is not None else pattern

    words = re.findall(regexp, text, flags)

这里的关键就在于使用的是正则表达式进行分词("\w[\w']+"),学过正则表达式的都知道,\w[\w]+代表的是匹配2个至多个字母,数字,中文,下划线(python正则表达式中\w可代表中文)。

所以中文没法切分,只会在各种标点符号中切分中文,这是不符合中文分词的逻辑的。但英文文本本身就是通过空格进行了分割,所以英文单词可以轻松的分词出来。

总结来说,wordcloud本身就是为了英文文本来做词云的,如果需要制作中文文本词云,就需要先对中文进行分词。

generate_from_frequencies函数

最后再简单说下这个函数,这个函数的功能就是词频归一化,创建绘图对象。

绘图这个代码很多,也不是我们今天要讲的重点,我们只需要了解到底是需要什么数据来绘制词云图,下面是词频归一化的代码,我想大家应该能看的懂。

from operator import itemgetter

def generate_from_frequencies(frequencies):
    frequencies = sorted(frequencies.items(), key=itemgetter(1), reverse=True)
    if len(frequencies) <= 0:
        raise ValueError("We need at least 1 word to plot a word cloud, "
                         "got %d." % len(frequencies))

    max_frequency = float(frequencies[0][1])

    frequencies = [(word, freq / max_frequency)
                   for word, freq in frequencies]
    return frequencies

test = generate_from_frequencies({'我叫罗攀': 2, '他叫张三': 1})
test

# [('我叫罗攀', 1.0), ('他叫张三', 0.5)]

中文文本制作词云图的正确方式

我们先通过jieba分词,用空格拼接文本,这样process_text函数就能返回正确的分词计数的字典。

from matplotlib import pyplot as plt
from wordcloud import WordCloud
import jieba

text = '我叫罗攀,他叫张三,我叫罗攀'
cut_word = " ".join(jieba.cut(text))

wc = WordCloud(font_path = r'/System/Library/Fonts/Supplemental/Songti.ttc')
wc.generate(cut_word)

plt.imshow(wc)

当然,如果你直接就有分词计数的字典,就不需要调用generate函数,而是直接调用generate_from_frequencies函数。

text = {
    '罗攀':2,
    '张三':1
}

wc = WordCloud(font_path = r'/System/Library/Fonts/Supplemental/Songti.ttc')
wc.generate_from_frequencies(text)

plt.imshow(wc)

总结

(1)通过process_text函数分析,wordcloud本身是对英文文本进行词云制作的第三方库。

(2)如果需要制作中文词云,就需要先通过jieba等中文分词库把中文文本分割开。

最后,上述的中文词云也并不上我们最终理想的词云,例如我,他等不需要显示出来,还有就是让词云更美化,这些内容下期再告诉你~


更多阅读



2020 年最佳流行 Python 库 Top 10


2020 Python中文社区热门文章 Top 10


5分钟快速掌握 Python 定时任务框架

特别推荐




点击下方阅读原文加入社区会员

浏览 103
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报