Einfach mal irgendwas mit Twitter machen, dacht ich so. Also mal irgendwie erstmal was runterladen.
Verwendete Tools: twitter-API, python, twython und pymongo
Seit dem 12.01.2018 lauscht das Skript im live-stream nach folgendem, wahnsinnig kreativen Suchquery
CDU,CSU,SPD,AfD,#FDP,NPD,#Linke,#Rechte,Merkel,Schulz,#Gabriel,#GroKo, #NoGroKo,Asyl,Meinungsfreiheit,Deutschland,Angst,Liebe,subgenius
und alles in die MongoDB rein. Da kommen im Moment so gefühlt 1 bis 5 tweets pro Sekunde.
Während der livestream reinkam, begann ich die timelines der jeweiligen User runterzuladen, welche in dem live-stream auftauchen. Mit einem API-Zugang kann man etwa 200 Tweets pro Sekunde runterladen und von jedem Twitteruser bekommt man maximal die letzten 3200 Tweets. (rate limits)
Das Speichern läuft so: Alles was twitter ausliefert, kommt fast unverändert in die Datenbank. Die Felder user und retweeted_status und quoted_status schickt twitter immer als vollwertige entities. Der user wird in eine eigene Tabelle geschrieben und das user-feld in dem tweet mit der user-id ersetzt. Genauso bei retweeted- und quoted-status. Sie werden separat gespeichert und die Felder auch jeweils mit der ID ersetzt. Da retweets keine Änderung am original-Text vornehmen, werden alle infos ausser dem user, der tweet-Zeit und der Referenz zum original-tweet verworfen. Um Platz zu sparen könnte man generell auch die anderen entities wie z.b. place in separaten Tabellen ablegen.
Bei 80Gb in der Datenbank hab ich den download erstmal gestoppt. Dabei sind die timelines von nicht mal 10% der User aus dem livestream gespeichert. Es gab auch hin und wieder einen timeout oder die Verbindung zur Twitter-API ist sonst irgendwie hängen geblieben. Dann wurde der download neu gestartet, z.T. die Reihenfolge der Abarbeitung geändert und nur User, die weniger als ein paar hundert Tweets in der DB haben, angefragt. Natürlich waren bei jedem Neustart neue User dabei, die in den timelines aufgetaucht sind.
Wir haben hier also ein sample von knapp 50 Mio Tweets und Retweets, die irgendwie zumindest politisch angefangen haben und eine gewisse Schnittmenge zu allem anderen aufspannen. Für die folgende grobe Analyse wurden die tweets gefiltert nach solchen, die keine retweets sind, von Twitter die Sprache Deutsch drangetaggt bekommen haben und die nicht truncated sind. In pymongo:
.find({"retweeted_status": None, "truncated": False, "lang": "de"})
Truncated, bedeutet, dass der Tweet nach 140 Zeichen abgeschnitten wurde. Das ist bei den meisten nicht der Fall. Man darf allerdings nicht vergessen, tweet_mode="extended" an den API-request zu hängen sonst bekommt man die Zeichen über 140 nicht.
Nach dem Filtern bleiben knapp 20 Mio tweets übrig. Hier die Verteilung in der Zeit (gezeigt ab ende Nov. 2017, Datum im tooltip)
Man beachte den peak am 12.01. An dem Tag hab ich zum ersten mal den live-stream gestartet und auch mal mit queries wie trump experimentiert. Da kam natürlich deutlich mehr rein. Durch die user-timelines kommen dann die ganzen tweets aus der Zeit vor dem 12.01. Am 28.01. war die Festplatte voll... bisschen Platz gemacht und livestream wieder gestartet.
Als erstes hab ich mal die Signifikante-Worte-zähl-Methode von fefe analysieren ausprobiert. Da der sample-Zeitraum relativ klein ist, lohnt sich kaum der Wochentags-Vergleich, deshalb gibts zusätzlich auch den Vergleich pro Stunde. Für einen kurzen Überblick über temporale Ebenen kann man sich diese kurze Video mal reinziehen: "Space-Time as a Sampling Condition for New Social Media Research" Der Mensch ist ganz aufgeregt ob der neuen wissenschaftlichen Herausforderungen.
Alle gefundenen Worte oder tokens wurden berücksichtigt. Die Symbole ,:.…!?-"()& wurden allerdings von Wortanfängen und -enden entfernt. Die Worte wurden mit wiktionary normalisiert und #hashtags und @usertags immer in Kleinbuchstaben umgewandelt. Dadurch kommen auch links und jede Menge Zeichenketten in die Wertung. Eine Version nur mit Substantiven gibts hier.
Signifikant bedeutet hier, dass sich die Häufigkeit eines Wortes pro Tweet mit einem bestimmten Indikator von der Durchschnittshäufigkeit des Wortes pro Tweet abhebt. Der Indikator ist z.B. der Wochentag oder die Stunde in der getweetet wurde.
Normalisiert wird hier also nach Häufigkeit eines Wortes pro Tweet. Im Folgenden seht ihr pro Kategorie ( Wochentag, Uhrzeit, Ort, #Partei) einmal die Anzahl der tweets für jeden Indikator, also z.B. die Anzahl und der Prozentsatz der tweets am Montag im Vergleich zur gesammten Menge der betrachteten tweets.
Die nächste Tabelle zeigt dann die "signifikantesten" Worte pro Kategorie. Diese wurden folgendermaßen ermittelt (am Beispiel für Wochentage):
signifikanz_der_worte_am_montag = freq_wort_pro_tweet_am_montag - 3 * freq_wort_pro_tweet_gesammt
Die Liste der Worte mit der errechneten Signifikanz wurde sortiert und die ersten 20 oder 30 davon übrig gelassen. Danach wurde die Liste nochmal sortiert nach dem Faktor der signifikanten Häufigkeit, also z.b. wie viel mal Häufiger ein Wort am Montag im Vergleich zum Durchschnitt ist. An jeder Zeile ist ein tooltip mit den genauen Zahlen. Der Balken zeigt die Häufigkeit des Wortes im Vergleich zur Häufigkeit der anderen Worte.
Der Faktor 3 in obiger Formel ist halt irgendwie magic und sehr subjektiv. Ist er zu klein, werden viele Worte mit wenig Unterschied zum Gesammtdurchschnitt gefunden, ist er zu groß bleiben nur ganz selten verwendete Worte (oder besser Zeichenketten) übrig, die total signifikant für den jeweiligen Indikator sind aber eben kaum verwendet werden.
Wochentage
Substantive pro Wochentaggescannte tweets: 19870680
Montag
Dienstag
Mittwoch
Donnerstag
Freitag
Samstag
Sonntag
Uhrzeit
Substantive pro UhrzeitSignifikante tweet-Worte pro Stunde am Tag. Die Uhrzeit ist jeweils abgerundet auf die volle Stunde.
gescannte tweets: 19870680
0:00
1:00
2:00
3:00
4:00
5:00
6:00
7:00
8:00
9:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
23:00
Orte
Substantive pro OrtSignifikante tweet-Worte pro Stadt/Ort/Platz. Hier ist die Datenmenge etwas kleiner, da nicht jeder seine location mitsendet. In diesem sample sind nicht mal 3.8% der tweets mit einem place-Feld versehen. Wenn es eins gibt, sieht es z.b. so aus:
'place': { 'attributes': {}, 'bounding_box': { 'coordinates': [[ [-2.5139084, 53.531967], [-2.355997, 53.531967], [-2.355997, 53.636402], [-2.5139084, 53.636402] ]], 'type': 'Polygon' }, 'contained_within': [], 'country': 'United Kingdom', 'country_code': 'GB', 'full_name': 'Bolton, England', 'id': '548c7806c1e1b70f', 'name': 'Bolton', 'place_type': 'city', 'url': 'https://api.twitter.com/1.1/geo/id/548c7806c1e1b70f.json'}, }
Der Indikator ist hier das place.name-Feld.
gescannte tweets: 778264
Berlin
Bochum
Bonn
Bremen
Dortmund
Dresden
Düsseldorf
Essen
Frankfurt am Main
Hamburg
Hannover
Karlsruhe
Köln
Leipzig
Magdeburg
Mainz
München
Münster
Potsdam
Stuttgart
Wien
Zürich
Partei-tags
Substantive pro Partei-tagHier werden die tweets nach enthaltenen hashtags kategorisiert. In dieser Reihenfolge #noafd, #afd, #cdu, #csu, #spd, #linke.
Bedeutet, wenn ein tweet #noafd enthält, zählt er in die erste Gruppe. Wenn kein #noafd aber #afd, dann zweite, wenn kein #noafd und #afd aber dafür #cdu, dann 3. usw..
Das Durchschnittsaufkommen für ein hashtag wird aus der Menge aller tweets genommen, welche mind. eines der oben genannten tags enthalten. Der Faktor 3 aus obiger Formel für die Signifikanz ist hier auf Faktor 1 runtergesetzt.
Keine Kommentare:
Kommentar veröffentlichen