5.3. Implementation of BoWs

  • Author: Johannes Maucher

  • Last update: 05.11.2020

This notebook demonstrates how documents can be described in a vector space model. Applying this type of model

  1. similarities between documents

  2. similarities between documents and a query

can easily be calculated.

5.3.1. Read documents from a textfile

It is assumed that a set of documents is stored in a textfile, as e.g. in MultiNewsFeeds2014-09-12.txt. The individual documents are separated by line-break. In this case the documents can be assigned to the list listOfNews as follows:

filename="../Data/MultiNewsFeeds2014-09-12.txt"
#filename="../Data/MultiNewsFeeds2016-10-14.txt"
listOfNews=[]
with open(filename,"r",encoding="utf-8") as fin:
    for line in fin:
        line = line.strip()
        print(line)
        listOfNews.append(line)
print("Number of Lines:  ",len(listOfNews))
fin.close()
Kommunikation: Gut kommunizieren macht glücklich    Wie kaum ein anderer hat der Psychologe Friedemann Schulz von Thun untersucht, was Kommunikation ausmacht. In seinem neuen Buch entwickelt er eine Quintessenz davon.
Ukraine-Krise: Bundesregierung toleriert ukrainischen Mauerbau    Im Kanzleramt hat man Verständnis für die Pläne der ukrainischen Regierung, eine Mauer entlang der Grenze zu Russland zu bauen. Dies sei allein Entscheidung der Ukraine.
Radfahren: Eine Deutsche bringt die Chinesen aufs Fixie    Einst war Peking Fahrrad-Welthauptstadt. Dann kamen die Autos. Ines Brunn kämpft dagegen: Die Inhaberin eines Fixie-Shops will die Chinesen wieder aufs Rad bringen.
Zurück in die Wildnis : Jenseits allen Komforts    Drei Jahre lang suchte der Fotograf Antoine Bruy Aussteiger in Europa. Seine Bilder zeigen Menschen, die der Konsum- und Leistungsgesellschaft den Rücken gekehrt haben.
Antisemitismus: Zentralrat beklagt "Schockwellen von Judenhass"    Judenfeindliche Hetze wie bei Protesten gegen Israel haben die Juden tief getroffen, sagte der Vorsitzende des Zentralrats. Eine Kundgebung soll ein Zeichen setzen.
Artenschutz: Diese Haie muss der Mensch nun besser schützen    Ihr Fleisch ist begehrt und wer sie künftig jagt, könnte mit Strafe rechnen: Fünf Haiarten werden auf die Rote Liste des internationalen Artenschutzabkommens gesetzt.
Freihandelsabkommen: CETA ist nicht zustimmungsfähig    Das Freihandelsabkommen zwischen Kanada und der EU hebelt demokratische Kontrolle aus. Die TTIP-Blaupause bevorzugt Wirtschaftsinteressen. Dagegen sollte geklagt werden.
Afrika: Libyen muss schnell auf die europäische Agenda    Frankreich will einen Militäreinsatz, um Libyen zu stabilisieren. Man muss nicht zustimmen, aber wenigstens hat das verschämte Schweigen zum Kollaps des Landes ein Ende.
Hamburger Kunstverein: "Ich mag Dekoration nicht"    Bettina Steinbrügge ist Direktorin des Hamburger Kunstvereins. Im Gespräch berichtet sie von ihrer Arbeit und dass sie persönlich ein Freund von Klarheit und Ordnung ist.
Digitale Familie : "Kuss, Mutter"    Von Fortschrittsverweigerung keine Spur: Auch die Großeltern haben das Internet für sich entdeckt und tragen dort ein digitales Wettrüsten der Silver Surfer aus.
Russland-Sanktionen: Rosneft in Schwierigkeiten    Russlands Ölkonzerne brauchen dringend Geld und Know-how aus dem Westen, um ihre Fördermethoden zu modernisieren. Sanktionen treffen sie hart – und damit das ganze Land.
Bundesliga-Vorschau: Sensationelle Neuigkeiten von Schweinsteiger    Alle reden nur noch über dieses eine neue Bild von Bastian Schweinsteiger. Und der Menschenrechtler Rummenigge beschützt uns vor Rassismus. Alles Wichtige zum 3. Spieltag
Radio Doria: Liefers liebt Pathos    Seine Frau singt, sein "Tatort"-Kollege auch. Jetzt veröffentlicht der Schauspieler Jan Josef Liefers ein neues Album mit seiner Band Radio Doria. Muss das denn sein?
PKW-Maut: Dobrindt legt Maut-Gesetz im Oktober vor    Der Verkehrsminister kämpft weiter für seine Maut-Pläne. Er kündigte einen Termin für den Gesetzentwurf an. Dann sei auch Gelegenheit für Diskussionen, sagte Dobrindt.
Interaktiv: So viel verdienen unsere Abgeordneten nebenher  Peter Gauweiler, Gregor Gysi, Peer Steinbrück: Jeder vierte Bundestagsabgeordnete verdient sich mit Nebentätigkeiten teils erkleckliche Summen dazu. Mancher wird dabei zum Millionär. Mit dem ständig aktualisierten FAZ.NET-Monitor können Sie ab sofort sehen, welcher Abgeordneter wie viel Geld nebenher verdient.
Ukraine-Krise: Lesen Sie Putins Stellenbeschreibung  Vor der Höhle des russischen Bären ist ein Sicherheitsabstand einzuhalten, sonst greift er an. Dennoch braucht Europa Russland – und die Ukraine eine Finnlandisierung.
Brief an Netanjahu: Israelische Eliteeinheit will Einsätze verweigern  In einem Brief an Ministerpräsident Netanjahu und die israelische Armeeführung kündigen 43 Mitglieder einer Sonderheit des militärischen Geheimdienstes an, künftig Einsätze zu verweigern. Ihnen geht das grenzenlose Ausspionieren der Palästinenser zu weit.
Zwanzig Jahre Zeitung im Internet: Schafft den Online-Journalismus ab  Im Herbst 1994 gingen die ersten Medien online. Die Leser haben seitdem alles bekommen, was sie sich nie vorgestellt hatten. Jetzt müssen sie lernen, mit dieser Informationsvielfalt richtig umzugehen.
Der unbekannte Ökostrom-Riese China  China gilt nicht gerade als Vorreiter in Sachen Umwelt- und Klimaschutz. Dabei erleben die erneuerbaren Energien im Reich der Mitte gerade einen unglaublichen Boom.
Biografie über den Marquis de Sade: Der Skandalöseste aller Skandalösen  Im Vergleich zu seinen Texten liest sich „Shades of Grey“ wie ein Pixi-Buch: Volker Reinhardt gelingt es in seiner Biographie dennoch, sich dem Marquis de Sade nüchtern anzunähern.
Nude-Porträts: Ein Fotograf zieht sich aus  Trevor Christensen macht Nackt-Porträts. Allerdings behalten die Porträtierten dabei ihre Kleider an. Nackt ist hier nur der Fotograf selbst. Warum er das macht, erzählt er im Interview.
De Maizière verkündet Verbot des „Islamischen Staats“  Bundesinnenminister de Maizière hat „mit sofortiger Wirkung“ Aktivitäten der Terrorgruppe „Islamischer Staat“ verboten. Der Dschihad gefährde Deutschlands Sicherheit und dürfe nicht „durch die Straßen“ getragen werden, sagte de Maizière.
Gefallener Superstar Pistorius: „Kein Platz mehr in der paralympischen Bewegung“  Er war der Held des Behindertensports, nun ist er ein verurteilter Straftäter. Ob Oscar Pistorius nach Verbüßen seiner Strafe wieder an Paralympischen Spielen teilnehmen, darüber scheiden sich die Geister.
Wen die EU mit den neuen Sanktionen trifft  Die neuen Strafmaßnahmen der EU sind in Kraft. Sie treffen russische Rohstoffriesen, Separatisten-Anführer und einen engen Freund von Staatspräsident Wladimir Putin.
Fraktur: Lieber Hybridschweine als Hybridkriege  Putins Schnitzeljagd: Warum ist uns bloß nie aufgefallen, dass die Krim die Form eines Koteletts hat?
Wann lohnt sich die Reparatur der Windschutzscheibe?  Hoffnungsfroh von der Werbung eingestimmt, bringt mancher sein Auto zum Spezialisten, damit der seine Windschutzscheibe vom Steinschlag heile. Manchmal geht das, aber oft bleibt nur der Austausch.
TV-Kritik „Maybrit Illner“: Der neue Freizeitsozialismus  An Jeremy Rifkins Buch „Die Null-Grenzkosten-Gesellschaft“ kommt auch die Bundesregierung nicht vorbei. Bei Maybrit Illner zeigt Kanzleramtschef Altmaier im Gespräch mit dem Autor, wie drängend das Thema und wie ideenlos die Politik ist.
Ausspähprogramm „Prism“: Wie die amerikanische Regierung Yahoo drohte  Gewährt uns Zugang zu Nutzerdaten oder zahlt 250.000 Dollar Bußgeld pro Tag – mit dieser Forderung soll die amerikanische Regierung im Jahr 2008 Yahoo unter Druck gesetzt haben. Bedenken des Internetkonzerns wischten Gerichte angeblich beiseite.
Gesundheitssystem: Mit bestem Dank an  die Privatpatienten  Ein früherer Termin beim Facharzt? Ein ausführlicheres Arztgespräch? Kein Problem für Privatpatienten. Für sie haben Ärzte mehr Zeit. Gesetzlich Versicherte ärgert das. Doch ohne die Privatversicherungen müssten viele Praxen schließen.
Zum Tod von Joachim Fuchsberger: Der letzte deutsche Fernseh-Altmeister  Moderator, Schauspieler, Schlagertexter - Joachim Fuchsberger war ein Tausendsassa, der die deutsche Fernsehgeschichte prägte. In Männern wie ihm erkennen wir unser Land.
Ukraine-Krise: Zweifelhafte Majdan-Kämpfer  Die Berichte über Verbrechen der Separatisten in der Ostukraine sind fast schon Routine geworden. Nun häufen sich Nachrichten, dass Schuld und Verstrickung nicht mehr nur einer einzigen Seite anzulasten sind.
Das Trikot und der Lohn: 15 Cent für vier Sterne  Entwicklungsminister Müller kritisiert das Geschäft mit dem DFB-Trikot. Der Vorwurf sei falsch, sagt Adidas. Für Claudia Roth sind die Fertigungsumstände „unerträglich“.
Gabriele Pauli auf Sylt: Reif für die Insel  Die frühere fränkische Landrätin Gabriele Pauli, die einst am Sturz Stoibers mitwirkte, strebt das Bürgermeisteramt auf Sylt an. Nun sammelt sie in Westerland Unterschriften für ihre Kandidatur.
Flüchtlinge aus der Ostukraine: Kostenlos nach Sibirien  Russland verspricht Übersiedlern aus der Ostukraine Arbeit und ein Dach über dem Kopf. Doch nach der Landung gibt es für die Flüchtlinge oft ein böses Erwachen.
Artikel in Listenform: Die Qual der Zahl  Im Netz verbreiten sich Kurzartikel, die ihre Leser mit „10 guten Gründen“ oder „15 unglaublichen Fakten“ überzeugen wollen. Häufig findet man diese Posts bei Buzzfeed – bald auch in deutscher Sprache.
100 Jahre Marne-Schlacht: Verloren im Nebel des Krieges  Wenige Wochen nach dem Ausbruch des Ersten Weltkrieges kam der Vormarsch der deutschen Armeen in Frankreich an der Marne zum Stillstand. Doch selbst bei einem Erfolg in der Marne-Schlacht wäre ein Zweifrontenkrieg nicht mehr zu vermeiden gewesen.
Neue Optik für Scheine: Jetzt kommen die neuen Zehner  In zwei Wochen kommen die neuen Zehn-Euro-Scheine. Die Fehler der Fünfer-Umstellung sollen sich nicht wiederholen. Fahrkartenautomaten sollen alle, Zigarettenautomaten fast alle reibungslos funktionieren.
Tagesgeld: Der Zinssatz ist nicht alles  Die Leitzinssenkung der EZB schlägt derzeit noch nicht auf Tagesgeld durch. Der passende Anbieter hängt aber von mehr als nur dem Zins ab. Ein Vergleich lohnt.
Südafrika: Fassungslosigkeit über das milde Pistorius-UrteilPistorius schien sich zu verbeugen, Steenkamps Eltern blieben gefasst, ein Raunen ging durch den Saal: Das Urteil wegen fahrlässiger Tötung halten Südafrikas Frauen für einen schlechten Präzedenzfall.
Islamischer Staat: Warum die Türkei bei der Anti-Terror-Allianz fehltErst unterstützte die Türkei indirekt islamische Extremisten in Syrien, nun will sie nicht in die von den USA geführte Koalition gegen den Islamischen Staat eintreten. Ankara hat eine eigene Agenda.
Terrormiliz: Bundesregierung verbietet IS in DeutschlandInnenminister Thomas de Maizière hat das Verbot der Extremistengruppe IS in Deutschland verkündet, auch ihre Symbole dürfen nicht mehr zeigt werden. Er sieht die Sicherheit in Deutschland bedroht.
Verkehrspolitik: Große Koalition sabotiert Maut-Debatte im BundestagAls Minister Dobrindt im Bundestag seine Pläne vorstellt, weichen Union und SPD allen Sachthemen aus. Dabei sind sie beim Thema Verkehr fundamental zerstritten: von der Pkw-Maut bis zum Straßenbau.
Separatismus: Angst vor dem Zerfall Europas lässt Märkte bebenAm 18. September stimmen die Schotten über einen eigenen Staat ab. Auch Katalonien und Flandern streben nach Unabhängigkeit. Investoren fürchten den Separatismus. Doch nicht alle Effekte sind negativ.
Sanktionen: Die Folterwerkzeuge der EU werden Moskau wehtunDie nächste Stufe der Sanktionen ist erreicht. Das Maßnahmenpaket gegen Moskau könnte reiche Russen unzufrieden machen und ihren Präsidenten Wladimir Putin in ernsthafte Schwierigkeiten bringen.
Chinas Machtanspruch: Der nächste Weltkrieg könnte in Asien beginnenDas Pulverfass, das unser Weltgefüge sprengen kann, liegt in Asien mitten im Meer. Winzige Inselchen könnten einen Flächenbrand entfachen. Ein Angriff Chinas auf Japan ist mehr als düstere Fantasie.
Umweltpolitik: Geht doch! Wie wir das Ozonloch repariertenVor 40 Jahren wurde erstmals vor dem Verlust der Schutzschicht gegen die UV-Strahlung gewarnt. Heute ist sie fast wieder intakt. Wie damals diskutiert wurde, erinnert an heutige Umweltdebatten.
Apple: Neue iPhones jetzt schon ausverkauftWenige Stunden nach dem Vorbestellstart sind einige Modelle des iPhone 6 bereits ausverkauft. Drei bis vier Wochen müssen Kunden auf ihre Geräte warten. Telekom und Vodafone melden Bestellrekorde.
Vor dem Referendum: Englands "Schlächter" brachte Schottland zur RäsonEin planloses Heldenstück führte 1746 zur endgültigen Unterwerfung Schottlands unter die englische Herrschaft. Der Aufstand des Stuart "Bonnie Prince Charlie" endete bei Culloden in einem Blutbad.
Number of Lines:   48

5.3.2. Split documents into words, normalize and remove stopwords

In listOfNews each document is stored as a single string variable. Each of these document-strings is now split into a set of words. All words are transformed to a lower-case representation and stop-words are removed.

from nltk.corpus import stopwords
stopwordlist=stopwords.words('german')
docWords = [[word.strip('?!.:",') for word in document.lower().split() 
             if word.strip('?!.:",') not in stopwordlist] for document in listOfNews]
#print(docWords)

Display the list of words of the first 5 documents:

idx=0
for doc in docWords[:5]:
    print('------ document %d ----------'%idx)
    for d in doc:
        print(d)
    idx+=1
------ document 0 ----------
kommunikation
gut
kommunizieren
macht
glücklich
kaum
psychologe
friedemann
schulz
thun
untersucht
kommunikation
ausmacht
neuen
buch
entwickelt
quintessenz
davon
------ document 1 ----------
ukraine-krise
bundesregierung
toleriert
ukrainischen
mauerbau
kanzleramt
verständnis
pläne
ukrainischen
regierung
mauer
entlang
grenze
russland
bauen
sei
allein
entscheidung
ukraine
------ document 2 ----------
radfahren
deutsche
bringt
chinesen
aufs
fixie
einst
peking
fahrrad-welthauptstadt
kamen
autos
ines
brunn
kämpft
dagegen
inhaberin
fixie-shops
chinesen
aufs
rad
bringen
------ document 3 ----------
zurück
wildnis

jenseits
komforts
drei
jahre
lang
suchte
fotograf
antoine
bruy
aussteiger
europa
bilder
zeigen
menschen
konsum-
leistungsgesellschaft
rücken
gekehrt
------ document 4 ----------
antisemitismus
zentralrat
beklagt
schockwellen
judenhass
judenfeindliche
hetze
protesten
israel
juden
tief
getroffen
sagte
vorsitzende
zentralrats
kundgebung
zeichen
setzen

5.3.3. Generate Dictionary

The elements of the list docWords are itself lists. Each of these lists contains all relevant words of a document. The set of all relevant words in the document collection, i.e. relevant words, which appear in at least one document, are stored in a gensim-dictionary. In the dictionary to each of the relevant words an unique integer ID is assigned:

from gensim import corpora, models, similarities
dictionary = corpora.Dictionary(docWords)
dictionary.save('multiNews.dict') # store the dictionary, for future reference
print(dictionary.token2id)
{'ausmacht': 0, 'buch': 1, 'davon': 2, 'entwickelt': 3, 'friedemann': 4, 'glücklich': 5, 'gut': 6, 'kaum': 7, 'kommunikation': 8, 'kommunizieren': 9, 'macht': 10, 'neuen': 11, 'psychologe': 12, 'quintessenz': 13, 'schulz': 14, 'thun': 15, 'untersucht': 16, '\ufeffkommunikation': 17, 'allein': 18, 'bauen': 19, 'bundesregierung': 20, 'entlang': 21, 'entscheidung': 22, 'grenze': 23, 'kanzleramt': 24, 'mauer': 25, 'mauerbau': 26, 'pläne': 27, 'regierung': 28, 'russland': 29, 'sei': 30, 'toleriert': 31, 'ukraine': 32, 'ukraine-krise': 33, 'ukrainischen': 34, 'verständnis': 35, 'aufs': 36, 'autos': 37, 'bringen': 38, 'bringt': 39, 'brunn': 40, 'chinesen': 41, 'dagegen': 42, 'deutsche': 43, 'einst': 44, 'fahrrad-welthauptstadt': 45, 'fixie': 46, 'fixie-shops': 47, 'ines': 48, 'inhaberin': 49, 'kamen': 50, 'kämpft': 51, 'peking': 52, 'rad': 53, 'radfahren': 54, '': 55, 'antoine': 56, 'aussteiger': 57, 'bilder': 58, 'bruy': 59, 'drei': 60, 'europa': 61, 'fotograf': 62, 'gekehrt': 63, 'jahre': 64, 'jenseits': 65, 'komforts': 66, 'konsum-': 67, 'lang': 68, 'leistungsgesellschaft': 69, 'menschen': 70, 'rücken': 71, 'suchte': 72, 'wildnis': 73, 'zeigen': 74, 'zurück': 75, 'antisemitismus': 76, 'beklagt': 77, 'getroffen': 78, 'hetze': 79, 'israel': 80, 'juden': 81, 'judenfeindliche': 82, 'judenhass': 83, 'kundgebung': 84, 'protesten': 85, 'sagte': 86, 'schockwellen': 87, 'setzen': 88, 'tief': 89, 'vorsitzende': 90, 'zeichen': 91, 'zentralrat': 92, 'zentralrats': 93, 'artenschutz': 94, 'artenschutzabkommens': 95, 'begehrt': 96, 'besser': 97, 'fleisch': 98, 'fünf': 99, 'gesetzt': 100, 'haiarten': 101, 'haie': 102, 'internationalen': 103, 'jagt': 104, 'künftig': 105, 'liste': 106, 'mensch': 107, 'rechnen': 108, 'rote': 109, 'schützen': 110, 'strafe': 111, 'wer': 112, 'bevorzugt': 113, 'ceta': 114, 'demokratische': 115, 'eu': 116, 'freihandelsabkommen': 117, 'geklagt': 118, 'hebelt': 119, 'kanada': 120, 'kontrolle': 121, 'ttip-blaupause': 122, 'wirtschaftsinteressen': 123, 'zustimmungsfähig': 124, 'afrika': 125, 'agenda': 126, 'ende': 127, 'europäische': 128, 'frankreich': 129, 'kollaps': 130, 'landes': 131, 'libyen': 132, 'militäreinsatz': 133, 'schnell': 134, 'schweigen': 135, 'stabilisieren': 136, 'verschämte': 137, 'wenigstens': 138, 'zustimmen': 139, 'arbeit': 140, 'berichtet': 141, 'bettina': 142, 'dekoration': 143, 'direktorin': 144, 'freund': 145, 'gespräch': 146, 'hamburger': 147, 'klarheit': 148, 'kunstverein': 149, 'kunstvereins': 150, 'mag': 151, 'ordnung': 152, 'persönlich': 153, 'steinbrügge': 154, 'digitale': 155, 'digitales': 156, 'entdeckt': 157, 'familie': 158, 'fortschrittsverweigerung': 159, 'großeltern': 160, 'internet': 161, 'kuss': 162, 'mutter': 163, 'silver': 164, 'spur': 165, 'surfer': 166, 'tragen': 167, 'wettrüsten': 168, 'brauchen': 169, 'dringend': 170, 'fördermethoden': 171, 'ganze': 172, 'geld': 173, 'hart': 174, 'know-how': 175, 'land': 176, 'modernisieren': 177, 'rosneft': 178, 'russland-sanktionen': 179, 'russlands': 180, 'sanktionen': 181, 'schwierigkeiten': 182, 'treffen': 183, 'westen': 184, 'ölkonzerne': 185, '–': 186, '3': 187, 'bastian': 188, 'beschützt': 189, 'bild': 190, 'bundesliga-vorschau': 191, 'menschenrechtler': 192, 'neue': 193, 'neuigkeiten': 194, 'rassismus': 195, 'reden': 196, 'rummenigge': 197, 'schweinsteiger': 198, 'sensationelle': 199, 'spieltag': 200, 'wichtige': 201, 'album': 202, 'band': 203, 'doria': 204, 'frau': 205, 'jan': 206, 'josef': 207, 'liebt': 208, 'liefers': 209, 'neues': 210, 'pathos': 211, 'radio': 212, 'schauspieler': 213, 'singt': 214, 'tatort"-kollege': 215, 'veröffentlicht': 216, 'diskussionen': 217, 'dobrindt': 218, 'gelegenheit': 219, 'gesetzentwurf': 220, 'kündigte': 221, 'legt': 222, 'maut-gesetz': 223, 'maut-pläne': 224, 'oktober': 225, 'pkw-maut': 226, 'termin': 227, 'verkehrsminister': 228, 'ab': 229, 'abgeordneten': 230, 'abgeordneter': 231, 'aktualisierten': 232, 'bundestagsabgeordnete': 233, 'dabei': 234, 'erkleckliche': 235, 'faz.net-monitor': 236, 'gauweiler': 237, 'gregor': 238, 'gysi': 239, 'interaktiv': 240, 'millionär': 241, 'nebenher': 242, 'nebentätigkeiten': 243, 'peer': 244, 'peter': 245, 'sehen': 246, 'sofort': 247, 'steinbrück': 248, 'ständig': 249, 'summen': 250, 'teils': 251, 'verdienen': 252, 'verdient': 253, 'vierte': 254, 'braucht': 255, 'bären': 256, 'dennoch': 257, 'einzuhalten': 258, 'finnlandisierung': 259, 'greift': 260, 'höhle': 261, 'lesen': 262, 'putins': 263, 'russischen': 264, 'sicherheitsabstand': 265, 'stellenbeschreibung': 266, '43': 267, 'armeeführung': 268, 'ausspionieren': 269, 'brief': 270, 'einsätze': 271, 'eliteeinheit': 272, 'geheimdienstes': 273, 'geht': 274, 'grenzenlose': 275, 'israelische': 276, 'kündigen': 277, 'militärischen': 278, 'ministerpräsident': 279, 'mitglieder': 280, 'netanjahu': 281, 'palästinenser': 282, 'sonderheit': 283, 'verweigern': 284, 'weit': 285, '1994': 286, 'bekommen': 287, 'ersten': 288, 'gingen': 289, 'herbst': 290, 'informationsvielfalt': 291, 'lernen': 292, 'leser': 293, 'medien': 294, 'müssen': 295, 'nie': 296, 'online': 297, 'online-journalismus': 298, 'richtig': 299, 'schafft': 300, 'seitdem': 301, 'umzugehen': 302, 'vorgestellt': 303, 'zeitung': 304, 'zwanzig': 305, 'boom': 306, 'china': 307, 'energien': 308, 'erleben': 309, 'erneuerbaren': 310, 'gerade': 311, 'gilt': 312, 'klimaschutz': 313, 'mitte': 314, 'reich': 315, 'sachen': 316, 'umwelt-': 317, 'unbekannte': 318, 'unglaublichen': 319, 'vorreiter': 320, 'ökostrom-riese': 321, 'anzunähern': 322, 'biografie': 323, 'biographie': 324, 'de': 325, 'gelingt': 326, 'grey“': 327, 'liest': 328, 'marquis': 329, 'nüchtern': 330, 'of': 331, 'pixi-buch': 332, 'reinhardt': 333, 'sade': 334, 'skandalösen': 335, 'skandalöseste': 336, 'texten': 337, 'vergleich': 338, 'volker': 339, '„shades': 340, 'allerdings': 341, 'behalten': 342, 'christensen': 343, 'erzählt': 344, 'interview': 345, 'kleider': 346, 'nackt': 347, 'nackt-porträts': 348, 'nude-porträts': 349, 'porträtierten': 350, 'trevor': 351, 'warum': 352, 'zieht': 353, 'aktivitäten': 354, 'bundesinnenminister': 355, 'deutschlands': 356, 'dschihad': 357, 'dürfe': 358, 'gefährde': 359, 'getragen': 360, 'maizière': 361, 'sicherheit': 362, 'sofortiger': 363, 'staats“': 364, 'staat“': 365, 'straßen“': 366, 'terrorgruppe': 367, 'verbot': 368, 'verboten': 369, 'verkündet': 370, 'wirkung“': 371, '„durch': 372, '„islamischen': 373, '„islamischer': 374, '„mit': 375, 'behindertensports': 376, 'bewegung“': 377, 'darüber': 378, 'gefallener': 379, 'geister': 380, 'held': 381, 'mehr': 382, 'oscar': 383, 'paralympischen': 384, 'pistorius': 385, 'platz': 386, 'scheiden': 387, 'spielen': 388, 'straftäter': 389, 'superstar': 390, 'teilnehmen': 391, 'verbüßen': 392, 'verurteilter': 393, '„kein': 394, 'engen': 395, 'kraft': 396, 'putin': 397, 'rohstoffriesen': 398, 'russische': 399, 'separatisten-anführer': 400, 'staatspräsident': 401, 'strafmaßnahmen': 402, 'trifft': 403, 'wen': 404, 'wladimir': 405, 'aufgefallen': 406, 'bloß': 407, 'form': 408, 'fraktur': 409, 'hybridkriege': 410, 'hybridschweine': 411, 'koteletts': 412, 'krim': 413, 'lieber': 414, 'schnitzeljagd': 415, 'austausch': 416, 'auto': 417, 'bleibt': 418, 'eingestimmt': 419, 'heile': 420, 'hoffnungsfroh': 421, 'lohnt': 422, 'manchmal': 423, 'oft': 424, 'reparatur': 425, 'spezialisten': 426, 'steinschlag': 427, 'wann': 428, 'werbung': 429, 'windschutzscheibe': 430, 'altmaier': 431, 'autor': 432, 'drängend': 433, 'freizeitsozialismus': 434, 'ideenlos': 435, 'illner': 436, 'illner“': 437, 'jeremy': 438, 'kanzleramtschef': 439, 'kommt': 440, 'maybrit': 441, 'null-grenzkosten-gesellschaft“': 442, 'politik': 443, 'rifkins': 444, 'thema': 445, 'tv-kritik': 446, 'vorbei': 447, 'zeigt': 448, '„die': 449, '„maybrit': 450, '2008': 451, '250.000': 452, 'amerikanische': 453, 'angeblich': 454, 'ausspähprogramm': 455, 'bedenken': 456, 'beiseite': 457, 'bußgeld': 458, 'dollar': 459, 'drohte': 460, 'druck': 461, 'forderung': 462, 'gerichte': 463, 'gewährt': 464, 'internetkonzerns': 465, 'jahr': 466, 'nutzerdaten': 467, 'pro': 468, 'tag': 469, 'wischten': 470, 'yahoo': 471, 'zahlt': 472, 'zugang': 473, '„prism“': 474, 'arztgespräch': 475, 'ausführlicheres': 476, 'beim': 477, 'bestem': 478, 'dank': 479, 'facharzt': 480, 'früherer': 481, 'gesetzlich': 482, 'gesundheitssystem': 483, 'müssten': 484, 'praxen': 485, 'privatpatienten': 486, 'privatversicherungen': 487, 'problem': 488, 'schließen': 489, 'versicherte': 490, 'viele': 491, 'zeit': 492, 'ärgert': 493, 'ärzte': 494, '-': 495, 'erkennen': 496, 'fernseh-altmeister': 497, 'fernsehgeschichte': 498, 'fuchsberger': 499, 'joachim': 500, 'letzte': 501, 'moderator': 502, 'männern': 503, 'prägte': 504, 'schlagertexter': 505, 'tausendsassa': 506, 'tod': 507, 'anzulasten': 508, 'berichte': 509, 'einzigen': 510, 'fast': 511, 'geworden': 512, 'häufen': 513, 'majdan-kämpfer': 514, 'nachrichten': 515, 'ostukraine': 516, 'routine': 517, 'schon': 518, 'schuld': 519, 'seite': 520, 'separatisten': 521, 'verbrechen': 522, 'verstrickung': 523, 'zweifelhafte': 524, '15': 525, 'adidas': 526, 'cent': 527, 'claudia': 528, 'dfb-trikot': 529, 'entwicklungsminister': 530, 'falsch': 531, 'fertigungsumstände': 532, 'geschäft': 533, 'kritisiert': 534, 'lohn': 535, 'müller': 536, 'roth': 537, 'sagt': 538, 'sterne': 539, 'trikot': 540, 'vier': 541, 'vorwurf': 542, '„unerträglich“': 543, 'bürgermeisteramt': 544, 'fränkische': 545, 'frühere': 546, 'gabriele': 547, 'insel': 548, 'kandidatur': 549, 'landrätin': 550, 'mitwirkte': 551, 'pauli': 552, 'reif': 553, 'sammelt': 554, 'stoibers': 555, 'strebt': 556, 'sturz': 557, 'sylt': 558, 'unterschriften': 559, 'westerland': 560, 'böses': 561, 'dach': 562, 'erwachen': 563, 'flüchtlinge': 564, 'gibt': 565, 'kopf': 566, 'kostenlos': 567, 'landung': 568, 'sibirien': 569, 'verspricht': 570, 'übersiedlern': 571, 'artikel': 572, 'bald': 573, 'buzzfeed': 574, 'deutscher': 575, 'fakten“': 576, 'findet': 577, 'gründen“': 578, 'guten': 579, 'häufig': 580, 'kurzartikel': 581, 'listenform': 582, 'netz': 583, 'posts': 584, 'qual': 585, 'sprache': 586, 'verbreiten': 587, 'zahl': 588, 'überzeugen': 589, '„10': 590, '„15': 591, '100': 592, 'armeen': 593, 'ausbruch': 594, 'deutschen': 595, 'erfolg': 596, 'kam': 597, 'krieges': 598, 'marne': 599, 'marne-schlacht': 600, 'nebel': 601, 'stillstand': 602, 'verloren': 603, 'vermeiden': 604, 'vormarsch': 605, 'weltkrieges': 606, 'wenige': 607, 'wochen': 608, 'wäre': 609, 'zweifrontenkrieg': 610, 'fahrkartenautomaten': 611, 'fehler': 612, 'funktionieren': 613, 'fünfer-umstellung': 614, 'kommen': 615, 'optik': 616, 'reibungslos': 617, 'scheine': 618, 'sollen': 619, 'wiederholen': 620, 'zehn-euro-scheine': 621, 'zehner': 622, 'zigarettenautomaten': 623, 'zwei': 624, 'anbieter': 625, 'derzeit': 626, 'ezb': 627, 'hängt': 628, 'leitzinssenkung': 629, 'passende': 630, 'schlägt': 631, 'tagesgeld': 632, 'zins': 633, 'zinssatz': 634, 'blieben': 635, 'eltern': 636, 'fahrlässiger': 637, 'fassungslosigkeit': 638, 'frauen': 639, 'gefasst': 640, 'ging': 641, 'halten': 642, 'milde': 643, 'pistorius-urteilpistorius': 644, 'präzedenzfall': 645, 'raunen': 646, 'saal': 647, 'schien': 648, 'schlechten': 649, 'steenkamps': 650, 'südafrika': 651, 'südafrikas': 652, 'tötung': 653, 'urteil': 654, 'verbeugen': 655, 'wegen': 656, 'ankara': 657, 'anti-terror-allianz': 658, 'eigene': 659, 'eintreten': 660, 'extremisten': 661, 'fehlterst': 662, 'geführte': 663, 'indirekt': 664, 'islamische': 665, 'islamischen': 666, 'islamischer': 667, 'koalition': 668, 'staat': 669, 'syrien': 670, 'türkei': 671, 'unterstützte': 672, 'usa': 673, 'bedroht': 674, 'deutschland': 675, 'deutschlandinnenminister': 676, 'dürfen': 677, 'extremistengruppe': 678, 'is': 679, 'sieht': 680, 'symbole': 681, 'terrormiliz': 682, 'thomas': 683, 'verbietet': 684, 'bundestag': 685, 'bundestagals': 686, 'fundamental': 687, 'große': 688, 'maut-debatte': 689, 'minister': 690, 'sabotiert': 691, 'sachthemen': 692, 'spd': 693, 'straßenbau': 694, 'union': 695, 'verkehr': 696, 'verkehrspolitik': 697, 'vorstellt': 698, 'weichen': 699, 'zerstritten': 700, '18': 701, 'angst': 702, 'bebenam': 703, 'effekte': 704, 'eigenen': 705, 'europas': 706, 'flandern': 707, 'fürchten': 708, 'investoren': 709, 'katalonien': 710, 'lässt': 711, 'märkte': 712, 'negativ': 713, 'schotten': 714, 'separatismus': 715, 'september': 716, 'stimmen': 717, 'streben': 718, 'unabhängigkeit': 719, 'zerfall': 720, 'ernsthafte': 721, 'erreicht': 722, 'folterwerkzeuge': 723, 'maßnahmenpaket': 724, 'moskau': 725, 'nächste': 726, 'präsidenten': 727, 'reiche': 728, 'russen': 729, 'stufe': 730, 'unzufrieden': 731, 'wehtundie': 732, 'angriff': 733, 'asien': 734, 'beginnendas': 735, 'chinas': 736, 'düstere': 737, 'entfachen': 738, 'fantasie': 739, 'flächenbrand': 740, 'inselchen': 741, 'japan': 742, 'könnten': 743, 'liegt': 744, 'machtanspruch': 745, 'meer': 746, 'mitten': 747, 'pulverfass': 748, 'sprengen': 749, 'weltgefüge': 750, 'weltkrieg': 751, 'winzige': 752, '40': 753, 'damals': 754, 'diskutiert': 755, 'erinnert': 756, 'erstmals': 757, 'gewarnt': 758, 'heute': 759, 'heutige': 760, 'intakt': 761, 'jahren': 762, 'ozonloch': 763, 'repariertenvor': 764, 'schutzschicht': 765, 'umweltdebatten': 766, 'umweltpolitik': 767, 'uv-strahlung': 768, 'verlust': 769, 'wurde': 770, '6': 771, 'apple': 772, 'ausverkauft': 773, 'ausverkauftwenige': 774, 'bereits': 775, 'bestellrekorde': 776, 'geräte': 777, 'iphone': 778, 'iphones': 779, 'kunden': 780, 'melden': 781, 'modelle': 782, 'stunden': 783, 'telekom': 784, 'vodafone': 785, 'vorbestellstart': 786, 'warten': 787, '1746': 788, 'aufstand': 789, 'blutbad': 790, 'bonnie': 791, 'brachte': 792, 'charlie': 793, 'culloden': 794, 'endete': 795, 'endgültigen': 796, 'englands': 797, 'englische': 798, 'führte': 799, 'heldenstück': 800, 'herrschaft': 801, 'planloses': 802, 'prince': 803, 'referendum': 804, 'räsonein': 805, 'schlächter': 806, 'schottland': 807, 'schottlands': 808, 'stuart': 809, 'unterwerfung': 810}
print("Total number of documents in the dictionary: ",dictionary.num_docs)
print("Total number of corpus positions: ",dictionary.num_pos)
print("Total number of non-zeros in the BoW-Matrix: ",dictionary.num_nnz)
print("Total number of different words in the dictionary: ",len(dictionary))
Total number of documents in the dictionary:  48
Total number of corpus positions:  975
Total number of non-zeros in the BoW-Matrix:  914
Total number of different words in the dictionary:  811

5.3.4. Bag of Word (BoW) representation

Now arbitrary text-strings can be efficiently represented with respect to this dictionary. E.g. the code snippet below demonstrates how the text string “putin beschützt russen” is represented as a list of tuples. The first element of such a tuple is the dictionary index of a word in the text-string and the second number defines how often this word occurs in the text-string. The list contains only tuples for words which occur in the text-string and in the dictionary. This representation is called sparse Bag of Word representation (sparse because it contains only the non-zero elements).

newDoc = "putin beschützt russen"
newVec = dictionary.doc2bow(newDoc.lower().split())
print("Sparse BoW representation of %s: %s"%(newDoc,newVec))
for idx,freq in newVec:
    print("Index %d refers to word %s. Frequency of this word in the document is %d"%(idx,dictionary[idx],freq))
Sparse BoW representation of putin beschützt russen: [(189, 1), (397, 1), (729, 1)]
Index 189 refers to word beschützt. Frequency of this word in the document is 1
Index 397 refers to word putin. Frequency of this word in the document is 1
Index 729 refers to word russen. Frequency of this word in the document is 1

From this output we infer, that

  • the word at index 189 in the dictionary, which is beschützt , appears once in the text-string newDoc

  • the word at index 397 in the dictionary, which is putin , appears once in the text-string newDoc

  • the word at index 729 in the dictionary, which is russen , appears once in the text-string newDoc .

The text-string “schottland stimmt ab” is represented as a list of 2 tuples (see code snippet below). The first says, that the word at index 229 ( ab ) appears once, the second tuple says that the word at index 807 ( schottland ) also appears once in the text-string. Since the word stimmt does not appear in the dictionary, there is no corresponding tuple for this word in the list.

newDoc2 = "schottland stimmt ab"
newVec2 = dictionary.doc2bow(newDoc2.lower().split())
print("Sparse BoW representation of %s: %s"%(newDoc2,newVec2))
for idx,freq in newVec2:
    print("Index %d refers to word %s. Frequency of this word in the document is %d"%(idx,dictionary[idx],freq))
Sparse BoW representation of schottland stimmt ab: [(229, 1), (807, 1)]
Index 229 refers to word ab. Frequency of this word in the document is 1
Index 807 refers to word schottland. Frequency of this word in the document is 1

5.3.5. Efficient Corpus Representation

A corpus is a collection of documents. Such corpora may be annotated with meta-information, e.g. each word is tagged with its part of speech (POS-Tag). In this notebook, the list docWords, is a corpus without any annotations. So far this corpus has been applied to build the dictionary. In practical NLP tasks corpora are usually very large and therefore require an efficient representation. Using the already generated dictionary, each document (list of relevant words in a document) in the list docWords can be transformed to its sparse BoW representation.

corpus = [dictionary.doc2bow(doc) for doc in docWords]
corpora.MmCorpus.serialize('multiNews.mm', corpus)
print("------------------------- First 10 documents of the corpus ---------------------------------")
idx=0
for d in corpus[0:10]:
    print("-------------document %d ---------------" %idx)
    print(d)
    idx+=1
------------------------- First 10 documents of the corpus ---------------------------------
-------------document 0 ---------------
[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), (14, 1), (15, 1), (16, 1), (17, 1)]
-------------document 1 ---------------
[(18, 1), (19, 1), (20, 1), (21, 1), (22, 1), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), (29, 1), (30, 1), (31, 1), (32, 1), (33, 1), (34, 2), (35, 1)]
-------------document 2 ---------------
[(36, 2), (37, 1), (38, 1), (39, 1), (40, 1), (41, 2), (42, 1), (43, 1), (44, 1), (45, 1), (46, 1), (47, 1), (48, 1), (49, 1), (50, 1), (51, 1), (52, 1), (53, 1), (54, 1)]
-------------document 3 ---------------
[(55, 1), (56, 1), (57, 1), (58, 1), (59, 1), (60, 1), (61, 1), (62, 1), (63, 1), (64, 1), (65, 1), (66, 1), (67, 1), (68, 1), (69, 1), (70, 1), (71, 1), (72, 1), (73, 1), (74, 1), (75, 1)]
-------------document 4 ---------------
[(76, 1), (77, 1), (78, 1), (79, 1), (80, 1), (81, 1), (82, 1), (83, 1), (84, 1), (85, 1), (86, 1), (87, 1), (88, 1), (89, 1), (90, 1), (91, 1), (92, 1), (93, 1)]
-------------document 5 ---------------
[(94, 1), (95, 1), (96, 1), (97, 1), (98, 1), (99, 1), (100, 1), (101, 1), (102, 1), (103, 1), (104, 1), (105, 1), (106, 1), (107, 1), (108, 1), (109, 1), (110, 1), (111, 1), (112, 1)]
-------------document 6 ---------------
[(42, 1), (113, 1), (114, 1), (115, 1), (116, 1), (117, 2), (118, 1), (119, 1), (120, 1), (121, 1), (122, 1), (123, 1), (124, 1)]
-------------document 7 ---------------
[(125, 1), (126, 1), (127, 1), (128, 1), (129, 1), (130, 1), (131, 1), (132, 2), (133, 1), (134, 1), (135, 1), (136, 1), (137, 1), (138, 1), (139, 1)]
-------------document 8 ---------------
[(140, 1), (141, 1), (142, 1), (143, 1), (144, 1), (145, 1), (146, 1), (147, 2), (148, 1), (149, 1), (150, 1), (151, 1), (152, 1), (153, 1), (154, 1)]
-------------document 9 ---------------
[(55, 1), (155, 1), (156, 1), (157, 1), (158, 1), (159, 1), (160, 1), (161, 1), (162, 1), (163, 1), (164, 1), (165, 1), (166, 1), (167, 1), (168, 1)]

5.3.6. Similarity Analysis

Typical information retrieval tasks include the task of determining similarities between collections of documents or between a query and a collection of documents. Using gensim a fast similarity calculation and search is supported. For this, first a cosine-similarity-index of the given corpus is calculated as follows:

index = similarities.SparseMatrixSimilarity(corpus, num_features=len(dictionary))

Now, assume that for a given query, e.g. “putin beschützt russen” the best matching document in the corpus must be determined. The sparse BoW representation of this query has already been calculated and stored in the variable newVec. The similarity between this query and all documents in the corpus can be calculated as follows:

sims = index[newVec]
print(list(enumerate(sims)))
[(0, 0.0), (1, 0.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.13608277), (12, 0.0), (13, 0.0), (14, 0.0), (15, 0.0), (16, 0.0), (17, 0.0), (18, 0.0), (19, 0.0), (20, 0.0), (21, 0.0), (22, 0.0), (23, 0.12309149), (24, 0.0), (25, 0.0), (26, 0.0), (27, 0.0), (28, 0.0), (29, 0.0), (30, 0.0), (31, 0.0), (32, 0.0), (33, 0.0), (34, 0.0), (35, 0.0), (36, 0.0), (37, 0.0), (38, 0.0), (39, 0.0), (40, 0.0), (41, 0.0), (42, 0.0), (43, 0.23570226), (44, 0.0), (45, 0.0), (46, 0.0), (47, 0.0)]

The tuples in this output contain as first element the index of the document in the corpus. The second element is the cosine-similarity between this corpus-document and the query.

In order to get a sorted list of increasingly similar documents, the argsort()-method can be applied as shown below. The last value in this list is the index of the most similar document:

print(sims.argsort())
[ 0 25 26 27 28 29 30 31 32 33 24 34 36 37 38 39 40 41 42 44 45 35 46 47
 21  1  2  3  4  5  6  7  8  9 22 10 12 13 14 15 16 17 18 19 20 23 11 43]

In this example document 43 best matches to the query. The cosine-similarity between the query and document 43 is 0.2357.

Question: Manually verify the calculated similiarity value between the query and document 43.

In the same way the similarity between documents in the corpus can be calculated. E.g. the similiarity between document 1 and all other documents in the corpus is determined as follows:

sims = index[corpus[1]]
print((list(enumerate(sims))))
print(sims.argsort())
[(0, 0.0), (1, 1.0), (2, 0.0), (3, 0.0), (4, 0.0), (5, 0.0), (6, 0.0), (7, 0.0), (8, 0.0), (9, 0.0), (10, 0.0), (11, 0.0), (12, 0.0), (13, 0.05143445), (14, 0.0), (15, 0.15877683), (16, 0.0), (17, 0.0), (18, 0.0), (19, 0.0), (20, 0.0), (21, 0.0), (22, 0.0), (23, 0.0), (24, 0.0), (25, 0.0), (26, 0.044543542), (27, 0.0727393), (28, 0.0), (29, 0.0), (30, 0.05006262), (31, 0.048795003), (32, 0.0), (33, 0.04761905), (34, 0.0), (35, 0.0), (36, 0.0), (37, 0.0), (38, 0.0), (39, 0.0), (40, 0.04364358), (41, 0.045501575), (42, 0.0), (43, 0.0), (44, 0.0), (45, 0.0), (46, 0.0), (47, 0.0)]
[ 0 22 46 24 25 28 29 32 34 35 36 37 38 39 42 43 44 45 21 20 23 18  2  3
  4  5  6  7  8  9 19 11 10 12 14 16 17 47 40 26 41 33 31 30 13 27 15  1]

Thus document 15 is the most similar document to document 1. As can easily be verified both documents refer to the same topic (crisis in ukraine).

5.3.7. TF-IDF representation

So far in the BoW representation of the documents the term frequency (tf) has been applied. This value measures how often the term (word) appears in the document. If document similarity is calculated on such tf-based BoW representation, common words which appear quite often (in many documents) but have low semantic focus have a strong impact on the similarity-value. In most cases this is a drawback, since similarity should be based on terms with a high semantic focus. Such semantically meaningful words usually appear only in a few documents. The term frequency inversed document frequency measure (tf-idf) does not only count the frequency of a term in a document, but weighs those terms stronger, which occur only in a few documents of the corpus.

In gensim the tfidf - model of a corpus can be calculated as follows:

tfidf = models.TfidfModel(corpus)

The tf-idf-representation of the first 3 documents in the corpus are:

idx=0
for d in corpus[:3]:
    print("-------------tf-idf BoW of document %d ---------------" %idx)
    print(tfidf[d])
    idx+=1
-------------tf-idf BoW of document 0 ---------------
[(0, 0.24353425982511087), (1, 0.1999289070955868), (2, 0.24353425982511087), (3, 0.24353425982511087), (4, 0.24353425982511087), (5, 0.24353425982511087), (6, 0.24353425982511087), (7, 0.24353425982511087), (8, 0.24353425982511087), (9, 0.24353425982511087), (10, 0.1999289070955868), (11, 0.1744214109180963), (12, 0.24353425982511087), (13, 0.24353425982511087), (14, 0.24353425982511087), (15, 0.24353425982511087), (16, 0.24353425982511087), (17, 0.24353425982511087)]
-------------tf-idf BoW of document 1 ---------------
[(18, 0.23522128928414127), (19, 0.23522128928414127), (20, 0.16846758720673122), (21, 0.23522128928414127), (22, 0.23522128928414127), (23, 0.23522128928414127), (24, 0.23522128928414127), (25, 0.23522128928414127), (26, 0.23522128928414127), (27, 0.19310439248245848), (28, 0.19310439248245848), (29, 0.16846758720673122), (30, 0.16846758720673122), (31, 0.23522128928414127), (32, 0.19310439248245848), (33, 0.16846758720673122), (34, 0.47044257856828253), (35, 0.23522128928414127)]
-------------tf-idf BoW of document 2 ---------------
[(36, 0.4166329302664041), (37, 0.20831646513320204), (38, 0.17101693714061422), (39, 0.17101693714061422), (40, 0.20831646513320204), (41, 0.4166329302664041), (42, 0.17101693714061422), (43, 0.17101693714061422), (44, 0.17101693714061422), (45, 0.20831646513320204), (46, 0.20831646513320204), (47, 0.20831646513320204), (48, 0.20831646513320204), (49, 0.20831646513320204), (50, 0.20831646513320204), (51, 0.17101693714061422), (52, 0.20831646513320204), (53, 0.20831646513320204), (54, 0.20831646513320204)]

In this representation the second element in the tuples is not the term frequency, but the tfidf. Note that default configuration of tf-idf in gensim calculates tf-idf values such that each document-vector has a norm of 1. The tfidf-model without normalization is generated at the end of this notebook.

Question: Find the maximum tf-idf value in these 3 documents. To which word does this maximum value belong? How often does this word occur in the document?

The tf-idf-representation of the text-string “putin beschützt russen” is determined as follows:

newVecTfIdf = tfidf[newVec]
print("tf BoW representation of %s is:\n %s"%(newDoc,newVec))
print("tf-idf BoW representation of %s is:\n %s"%(newDoc,newVecTfIdf))
tf BoW representation of putin beschützt russen is:
 [(189, 1), (397, 1), (729, 1)]
tf-idf BoW representation of putin beschützt russen is:
 [(189, 0.6115372747558391), (397, 0.5020401609118563), (729, 0.6115372747558391)]

Question: Explain the different values in the tfidf BoW representation newVecTfIdf.

TF-IDF-Model without normalization:

tfidfnoNorm = models.TfidfModel(corpus,normalize=False)

Display tf-idf BoW of first 3 documents:

idx=0
for d in corpus[:3]:
    print("-------------tf-idf BoW of document %d ---------------" %idx)
    print(tfidfnoNorm[d])
    idx+=1
-------------tf-idf BoW of document 0 ---------------
[(0, 5.584962500721157), (1, 4.584962500721157), (2, 5.584962500721157), (3, 5.584962500721157), (4, 5.584962500721157), (5, 5.584962500721157), (6, 5.584962500721157), (7, 5.584962500721157), (8, 5.584962500721157), (9, 5.584962500721157), (10, 4.584962500721157), (11, 4.0), (12, 5.584962500721157), (13, 5.584962500721157), (14, 5.584962500721157), (15, 5.584962500721157), (16, 5.584962500721157), (17, 5.584962500721157)]
-------------tf-idf BoW of document 1 ---------------
[(18, 5.584962500721157), (19, 5.584962500721157), (20, 4.0), (21, 5.584962500721157), (22, 5.584962500721157), (23, 5.584962500721157), (24, 5.584962500721157), (25, 5.584962500721157), (26, 5.584962500721157), (27, 4.584962500721157), (28, 4.584962500721157), (29, 4.0), (30, 4.0), (31, 5.584962500721157), (32, 4.584962500721157), (33, 4.0), (34, 11.169925001442314), (35, 5.584962500721157)]
-------------tf-idf BoW of document 2 ---------------
[(36, 11.169925001442314), (37, 5.584962500721157), (38, 4.584962500721157), (39, 4.584962500721157), (40, 5.584962500721157), (41, 11.169925001442314), (42, 4.584962500721157), (43, 4.584962500721157), (44, 4.584962500721157), (45, 5.584962500721157), (46, 5.584962500721157), (47, 5.584962500721157), (48, 5.584962500721157), (49, 5.584962500721157), (50, 5.584962500721157), (51, 4.584962500721157), (52, 5.584962500721157), (53, 5.584962500721157), (54, 5.584962500721157)]

Verify the tf-idf-values as calculated in the code-cell above, by own tf-idf-formula:

import numpy as np
tf=1 #term frequency
NumDocs=dictionary.num_docs #number of documents
df=1 #number of documents in which the word appears
tfidf=tf*np.log2(float(NumDocs)/df)
print(tfidf)
5.584962500721156

5.3.8. Tokenisation and Document models with Keras

This section demonstrates how Keras can be applied for tokenisation and BoW document-modelling. I.e. no new techniques are introduced here. Instead it is shown how Keras can be applied to implement already known procedures. This is usefunl, because Keras will be applied later on to implement Neural Networks.

5.3.8.1. Tokenizsation

5.3.8.1.1. Text collections as lists of strings

Tokens are atomic text elements. Depending on the NLP task and the selected approach to solve this task, tokens can either be

  • characters

  • words (uni-grams)

  • n-grams

Single texts are often represented as variables of type string. Collections of texts are then represented as lists of strings.

Below, a collection of 3 texts is generated as a list of string-variables:

text1="""Florida shooting: Nikolas Cruz confesses to police Nikolas Cruz is said
to have killed 17 people before escaping and visiting a McDonalds."""
text2="""Winter Olympics: Great Britain's Dom Parsons wins skeleton bronze medal
Dom Parsons claims Great Britain's first medal of 2018 Winter Olympics with bronze in the men's skeleton."""
text3="""Theresa May to hold talks with Angela Merkel in Berlin
The prime minister's visit comes amid calls for the UK to say what it wants from Brexit."""
print(text1)
Florida shooting: Nikolas Cruz confesses to police Nikolas Cruz is said
to have killed 17 people before escaping and visiting a McDonalds.
textlist=[text1,text2,text3]

5.3.8.1.2. Keras class Tokenizer

In Keras methods for preprocessing texts are contained in keras.preprocessing.text. From this module, we apply the Tokenizer-class to

  • transform words to integers, i.e. generating a word-index

  • represent texts as sequences of integers

  • represent collections of texts in a Bag-of-Words (BOW)-matrix

from keras.preprocessing import text

Generate a Tokenizer-object and fit it on the given list of texts:

tokenizer=text.Tokenizer()
tokenizer.fit_on_texts(textlist)

The Tokenizer-class accepts a list of arguments, which can be configured at initialisation of a Tokenizer-object. The default-values are printed below:

print("Configured maximum number of words in the vocabulary: ",tokenizer.num_words) #Maximum number of words to regard in the vocabulary
print("Configured filters: ",tokenizer.filters) #characters to ignore in tokenization
print("Map all characters to lower case: ",tokenizer.lower) #Mapping of characters to lower-case
print("Tokenizsation on character level: ",tokenizer.char_level) #whether tokens are words or characters
Configured maximum number of words in the vocabulary:  None
Configured filters:  !"#$%&()*+,-./:;<=>?@[\]^_`{|}~	

Map all characters to lower case:  True
Tokenizsation on character level:  False
print("Number of documents: ",tokenizer.document_count)
Number of documents:  3

Similar as the dictionary in gensim (see above), the Keras Tokenizer provides a word-index, which uniquely maps each word to an integer:

print("Index of words: ",tokenizer.word_index)
Index of words:  {'to': 1, 'the': 2, 'nikolas': 3, 'cruz': 4, 'winter': 5, 'olympics': 6, 'great': 7, "britain's": 8, 'dom': 9, 'parsons': 10, 'skeleton': 11, 'bronze': 12, 'medal': 13, 'with': 14, 'in': 15, 'florida': 16, 'shooting': 17, 'confesses': 18, 'police': 19, 'is': 20, 'said': 21, 'have': 22, 'killed': 23, '17': 24, 'people': 25, 'before': 26, 'escaping': 27, 'and': 28, 'visiting': 29, 'a': 30, 'mcdonalds': 31, 'wins': 32, 'claims': 33, 'first': 34, 'of': 35, '2018': 36, "men's": 37, 'theresa': 38, 'may': 39, 'hold': 40, 'talks': 41, 'angela': 42, 'merkel': 43, 'berlin': 44, 'prime': 45, "minister's": 46, 'visit': 47, 'comes': 48, 'amid': 49, 'calls': 50, 'for': 51, 'uk': 52, 'say': 53, 'what': 54, 'it': 55, 'wants': 56, 'from': 57, 'brexit': 58}

The method word_docs() returns for each word the number of documents, in which the word appears:

print("Number of docs, in which word appears: ",tokenizer.word_docs)
Number of docs, in which word appears:  defaultdict(<class 'int'>, {'a': 1, 'people': 1, 'cruz': 1, 'killed': 1, 'florida': 1, 'escaping': 1, 'mcdonalds': 1, 'to': 2, 'shooting': 1, 'and': 1, 'police': 1, 'have': 1, 'confesses': 1, 'nikolas': 1, 'before': 1, 'visiting': 1, 'is': 1, '17': 1, 'said': 1, 'the': 2, 'winter': 1, 'olympics': 1, 'dom': 1, 'medal': 1, 'of': 1, "britain's": 1, 'parsons': 1, 'first': 1, 'with': 2, "men's": 1, 'in': 2, 'wins': 1, 'skeleton': 1, 'great': 1, '2018': 1, 'bronze': 1, 'claims': 1, 'wants': 1, 'theresa': 1, 'uk': 1, 'it': 1, 'hold': 1, 'brexit': 1, 'merkel': 1, "minister's": 1, 'calls': 1, 'prime': 1, 'comes': 1, 'berlin': 1, 'talks': 1, 'what': 1, 'say': 1, 'for': 1, 'visit': 1, 'angela': 1, 'amid': 1, 'may': 1, 'from': 1})

5.3.8.2. Represent texts as sequences of word-indices:

The following representation of texts as sequences of word-indicees is a common input to Neural Networks implemented in Keras.

textSeqs=tokenizer.texts_to_sequences(textlist)
for i,ts in enumerate(textSeqs):
    print("text %d sequence: "%i,ts)
text 0 sequence:  [16, 17, 3, 4, 18, 1, 19, 3, 4, 20, 21, 1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
text 1 sequence:  [5, 6, 7, 8, 9, 10, 32, 11, 12, 13, 9, 10, 33, 7, 8, 34, 13, 35, 36, 5, 6, 14, 12, 15, 2, 37, 11]
text 2 sequence:  [38, 39, 1, 40, 41, 14, 42, 43, 15, 44, 2, 45, 46, 47, 48, 49, 50, 51, 2, 52, 1, 53, 54, 55, 56, 57, 58]

5.3.8.3. Represent text-collection as binary BoW:

A Bag-Of-Words representation of documents contains \(N\) rows and \(|V|\) columns, where \(N\) is the number of documents in the collection and \(|V|\) is the size of the vocabulary, i.e. the number of different words in the entire document collection.

The entry \(x_{i,j}\) of the BoW-Matrix indicates the relevance of word \(j\) in document \(i\).

In this lecture 3 different types of word-relevance are considered:

  1. Binary BoW: Entry \(x_{i,j}\) is 1 if word \(j\) appears in document \(i\), otherwise 0.

  2. Count-based BoW: Entry \(x_{i,j}\) is the frequency of word \(j\) in document \(i\).

  3. Tf-idf-based BoW: Entry \(x_{i,j}\) is the tf-idf of word \(j\) with respect to document \(i\).

The BoW-representation of texts is a common input to conventional Machine Learning algorithms (not Neural Netorks like CNN and RNN).

5.3.8.3.1. Binary BoW

print(tokenizer.texts_to_matrix(textlist))
[[0. 1. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1.
  1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
  1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]

5.3.8.3.2. Count-based BoW

Represent text-collection as BoW with word-counts:

print(tokenizer.texts_to_matrix(textlist,mode="count"))
[[0. 2. 0. 2. 2. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1.
  1. 1. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0. 2. 2. 2. 2. 2. 2. 2. 2. 2. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 2. 2. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
  1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]

5.3.8.3.3. Tf-idf-based BoW

In the BoW representation above the term frequency (tf) has been applied. This value measures how often the term (word) appears in the document. If document similarity is calculated on such tf-based BoW representation, common words which appear quite often (in many documents) but have low semantic focus, have a strong impact on the similarity-value. In most cases this is a drawback, since similarity should be based on terms with a high semantic focus. Such semantically meaningful words usually appear only in a few documents. The term frequency inversed document frequency measure (tf-idf) does not only count the frequency of a term in a document, but weighs those terms stronger, which occur only in a few documents of the corpus.

print(tokenizer.texts_to_matrix(textlist,mode="tfidf"))
[[0.         1.17360019 0.         1.55141507 1.55141507 0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.91629073 0.91629073
  0.91629073 0.91629073 0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.        ]
 [0.         0.         0.69314718 0.         0.         1.55141507
  1.55141507 1.55141507 1.55141507 1.55141507 1.55141507 1.55141507
  1.55141507 1.55141507 0.69314718 0.69314718 0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.        ]
 [0.         1.17360019 1.17360019 0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.69314718 0.69314718 0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.         0.         0.         0.
  0.         0.         0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.91629073 0.91629073 0.91629073 0.91629073
  0.91629073 0.91629073 0.91629073 0.91629073 0.91629073]]