Ten zeszyt służy jako wstęp do korzystania z biblioteki emuR do analizy korpusów mowy w języku R.

Zaczniemy od załadowania biblioteki emuR:

library(emuR)

Attaching package: ‘emuR’

The following object is masked from ‘package:base’:

    norm

Po ściągnięciu i rozpakowaniu archiwum z tego linku: http://mowa.clarin-pl.eu/korpusy/clarin-small.zip

Zakładając, że jesteśmy w katalogu, gdzie rozpakowano powyższe archiwum, możemy wczytać naszą bazę:

db <- load_emuDB('clarin', verbose = FALSE)

Uwaga: argument verbose jest tu potrzebny tylko do poprawnego wyświetlania w tym zeszycie - normalnie nie jest potrzebny.

W pamięci znalazła się zmienna o nazwie db, ale w tej postaci nie można wiele z niej wyczytać. Biblioteka emuR zawiera specjalne funkcje do wyświetlania zawartości bazy.

Możemy, na przykład, wyświetlić listę sesji:

list_sessions(db)

Albo poszczególnych paczek:

list_bundles(db)

Możemy obejrzeć hierarchię opisującą naszą bazę:

list_levelDefinitions(db)

Możemy też uruchomić specjalny intefejs webowy do przeglądania i edycji całej bazy:

serve(db)
Navigate your browser to the EMU-webApp URL: http://ips-lmu.github.io/EMU-webApp/ (should happen automatically)
Server connection URL: ws://localhost:17890
To stop the server press 'clear' button in the EMU-webApp or close/reload the webApp in your browser.
emuR websocket service established
emuR websocket service closed
[1] FALSE

Ale przede wszystkim możemy wykonywać specjalne kwerendy do wyszukiwania konkrentnych segmentów w bazie.

Poszukajmy wszystkich samogłosek dla mówcy nagranego w sesji SES0100:

segs=query(db,'Phoneme=a|e|i|I|o|u', sessionPattern = "SES0100")

Otrzymujemy listę segmentów, która zawiera mnóstwo ciekawych informacji. Spójrzmy na jej srtukturę:

str(segs)
Classes ‘emuRsegs’, ‘emusegs’ and 'data.frame': 1689 obs. of  16 variables:
 $ labels            : chr  "o" "e" "a" "o" ...
 $ start             : num  4410 4670 4850 5470 5590 ...
 $ end               : num  4490 4790 4990 5550 5720 ...
 $ utts              : chr  "SES0100:rich001" "SES0100:rich001" "SES0100:rich001" "SES0100:rich001" ...
 $ db_uuid           : chr  "604e8448-241d-11e7-9cad-507b9d83c0f9" "604e8448-241d-11e7-9cad-507b9d83c0f9" "604e8448-241d-11e7-9cad-507b9d83c0f9" "604e8448-241d-11e7-9cad-507b9d83c0f9" ...
 $ session           : chr  "SES0100" "SES0100" "SES0100" "SES0100" ...
 $ bundle            : chr  "rich001" "rich001" "rich001" "rich001" ...
 $ start_item_id     : int  753449 753452 753454 753458 753460 753463 753467 753472 753474 753478 ...
 $ end_item_id       : int  753449 753452 753454 753458 753460 753463 753467 753472 753474 753478 ...
 $ level             : chr  "Phoneme" "Phoneme" "Phoneme" "Phoneme" ...
 $ start_item_seq_idx: int  9 12 14 18 20 23 27 32 34 38 ...
 $ end_item_seq_idx  : int  9 12 14 18 20 23 27 32 34 38 ...
 $ type              : chr  "SEGMENT" "SEGMENT" "SEGMENT" "SEGMENT" ...
 $ sample_start      : int  70560 74720 77600 87520 89440 94400 140800 149280 152320 166400 ...
 $ sample_end        : int  71840 76640 79840 88800 91520 97280 142400 151360 156320 170720 ...
 $ sample_rate       : int  16000 16000 16000 16000 16000 16000 16000 16000 16000 16000 ...
 - attr(*, "query")= chr "Phoneme=a|e|i|I|o|u"
 - attr(*, "type")= chr "segment"
 - attr(*, "database")= chr "clarin"

Z powyższej tabelki można wyczytać dużo ciekawych informacji i pisać własne programy, które je przetworzą, ale emuR posiada narzędzia, które nam jeszcze bardziej ułatwią życie.

Czasami baza danych zawiera już przetworzone dane (tak jak w tym przypadku), ale czasami trzeba użyć biblioteki wrassp do przeliczenia informacji z korpusu. W każdym przypadku używamy poniższego polecenia.

Następujące polecenie wylicza (wczytuje z dysku) formanty dla wszystkich segmentów. Argument cut jest kolejnym udogodnieniem, gdyż dla każdego segmentu zwraca tylko jedną wartość, znajdującą się dokładnie pośrodku każdego segmentu. Argument consistentOutputType jest potrzebny do ułatwienia pracy z wynikową tabelką jaką zwraca to polecenie:

td <- get_trackdata(db, seglist = segs, ssffTrackName = 'Formants', cut=0.5,
                    consistentOutputType = FALSE,  verbose = FALSE)
td

Tak jak widać, powyższa tabelka zawiera dokładnie tyle samo elementów co tabelka segmentów. Niestety, informacje o samogłosce i jej formantach są w dwóch różnych tabelkach. Połączymy je zatem w nowej tabelce i nazwiemy bardziej intuicyjnie:

dane <- data.frame(vowel=segs$labels, F1=td$fm1, F2=td$fm2)
dane

Mamy tutaj tylko jeden mały problem. Algorytmy aproksymacji formantów czasami zawodzą, a w takich sytuacjach najczęściej wpisują jakąś przewidywalną wartość - w tym przypadku 0. Część znalezionych formantów ma wartość 0, więc warto je usunąć z naszej listy przed narysowaniem. Użyjemy do tego wygodnej biblioteki dplyr:

library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
dane <- filter(dane,F1>0 & F2>0)

W tej chwili straciliśmy ok. 3% danych, ale mamy pewność, że nie ma w nich “śmieci”.

Teraz można już rysować zawartość stworzonej tabelki używając funkcji plot, ale są sposoby dobre i lepsze. Otóż, ktoś stworzył świetną bibliotekę do wyświetlania fonemów w przestrzeni 2D i warto z niej skorzystać:

library(phonR)

Rysowanie używając tej biblioteki jest trywialne:

plotVowels(dane$F1,dane$F2,dane$vowel)

Ale przy odrobinie czytania dokumentacji i ustawiania argumentów, można uzyskać bardzo ładne rysunki:

plotVowels(dane$F1, dane$F2,
           vowel = dane$vowel, 
           var.col.by = dane$vowel, 
           plot.tokens = TRUE, 
           pch.tokens = dane$vowel,
           plot.means = TRUE, 
           pch.means = dane$vowel,
           cex.tokens = 1.2,
           alpha.tokens = 0.4, 
           cex.means = 4, 
           ellipse.line = TRUE, 
           pretty = TRUE)