Topic modelling of news headlines for prediction of news category with 7 topics¶

Now we wan't to perform topic modelling with unsupervised learning and train a modell for prediction of news category labels without using the provided labels in the corpus. We use the LDA classifier on the combined feature 'title_description_text' feature and assume 7 topics.

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

import joblib

import pyLDAvis.lda_model

# from wordcloud
from wordcloud import WordCloud

import warnings
warnings.filterwarnings( "ignore")

Load data¶

In [2]:
train = pd.read_csv('../../data/01_train_nosplit_preprocessed.csv')
In [3]:
train.head()
Out[3]:
date title description category text source title_description_text title_cleaned title_description_text_cleaned
0 2022-06-01 00:13:42 Preise: Grüne halten Senkung der Spritsteuer f... Heute tritt die Steuersenkung auf Kraftstoffe ... Other NaN stern Preise: Grüne halten Senkung der Spritsteuer f... Preis grüne halten Senkung Spritsteuer falsch ... Preis grüne halten Senkung Spritsteuer falsch ...
1 2022-06-01 01:55:03 Biden warnt Putin: USA liefern moderne Raketen... Die USA rüsten die Ukraine mit fortschrittlich... Other NaN stern Biden warnt Putin: USA liefern moderne Raketen... Biden warnen Putin USA liefern modern Raketens... Biden warnen Putin USA liefern modern Raketens...
2 2022-06-01 02:04:08 Soziale Medien: FDP-Politiker Kuhle: Internet-... Eine «ZDF Magazin Royale»-Recherche beschäftig... Other NaN stern Soziale Medien: FDP-Politiker Kuhle: Internet-... sozial Medium FDP-Politiker Kuhle Internet-Str... sozial Medium FDP-Politiker Kuhle Internet-Str...
3 2022-06-01 02:26:58 Liveblog: ++ Zwei von drei ukrainischen Kinder... Rund zwei von drei Mädchen und Jungen in der U... Missing NaN Tagesschau Liveblog: ++ Zwei von drei ukrainischen Kinder... Liveblog ukrainisch Kind vertreiben Liveblog ukrainisch Kind vertreiben rund Mädch...
4 2022-06-01 02:31:43 Finanzen: Dänemark stimmt über EU-Verteidigung... Vorbehalt verteidigen oder Verteidigung ohne V... Other NaN stern Finanzen: Dänemark stimmt über EU-Verteidigung... Finanz Dänemark stimmen EU-Verteidigungsvorbehalt Finanz Dänemark stimmen EU-Verteidigungsvorbeh...

Load German stopwords¶

In [4]:
stop_words = pd.read_csv('german_stopwords.txt', header=None)[0].values.tolist()
In [5]:
#print(stop_words)
In [6]:
# append words to stopwords that contain no information
stop_words += ['wegen', 'melden', 'meldet', 'können', 'könnte', 'könnten', 'update', 'neu', 'neue', 'neues', 'ohne', 'letzte', 'letzter',
                  'letztes', 'eins', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun', 'zehn',
                   'gehen', 'geht', 'wollen', 'wollte', 'wollt', 'jahr', 'jahre', 'fordern', 'fordert',
                  'warnen', 'warnt', 'frühjahr', 'frühling', 'sommer', 'herbst', 'winter', 'erneut', 'deutlich', 'schwer', 'jahren', 'woche',
                  'wochen', 'monat', 'monate', 'tag', 'tage', 'stunden', 'stunde', 'minuten', 'minuten', 'ende', 'beenden', 'endet']

Extract features from 'title_description_text'¶

In [7]:
from sklearn.feature_extraction.text import CountVectorizer
In [8]:
cv = CountVectorizer(max_df=0.95, min_df=3, stop_words=stop_words)
In [9]:
# create Document-Term-Matrix
dtm = cv.fit_transform(train['title_description_text_cleaned'])
In [10]:
dtm
Out[10]:
<67513x29998 sparse matrix of type '<class 'numpy.int64'>'
	with 1026097 stored elements in Compressed Sparse Row format>
In [11]:
from sklearn.decomposition import LatentDirichletAllocation
In [12]:
lda = LatentDirichletAllocation(n_components=7, random_state=42)
lda.fit(dtm)
Out[12]:
LatentDirichletAllocation(n_components=7, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LatentDirichletAllocation(n_components=7, random_state=42)

Analyse extracted features¶

In [13]:
len(cv.get_feature_names_out())
Out[13]:
29998
In [14]:
#cv.get_feature_names_out()
In [15]:
len(lda.components_)
Out[15]:
7
In [16]:
#lda.components_
In [17]:
len(lda.components_[0])
Out[17]:
29998

Show most important words of extracted topics¶

In [18]:
for index, topic in enumerate(lda.components_):
    print(f'Die TOP-15 Wörter für das Thema #{index}')
    print([cv.get_feature_names_out()[i] for i in topic.argsort()[-15:]])
    print('\n')
Die TOP-15 Wörter für das Thema #0
['präsident', 'kreml', 'westen', 'baerbock', 'focus', 'online', 'usa', 'moskau', 'un', 'russisch', 'china', 'krieg', 'ukraine', 'putin', 'russland']


Die TOP-15 Wörter für das Thema #1
['team', 'spiel', 'sieg', 'erster', 'dfb', 'partei', 'fc', 'bundesliga', 'frau', 'gewinnen', 'deutsch', 'fußball', 'bayern', 'wm', 'afd']


Die TOP-15 Wörter für das Thema #2
['heute', 'stehen', 'deutschland', 'groß', 'cdu', 'geben', 'wahl', 'kanzler', 'gut', 'spd', 'deutsch', 'online', 'focus', 'scholz', 'berlin']


Die TOP-15 Wörter für das Thema #3
['präsident', 'gipfel', 'zahl', 'generation', 'usa', 'land', 'steigen', 'biden', 'deutschland', 'pandemie', 'eu', 'geben', 'türkei', 'us', 'corona']


Die TOP-15 Wörter für das Thema #4
['geben', 'weit', 'tote', 'iran', 'regierung', 'sterben', 'gericht', 'ex', 'usa', 'polizei', 'protest', 'trump', 'mensch', 'präsident', 'us']


Die TOP-15 Wörter für das Thema #5
['gut', 'fdp', 'groß', 'bundesregierung', 'grüne', 'land', 'deutsch', 'gas', 'hoch', 'geben', 'ampel', 'habeck', 'eu', 'euro', 'deutschland']


Die TOP-15 Wörter für das Thema #6
['deutschland', 'nato', 'entwicklung', 'angriff', 'putin', 'präsident', 'liveblog', 'eu', 'selenskyj', 'kiew', 'ukrainisch', 'russland', 'krieg', 'russisch', 'ukraine']


Get topics with highest probability for news items in train data¶

In [19]:
topic_results = lda.transform(dtm)
topic_results.shape
Out[19]:
(67513, 7)
In [20]:
topic_results[0].round(5)
Out[20]:
array([0.00623, 0.00622, 0.10575, 0.08399, 0.00622, 0.78537, 0.00622])
In [21]:
topic_results[0].argmax()
Out[21]:
5
In [22]:
# save topic to train dataset
train['topic'] = topic_results.argmax(axis=1)
In [23]:
train.head()
Out[23]:
date title description category text source title_description_text title_cleaned title_description_text_cleaned topic
0 2022-06-01 00:13:42 Preise: Grüne halten Senkung der Spritsteuer f... Heute tritt die Steuersenkung auf Kraftstoffe ... Other NaN stern Preise: Grüne halten Senkung der Spritsteuer f... Preis grüne halten Senkung Spritsteuer falsch ... Preis grüne halten Senkung Spritsteuer falsch ... 5
1 2022-06-01 01:55:03 Biden warnt Putin: USA liefern moderne Raketen... Die USA rüsten die Ukraine mit fortschrittlich... Other NaN stern Biden warnt Putin: USA liefern moderne Raketen... Biden warnen Putin USA liefern modern Raketens... Biden warnen Putin USA liefern modern Raketens... 6
2 2022-06-01 02:04:08 Soziale Medien: FDP-Politiker Kuhle: Internet-... Eine «ZDF Magazin Royale»-Recherche beschäftig... Other NaN stern Soziale Medien: FDP-Politiker Kuhle: Internet-... sozial Medium FDP-Politiker Kuhle Internet-Str... sozial Medium FDP-Politiker Kuhle Internet-Str... 2
3 2022-06-01 02:26:58 Liveblog: ++ Zwei von drei ukrainischen Kinder... Rund zwei von drei Mädchen und Jungen in der U... Missing NaN Tagesschau Liveblog: ++ Zwei von drei ukrainischen Kinder... Liveblog ukrainisch Kind vertreiben Liveblog ukrainisch Kind vertreiben rund Mädch... 6
4 2022-06-01 02:31:43 Finanzen: Dänemark stimmt über EU-Verteidigung... Vorbehalt verteidigen oder Verteidigung ohne V... Other NaN stern Finanzen: Dänemark stimmt über EU-Verteidigung... Finanz Dänemark stimmen EU-Verteidigungsvorbehalt Finanz Dänemark stimmen EU-Verteidigungsvorbeh... 3

Visualize model¶

In [24]:
pyLDAvis.enable_notebook()
panel = pyLDAvis.lda_model.prepare(lda, dtm, cv, mds='tsne')
panel
Out[24]:

Visualize Topics¶

In [25]:
def generate_wordclouds(nmb_terms, lda, count_vect):
    for index, topic in enumerate(lda.components_):
        print(f'Die TOP-{nmb_terms} Wörter für das Thema #{index}')
        print([count_vect.get_feature_names_out()[index] for index in topic.argsort()[-nmb_terms:]])
        print('\n')
        abs_topic = abs(topic)
        #print(abs_topic)
        topic_terms = [[count_vect.get_feature_names_out()[i],topic[i]] for i in abs_topic.argsort()[:-nmb_terms-1:-1]]
        #print(topic_terms)
        topic_terms_sorted = [[count_vect.get_feature_names_out()[i], topic[i]] for i in abs_topic.argsort()[:-nmb_terms-1:-1]]
        #print(topic_terms)

        topic_words = []
        for i in range(nmb_terms):
            topic_words.append(topic_terms_sorted[i][0])
            #print(','.join( word for word in topic_words))
            #print("")
            dict_word_frequency = {}
            for i in range(nmb_terms):
                dict_word_frequency[topic_terms_sorted[i][0]] = topic_terms_sorted[i][1]

        wcloud = WordCloud(background_color="white",mask=None, max_words=100,
                           max_font_size=60,min_font_size=10,
                           prefer_horizontal=0.9,
                           contour_width=3,contour_color='black')
        wcloud.generate_from_frequencies(dict_word_frequency)
        plt.figure()
        plt.imshow(wcloud, interpolation='bilinear')
        plt.axis("off")
        plt.show()          
In [26]:
# show 15 most important words of topics
generate_wordclouds(15, lda, cv)
Die TOP-15 Wörter für das Thema #0
['präsident', 'kreml', 'westen', 'baerbock', 'focus', 'online', 'usa', 'moskau', 'un', 'russisch', 'china', 'krieg', 'ukraine', 'putin', 'russland']


Die TOP-15 Wörter für das Thema #1
['team', 'spiel', 'sieg', 'erster', 'dfb', 'partei', 'fc', 'bundesliga', 'frau', 'gewinnen', 'deutsch', 'fußball', 'bayern', 'wm', 'afd']


Die TOP-15 Wörter für das Thema #2
['heute', 'stehen', 'deutschland', 'groß', 'cdu', 'geben', 'wahl', 'kanzler', 'gut', 'spd', 'deutsch', 'online', 'focus', 'scholz', 'berlin']


Die TOP-15 Wörter für das Thema #3
['präsident', 'gipfel', 'zahl', 'generation', 'usa', 'land', 'steigen', 'biden', 'deutschland', 'pandemie', 'eu', 'geben', 'türkei', 'us', 'corona']


Die TOP-15 Wörter für das Thema #4
['geben', 'weit', 'tote', 'iran', 'regierung', 'sterben', 'gericht', 'ex', 'usa', 'polizei', 'protest', 'trump', 'mensch', 'präsident', 'us']


Die TOP-15 Wörter für das Thema #5
['gut', 'fdp', 'groß', 'bundesregierung', 'grüne', 'land', 'deutsch', 'gas', 'hoch', 'geben', 'ampel', 'habeck', 'eu', 'euro', 'deutschland']


Die TOP-15 Wörter für das Thema #6
['deutschland', 'nato', 'entwicklung', 'angriff', 'putin', 'präsident', 'liveblog', 'eu', 'selenskyj', 'kiew', 'ukrainisch', 'russland', 'krieg', 'russisch', 'ukraine']


Save train set with topics and dump countvectorizer and model¶

In [27]:
#Save result to csv
train.to_csv('evaluation/train_lda_combined_7.csv')
In [28]:
# Dump countvectorizer
joblib.dump(cv, 'models/cv_combined.jl')
Out[28]:
['models/cv_combined.jl']
In [29]:
# Dump LDA model
joblib.dump(lda, 'models/lda_model_7_topics_combined.jl')
Out[29]:
['models/lda_model_7_topics_combined.jl']