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)
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
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.
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)
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)