Piątek, chmura słów, TextMining, morfologik i oczywiście R

Dzisiaj jest piątek, więc zamiast zaprzątać sobie głowę liczbami pooglądamy obrazki.

Dwa dni temu, w tym wpisie opisałem jak ściągnąć dane z Twittera i jako przykład ściągnąłem dane z kanału #debataACTA. Pokazałem też kilka podsumowań odkładając na później bardziej złożone analizy. Analizę nastawienia odłożę jeszcze na później, a dzisiaj pokażę jak danych tekstowych zrobić chmurę słów (ang. word cloud) używając R.

Samą chmurę słów można wykonać używając funkcji wordcloud() z pakietu wordcloud. Jako argumenty należy podać listę słów oraz współczynnik skalujący, odpowiadający wielkości danego słowa (najczęściej odpowiadający liczbie wystąpień danego słowa w tekście). Listę słów oraz częstości ich wystąpień można wygenerować używając funkcji str_split() i table(), ale aby było ciekawiej użyję w tym celu pakietu tm, który ma wiele przydatnych narzędzi do analizy tekstu (najwięcej przydatnych narzędzi ma do analizy tekstu angielskiego, ale z polskim też coś można zrobić).

Poniższy fragment kodu wczytuje dane, liczy tablicę częstości a następnie przedstawia ją graficznie z użyciem funkcji wordcloud(),

 

# Odczytaj dane
tweets = read.table(file="debataACTA4_13_luty.csv", sep=";", head=T)
 
# funkcje z pakietu tm i wordcloud
# do tworzenia korpusu wyrazów i ich wizualizacji
actaSlownik = Corpus(DataframeSource(data.frame(tweets[,2])))
actaSlownik = tm_map(actaSlownik, removePunctuation)
actaSlownik = tm_map(actaSlownik, tolower)
tdm = TermDocumentMatrix(actaSlownik)
m = as.matrix(tdm)
v = sort(rowSums(m),decreasing=TRUE)
 
# mając policzone częstości występowania możemy je zwizualizować
wordcloud(names(v), v^0.3, scale=c(5,0.5),random.order=F, colors="black")

 

Wykres otrzymany w ten sposób nie wygląda zbyt dobrze, ponieważ słowa występują w najróżniejszych odmianach a liczenie częstości osobno dla ,premierze’, 'premier’, 'premiera’, 'premierem’ nie wiele daje. Aby wykres wyglądał lepiej chcielibyśmy dla każdego ze słów znaleźć jego rdzeń i zliczać liczbę wystąpień rdzeni a nie różnych form. Jak to zrobić? Potrzebny będzie lematyzator, a dokładniej tzw. stemmer, czyli narzędzie, które dla każdego słowa wyodrębni tzw. 'stem’ (nie znam niestety polskiej terminologii a nie chcę tworzy kwiecistych tłumaczeń) czyli część słowa nie ulegającą odmianie.

Wykorzystam bezpłatny morfologik-stemmer dostępny na blogu http://morfologik.blogspot.com/ rozwijany przez Dawida Weissa i współpracowników. Narzędzie z którego skorzystałem to duży plik tekstowy mający w jednej kolumnie różne formy słów, w drugiej rdzenie a w trzeciej informacje o formie gramatycznej danego słowa. Oczywiście nie wszystkie słowa z twittera można znaleźć w tym zbiorze, nie ma tam np. wszystkich nazwisk. W każdym razie pierwsza transformacja polegała na przemapowaniu słowa na jego rdzeń, jeżeli słowo występuje w słowniku, lub pozostawienie słowa bez zmiany jeżeli w słowniku nie występuje. Dzięki temu z 13334 różnych słów zostaliśmy z 8597 słowami, z czego zdecydowana większość występuje tyko raz i nie znajdzie się na mapie tagów.

Zobaczmy jak wygląda rzeczona mapa.

[Rysunek 1. Chmura słów występujących przynajmniej dwa razy w zapisach z kanału debataACTA. Gdy było to możliwe słowa zostały przekształcone do swoich rdzeni. Wersja wektorowa tego rysunku (uwaga 7MB) znajduje się tutaj]

Dużo tych słów, mało widać, zróbmy więc jeszcze jedną iterację. Usuńmy wszystko co nie jest rzeczownikiem. Szczęśliwie słownik z pakietu morfologik ma informacje o tym czy dane słowo jest czy nie rzeczownikiem, więc zostawiamy tylko słowa, które znajdują się w słowniku i mają 'subst’ w trzeciej kolumnie.

[Rysunek 2. Chmura rzeczowników występujących przynajmniej dwa razy w zapisach z kanału debataACTA. Wersja wektorowa tego rysunku (uwaga 8MB) znajduje się tutaj]

Zróbmy jeszcze jeden eksperyment, mianowicie sprawdźmy jakie inne kanały występowały w wiadomościach z kanału #debataACTA. Zostawiamy więc tylko słowa zaczynające się od znaku #.

 

[Rysunek 3. Chmura nazw kanałów w wiadomościach na kanale debataACTA. Wersja wektorowa tego rysunku (uwaga 7MB) znajduje się tutaj]

22 thoughts on “Piątek, chmura słów, TextMining, morfologik i oczywiście R”

  1. @Maciej
    Można np. spróbować powiedzieć coś o „Martinie”, na podstawie jego twittów (???).
    A więc: jest „typowy”, twittuje w pracy (zamiast pracować, hahaha…). Zaczyna pracę dość wcześnie, ok. 7 – 7:30 rano (chyba że skala czasu nie uwzględnia zmiany czas zimowy/letni, wtedy zaczyna pracę ok. 8 – 8:30 rano) i kończy ok. 15:30 – 16:00 (albo 16:30 – 17:00). Widać wyraźnie ostro zaznaczony początek twittów w pracy i jasną linię powrotu do domu, po której zaczyna się znowu twittowanie (to dotyczy tez osób, z którymi się kontaktuje, no ale to ten sam krąg kulturowy). Pracuje dość wcześnie i dość długo, więc to ktoś młody, na niskim stanowisku, dość prosta (nisko płatna?) praca.
    Konwencjonalny pod względem kulturowo-socjalnym: ogromny szczyt twittów w okresie Bożego Narodzenia oraz spadek intensywności w okresie wakacyjnym (a więc nie jest na „odpowiedzialnym” stanowisku czy na wysokim szczeblu drabiny socjo-ekonomicznej, wymuszającym niejako dostępność nawet w czasie wakacji). Przez chwilę nawet myślałem, że to student, ale nie twittowałby chyba tak intensywnie podczas zajęć. Poza tym znaczący spadek twittów w czasie weekendu wskazuje na to, że ma chyba rodzinę!!! Może nawet małe dziecko!!! Ale jest młody i „typowy”: w piątek wieczorem (6. dzień kalendarza) – nie ma go. Bawi się. W sobotę rano wstaje wcześniej, w niedzielę później: może mają taki układ z partnerką/żoną? (Chyba nie partnerem, bo nie byłoby dziecka i większa byłaby cisza w weekendowe poranki – znowu krąg kulturowy, nie tylko sam „Martin”). Ciekawe….

  2. @Maciej, ciekawe, najbardziej spodobał mi się ten kalendarz z częstością wpisów. Podsunął pomysł na wpis, mianowicie sprawdzenie kiedy najczęściej piszą na Twitterze nasi posłowie.

    @mw, zupełnie jakbym oglądał serial o Sherlocku

  3. @Paweł
    Proszę bardzo, nie jest super optymalny, ale krótki w zapisie

    # słownik2 to jedna kolumna z nazwami wierszy
    slownik <- read.table("morfologik.txt", header=F, sep="\t", stringsAsFactors=FALSE)
    slownik <- slownik[!duplicated(slownik[,1]),]
    rownames(slownik) <- slownik[,1]
    slownik2 <- slownik[,2,drop=F]

    # słowa to wektor słów do przekonwertowania
    rdzenie <- slownik2[as.character(slowa),]

  4. @mw dokładnie tak samo pomyślałem jak smarterpoland 🙂
    @smarterpoland – ten heatcalendar jest świetny.

    Swoją drogą ciekawe czy da radę scrapować facebooka, bo wydaje mi się, że posłowie mogą więcej pisać na swoich oficjalnych stronach niż na twittera, którego w Polsce, tak mi się wydaje, używa mniej ludzi niż facebooka. 🙂

  5. @smarterpoland Próbowałem Twój kod podany w komentarzu ale przy wczytywaniu morfologik.txt czyli poleceniu:

    slownik <- read.table("morfologik.txt", header=F, sep="\t", stringsAsFactors=FALSE)

    Pojawia mi się błąd:

    "Error in scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, :
    line 105885 did not have 3 elements"

    Uczę się R i troche mam jeszcze zbyt mała wiedze na ten temat, więc nie bardzo rozumiem dlaczego pojawia się ten błąd. Z opisu rozumie, ze cos jest nie tak w linii 105885, ale patrząc na nią nie widze żadnego błędu w formacie danych. Myslałem tez, że może to jest kwestia RStudio ale w konsoli R błąd jest taki sam.
    Może to kwestia wersji słownika? Uzywam ostatniej (1.9) a zapewne Ty uzywałes wczesniejszej.

    Bede wdzięczny za pomoc

    1. Dodaj do funkcji read.table() argument fill=T, powinno się wczytać choć być może z błędami. Zobacz później co jest we wczytanym obiekcie w linii gdzie masz błąd. Pewnie jest tam jakiś znak typu ’, „, # który R stara się zinterpretować.

  6. @smarterpoland to raczej nie to. Po dodaniu paramteru fill=”T” wyskakuje kolejny, ale inny error:

    # linia kodu
    slownik cols : invalid 'x’ type in 'x && y’

    Wedytowałem plik w Notepad+ i wiersze na których się wywala wygladają tak:

    105884 Gottwaldowi Gottwald subst:sg:dat:m1
    105885 Gottwaldowie Gottwald subst:pl:nom:m1
    105886 Gottwaldowie Gottwald subst:pl:voc:m1

    Błąd sygnalizuje wiersz 105885, ale podałem dodatkowe przed i po wierszu w którym jest błąd.

    Dodatkowo przeprowadziłem eksperyment 🙂 polegający na usunięciu z pliku wiersza wskazującego na błąd i efekt jest taki sam, to znaczy błąd jest zawsze w tym samym wierszu.

    Zastanawiam się czy to nie jest kwestia jakiegoś ustwienia w R albo systemie (locales albo coś takiego). Używam Wingrozy.

  7. @smarterpoland cos mi sie zle skopiowal komunikat błędu (w poprzednim poście). Brzmi:

    Error in fill && length(col.names) > cols : invalid 'x’ type in 'x && y’

    1. Dodaj argumenty quote = „”, comment.char = „”.
      Jak dla mnie wygląda to na problem we wcześniejszej linii, który we wskazanej linii przepełnia bufor.

      ewentualnie spróbuj skip=105880, będzie to wczytywało dane z pominięciem wcześniejszych linii, upewnisz się czy problem jest rzeczywiście w linii 105884

  8. @smarterpoland Mam jeszcze jeden problem, tym razem z pl znaczkami. Jesli w wektorze słów są wyrazy z pl znakami to nie są te słowa wyszukiwane. Domyślam się, że to jest kwestia kodowania. Czy możesz jeszcze w tej kwestii pomóc?

  9. Analizą językową zajmuję się od niedawna. Dotychczas używałem do analizy frekwencji słów programu antconc, który dzięki załadowaniu słownika pwn pozwalał mi na redukcję słów do form podstawowych. Pakiet r, szczególnie tm to na ten moment objawienie, ale oczywiście brakuje mi informacji dotyczących obsługi języka polskiego. To co robisz właśnie ze słownikiem morfologika jest czymś co mnie interesuje najmocniej.
    Sytuacja jest taka – mam korpus tekstów z gazet zapisanych w jednym pliku txt. Tworzę korpus z pliku w katalogu, następnie czyszczę korpus z kropek i liczb, oraz przekształcam słowa na małe litery. Tworzę textdocumentmatrix, i albo generuję wordcloud, albo eksportuję dane o frekwencjach słów do pliku csv. Wszystko działa sprawnie i szybko. Ale, w którym momencie mam wrzucić ten kawałek kodu dotyczący morfologika? Czy dobrze rozumiem, że:
    # słowa to wektor słów do przekonwertowania
    rdzenie <- slownik2[as.character(slowa),]
    dotyczy, że słowa to plik korpusu tekstów?
    I czy z rdzenia później tworzy się dopiero textdocumentmatrix i wykonuje dalsze operacje – np. eksport do pliku?
    2. Kwestia, czy istnieje taka możliwość, że posiadając plik, w którym jedna kolumna to słowa, a druga to frekwencja ich występowania zrobić z niego chmurę słów?

    1. Niedługo podobny przykład opiszę na blogu, też poświęcony analizie językowej artykułów z polskich gazet.

  10. Cześć,

    próbuję własnie użyc polski słownik z morfologik i mam problem z przekonwertowaniem słów wystepujcych w korpusie na formy podstawowe z II kolumny słownika. Mógłby ktoś z Was zamieścić kod, który to robi?

    byłabym bardzo wdzięczna za pomoc bądz jakiekolwiek wskazówki!!

  11. Witam,

    mam problem z krokiem „(…) pierwsza transformacja polegała na przemapowaniu słowa na jego rdzeń, jeżeli słowo występuje w słowniku, lub pozostawienie słowa bez zmiany jeżeli w słowniku nie występuje.”

    Używam kodu: rdzenie <- slownik2[as.character(slowa),]

    Jakiego polecenia należy użyć, żeby słownik nie zamieniał nieznanych słów na NA?
    Dziękuję za pomoc.

Skomentuj Maciej Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *