<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0">

<channel>

<title>Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: бордерлайн</title>
<link>http://test.leftjoin.ru/tags/borderlayn/</link>
<description></description>
<generator>E2 (v3365; Aegea)</generator>

<item>
<title>Python и тексты нового альбома Земфиры: анализируем суть песен</title>
<guid isPermaLink="false">100</guid>
<link>http://test.leftjoin.ru/all/borderline-text-analysis/</link>
<comments>http://test.leftjoin.ru/all/borderline-text-analysis/</comments>
<description>
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/Zemfira_borderline-2.jpg" width="600" height="600" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Неделю назад вышёл первый за 8 лет студийный альбом Земфиры «Бордерлайн». К работе помимо рок-певицы приложили руку разные люди, в том числе и её родственники — рифф для песни «таблетки» написал её племянник из Лондона. Альбом получился разнообразным: например, песня «остин» посвящена главному персонажу игры Homescapes российской студии Playrix (кстати, посмотрите свежие &lt;a href="https://youtu.be/SOx8afEUTnE"&gt;Бизнес-секреты с братьями Бухманами&lt;/a&gt;, там они тоже про это рассказывают) — Земфире нравится игра, и для трека она связалась со студией. А сингл «крым» был написан в качестве саундтрека к новой картине соратницы Земфиры — Ренаты Литвиновой.&lt;/p&gt;
&lt;p class="note"&gt;Послушать альбом в &lt;a href="https://music.apple.com/ru/album/бордерлайн/1554865105"&gt;Apple Music&lt;/a&gt; / &lt;a href="https://music.yandex.ru/album/14052981"&gt;Яндекс.Музыке&lt;/a&gt; / &lt;a href="https://open.spotify.com/album/6khBsXmKA1FKjYVCIBy9kt"&gt;Spotify&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Тем не менее, дух всего альбома довольно мрачен — в песнях часто повторяются слова «боль», «ад», «бесишь» и прочие по смыслу. Мы решили провести разведочный анализ нового альбома, а затем при помощи модели Word2Vec и косинусной меры посмотреть на семантическую близость песен между собой и вычислить общее настроение альбома.&lt;/p&gt;
&lt;p&gt;Для тех, кому скучно читать про подготовку данных и шаги анализа можно &lt;a href="http://test.leftjoin.ru/all/borderline-text-analysis/#result"&gt;перейти сразу к результатам&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Подготовка данных&lt;/h2&gt;
&lt;p&gt;Для начала работы напишем скрипт обработки данных. Цель скрипта — из множества текстовых файлов, в каждом из которых лежит по песне, собрать единую csv-таблицу. При этом текст треков очищаем от знаков пунктуации и ненужных слов.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;import pandas as pd
import re
import string
import pymorphy2
from nltk.corpus import stopwords&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Создаём морфологический анализатор и расширяем список всего, что нужно отбросить:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;morph = pymorphy2.MorphAnalyzer()
stopwords_list = stopwords.words('russian')
stopwords_list.extend(['куплет', 'это', 'я', 'мы', 'ты', 'припев', 'аутро', 'предприпев', 'lyrics', '1', '2', '3', 'то'])
string.punctuation += '—'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Названия песен приведены на английском — создадим словарь для перевода на русский и словарь, из которого позднее сделаем таблицу:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;result_dict = dict()

songs_dict = {
    'snow':'снег идёт',
    'crimea':'крым',
    'mother':'мама',
    'ostin':'остин',
    'abuse':'абьюз',
    'wait_for_me':'жди меня',
    'tom':'том',
    'come_on':'камон',
    'coat':'пальто',
    'this_summer':'этим летом',
    'ok':'ок',
    'pills':'таблетки'
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Опишем несколько функций. Первая читает целиком песню из файла и удаляет переносы строки, вторая очищает текст от ненужных символов и слов, а третья при помощи морфологического анализатора pymorphy2 приводит слова к нормальной форме. Модуль pymorphy2 не всегда хорошо справляется с неоднозначностью — для слов «ад» и «рай» потребуется дополнительная обработка.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;def read_song(filename):
    f = open(f'{filename}.txt', 'r').read()
    f = f.replace('\n', ' ')
    return f

def clean_string(text):
    text = re.split(' |:|\.|\(|\)|,|&amp;quot;|;|/|\n|\t|-|\?|\[|\]|!', text)
    text = ' '.join([word for word in text if word not in string.punctuation])
    text = text.lower()
    text = ' '.join([word for word in text.split() if word not in stopwords_list])
    return text

def string_to_normal_form(string):
    string_lst = string.split()
    for i in range(len(string_lst)):
        string_lst[i] = morph.parse(string_lst[i])[0].normal_form
        if (string_lst[i] == 'аду'):
            string_lst[i] = 'ад'
        if (string_lst[i] == 'рая'):
            string_lst[i] = 'рай'
    string = ' '.join(string_lst)
    return string&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Проходим по каждой песне и читаем файл с соответствующим названием:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;name_list = []
text_list = []
for song, name in songs_dict.items():
    text = string_to_normal_form(clean_string(read_song(song)))
    name_list.append(name)
    text_list.append(text)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Затем объединяем всё в DataFrame и сохраняем в виде csv-файла.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;df = pd.DataFrame()
df['name'] = name_list
df['text'] = text_list
df['time'] = [290, 220, 187, 270, 330, 196, 207, 188, 269, 189, 245, 244]
df.to_csv('borderline.csv', index=False)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Результат:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/1-26.png" width="477" height="365" alt="" /&gt;
&lt;/div&gt;
&lt;h2&gt;Облако слов по всему альбому&lt;/h2&gt;
&lt;p&gt;Начнём анализ с построения облака слов — оно отобразит, какие слова чаще всего встречаются в песнях. Импортируем нужные библиотеки, читаем csv-файл и устанавливаем конфигурации:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;import nltk
from wordcloud import WordCloud
import pandas as pd
import matplotlib.pyplot as plt
from nltk import word_tokenize, ngrams

%matplotlib inline
nltk.download('punkt')
df = pd.read_csv('borderline.csv')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь создаём новую фигуру, устанавливаем параметры оформления и при помощи библиотеки wordcloud отображаем слова с размером прямо пропорциональным частоте упоминания слова. Над каждым графиком дополнительно указываем название песни.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;fig = plt.figure()
fig.patch.set_facecolor('white')
plt.subplots_adjust(wspace=0.3, hspace=0.2)
i = 1
for name, text in zip(df.name, df.text):
    tokens = word_tokenize(text)
    text_raw = &amp;quot; &amp;quot;.join(tokens)
    wordcloud = WordCloud(colormap='PuBu', background_color='white', contour_width=10).generate(text_raw)
    plt.subplot(4, 3, i, label=name,frame_on=True)
    plt.tick_params(labelsize=10)
    plt.imshow(wordcloud)
    plt.axis(&amp;quot;off&amp;quot;)
    plt.title(name,fontdict={'fontsize':7,'color':'grey'},y=0.93)
    plt.tick_params(labelsize=10)
    i += 1&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/2@2x-2.png.jpg" width="2560" height="1707" alt="" /&gt;
&lt;/div&gt;
&lt;h2&gt;EDA текстов альбома&lt;/h2&gt;
&lt;p&gt;Теперь проанализируем тексты песен — импортируем библиотеки для работы с данными и визуализации:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;import plotly.graph_objects as go
import plotly.figure_factory as ff
from scipy import spatial
import collections
import pymorphy2
import gensim

morph = pymorphy2.MorphAnalyzer()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Сначала посчитаем число слов в каждой песне, число уникальных слов и процентное соотношение:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;songs = []
total = []
uniq = []
percent = []

for song, text in zip(df.name, df.text):
    songs.append(song)
    total.append(len(text.split()))
    uniq.append(len(set(text.split())))
    percent.append(round(len(set(text.split())) / len(text.split()), 2) * 100)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;А теперь составим из этого DataFrame и дополнительно посчитаем число слов в минуту для каждой песни:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;df_words = pd.DataFrame()
df_words['song'] = songs
df_words['total words'] = total
df_words['uniq words'] = uniq
df_words['percent'] = percent
df_words['time'] = df['time']
df_words['words per minute'] = round(total / (df['time'] // 60))
df_words = df_words[::-1]&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/3-21.png" width="480" height="369" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Данные хорошо бы визуализировать — построим две столбиковые диаграммы: одну для числа слов в песне, а другую для числа слов в минуту.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;colors_1 = ['rgba(101,181,205,255)'] * 12
colors_2 = ['rgba(62,142,231,255)'] * 12

fig = go.Figure(data=[
    go.Bar(name='📝 Всего слов',
           text=df_words['total words'],
           textposition='auto',
           x=df_words.song,
           y=df_words['total words'],
           marker_color=colors_1,
           marker=dict(line=dict(width=0)),),
    go.Bar(name='🌀 Уникальных слов',
           text=df_words['uniq words'].astype(str) + '&amp;lt;br&amp;gt;'+ df_words.percent.astype(int).astype(str) + '%' ,
           textposition='inside',
           x=df_words.song,
           y=df_words['uniq words'],
           textfont_color='white',
           marker_color=colors_2,
           marker=dict(line=dict(width=0)),),
])

fig.update_layout(barmode='group')

fig.update_layout(
    title = 
        {'text':'&amp;lt;b&amp;gt;Соотношение числа уникальных слов к общему количеству&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color:#666666&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;'},
    showlegend = True,
    height=650,
    font={
        'family':'Open Sans, light',
        'color':'black',
        'size':14
    },
    plot_bgcolor='rgba(0,0,0,0)',
)
fig.update_layout(legend=dict(
    yanchor=&amp;quot;top&amp;quot;,
    xanchor=&amp;quot;right&amp;quot;,
))

fig.show()&lt;/code&gt;&lt;/pre&gt;&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/76.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;colors_1 = ['rgba(101,181,205,255)'] * 12
colors_2 = ['rgba(238,85,59,255)'] * 12

fig = go.Figure(data=[
    go.Bar(name='⏱️ Длина трека, мин.',
           text=round(df_words['time'] / 60, 1),
           textposition='auto',
           x=df_words.song,
           y=-df_words['time'] // 60,
           marker_color=colors_1,
           marker=dict(line=dict(width=0)),
          ),
    go.Bar(name='🔄 Слов в минуту',
           text=df_words['words per minute'],
           textposition='auto',
           x=df_words.song,
           y=df_words['words per minute'],
           marker_color=colors_2,
           textfont_color='white',
           marker=dict(line=dict(width=0)),
          ),
])

fig.update_layout(barmode='overlay')

fig.update_layout(
    title = 
        {'text':'&amp;lt;b&amp;gt;Длина трека и число слов в минуту&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color:#666666&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;'},
    showlegend = True,
    height=650,
    font={
        'family':'Open Sans, light',
        'color':'black',
        'size':14
    },
    plot_bgcolor='rgba(0,0,0,0)'
)


fig.show()&lt;/code&gt;&lt;/pre&gt;&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/78.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;h2&gt;Работа с Word2Vec моделью&lt;/h2&gt;
&lt;p&gt;При помощи модуля gensim загружаем модель, указывая на бинарный файл:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;model = gensim.models.KeyedVectors.load_word2vec_format('model.bin', binary=True)&lt;/code&gt;&lt;/pre&gt;&lt;p class="note"&gt;Для материала мы использовали готовую обученную на Национальном Корпусе Русского Языка модель от сообщества &lt;a href="https://rusvectores.org/ru/models/"&gt;RusVectōrēs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Модель Word2Vec основана на нейронных сетях и позволяет представлять слова в виде векторов, учитывая семантическую составляющую. Это означает, что если мы возьмём два слова — например, «мама» и «папа», представим их в виде двух векторов и посчитаем косинус, значения будет близко к 1. Аналогично, у двух слов, не имеющих ничего общего по смыслу косинусная мера близка к 0.&lt;/p&gt;
&lt;p&gt;Опишем функцию get_vector: она будет принимать список слов, распознавать для каждого часть речи, а затем получать и суммировать вектора — так мы сможем находить вектора не для одного слова, а для целых предложений и текстов.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;def get_vector(word_list):
    vector = 0
    for word in word_list:
        pos = morph.parse(word)[0].tag.POS
        if pos == 'INFN':
            pos = 'VERB'
        if pos in ['ADJF', 'PRCL', 'ADVB', 'NPRO']:
            pos = 'NOUN'
        if word and pos:
            try:
                word_pos = word + '_' + pos
                this_vector = model.word_vec(word_pos)
                vector += this_vector
            except KeyError:
                continue
    return vector&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для каждой песни находим вектор и собираем соответствующий столбец в DataFrame:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;vec_list = []
for word in df['text']:
    vec_list.append(get_vector(word.split()))
df['vector'] = vec_list&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь сравним вектора между собой, посчитав их косинусную близость. Те песни, у которых косинусная метрика выше 0,5 запомним отдельно — так мы получим самые близкие пары песен. Данные о сравнении векторов запишем в двумерный список result.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;similar = dict()
result = []
for song_1, vector_1 in zip(df.name, df.vector):
    sub_list = []
    for song_2, vector_2 in zip(df.name.iloc[::-1], df.vector.iloc[::-1]):
        res = 1 - spatial.distance.cosine(vector_1, vector_2)
        if res &amp;gt; 0.5 and song_1 != song_2 and (song_1 + ' / ' + song_2 not in similar.keys() and song_2 + ' / ' + song_1 not in similar.keys()):
            similar[song_1 + ' / ' + song_2] = round(res, 2)
        sub_list.append(round(res, 2))
    result.append(sub_list)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Самые похожие треки соберём в отдельный DataFrame:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;df_top_sim = pd.DataFrame()
df_top_sim['name'] = list(similar.keys())
df_top_sim['value'] = list(similar.values())
df_top_sim.sort_values(by='value', ascending=False)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И построим такой же bar chart:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;colors = ['rgba(101,181,205,255)'] * 5

fig = go.Figure([go.Bar(x=df_top_sim['name'],
                        y=df_top_sim['value'],
                        marker_color=colors,
                        width=[0.4,0.4,0.4,0.4,0.4],
                        text=df_top_sim['value'],
                        textfont_color='white',
                        textposition='auto')])

fig.update_layout(
    title = 
        {'text':'&amp;lt;b&amp;gt;Топ-5 схожих песен&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&amp;quot;color:#666666&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;'},
    showlegend = False,
    height=650,
    font={
        'family':'Open Sans, light',
        'color':'black',
        'size':14
    },
    plot_bgcolor='rgba(0,0,0,0)',
    xaxis={'categoryorder':'total descending'}
)

fig.show()&lt;/code&gt;&lt;/pre&gt;&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/80.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;p&gt;Имея вектор каждой песни, давайте посчитаем вектор всего альбома — сложим вектора песен. Затем для такого вектора при помощи модели получим самые близкие по духу и смыслу слова.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;def get_word_from_tlist(lst):
    for word in lst:
        word = word[0].split('_')[0]
        print(word, end=' ')

vec_sum = 0
for vec in df.vector:
    vec_sum += vec
sim_word = model.similar_by_vector(vec_sum)
get_word_from_tlist(sim_word)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style="color: '#65b5cd'; font-size: 1.2em"&gt;&lt;b&gt;небо тоска тьма пламень плакать горе печаль сердце солнце мрак&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Наверное, это ключевой результат и описание альбома Земфиры всего лишь в 10 словах.&lt;/p&gt;
&lt;p&gt;Наконец, построим общую тепловую карту, каждая ячейка которой — результат сравнения косинусной мерой текстов двух треков.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code&gt;colorscale=[[0.0, &amp;quot;rgba(255,255,255,255)&amp;quot;],
            [0.1, &amp;quot;rgba(229,232,237,255)&amp;quot;],
            [0.2, &amp;quot;rgba(216,222,232,255)&amp;quot;],
            [0.3, &amp;quot;rgba(205,214,228,255)&amp;quot;],
            [0.4, &amp;quot;rgba(182,195,218,255)&amp;quot;],
            [0.5, &amp;quot;rgba(159,178,209,255)&amp;quot;],
            [0.6, &amp;quot;rgba(137,161,200,255)&amp;quot;],
            [0.7, &amp;quot;rgba(107,137,188,255)&amp;quot;],
            [0.8, &amp;quot;rgba(96,129,184,255)&amp;quot;],
            [1.0, &amp;quot;rgba(76,114,176,255)&amp;quot;]]

font_colors = ['black']
x = list(df.name.iloc[::-1])
y = list(df.name)
fig = ff.create_annotated_heatmap(result, x=x, y=y, colorscale=colorscale, font_colors=font_colors)
fig.show()&lt;/code&gt;&lt;/pre&gt;&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/82.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;h2&gt;&lt;a name="result"&gt;Результаты анализа и интерпретация данных&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Давайте ещё раз посмотрим на всё, что у нас получилось — начнём с облака слов. Нетрудно заметить, что у слов «боль», «невозможно», «сорваться», «растерзаны», «сложно», «терпеть», «любить» размер весьма приличный — всё потому, что такие слова встречаются часто на протяжении всего текста песен:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="http://test.leftjoin.ru/pictures/2@2x-2.png-1.jpg" width="2560" height="1707" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Одной из самых «разнообразных» песен оказался сингл «крым» — в нём 74% уникальных слов. А в песне «снег идёт» слов совсем мало, поэтому большинство — 82% уникальны. Самой большой песней в альбоме получился трек «таблетки» — суммарно там около 150 слов.&lt;/p&gt;
&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/76.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;p&gt;Как было выяснено на прошлом графике, самый «динамичный» трек — «таблетки», целых 37 слов в минуту — практически по слову на каждые две секунды. А самый длинный трек — «абъюз», в нём же и согласно предыдущему графику практически самый низкий процент уникальных слов — 46%.&lt;/p&gt;
&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/78.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;p&gt;Топ-5 самых семантически похожих пар текстов:&lt;/p&gt;
&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/80.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;p&gt;Ещё мы получили вектор всего альбома и подобрали самые близкие слова. Только посмотрите на них — «тьма», «тоска», «плакать», «горе», «печаль», «сердце» — это же ведь и есть тот перечень слов, который характеризует лирику Земфиры!&lt;/p&gt;
&lt;p&gt;&lt;span style="color: '#65b5cd'; font-size: 1.2em"&gt;&lt;b&gt;небо тоска тьма пламень плакать горе печаль сердце солнце мрак&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Финал — тепловая карта. По визуализации заметно, что практически все песни достаточно схожи между собой — косинусная мера у многих пар превышает значение в 0.4.&lt;/p&gt;
&lt;iframe id="igraph" scrolling="no" style="border:none;" seamless="seamless" src="https://plotly.com/~Elisejj/82.embed?showlink=false" height="650" width="100%"&gt;&lt;/iframe&gt;
&lt;h2&gt;Выводы&lt;/h2&gt;
&lt;p&gt;В материале мы провели EDA всего текста нового альбома и при помощи предобученной модели Word2Vec доказали гипотезу — большинство песен «бордерлайна» пронизывают довольно мрачные и тексты. И это нормально, ведь Земфиру мы любим именно за искренность и прямолинейность.&lt;/p&gt;
</description>
<pubDate>Fri, 05 Mar 2021 19:54:06 +0300</pubDate>
</item>


</channel>
</rss>