1.

I will demonstrate all tasks using the manifesto data. You may have chosen a different data set.

Loading the required libraries:

library(dplyr)
library(tm)

Loading the data frame from the RDS file:

corpus_df <- readRDS('08textmining-resources/election_ger17_manifestos.RDS')
head(corpus_df)
## # A tibble: 6 x 3
##   party   date   text                                                                              
##   <chr>   <chr>  <chr>                                                                             
## 1 Grüne   201709 "A. EINLEITUNG\nLiebe Bürgerinnen und Bürger,am 24. September ist Bundestagswahl.…
## 2 Linke   201709 "Die Zukunft, für die wir kämpfen: SOZIAL. GERECHT. FRIEDEN. FÜR ALLE.\nEinführun…
## 3 SPD     201709 "Es ist Zeit für mehr Gerechtigkeit!\n2017 ist ein entscheidendes Jahr.\nDie SPD …
## 4 FDP     201709 "Schauen wir nicht länger zu!\nWir sehen die Herausforderungen\nWir sehen, wie si…
## 5 CDU/CSU 201709 "Ein gutes Land in dieser Zeit\nDeutschland ist ein liebens- und lebenswertes Lan…
## 6 AfD     201709 "KAPITEL 1\nVerteidigung der Demokratie in Deutschland\n1.1 Ohne Volkssouveränitä…

Converting the data frame to a VCorpus object:

# DataframeSource expects the data frame to have a doc_id,
# then the text and then additional metadata columns
corpus_df_for_tm <- select(corpus_df, doc_id = party, text, date)

corpus_tm <- VCorpus(DataframeSource(corpus_df_for_tm),
                     readerControl = list(language = 'de'))

Inspecting the corpus:

inspect(corpus_tm)
## <<VCorpus>>
## Metadata:  corpus specific: 0, document level (indexed): 1
## Content:  documents: 6
## 
## [[1]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 460974
## 
## [[2]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 443297
## 
## [[3]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 289707
## 
## [[4]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 267938
## 
## [[5]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 147496
## 
## [[6]]
## <<PlainTextDocument>>
## Metadata:  7
## Content:  chars: 129388

Inspecting a single document in a corpus would be (I don’t execute this here, as it prints too much text):

inspect(corpus_tm[[2]])

meta() can be used to show meta data of a document:

meta(corpus_tm[[1]])
##   author       : character(0)
##   datetimestamp: 2018-11-30 13:52:03
##   description  : character(0)
##   heading      : character(0)
##   id           : Grüne
##   language     : de
##   origin       : character(0)

2.

Term frequency DTM without stopword removal:

corpus_tm1 <- tm_map(corpus_tm, content_transformer(tolower)) %>%
  tm_map(removePunctuation) %>%
  tm_map(removeNumbers) %>%
  tm_map(stripWhitespace)
dtm1 <- DocumentTermMatrix(corpus_tm1)
inspect(dtm1)
## <<DocumentTermMatrix (documents: 6, terms: 24039)>>
## Non-/sparse entries: 43761/100473
## Sparsity           : 70%
## Maximal term length: 44
## Weighting          : term frequency (tf)
## Sample             :
##          Terms
## Docs      den  der  die eine  für  und von werden  wir wollen
##   AfD     143  620  751  150  195  603 189    169  110     43
##   CDU/CSU 215  558  648  133  289 1098 218    218  617    157
##   FDP     387 1046 1287  305  588 1387 329    336  710    312
##  [ reached getOption("max.print") -- omitted 3 rows ]
findMostFreqTerms(dtm1, n = 20)
## $Grüne
##    und    die    wir    der    für wollen    den    das werden    mit   eine    von   auch    ein 
##   3030   2076   1656   1420   1056    719    613    578    566    558    554    538    506    437 
##    auf    ist  nicht    sie   dass   sind 
##    433    413    364    333    310    283 
## 
## $Linke
##      und      die      der      wir      für   werden      von      den   wollen     eine      das 
##     3018     2353     1699     1106      925      924      728      648      641      550      451 
##      ein      auf      mit   müssen    nicht     muss      ist menschen      sie 
##      451      431      385      375      362      324      318      278      256 
## 
## $SPD
##    und    die    wir    der    für werden    den    von wollen   eine    das    mit    ein   auch 
##   1969   1368   1117   1048    712    638    403    403    376    374    316    309    304    294 
##    ist    auf    des   dass   sind   sich 
##    269    260    188    186    182    149 
## 
## $FDP
##        und        die        der        wir        für        den     werden        von     wollen 
##       1387       1287       1046        710        588        387        336        329        312 
##       eine        das      freie        ein demokraten        ist        mit       auch        auf 
##        305        284        264        256        255        242        239        238        231 
##      nicht        des 
##        218        211 
## 
## $`CDU/CSU`
##         und         die         wir         der         für         von      werden         den 
##        1098         648         617         558         289         218         218         215 
##         mit         ist      wollen deutschland         das        auch         ein        sind 
##         167         157         157         147         144         140         138         138 
##        eine       haben         auf        dass 
##         133         126         116         101 
## 
## $AfD
##    die    der    und    für    von werden    ist    das   eine    den  durch    wir    afd    des 
##    751    620    603    195    189    169    163    151    150    143    116    110    108    104 
##    auf    ein  nicht   sich   sind   auch 
##    103    102    102     96     96     81

Term frequency DTM with stopword removal:

# add some more common stopwords for German which are not part of the default set
stopword_list <- c(stopwords('de'), 'dafür', 'dass', 'deshalb', 'dabei', 'sowie', 'daher', 'seit', 'deren')

corpus_tm2 <- tm_map(corpus_tm, content_transformer(tolower)) %>%
  tm_map(removeWords, stopword_list) %>%
  tm_map(removePunctuation) %>%
  tm_map(removeNumbers) %>%
  tm_map(stripWhitespace)
dtm2 <- DocumentTermMatrix(corpus_tm2)
inspect(dtm2)
## <<DocumentTermMatrix (documents: 6, terms: 23795)>>
## Non-/sparse entries: 42754/100016
## Sparsity           : 70%
## Maximal term length: 44
## Weighting          : term frequency (tf)
## Sample             :
##          Terms
## Docs      arbeit deutschland leben mehr menschen müssen setzen sollen stärken unternehmen
##   AfD          6          68    11   15       21     34      7     30      14          12
##   CDU/CSU     23         147    31   56       82     26     25     18      32          12
##   FDP         19         115    26  116       91     95     86     81      41          76
##  [ reached getOption("max.print") -- omitted 3 rows ]
findMostFreqTerms(dtm2, n = 20)
## $Grüne
##     menschen         mehr       müssen  deutschland        grüne       unsere        leben 
##          272          215          172          150          143          142          120 
##       sollen       setzen      stärken       frauen        immer gesellschaft     schaffen 
##          112          107          105           95           95           94           88 
##       europa         neue  unternehmen       arbeit unterstützen        viele 
##           85           82           82           81           81           81 
## 
## $Linke
##        müssen      menschen         linke          mehr        sollen        arbeit   öffentliche 
##           375           278           190           180           127           119            98 
## beschäftigten          euro       soziale  öffentlichen   unternehmen      sozialen   deutschland 
##            92            92            92            90            90            88            86 
##         leben         viele       prozent         statt      schaffen          gute 
##            83            82            81            77            75            73 
## 
## $SPD
##         mehr     menschen       müssen  deutschland       sollen unterstützen       arbeit 
##          129          129          111           97           86           77           71 
##      stärken     brauchen       setzen       europa     schaffen  unternehmen       frauen 
##           70           66           64           63           62           62           61 
##        leben europäischen      fördern       unsere   verbessern   sicherheit 
##           57           54           52           52           49           47 
## 
## $FDP
##        freie   demokraten         mehr  deutschland       müssen     menschen       setzen 
##          264          255          116          115           95           91           86 
##       sollen  unternehmen      fordern europäischen      bildung       bürger        staat 
##           81           76           71           68           53           49           45 
##         neue        zudem      stärken   leistungen        viele     beispiel 
##           44           42           41           40           37           36 
## 
## $`CDU/CSU`
##   deutschland      menschen          mehr        unsere       unserer          land           cdu 
##           147            82            56            56            54            48            39 
##           csu        jahren        europa       stärken       ländern         leben          neue 
##            39            37            35            32            31            31            30 
##      schaffen    sicherheit arbeitsplätze        erfolg        kinder          zahl 
##            30            29            28            27            27            27 
## 
## $AfD
##          afd  deutschland    deutschen       müssen      fordert       sollen       bürger 
##          108           68           43           34           30           30           29 
## insbesondere     erhalten     deutsche     familien     menschen       unsere         euro 
##           27           23           21           21           21           21           20 
##        staat      fordern         land       kinder      bereits       eltern 
##           20           19           19           18           16           16

3.

Create a tf-idf weighted DTM:

dtm3 <- DocumentTermMatrix(corpus_tm1, control = list(weighting = weightTfIdf))    # re-use first corpus without stopword removal
inspect(dtm3)
## <<DocumentTermMatrix (documents: 6, terms: 24039)>>
## Non-/sparse entries: 37635/106599
## Sparsity           : 74%
## Maximal term length: 44
## Weighting          : term frequency - inverse document frequency (normalized) (tf-idf)
## Sample             :
##          Terms
## Docs               afd          cdu          csu   demokraten      fordert        grüne
##   AfD     7.245891e-03 6.709158e-05 0.000000e+00 0.000000e+00 2.012747e-03 0.000000e+00
##   CDU/CSU 0.000000e+00 2.149826e-03 3.407394e-03 0.000000e+00 0.000000e+00 0.000000e+00
##   FDP     0.000000e+00 0.000000e+00 0.000000e+00 1.264835e-02 0.000000e+00 4.960138e-05
##          Terms
## Docs           kapitel        linke         vgl  weltbeste
##   AfD     0.0010063737 0.000000e+00 0.000000000 0.00000000
##   CDU/CSU 0.0000000000 0.000000e+00 0.000000000 0.00000000
##   FDP     0.0000000000 9.920276e-05 0.000000000 0.00177972
##  [ reached getOption("max.print") -- omitted 3 rows ]
findMostFreqTerms(dtm3, n = 20)
## $Grüne
##                   grüne                    grün                  stimmt                   wählt 
##            0.0040056137            0.0015075864            0.0007002821            0.0006722708 
##                    doch              bäuerinnen              klimakrise             ökologische 
##            0.0006185603            0.0005482132            0.0004761918            0.0004755541 
##          familienbudget        verbraucherinnen             akteurinnen       massentierhaltung 
##            0.0004568444            0.0004135253            0.0004111599            0.0004111599 
##                   sonne             geflüchtete            ökologischen bürgerinnenversicherung 
##            0.0004111599            0.0003711362            0.0003711362            0.0003654755 
##                  lsbtiq           selbständigen             superreiche                  bauern 
##            0.0003654755            0.0003654755            0.0003654755            0.0003641467 
## 
## $Linke
##                   linke                     vgl                 kapitel                   hartz 
##            0.0057266739            0.0028019409            0.0009698399            0.0007373529 
##        mindestsicherung          ostdeutschland            solidarische                   »gute 
##            0.0007373529            0.0007373529            0.0006275435            0.0005898823 
##                    unte            ostdeutschen          gewerkschaften                 fordert 
##            0.0005407254            0.0004915686            0.0004783286            0.0004563952 
##             geflüchtete        erwerbslosigkeit        privatisierungen             dauerstress 
##            0.0004563952            0.0004521058            0.0004521058            0.0004424117 
##            erwerbslosen gesundheitsversicherung             niedriglohn                  profit 
##            0.0004424117            0.0004424117            0.0004424117            0.0004424117 
## 
## $SPD
##     sozialdemokraten  sozialdemokratinnen           fortsetzen  familienarbeitszeit 
##         0.0005249416         0.0005249416         0.0004598093         0.0004499500 
##   bürgerversicherung         familiengeld    regelaltersgrenze         solidarrente 
##         0.0004138283         0.0003749583         0.0003749583         0.0003749583 
##  sozialdemokratische sozialdemokratischer      ganztagsschulen sozialversicherungen 
##         0.0003749583         0.0003749583         0.0003218665         0.0003218665 
##       gewerkschaften           aufgreifen        familientarif          kinderbonus 
##         0.0003054634         0.0002999666         0.0002999666         0.0002999666 
##     sozialdemokratie               stadt“         studiengänge     tarifvertraglich 
##         0.0002999666         0.0002999666         0.0002999666         0.0002999666 
## 
## $FDP
##      demokraten       weltbeste        auflagen unkomplizierter      zeitarbeit      gründungen 
##    0.0126483519    0.0017797201    0.0005662746    0.0004853782    0.0004853782    0.0004464124 
##     vorankommen  istbesteuerung         patient        schwelle studienbeiträge  unbescholtener 
##    0.0004464124    0.0004044818    0.0004044818    0.0004044818    0.0004044818    0.0004044818 
##       effizienz            doch  bildungssparen      bürgergeld    effizientere        englisch 
##    0.0003968110    0.0003755398    0.0003235855    0.0003235855    0.0003235855    0.0003235855 
##   langzeitkonto        neuesten 
##    0.0003235855    0.0003235855 
## 
## $`CDU/CSU`
##                      csu                      cdu              wahlperiode                  führend 
##             0.0034073942             0.0021498264             0.0012231671             0.0008549570 
##          verlässlichkeit             freundschaft             marshallplan            baukindergeld 
##             0.0007124642             0.0005699713             0.0005699713             0.0004274785 
##             betreffenden entgelttransparenzgesetz                 fortgang              realisieren 
##             0.0004274785             0.0004274785             0.0004274785             0.0004274785 
##                  seither          digitalisierung                  künftig          bundeskanzlerin 
##             0.0004274785             0.0003624861             0.0003624861             0.0003494763 
##                   garant            hervorragende         kinderfreibetrag               leitkultur 
##             0.0003494763             0.0003494763             0.0003494763             0.0003494763 
## 
## $AfD
##                         afd                     fordert                        volk 
##                0.0072458906                0.0020127474                0.0013874337 
##                       lehnt        parteienfinanzierung                     kapitel 
##                0.0011697140                0.0010405753                0.0010063737 
## sozialversicherungsabkommen                  bundesbank                  ausweisung 
##                0.0008671461                0.0007443635                0.0006937169 
##                bürgerarbeit             genderideologie        sicherheitsstrategie 
##                0.0006937169                0.0006937169                0.0006937169 
##                  türkischer          windenergieanlagen                         ezb 
##                0.0006937169                0.0006937169                0.0006038242 
##                         abs                 zuwanderung              beitragszahler 
##                0.0005367326                0.0005367326                0.0005316882 
##                       imame                  „politisch 
##                0.0005316882                0.0005202877

Due to tf-idf weighting, stopwords do not appear in the list of most frequent words anymore.

4.

Load plotting functions:

source('08textmining-resources/plot_heatmaps.R')
## 
## Attaching package: 'ggplot2'
## The following object is masked from 'package:NLP':
## 
##     annotate

Matrix subset with absolute word counts:

selected_terms <- c('hartz', 'armut', 'gerechtigkeit', 'arbeit', 'umwelt', 'stabilität', 'recht', 'gesetz', 'volk')

mat_dtm1 <- as.matrix(dtm1)
mat_dtm1_subset <- mat_dtm1[, selected_terms]
mat_dtm1_subset
##          Terms
## Docs      hartz armut gerechtigkeit arbeit umwelt stabilität recht gesetz volk
##   Grüne       0    26            18     81     27          2    43      8    0
##   Linke      15    44            24    119     18          0    64      5    0
##   SPD         0     4            18     71     12          6    28      4    0
##  [ reached getOption("max.print") -- omitted 3 rows ]

As heatmap:

plot_dtm_heatmap(mat_dtm1_subset)

Normalize matrix to word proportions per document:

mat_dtm1_norm <- mat_dtm1 / rowSums(mat_dtm1)
plot_dtm_heatmap(mat_dtm1_norm[, selected_terms], round_values_digits = 4)

5.

Distance with absolute word counts in DTM:

(dist_dtm1 <- dist(mat_dtm1))
##             Grüne     Linke       SPD       FDP   CDU/CSU
## Linke   1118.8820                                        
## SPD     1797.1475 1884.4129                              
## FDP     2471.7308 2477.6017  961.3875                    
## CDU/CSU 3314.8692 3370.8069 1633.2780 1185.8773          
## AfD     3855.2076 3790.0516 2181.9319 1536.8588  823.0541
plot_dist_heatmap(dist_dtm1)

Distance with word proportions in DTM:

(distdtm1_norm <- dist(mat_dtm1_norm))
##              Grüne      Linke        SPD        FDP    CDU/CSU
## Linke   0.02161623                                            
## SPD     0.01577861 0.01930394                                 
## FDP     0.02263617 0.02537671 0.02533564                      
## CDU/CSU 0.01991230 0.02686002 0.01785408 0.02969224           
## AfD     0.04055012 0.03343097 0.04097913 0.03030081 0.04393106
plot_dist_heatmap(distdtm1_norm, round_values_digits = 3)