The following tutorial covers a complete Python example on how to read data from a MongoDB database, process it with Pandas and NLTK and get info out of the data

We analyze the Twitter Stream for German #Tatort („Am Ende des Flurs“) from 04.05.2014

This post is a (unformatted) copy of the IPython notebook, which can be found in our Github.

OK, lets go…

»I hob scho immer Frauen mögn, wo ma übern Zaun steigen muass.«

Some Example Tweets out of the Database

Out[7]:
follower geo replyto source text user userfriends
Zeit
2014-05-04 22:09:25+02:00 20 None None Twitter for Android Das offene Ende der heutigen Folge und die Twe… GuidoNeumann 43
2014-05-04 22:09:35+02:00 258 None None Twitter for iPad Also, bei der Fallanalyse wär ich sofort mit d… new_reads 453
2014-05-04 22:09:41+02:00 29 None None WordPress.com Am Ende des Flurs – Tatort 910 / TatortAntholo… DerWahlberliner 35
2014-05-04 22:09:42+02:00 499 None None web Der #tatort war ganz gut. Viel Gefühl und noch… kass_basel 1035
2014-05-04 22:09:43+02:00 10 None Tatort Twitter for iPhone @Tatort @BertInter „Kroate“ ist man im Zweifel… TscheijPieh 57
2014-05-04 22:09:43+02:00 1606 None None TweetDeck Muss das nicht schrecklich sein, wenn Menschen… hdBerretz 1539
2014-05-04 22:09:47+02:00 226 None FrauPe Twitter for Android @FrauPe im wirklichen Leben !! Hoffe auf näch… GVacek 240
2014-05-04 22:09:51+02:00 29 None None Tweet Button Am Ende des Flurs – Tatort 910 / TatortAntholo… DerWahlberliner 35
2014-05-04 22:10:02+02:00 540 None None web RT @karstenlucke: Guck‘ mal Mama ich bin im Fe… ardtext777 81
2014-05-04 22:10:02+02:00 179 None Tatort Twitter for iPhone @Tatort bestes tatort seit langem camerlengo73 333

10 rows × 7 columns

Tweets per Minute

tweetsperminute=tweets[‚text‘].resample(‚1t‘,how=‚count‘)

What do you think, when the Tatort started? 🙂

Localisation of the Tweets

Text Processing with the Natural Language Toolkit

That great Book covers almost everything shown here:

Natural Language Processing with Python by Steven Bird, Ewan Klein, and Edward Loper O’Reilly Media, 2009

In [102]:
import nltk
from nltk.corpus import stopwords
from nltk import FreqDist

text = tweets['text']

Common Words of a Language to filter out

In [103]:
stop_eng = stopwords.words('english')
stop_ger = stopwords.words('german')
customstopwords = ['tatort', 'mal', 'heute', 'gerade', 'erst', 'macht', 'eigentlich', 'warum', 'gibt', 'gar', 'immer', 'schon', 'beim', 'ganz', 'dass', 'wer', 'mehr', 'gleich', 'wohl']

Clean the Tweets from a bunch of stuff we are not interested in

In [104]:
tokens = []
sentences = []
for txt in text.values:
    sentences.append(txt.lower())
    tokens.extend([t.lower().encode('utf-8').strip(":,.!?") for t in txt.split()])

hashtags = [w for w in tokens if w.startswith('#')]
mentions = [w for w in tokens if w.startswith('@')]
links = [w for w in tokens if w.startswith('http') or w.startswith('www')]
filtered_tokens = [w for w in tokens \
                   if not w in stop_eng \
                   and not w in stop_ger \
                   and not w in customstopwords \
                   and w.isalpha() \
                   and not len(w)<3 \
                   and not w in hashtags \
                   and not w in links \
                   and not w in mentions]

Top 30 Words

In [105]:
freq_dist = nltk.FreqDist(filtered_tokens)
freq_dist

When does the community got, who the murderer was?

The murderer was the neighbour Ms Höllerer, an pharmacist ([ger] ‚Apothekerin‘)

In [21]:
tweets[tweets.text.str.contains('Apothekerin')==True][['user','text','follower']].head(5)
Out[21]:
user text follower
Zeit
2014-05-04 20:26:32+02:00 T34_unstoppable #Tatort: Der übereifrige Praktikant nervt etwa… 9
2014-05-04 20:54:32+02:00 ClaudeeyaS Ha, die Apothekerin war’s, oder?! #tatort 138
2014-05-04 20:54:54+02:00 S_Jahns Die Apothekerin wars! Das kenn ich irgendwo he… 19
2014-05-04 20:55:38+02:00 RALupo Die Apothekerin kannte sie etwas besser. – Wom… 427
2014-05-04 20:55:51+02:00 MichaelWei15 Die Apothekerin auf der gleichen Etage hat den… 0

5 rows × 3 columns

In []:
Let's take a look at the percentual amount of the names, which point to the murderer.

The Tatort ended at 21:45, the peaks with Apothekerin after that are reviews and mostly, because it was Trending Topic and so the bots came to use the hashtag while real people ended writing about #Tatort.

In [24]:
tweets[tweets.text.str.contains('Apothekerin')==True]['201405042145':][['user','text','follower']].sort('follower', ascending=False).head(10)
Out[24]:
user text follower
Zeit
2014-05-04 21:50:27+02:00 Tatort Ok, Apothekerin ist Trending Topic. #apotheker… 53848
2014-05-04 21:51:38+02:00 Die_Mutti “@trendinaliaDE: Tweet with the most impact of… 9020
2014-05-04 21:45:47+02:00 DerSchwatten RT @Tatort: Gruselig, diese Apothekerin, oder?… 1529
2014-05-04 21:49:24+02:00 trendinaliaDE RT @Tatort: Gruselig, diese Apothekerin, oder?… 1369
2014-05-04 21:49:19+02:00 trendinaliaDE 3 verified accounts helped to turn ‚Apothekeri… 1369
2014-05-04 21:47:56+02:00 Srevilo Gollum Assoziationen bei der Apothekerin. #Tatort 1108
2014-05-04 21:51:45+02:00 GrafvonMonte RT @ChrickifromHell: Heute bin ich sehr zufrie… 1058
2014-05-04 21:49:23+02:00 trendinalios RT @Tatort: Gruselig, diese Apothekerin, oder?… 1027
2014-05-04 22:00:08+02:00 derschutzpatron Durchgeknallte Apothekerin im Tatort. An wen e… 860
2014-05-04 21:49:50+02:00 schmarsten Toller #Tatort. Gute Nacht Apotheker und Apoth… 855

10 rows × 3 columns

Concordance

Use of the same word in context

Praktikant

Displaying 25 of 47 matches:
atort ', u ' wie alt ist denn der praktikant ? # tatort ', u ' so jung und und
 polizist im # tatort ?', u ' der praktikant ist ja der hammer . # tatort ', u
rpennt # tatort ', u ' der bubi - praktikant geht mir auf den keks ! wer soll 
st ! # tatort ', u ' ist der herr praktikant schon vollj \ xe4hrig ? # tatort 
t dieser bubi der neue unbezahlte praktikant im # tatort ?', u ' sie ist eine 
er ist denn dieser 12j \ xe4hrige praktikant mit adhs ? # tatort ', u ' rt @ u
wer ist denn diese lachnummer von praktikant ? # tatort ', u ' der neue assist
er ist denn dieser 12j \ xe4hrige praktikant mit adhs ? # tatort ', u ' haben 
 ' wie alt ist der nervige adhs - praktikant ? 12 ? # tatort ', u ' ich mach j
u '# tatort : der \ xfcbereifrige praktikant nervt etwas . und " parodexan " g
 jetzt schon aufn keks ', u ' der praktikant nervt ! # tatort ', u ' ich glaub
tort ', u ' ich glaube der bogy - praktikant hat zuviel csi gesehen # tatort '
, u '# tatort wer ist der junge ? praktikant ?', u ' top motiviert der neue # 
 ?', u ' top motiviert der neue # praktikant - lasst die jungen auch mal ran !
 sozialkritik im # tatort .\ nals praktikant auf keinen fall den ermittlern mi
enig , dass er sich als polizei - praktikant verdingen muss ? und holt goetze 
atort mitteilen ?', u ' wow , der praktikant darf schon eine wumme tragen # ta
ub bei der polizei ? ist der dort praktikant oder was ? # tatort ', u ' pornot
at sie nie ..." super !', u ' der praktikant darf wichtigtuerisch rumtelefonie
ort ', u ' hat der 12j \ xe4hrige praktikant auch das heutige drehbuch geschri
, hmmm was soll ich sagen ... der praktikant ist ... ja nett # tatort ', u ' r
 ', u '@ miss__jeanne das hat der praktikant im tatort behauptet ;-)', u ' fre
 # tatort . koennte auch der neue praktikant sein ?', u ' dat is die pilzkopfd
ine z \ xe4hlt nicht !)', u ' der praktikant darf mit ner knarre rumlaufen ? #
achwuchs ein chance :-)', u ' der praktikant hat eine knarre ??? # tatort ', u

What else the community said to the young man?

In [26]:
rawtweettext.similar('praktikant')
Building word-context index...
tatort franz bubi chef m neue assistent azubi batic jetzt junge kalli
kleine leitmayr mann panamera polizei song alte apothekerin

(Justin) Bieber

In [27]:
rawtweettext.concordance("Bieber")
Displaying 25 of 52 matches:
 sagen . # tatort ', u ' hat justin bieber n neues n job ? # tatort ', u ' tat
tort hat \ xe4hnlichkeit mit justin bieber , oder ? \ n \ nwenn ihr mir jetzt 
 ' rt @ sonderstellung : hat justin bieber n neues n job ? # tatort ', u ' ab 
 ' rt @ sonderstellung : hat justin bieber n neues n job ? # tatort ', u ' und
 ' n kopf stellen ", u ' der justin bieber der mordkommission # tatort ', u ' 
rehte , bayerisch sprechende justin bieber geht mir jetzt schon auf den keks .
 u ' rt @ miss_stresss : der justin bieber der mordkommission # tatort ', u " 
viert .', u ' der bayerische justin bieber . # tatort ', u ' ob die ger \ xfcc
 u ' rt @ miss_stresss : der justin bieber der mordkommission # tatort ', u ' 
, u ' wer fand denn diesen justin - bieber - assistenten eine gute idee ? # ta
eren . # tatort ', u ' sucht justin bieber noch seinen affen in m \ xfcnchen u
u ' was ist das f \ xfcr ein justin bieber verschnitt bei der polizei ? # tato
 # tatort ', u '# tatort hat justin bieber eigentlich schon den f \ xfchrersch
tze aus .', u ' wer gibt dem justin bieber bzw karli vom # tatort ne waffe ???
r vor . # tatort ', u ' muss justin bieber bei der m \ xfcnchner polizei sozia
atort !!', u ' ist das jetzt justin bieber oder manuel neuer ?\ n # tatort ', 
 ' rt @ annahascoffee : muss justin bieber bei der m \ xfcnchner polizei sozia
 ' rt @ annahascoffee : muss justin bieber bei der m \ xfcnchner polizei sozia
 ' rt @ annahascoffee : muss justin bieber bei der m \ xfcnchner polizei sozia
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x
zwischenstand zum # tatort : justin bieber ist neuer assistent . und holzf \ x

Collocations

In corpus linguistics, a collocation is a sequence of words or terms that co-occur more often than would be expected by chance.

In [28]:
tweettext = nltk.Text(filtered_tokens)
tweettext.collocations()
Building collocations list
mike hansen; justin bieber; franz xaver; xaver kroetz; waylon
jennings; hauptkommissar kroate; johnny cash; umgedrehte frage;
kontakte ehen; eigene faust; frage gelesen; googletreffern sucht;
domain frei; parodexan domain; beatrice richter; ketty lester; wort
googletreffern; sucht parodexan; tanten holen; neuen assistenten

Search for Words

In [29]:
fdist = nltk.FreqDist([w.lower() for w in tweettext])
modals = ['apothekerin', 'angst', 'leitmayr', 'nutte', 'messer', 'irre', 'professionelle', 'praktikant']
for m in modals:
    print m + ':', fdist[m],
apothekerin: 230 angst: 26 leitmayr: 196 nutte: 32 messer: 26 irre: 39 professionelle: 27 praktikant: 40

Names in this Tatort

names=nltk.corpus.names
namen=[n.lower().encode(‚utf-8‘for in names.words(‚male.txt‘or names.words(‚female.txt‘)]
name_freq=nltk.FreqDist([for in filtered_tokens if in namen])
name_freq.plot(6)

important-people-Tatort
People named the assistant Justin Bieber, music was not from Johnny Cash but from Waylon Jenning. And obviously, Mike 'Magnum' Hansen was famous.

Dispersion Plot

Determine the location of a word in the text: how many words from the beginning it appears. This positional information can be displayed using a dispersion plot.

In [35]:
plt.figure(figsize=(16,2))
rawtweettext.dispersion_plot(["franz", u"mike", "justin", "johnny"])

Sentiment Analysis

Use SentiWS as training set.

R. Remus, U. Quasthoff & G. Heyer: SentiWS - a Publicly Available German-language Resource for Sentiment Analysis.
In: Proceedings of the 7th International Language Ressources and Evaluation (LREC'10), pp. 1168--1171, 2010

SentiWS is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
The more realistic, the better, but language is complicated and especially on twitter, where actually nobody using sentences but abbraviations etc. Never the less, let’s try it:

Train a Naive Bayes Classifier

Basically, this is supervised machine learning and Jake Vanderplas made a great talk about that: Machine Learning with Scikit-Learn – Jake Vanderplas on Vimeo

First, we need samples. Our samples are the 2000 most used words out of all tweets (freq_dist to just use every word once):

In [79]:
samples = freq_dist.keys()[:2000]
samples[:10]
Out[79]:
['apothekerin',
 'franz',
 'batic',
 'leitmayr',
 'gut',
 'ende',
 'herr',
 'frau',
 'neue',
 'kommt']
We have 2000 samples.

Second, we need a feature.

A feature here is following:

  • Every word from the collected Tweets get it’s feature with True or False value, depending on, if it is in the Tweet or not
  • so by iterating over every Tweet, every word in the sample set should at least one time get the feature True
  • because we use a training set and have known sentiment values (Supervised Learning), these true or false will get positive or negative values as features later

That is the easiest way of sentiment analysis. It will not cover negations, like

this was not a good movie

because it is just checking for good and movie.

The dictionary that is returned by this function is called a feature set and maps from features’ names to their values. Feature names are case-sensitive strings that typically provide a short human-readable description of the feature. Feature values are values with simple types, such as Booleans, numbers, and strings.

In [81]:
def tweet_features(tweet):
    features={}
    for word in samples:
        features['contains(%s)' % word] = (word in tweet)
    return features

Create a Training Featureset

Now we take our SentiWS training set and threat it like it were a Tweet. So, if a word from the SentiWS training set is in the samples list of the words we have in all the Tweets, we also have a sentiment (positive or negative) to classify it.

All that is saved in the trainingfeatureset.

In [82]:
trainingfeatureset = [(tweet_features(word), sentiment) for (word, sentiment) in training_set]

Build the Classifier

The classfier now checks, if some words are more likely tagged with positive or negative values.

In [83]:
classifier = nltk.NaiveBayesClassifier.train(trainingfeatureset)

And there are some words:

In [87]:
classifier.show_most_informative_features(14)
Most Informative Features
          contains(hoch) = True           positi : negati =     10.7 : 1.0
           contains(iss) = True           negati : positi =      7.4 : 1.0
       contains(brechen) = True           negati : positi =      7.0 : 1.0
        contains(schwer) = True           negati : positi =      6.4 : 1.0
        contains(besser) = True           positi : negati =      6.2 : 1.0
           contains(och) = True           positi : negati =      5.8 : 1.0
           contains(min) = True           negati : positi =      5.3 : 1.0
           contains(rot) = True           negati : positi =      5.3 : 1.0
          contains(frei) = True           positi : negati =      5.2 : 1.0
          contains(gern) = True           negati : positi =      5.1 : 1.0
           contains(nit) = True           positi : negati =      4.8 : 1.0
        contains(sicher) = True           positi : negati =      4.6 : 1.0
          contains(sinn) = True           negati : positi =      4.5 : 1.0
           contains(rtl) = True           negati : positi =      4.5 : 1.0

These ratios are known as likelihood ratios, and can be useful for comparing different feature-outcome relationships.

Notice the last shown: If a tweets contains RTL (a german TV channel), the tweet is 4.5x more likely to be negative. 🙂

Example Automatic Sentiment Classification based on the SentiWS Training Set

just Tweets from 10 seconds after the end of the Tatort.

In [88]:
fr = '201405042145'
to = '20140504214510'
positivtweets = []
negativtweets = []
for t in range(len(tweets[fr:to].text)):
    tt = tweets[fr:to].text[t]
    ts = classifier.classify(tweet_features(tt))
    if ts=='positive':
        positivtweets.append(tt)
    else:
        negativtweets.append(tt)

Positive

Gemein! #Tatort
Den #tatort muss ich erst mal verdauen!
@Tatort Endlich mal wieder ein spannender Tatort ! Danke 
Ich muss es zugeben: Das war ein echt großartiger #tatort.
Oh man was war das denn für ein #Tatort! 
Sehr gut!
Und schafft es Franz? #Tatort @Tatort Daumen hoch für einen guten Münchner Tatort 
RT @Paxter_Redwyne: Bis auf den grandiosen Kurzauftritt von Franz Xaver Kroetz leider wieder ein sehr bescheidener #Tatort.
@LutzStroppe sie wollte ein Verhältnis und war es. Bin platt, guter #Tatort
RT @stoltenberg: ein guter #tatort lässt keine zeit zum twittern.
Wenn Til Schweiger das toppen möchte, dann muss er auch am Ende der nächsten Folge sterben! Oder besser schon am Anfang. #tatort
Genialer Spannungsbogen. Sehr guter @Tatort heute.
RT @LaLameck: Gibt's jetzt noch einen zweiten Teil des Münchner #tatort? Ich warte nämlich noch auf die Tanten aus Split...
Ich fand den #Tatort gut.
Zitat des Abends: "Bondage, Herr Batic" #tatort
Glückwunsch @ARD_Presse und @BR_Presse. Ein ganz toller #tatort! @Tatort
@Tatort 1,5!!! Gutes ding
Ich fande den #Tatort heute echt gut.

Negative

Cliffhanger beim #tatort sind geschummelt
RT @einmalteduerr: Huren, Kriminelle, Amigo-Kultur und München. An welchen Verein erinnert mich dieser #tatort bloß? #followerpower
Die #Tatort -Version eines Cliffhanger ist schon eher arm.
Mit dem #tatort endet der Sonntag, mit #jauch beginnt der Montag. Furchtbar.
#Tatort ohne happy end :'(
Lasst Euch ja nicht einfallen, uns das Ermittlerduo Batic / Leitmayr zu nehmen @Tatort #Zuschauerpower
RT @Silbendrechsler: Der #Tatort mit Mörder-Cliffhanger, wer hätte das gedacht.
@Tatort schlecht
RT @carlacrypta: warum macht ihr das mit uuuns? Wir sind jetzt böse. #tatort #leitmayrdarfnichtsterben
RT @herr_arendt: Und wer sagt jetzt den vergeblich wartenden Tanten von Batic in Split Bescheid... #Tatort
@Tatort 3
Ja und, haben wir das jetzt geschafft oder nicht? #Tatort

Not bad for such a simple classifier!

Now let’s do it for all collected Tweets

Define a function which returns the sentiment from our classifier

In [91]:
def classifytweet(dataframe):
    return classifier.classify(tweet_features(dataframe.text))

Apply to all Tweets (takes a while!)

In [57]:
tweets['sentiment'] = tweets.apply(classifytweet, axis=1)

Now we can look, how the mood of the crowd was

mood-crowd-Tatort

Thanks for watching. 🙂

Kontakt

Social Media Analyse

If you need a Social Media Analysis like this, contact us! We support your campaign.
Kontakt