Twitter in segmenten 4: clusters vergelijken

Twitter in segmenten 4: clusters vergelijken

In mijn laatste posts heb ik drie verschillende methoden gebruikt om mijn dataset met Twitter-accounts in te delen in clusters. De verdelingen weken behoorlijk af: de hiërarchisch cluster-methode produceerde  7 clusters, de k-means methode produceerde er 8 en met de k-medioid methode kwam ik tot slechts 4 clusters. In deze post ga ik eerst een methode kiezen, vervolgens de clusters aan de bijbehorende tweets koppelen en uiteindelijk met een kleine analyse van tweets en profielen uitzoeken of er ook inhoudelijk verschillen zijn tussen de clusters.

Ik heb bij het clusteren gebruik gemaakt van gedragsdata: hoe vaak tweet men, zijn dit retweets of replies, zijn de accounts populair etc. Echter ben ik nu benieuwd of deze clusters ook qua inhoud verschillen. Is het ene cluster bijvoorbeeld meer geïnteresseerd in politiek of voetbal dan de ander – dat wil ik nu gaan uitzoeken.Voor deze post gebruik ik een dataset bestaande uit tweets van ruim 400 twitter accounts en de clustermodellen en bijbehorende datasets die ik in mijn vorige posts heb gemaakt. Qua pakketten gebruik ik dplyr, gridExtra, ggplot2,tm, tidytext en stringr.

library(dplyr)
library(gridExtra)
library(ggplot2)
library(tm)
library(tidytext)
library(stringr)
library(wordcloud)

Welke clusteroplossing is het beste?
Voordat ik de clusters inhoudelijk kan analyseren, ga ik eerst een definitief clustermodel kiezen. Ik heb in mijn vorige posts drie clusterverdelingen gemaakt, maar welke is de beste? Zoals ik in een eerdere post schreef, hangen de beslissingen die je neemt bij het clusteren meestal af van wat je ermee wilt bereiken. Ik had niet echt verwachtingen of eisen, behalve dat ik het liefst minder dan 10 clusters wilde en niet te veel kleine clusters. Alle drie de oplossingen voldoen hier in principe aan, dus als verkennend onderzoek volstaan ze allemaal. Echter wil ik nu de clusters gaan koppelen aan de inhoud van tweets, en op deze manier een meer inhoudelijke beschrijving geven. Als ik dit doe met een hoger clusteraantal – 7 of 8 –  levert dat me waarschijnlijk wat specifiekere resultaten op omdat er minder variatie binnenin de clusters is. Echter kost het me meer tijd, zijn de resultaten lastiger te generaliseren, wordt de uitleg hiervan erg lang en zijn sommige clusters zodanig klein dat er überhaupt niet veel data is om te analyseren. Daarom kies ik er nu voor om de wat compactere clusterverdeling van de k-medioid methode te gebruiken.

Clusterbeschrijving
De 4 clusters die voortkomen uit de k-medioid methode zijn van vergelijkbare grootte, variërend tussen de 86 en 123 Twitter accounts. Het eerste cluster onderscheidt zich door veruit het meest te tweeten. Van de tweets is een groot deel retweets, en deze groep is in het weekend actiever dan andere groepen. Ik doop hen de Fanatieke Tweeters. Groep 2 tweet het minst, maar scoort het hoogst op retweets. Ik noem hen de Casual Retweeters. Groep 3 tweet niet bijzonder veel, maar hun accounts en tweets zijn populairder. Ik noem hen de Trendsettende Tweeters. Groep 4 onderscheidt zich voornamelijk door het hoge aantal replies – zij heten bij deze de Sociale Tweeters. Voor meer informatie over de k-medioid methode en de bijbehorende clusterverdeling verwijs ik je naar mijn vorige post.

Clusters en tweets
Om te beginnen koppel ik het bestand met de clustersverdeling per account terug (werkbestandclusters3) aan mijn bestand met tweets en informatie over accounts (alletweets). Ik bewaar hierbij alleen relevante kolommen en maak een nieuwe kolom met het aantal tweets per cluster.

tweetsclusters <- werkbestandclusters3 %>% 
inner_join(alletweets[,c(2,4,5,54,73)]) %>%
group_by(cluster) %>%
mutate(aantal = n_distinct(status_id)) %>%
ungroup()

Ik kom uit op 73402 records, bestaande uit de tweets van 412 accounts.

Bigrams
Ik ga de inhoud van de clusters op 3 aspecten analyseren: bigrams in tweets, gebruikte hashtags en omschrijvingen van accounts. Ik begin met bigrams in tweets – dit zijn woordcombinaties bestaande uit 2 woorden die na elkaar komen. In de zin ‘Ik zit op een stoel’ zijn ‘ik zit’, ‘zit op’, ‘op een’ en ‘een stoel’ de verschillende bigrams. Ik wil vergelijken of de meest voorkomende bigrams verschillen tussen de clusters. Ik kies hiervoor omdat ik in een eerdere post al eens met enkele woorden heb gewerkt, maar daar de context toch behoorlijk bleek te ontbreken. Het nadeel van bigrams is dat ze wat lastiger zijn te maken dan enkele woorden. Om tekst om te zetten naar woorden gebruik je de unnest_token functie, maar om bigrams te maken, moet je de tekst al opschonen voordat je deze functie gebruikt. Als je dit niet doet, krijg je veel bigrams als ‘en een’ of ‘op het’ of met bijvoorbeeld URL’s, terwijl dat geen interpreteerbare woorden zijn. En als de bigrams eenmaal gemaakt zijn, is het lastig om nog woorden te gaan verwijderen omdat de bigrams dan niet meer kloppen.

bigrams <- tweetsclusters
bigrams$text <- bigrams$text %>%
#alles in kleine letters
tolower() %>%
#nummers verwijderen
removeNumbers() %>%
#urls weghalen
str_replace_all(" ?(f|ht)(tp)(s?)(://)(.*)[.|/](.*)", " ") %>%
#mentions weghalen
str_replace_all("@\\S+", " ") %>%
#hashtags weghalen
str_replace_all("#\\S+", " ") %>%
#tekens weghalen
str_replace_all("[[:punct:]]", " ") %>%
#woorden bestaande uit 1 letter weghalen
str_replace_all(" *\\b[[:alpha:]]{1}\\b *", " ") %>%
#extra spaties weghalen
stripWhitespace() %>%
#zowel nederlandse als engelse stopwoorden weghalen
removeWords(stopwoorden$words) %>%
removeWords(stopwords(kind = "en"))

Nu de tekst opgeschoond is, kan ik de tekst omzetten naar bigrams. In onderstaande code creeër ik bigrams met “ngrams” en n = 2 . Deze kun je eventueel ook verhogen als je geïnteresseerd bent in langere woordcombinaties.

bigrams <- unnest_tokens(bigrams, bigram, text, "ngrams", n = 2) 

Vervolgens maak ik in twee stappen een lijst met de top 20 meest voorkomende bigrams per cluster.

#tel bigrams per cluster en maak procentueel variabel
bigramscount <- bigrams %>%
group_by(cluster, aantal) %>%
count(bigram) %>%
mutate(procentvgeheel = round(n/aantal, 4))%>%
arrange(desc(procentvgeheel))

#top bigrams
topbigrams <- bigramscount %>%
filter(!is.na(bigram)) %>%
group_by(cluster) %>%
top_n(20, procentvgeheel)

Nu wil ik deze gaan plotten in een overzichtelijke staafdiagram. Dit doe ik door eerste te filteren per cluster, deze vervolgens te plotten en alle 4 vervolgens samen te voegen.

#voor elk cluster herhalen
plot1 <- topbigrams %>%
filter(cluster == 1) %>%
ggplot(aes(reorder(bigram, procentvgeheel), procentvgeheel)) +
	geom_col(fill = "#ffae1e") + 
	coord_flip() + 
	ggtitle("Bigrams Fanatieke Tweeters") + 
	xlab(NULL) + 
	ylab(NULL)

#plot alle clusters in een figuur
grid.arrange(plot1,plot2,plot3,plot4, nrow = 2)

We zien dat de Fanatieke Reweeters vooral over politieke onderwerpen tweeten, zoals ‘gele hesjes’, ‘den haag’, ‘tweede kamer’ en ‘pvv fvd’. De Casual Retweeters tweeten ook veel over politieke onderwerpen, zoals ‘den haag’, ‘rob jetten’ en ‘tweede kamer’, maar de bigram ‘bla bla’ staat met stip op nummer 1. Als ik door mijn data zoek hoe dat komt, valt het me op dat één gebruiker uit dit cluster zo’n 40 keer naar een andere gebruiker “bla bla bla bla” heeft getweet en eigenhandig deze bigram naar de eerste plek heeft geholpen. Kudos voor hem. De Trendsettende Tweeters tweeten duidelijk vaker over voetbal, aangezien de top 3 bigrams uit voetbalclubs bestaan. Echter staan er ook een aantal bigrams tussen die ik niet helemaal snap, maar die waarschijnlijk afkomstig zijn uit één tweet die vaak is geretweet onder deze groep. De Sociale Tweeters hebben ook een politieke oriëntatie in hun tweets – het populairste bigram is hier ‘den haag’. Uit de woorden ‘fake news’ en ‘linkse media’ vermoed ik een rechtse oriëntatie. Wat verder opvalt is dat ze meer dan andere clusters spreektaal gebruiken, zoals de bigram ‘man man’, ‘oh ja’ en ‘oh wacht’. Dit zijn immers de accounts die hoog scoren op replies, dus wellicht ook niet zo gek. Bij deze staafdiagram moet echter worden opgemerkt dat zelfs de populairste bigrams, zoals ‘fc groningen’, slechts in zo’n 0.065% van de tweets uit cluster 3 voorkomt. Dat vertaalt dus naar 59 tweets. Ook moet worden opgemerkt dat hoewel er verschillen zijn tussen de groepen, er ook veel overeenkomsten zijn. De woorden ‘den haag’, ‘gele hesjes’ en ‘tweede kamer’ komen bijvoorbeeld in alle clusters veel voor.

Hashtags
Een andere manier om de tweets inhoudelijk te analyseren, is door naar hashtags te kijken. De hashtags dienen immers vaak als keyword of als samenvatting van een tweet. Daarom ga ik per cluster een woordenveld maken met de populairste hashtags. Ik begin door de text opnieuw op te schonen, maar dit keer zonder de speciale tekens eruit te halen.

hashtags <- tweetsclusters

#tekst opschonen
hashtags$text <- hashtags$text %>%
tolower() %>%
str_replace_all(" ?(f|ht)(tp)(s?)(://)(.*)[.|/](.*)", " ") %>%
stripWhitespace() %>%
removeWords(stopwoorden$words) %>%
removeWords(stopwords(kind = "en"))

Dan verander ik de tekst naar woorden, met als token “tweets”, omdat hashtags op die manier behouden blijven. Vervolgens filter ik zodat alleen woorden met hashtags behouden blijven.

#van tekst naar woorden, alleen hashtags behouden
hashtags <- hashtags %>%
unnest_tokens(word, text, "tweets") %>%
filter(str_detect(word, "#"))

Daarna maak ik een telling.

#tellingbestand maken
counthashtags <- hashtags %>%
group_by(cluster,aantal) %>%
count(word, sort = TRUE) %>%
mutate(procentvgeheel = round(n/aantal, 4))%>%
arrange(desc(procentvgeheel))

Vervolgens maak ik vier losse bestandjes: één voor elk cluster. Daarna maak ik het woordveld.

#los bestand per cluster
counthashtags1 <- filter(counthashtags, cluster ==1)

#wordcloud maken, pas alleen het nummer elke keer aan voor andere clusters
wordcloud(counthashtags1$word, counthashtags1$procentvgeheel, max.words = 75, random.order = FALSE)

Dit resulteert in een woordveld met de 75 populairste hashtags in de tweets van de Fanatieke Retweeters. We zien ook hier dat de gele hesjes opnieuw populair zijn binnen deze groep: #gilletsjaunes, #gelehesjes en #yellowvests staan allemaal groot in het midden en worden dus vaak gebruikt. Ook #stemzeweg, #pvv, #fvd en #stempvv zijn populair. Ik denk dat we wel kunnen stellen dat deze groep een redelijk rechtse oriëntatie heeft. We zien ook veel hashtags over het klimaat, maar dit hoeft zeker niet te betekenen dat deze groep zich zorgen maakt over het klimaat – uit de tweets die ik gelezen heb, blijkt eerder het tegendeel. Vervolgens voer ik bovenstaande stappen opnieuw uit voor de andere clusters.

Ook de Casual Retweeters tweeten veel over het klimaat: #klimaatdebat is hier zelfs de populairste hashtag, en ook hashtags als #klimaatspijbelen en #klimaatakkoord worden vaak genoemd. Ook hier lijkt een redelijk rechtse oriëntatie de overhand te hebben, te zien aan #pvv en #fvd.

In de groep Trendsettende Tweeters zien we opnieuw veel rechtse politieke hashtags (zit links überhaupt op Twitter?), maar wordt er ook vooral veel gepraat over sport, zoals blijkt uit de hashtags #utrpsv, #ajax, #grovit, #feyenoord en #wkafstanden.

In de groep Sociale Tweeters zien we opnieuw veel politieke hashtags, zoals #stemzeweg, #pvv en #fvd, maar wordt er ook veel gesproken over tv programma’s. De hashtag #nieuws is de populairste, maar ook #jinek, #dwdd, #nieuwsuur en #luizenmoeder komen veel voor in deze groep.

Omschrijvingen
Op Twitter kun je op je account een omschrijving geven van jezelf of van waar je over tweet. De meeste accounts doen dit in mindere of meerdere mate, en daarom lijkt het me interessant om de meest voorkomende woorden te vergelijken tussen de clusters. Omdat mijn sample qua account beschrijvingen een stuk lager is (412 ipv 73402 tweets die ik eerder gebruikte), tel ik nu alleen enkele woorden en geen bigrams. Hiervoor maak ik eerst een apart bestand, waarin de functie distinct ervoor zorgt dat ik alle accounts slechts 1 keer meeneem. Dan zet ik de tekst om naar woorden, dit keer gebruik ik als token “words”, omdat alle onnodige opmaak er dan afgestript wordt.

#unieke accounts, tekst naar woord
omschrijvingen <- tweetsclusters[,c(8,9,13)] %>%
distinct() %>%
unnest_tokens(word, description, "words")

Vervolgens filter ik de stopwoorden eruit en maak ik een telling. Ook maak ik een variabel om per cluster uit te rekenen in hoeveel procent van de accounts een woord voorkomt.

#stopwoorden filteren
omschrijvingen$word <- omschrijvingen$word %>%
removeWords(stopwoorden$words) %>%
removeWords(stopwords(kind = "en"))

#tel hoe vaak een woord gebruikt wordt
countomschrijvingen <- omschrijvingen %>%
group_by(cluster, count) %>%
count(word) %>%
mutate(procentvgeheel = n/count) %>%
arrange(desc(procentvgeheel))

Dan maak ik een bestand met per cluster de top 10 meest voorkomende woorden in de omschrijving en plot ik deze.

#top 10 populairste woorden in omschrijving
topomschrijvingen <- countomschrijvingen %>%
filter(!word == "") %>%
group_by(cluster) %>%
top_n(10, procentvgeheel)

#plot de top 10, voor elk cluster herhalen
plotomschrijvingen1 <- topomschrijvingen %>%
filter(cluster == 1) %>%
ggplot(aes(reorder(word, procentvgeheel), procentvgeheel)) + 
	geom_col(fill = "#ffae1e") + 
	coord_flip() + 
	ggtitle("Omschrijvingen Fanatieke Retweeters") + 
	xlab(NULL) + 
	ylab(NULL)

#samen in 1 figuur plotten
grid.arrange(plotomschrijvingen1,plotomschrijvingen2,plotomschrijvingen3,plotomschrijvingen4, nrow = 2)

Het resultaat is opnieuw een staafdiagram per cluster. Een opmerking hierbij: zoals je ziet, zijn er meer dan 10 woorden bij sommige clusters. Dat komt doordat veel woorden even vaak voorkomen en daardoor komen ze allemaal in de diagram te staan. De meest voorkomende woorden in de omschrijvingen van de Fanatieke Tweeters hebben ook hier een duidelijk rechtse politieke connotatie: het populairste woord is ‘fvd’, maar ook ‘pvv’, ‘politiek’, ‘eu’ en ‘trump’ komen vaker voor. De omschrijvingen van de casual retweeters verschillen behoorlijk. Hier is het populairste woord ‘vader’, gevolgd door ‘manager’ en ‘fan’. Ook staat het woord ‘groenlinks’ ertussen – echter is het me niet duidelijk of deze persoon lid is van GroenLinks, of zo’n hekel heeft aan de partij dat hij het zelfs in z’n omschrijving zet. Bij de Trendsettende Tweeters is ‘voetbal’ het vaakst voorkomende woord. Ook zien we meer voetbal-gerelateerde woorden als ‘supporter’, ‘fan’, ‘football’, ‘fc’ en coach. Ook in de groep Sociale Tweeters zien we veel voetbalfans. Behalve voetbaltermen valt het woord ‘politiek’ ook op – we zagen immers ook in deze groep veel politiek-getinte bigrams en hashtags.

Conclusie
Ik zou nog veel meer kunnen analyseren aan de hand van deze clusterverdeling, echter laat ik het hier voor nu bij – ik heb nu immers een redelijk beeld van waar de verschillende groepen over tweeten. De Fanatieke Retweeters zijn over het algemeen redelijk rechts-politiek georiënteerd in hun tweets, en noemen vaak de gele hesjes, de pvv en fvd. Ook in de omschrijvingen van hun accounts komt dit duidelijk naar voren, waarbij fvd het populairste woord is. In de groep Casual Retweeters zien we ook veel politieke verwijzingen, maar springt vooral het klimaatdebat eruit. In hun omschrijvingen lijken ze echter niet zo politiek geëngageerd als de Fanatieke Retweeters – we zien vooral woorden als ‘vader’, ‘manager’ en ‘fan’. In het cluster van de Trendsettende Tweeters zien we vooral veel verwijzingen naar voetbal, zowel in de hashtags, de bigrams en in de omschrijvingen. De Sociale Tweeters lieten zich minder goed karakteriseren. In de bigrams zagen we veel verwijzingen naar de politiek, maar in de hashtags viel het mij vooral op dat er veel televisieprogramma’s genoemd werden. In de omschrijvingen waren de meest gebruikte woorden ‘I Love Voetbal’, dus weer iets geheel anders. Deze groep kenmerkte zich door een hoog aantal replies, dus wellicht zijn zij actief in meerdere discussies.

Dat was mijn laatste post over Twitter gebruik segmenteren in R. Ik hoop dat je het interessant vond en/of er iets aan had. Nog tips/vragen/opmerkingen/foutjes gespot? Mail me op gibbon@datagibbon