Облако слов (Word Cloud) — это визуализация, содержащая слова из некоторого набора данных, при этом размер шрифта прямо пропорционален частотности слова в наборе. В качестве примера можно привести облако слов, основанное на словах романа Роберта Льюиса Стивенсона «Остров Сокровищ«. Здесь можно выделить наиболее часто встречающиеся слова в романе — они представлены шрифтом большего размера:
Это облако слов получилось при помощи кода Python. Этот код и принцип его работы рассмотрим в статье. Всего я сделал 25 таких визуализаций по самым известным произведениям. Их можно найти на этом сайте в проекте «Книги». Кроме этого в статье рассмотрим как создавать кастомные облака слов в Tableau, предварительно рассчитав координаты слов в Python. Пример визуализации в Tableau, в которой представлены слова из названий величайших фильмах по их частотности:
Дополнительно про Word Clouds в Python можно прочитать в статьях:
1. Подготовка данных
В Python есть библиотека WordCloud, которая позволяет создавать облака слов. Примеры таких визуализаций и код на Python есть в документации к этой библиотеке. Для работы с этой библиотекой надо подготовить данные. Датасет о Top 1000 величайших фильмов я взял с веб-страницы сайта theyshootpictures.com. Простого копирования в Excel и немного работы с разделениями столбцов достаточно для того чтобы данные выглядели так:
В коде ниже будут анализироваться только названия на английском языке поля Film.Остальные поля могут использоваться как дополнительная информация в визуализации. Этот файл был сохранен как 1000 Top Films.csv Для анализа литературных произведений проекта «Книги» я использовал обычные текстовые файлы c произведениями. Сами произведения доступны на открытом ресурсе gutenberg.org. То есть, получился список файлов с произведениями:
2. Облака слов в Python
Код ниже создает облако слов из названий 1000 величайших фильмов.
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
from PIL import Image
from stop_words import get_stop_words
from nltk.corpus import stopwords
import time
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator, get_single_color_func
#read text
data2 = pd.read_csv ('C:/Docs/Tableau/Projects/Word Cloud/1000 Best Films/1000 Top Films.csv', usecols = ['Film'], sep = ',', encoding="ISO-8859-1")
start_time = time.time()
#read stoplist
stoplist = pd.read_csv ('C:/Docs/Tableau/Projects/Word Cloud/Books/Stop List.csv', usecols = ['Word'], sep = ',', encoding="utf-8")
print(data2)
print(stoplist)
df =pd.DataFrame (stoplist, columns=["Word"])
stop_list = df["Word"].tolist()
a1 =pd.DataFrame (data2)
a3 = pd.DataFrame.to_string(a1)
a0 = a3.upper()
stop = get_stop_words('en')
print (stop)
stop.extend(stop_list)
stop.extend(["thou", "thy", "thee", "dost", "les"])
stopwords = set(stop)
print(stopwords)
building = np.array(Image.open('C:/Docs/Tableau/Projects/Word Cloud/1000 Best Films/Video.jpg'))
wordcloud = WordCloud(max_words=50000,
stopwords = stopwords,
font_path='C:/Windows/Fonts/courbd.ttf',
prefer_horizontal=.7,
colormap='Blues',
min_font_size=5,
max_font_size=70,
background_color="Black",
width=7680,
height=4320,
margin=2,
collocations=False,
mask=building,
repeat=False,
relative_scaling=0,
scale=1,
min_word_length=3,
include_numbers = False,
normalize_plurals = False,
font_step=1).generate(a0)
print (wordcloud.layout_)
df = pd.DataFrame(wordcloud.layout_, columns = ['Name', 'Size', 'Coord', 'Direction','Color'])
df.to_csv('C:/Docs/Tableau/Projects/Word Cloud/1000 Best Films/Films_Coordinates.csv')
print(' ')
print ("time elapsed: {:.2f}s".format(time.time() - start_time))
plt.figure(figsize=(30,30))
plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout(pad=0)
plt.show()
Этот код читает текст из файла 1000 Top Films.csv, а также читает STOPLIST из другого файла .csv. В стоплисте содержатся слова, которые будут удаляться из исходного текста. Также можно использовать стоплисты из библиотек типа stop_words или nltk:
from stop_words import get_stop_words
from nltk.corpus import stopwords
Кроме этого в коде есть возможность ввода стоп-слов, это реализовано в строке, которая добавляет сова в список стоп-слов:
stop.extend(["thou", "thy", "thee", "dost", "les"])
Следующая строка читает файл маски:
building = np.array(Image.open('C:/Docs/Tableau/Projects/Word Cloud/1000 Best Films/Video.jpg'))
В результате выполнения кода получается такое облако слов:
Ниже перечислены параметры облака слов, которые используются в коде:
max_words=50000 | Максимальное количество слов в облаке = 50000 |
font_path=’C:/Windows/Fonts/courbd.ttf’ | Выбор шрифта |
prefer_horizontal=.7 | Ориентация слов. 70% слов будут располагаться горизонтально |
colormap=’Blues | Цветовая гамма |
min_font_size=5 | Минимальный размер шрифта |
max_font_size=70 | Максимальный размер шрифта |
background_color=»Black» | Цвет фона |
width=7680, height=4320, | Разрешение картинки |
margin=2 | Ширина границы между словами |
collocations=False | Использование словосочетаний. Отключено |
mask=building | Маска использует файл .jpg с кинокамерой |
repeat=False | Повторение слов в облаке. Отключено |
relative_scaling=0 | Масштабирование слов точно по количеству их повторений. Отключено. |
scale=1 | Масштабирование картинки. Не масштабируется |
min_word_length=3 | Минимальное количество букв в слове |
include_numbers = False | Использовать числительные. Отключено |
normalize_plurals = False | Нормализация множественного числа (убирает букву s в конце английских слов). Отключено |
font_step=1 | Шаг шрифта |
Это не все параметры, которые можно использовать. Все параметры и их описание можно найти в описании библиотеки WordCloud.
Важно: библиотека wordcloud не со всеми шрифтами работает корректно, поэтому сначала лучше проверить на небольшом объеме данных как отображается выбранный вами шрифт
Следующие строки в коде нужны для расчета времени выполнения кода и вывода времени на экран, они не влияют на саму визуализацию:
import time
start_time = time.time()
print ("time elapsed: {:.2f}s".format(time.time() - start_time))
Также после выполнения кода будет сгенерирован файл Film_Coordinates.csv, в котором будут перечислены пары координат X и Y для каждого слова. Этот файл нужен только для того, чтобы перенести облако слов в Tableau.
Все буквы приводим к верхнему регистру в строке:
a0 = a3.upper()
Это нужно для того, чтобы не различались слова с большой буквы в начале и середине предложений.
3. Стоп-слова
Стоп-слова — это слова, которые мы не хотим видеть в облаке слов, поэтому отбрасываем их.
В коде используется библиотека stop_words, в которой список стоп-слов на английском выглядит так:
['a', 'about', 'above', 'after', 'again', 'against', 'all', 'am', 'an', 'and', 'any', 'are', "aren't", 'as', 'at', 'be', 'because', 'been', 'before', 'being', 'below', 'between', 'both', 'but', 'by', "can't", 'cannot', 'could', "couldn't", 'did', "didn't", 'do', 'does', "doesn't", 'doing', "don't", 'down', 'during', 'each', 'few', 'for', 'from', 'further', 'had', "hadn't", 'has', "hasn't", 'have', "haven't", 'having', 'he', "he'd", "he'll", "he's", 'her', 'here', "here's", 'hers', 'herself', 'him', 'himself', 'his', 'how', "how's", 'i', "i'd", "i'll", "i'm", "i've", 'if', 'in', 'into', 'is', "isn't", 'it', "it's", 'its', 'itself', "let's", 'me', 'more', 'most', "mustn't", 'my', 'myself', 'no', 'nor', 'not', 'of', 'off', 'on', 'once', 'only', 'or', 'other', 'ought', 'our', 'ours', 'ourselves', 'out', 'over', 'own', 'same', "shan't", 'she', "she'd", "she'll", "she's", 'should', "shouldn't", 'so', 'some', 'such', 'than', 'that', "that's", 'the', 'their', 'theirs', 'them', 'themselves', 'then', 'there', "there's", 'these', 'they', "they'd", "they'll", "they're", "they've", 'this', 'those', 'through', 'to', 'too', 'under', 'until', 'up', 'very', 'was', "wasn't", 'we', "we'd", "we'll", "we're", "we've", 'were', "weren't", 'what', "what's", 'when', "when's", 'where', "where's", 'which', 'while', 'who', "who's", 'whom', 'why', "why's", 'with', "won't", 'would', "wouldn't", 'you', "you'd", "you'll", "you're", "you've", 'your', 'yours', 'yourself', 'yourselves']
При анализе текстов книг мне не хватило этого списка, поэтому я создал отдельный файл Stop List.csv с наречиями, числительными, местоимениями, междометиями, числительными, предлогами, устаревшими и нецензурными словами. В списке оказалось больше 1000 слов, часть списка выглядит так:
Обратите внимание, что я добавил в стоп-лист слово Chapter (глава), поскольку для книг с небольшими главами это слово было очень частотным, но на сюжет книг оно не влияет.
Кроме этого, для каждого источника текста могут встречаться специфические слова, например, у Шекспира. Их можно убирать прямо в коде, добавляя в список:
stop.extend(["thou", "thy", "thee", "dost", "thus", "going",
"doth", "many", "much", "nay", "aye", "don", "isn", "ain",
"didn", "tis", "vii", "viii", "les"])
Таким образом, в качестве источника стоп-слов можно использовать:
- Библиотеки
- Файлы
- Список внутри кода
Также все эти списки можно объединять.
Таким образом мы избавляемся от частотных слов: предлогов, наречий, местоимений и пр., которые не характеризуют сюжет книги или смысл другого источника текста
4. Облака слов в Tableau
Стандартное облако в Tableau выглядит следующим образом:
Это облако построена на частотности слов в книге «Алиса в стране чудес». Для его создания нужен датасет с каждым словом и частотностью его повторения в книге. Само Tableau не может создать такой датасет из текста книги, поэтому применяются сторонние инcтрументы, например, Python.
Такой тип визуализации подходит для небольших объёмов текстовых данных. Кроме того у этого облака слов есть существенные ограничения:
- Максимальный размер текста ограничен
- Слова можно располагать либо горизонтально либо вертикально
- Нельзя выбрать форму облака
В Python же таких ограничений нет. Кроме того, есть множество дополнительных настроек. Tableau же предоставляет дополнительные интерактивные возможности (фильтрация, actions). Поэтому можно рассчитать координаты слов в Python и потом перенести из в Tableau для создания кастомного облака слов.
Библиотека WordCloud позволяет забирать координаты слов, цвет и направление текста. Это делает метод wordcloud.layout_ в строках:
df = pd.DataFrame(wordcloud.layout_, columns = ['Name', 'Size', 'Coord', 'Direction','Color'])
df.to_csv('C:/Docs/Tableau/Projects/Word Cloud/1000 Best Films/Films_Coordinates.csv')
Файл Film_Coordinates.csv выгладит так:
Его можно либо допилить в питоне (разделить X и Y, перевести цвета в шестнадцатеричный формат и т.д), либо перенести вычисления в Tableau. Пойдём вторым путем и сначала разделим поле Name и координаты при помощи SPLIT:
Name
TRIM( SPLIT( SPLIT( [Name], «(«, 2 ), «‘», 2 ) )
X
INT(TRIM( SPLIT( SPLIT( SPLIT( [Coord], «(«, 2 ), «,», 2 ), «)», 1 ) ))
Y
INT(TRIM( SPLIT( SPLIT( [Coord], «(«, 2 ), «,», 1 ) ))
Вычисления выше выглядят достаточно громоздко, но в них просто разобраться. Если же вы знакомы с регулярными выражениями, то можно применять их. Например, для Y аналогичное вычисление выглядит так:
INT(REGEXP_EXTRACT_NTH([Coord], ‘(\d+)’, 1))
Спасибо Артему Прыткову за совет по регулярным выражениям.
Если хотите больше знать о том как работать с текстом в Tableau при помощи регулярных выражений, можете почитать статьи:
- ‘An Introduction To Tableau Regular Expressions (REGEX)’ by Ken Flerlage
- ‘Detailed Guide to Regular Expressions‘ by Rajeev Pandey
После этого построим точки начала координат на диаграмме разброса, используя картинку облака слов, полученное в Python, в качестве подложки:
Можно видеть, что Python передал координаты верхних левых углов каждого слова. Tableau же работает с центрами (центроидами) слов. То есть, если представить каждое слово в виде прямоугольника, мы имеем координаты левых верхних углов, а нам нужны геометрические координаты центров этих прямоугольников. Поэтому нам нужно рассчитать координаты центров.
Кроме этого, в наших данных есть два направления слов: горизонтальное и вертикальное. Они задаются в поле Direction. Значение 2 — вертикальное направление, пустая ячейка — горизонтальное направление. В Tableau будем использовать Dual Axis где по одной оси будем отражать только горизонтальные названия, а по другой — только вертикальные.
Вычисления для координат центров прямоугольников (слов).
X Shift
IF ISNULL([Direction]) THEN
[X]+ [Parameter 1]*[Size]/2* LEN([Name Split])
ELSE [X]+ [Parameter 1]*[Size]/2
END
Y Shift Horizontal
IF ISNULL([Direction]) THEN
—[Y]— [Parameter 1]*[Size]/2
END
Y Shift Vertical
IF ISNULL([Direction])=False THEN
—[Y]— [Parameter 1]*[Size]/2* LEN([Name Split])
END
Эти вычисления основаны на делении длины и высоты слов пополам так чтобы координаты сместились от углов к центрам слов.
Важно: в визуализации используется моноширинный шрифт Courier New у которого ширина всех букв одинакова. Так центры прямоугольников точнее находятся в Tableau .
Parameter 1 здесь нужен для подгонки слов. Это связано с тем что на разных разрешениях Tableau не меняет размер шрифта, поэтому приходится настраивать размер шрифта на панели Marks в Size. Параметром же дополнительно можно минимизировать зазоры между словами.
Поле Size в датасете — это размер шрифта, а Tableau использует площадь прямоугольника, в котором находится слово, поэтому в Size появляется квадрат:
([Size]/2)^2
В итоге получится такая визуализация с двумя осями:
На оси для вертикальных слов можно выбирать направление снизу вверх или сверху вниз в настройках направления текста.
Также надо настроить одинаковые шаги сетки по X и Y. Получим финальный виз:
В Tableau есть ограничения на высоту текста, поэтому больше и меньше определенных границ текст в облаке сделать нельзя. Таких ограничений нет в Python. Поэтому в Tableau иногда слова немного наезжают на другие слова, и такой идеальной картинки как в Python не получается.
В Color можно добавлять различные вычисления, например, если добавить X Shift то для фильмов получим такую картину:
Заключение
Облако слов — визуализация которую в задачах бизнеса не используют, но её можно встретить в инфографике.
Довольно интересно такие облака слов смотрятся в виде лого. Снизу примеры визуализаций, где анализировались сообщения в русскоязычных Telegram чатах Tableau (первая строка) и инфографики (вторая строка). Первый столбец — визы без стоп-слов, второй и третий — с отбросом стоп-слов.