Tuesday, February 19, 2019

Ayarwady and Sad WORDS


These days others were thinking hard about Ayarwady. Time to time we have to think about Ayarwady too. In reality, we are the only ones who will make or break Ayarwady. That being clear, I hope that I am not going to hear sad news about Ayarwady any time soon, or in what will be left of my lifetime.
In the context of a few posts back in this blog, I was fascinated by the idea of sentiment analysis of comments like those in Facebook, if possible in our own Myanmar language. That led me into playing with syllable segmentation and word segmentation on Myanmar text. While I was very much enjoying that, some articles on emotion detection in text caught my eyes. I was excited. Never heard of it before. Could dummies do that? Let me give a try.
In fact, these words from Emotion Detection in Text: a Review by Seyeditabari and others motivated me to leapfrog sentiments analysis in favor of emotion detection in my typical dummy-way:
“Emotion detection in computational linguistics is the process of identifying discrete emotion expressed in text. Emotion analysis can be viewed as a natural evolution of sentiment analysis and its more fine-grained model. … Sentiment analysis, with thousands of articles written about its methods and applications, is a well established field in natural language processing. It has proven very useful in several applications such as marketing, advertising … question answering systems … summarization … as part of recommendation systems … or even improving information extraction … . On the other hand, the amount of useful information which can be gained by moving past the negative and positive sentiments and towards identifying discrete emotions can help improve many applications mentioned above, and also open ways to new use cases”.
With a craze, more or less, I began by downloading the Wikipedia page on our untouchable Ayarwady (Irrawaddy) river. Actually I was sidetracked into this also by my frustrations from the failure to extract text out of the the entire set of Myanmar Wikipedia pages. I was unhappy about the scarcity of Myanmar language text collection, or Myanmar language corpus. as the NLP community calls it. Therefore, I’m going to create a big Myanmar corpus out ot the Wikipedia Myanmar pages, since that is a good sizable collection of Myanmar language in Unicode, I thought. Then this task proved to be too big for my tools at hand. But that certainly calls for another story.
Anyway, to make the story short, I choose to try out the Syuzhet R-package for detecting emotions from the Wikipedia article on the Ayarwaddy river. It was quite easy. It got no brain-work of mine. I just followed the examples given in the vignette (Introduction to the Syuzhet Package) that comes with the installation of the package.


Getting the data ready for processing

To analyze the data in sentence form in Syuzhet, you will have to begin by using get_sentences() function on your text. That will tokenize your text into sentences which means that a vector of sentences will be created.
For the following exercise, you need to have the R packages, syuzhet and htm2txt installed on your computer.
library(syuzhet)
# Loads a file as a single text string.
ayarT <- get_text_as_string("https://en.wikipedia.org/wiki/Irrawaddy_River")
# Get tokenized sentences from ayarT.
ayar_sen <- get_sentences(ayarT)
head(ayar_sen)
The above code block imports the Wikipedia page as a single text string and tokenize it into sentences. This text string contains html tags which need to be removed.So instead of it, I used htm2txt package to import plain text from the webpage and then saved it as a utf-8 format text file.
x <- htm2txt::gettxt("https://en.wikipedia.org/wiki/Irrawaddy_River")
str(x)
 chr "Irrawaddy River\n\nFrom Wikipedia, the free encyclopedia\n\nJump to navigation\tJump to search\n\n\"Ayeyarwady\"| __truncated__
I saved it to a text file.
zz <- file("ayar0.txt", "w")
writeLines(x, con=zz, useBytes = TRUE)
close(zz)
The plain text file contained 582 lines of text including blank lines.I deleted parts of the text by hand to leave only the body of the text. For the reproducibility of the results posted here, I’ve made this file available here.


Detecting emotions from the text

library(syuzhet)
package 㤼㸱syuzhet㤼㸲 was built under R version 3.5.2
# Loads the file as a single text string.
y <- get_text_as_string("ayar.txt")
# tokenize sentences
y_sen <- get_sentences(y)
str(y_sen)
 chr [1:121] "Irrawaddy River  The Irrawaddy or, officially, Ayeyarwady[4] River (Burmese: á\200§á\200›á\200¬á\200\235á\200\2"| __truncated__ ...
head(y_sen)
[1] "Irrawaddy River  The Irrawaddy or, officially, Ayeyarwady[4] River (Burmese: ဧရာá€\u009dá€\u0090ီမြစ်; MLCTS: erawa."                                       
[2] "ti mrac, pronounced [Ê\"èjàwÉ™dì mjɪÊ\"], also spelt Ayeyarwaddy[citation needed]) is a river that flows from north to south through Myanmar."                          
[3] "It is the country's largest river and most important commercial waterway."                                                                                                  
[4] "Originating from the confluence of the N'mai and Mali rivers,[5] it flows relatively straight North-South before emptying through the Irrawaddy Delta into the Andaman Sea."
[5] "Its drainage basin of about 404,200 square kilometres (156,100 sq mi) covers a large part of Burma."                                                                        
[6] "After Rudyard Kipling's poem, it is sometimes referred to as 'The Road to Mandalay'."                                                                                       
To identify emotions in the sentences the NRC Emotion lexicon by Saif MOhammad is used. According to him, “the NRC emotion lexicon is a list of words and their associations with eight emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive)” In the data frame returned by the function call below, each row represents a sentence from the originak text showing number of detection(s) by each emotion/sentiment.
nrc_data <- get_nrc_sentiment(y_sen)
package 㤼㸱bindrcpp㤼㸲 was built under R version 3.5.2
head(nrc_data)
anger
<dbl>
anticipation
<dbl>
disgust
<dbl>
fear
<dbl>
joy
<dbl>
sadness
<dbl>
surprise
<dbl>
trust
<dbl>
negative
<dbl>
1000000000
2000000000
3000000010
4000000000
5000000001
6000000000
tail(nrc_data)
anger
<dbl>
anticipation
<dbl>
disgust
<dbl>
fear
<dbl>
joy
<dbl>
sadness
<dbl>
surprise
<dbl>
trust
<dbl>
negative
<dbl>
116000000000
117200112104
118100101102
119100000001
120110100002
121110311113
summary(nrc_data)
     anger         anticipation       disgust             fear             joy        
 Min.   :0.0000   Min.   :0.0000   Min.   :0.00000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :0.0000   Median :0.0000   Median :0.00000   Median :0.0000   Median :0.0000  
 Mean   :0.1901   Mean   :0.2562   Mean   :0.08264   Mean   :0.2562   Mean   :0.2066  
 3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:0.00000   3rd Qu.:0.0000   3rd Qu.:0.0000  
 Max.   :3.0000   Max.   :3.0000   Max.   :1.00000   Max.   :3.0000   Max.   :2.0000  
    sadness          surprise          trust           negative         positive    
 Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.000  
 Median :0.0000   Median :0.0000   Median :0.0000   Median :0.0000   Median :1.000  
 Mean   :0.1983   Mean   :0.1322   Mean   :0.4463   Mean   :0.6198   Mean   :1.058  
 3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:2.000  
 Max.   :2.0000   Max.   :2.0000   Max.   :4.0000   Max.   :4.0000   Max.   :6.000  
Now for a plot of emotions.
pander::pandoc.table(nrc_data[, 1:8], split.table = Inf)
barplot(
  sort(colSums(prop.table(nrc_data[, 1:8]))), 
  horiz = TRUE, 
  cex.names = 0.7, 
  las = 1, 
  main = "Emotions in Sample text", xlab="Percentage"
  )
You could identify the sentences that have at least one identified emotion, for example, “sadness”.
nrc_data[nrc_data$sadness>0,]
anger
<dbl>
anticipation
<dbl>
disgust
<dbl>
fear
<dbl>
joy
<dbl>
sadness
<dbl>
surprise
<dbl>
trust
<dbl>
negative
<dbl>
11111201011
17000021121
36010001011
43000001001
46010111102
51101121123
57000012012
58000011021
59000011011
61000001002
This has been my first ever exercise on emotion detection from text and it gives me just a feel for this topic. Now the first thing you could do may be to inspect the text for the i-th sentence, say, from the vector of sentences “y_sen” for an emotion of your interest. You could do that as
y_sen[i]
to have some idea of how emotion detection works.

Thursday, February 7, 2019

Courage and Evil WORDS

Before, I’d looked at some of the papers on word segmentation of Myanmar text. Then I felt it’ll be a long way off for me. Suddenly I have a bag of Myanmar syllables of my own, better or worse. I looked at resources on word segmentation of Myanmar text again. Google search gave me strange-looking terms like Syllable level Longest Matching; syllable segmentation and syllable merging. A rule-based heuristic approach … for syllable segmentation, and a dictionary-based statistical approach for syllable merging; Foma-generated Finite State Automata; Conditional Random Fields, … .
For us to know what has been done in this area I found that Saini (June, 2016) has provided a “First Classified Annotated Bibliography of NLP Tasks in the Burmese Language” containing a description of nine approaches “On Word Identification, Segmentation, Disambiguation, Collation, Semantic Parsing and Tokenization for Burmese Language”.

Hmm, let me earmark all the above, and more, for the future. For now, I will shuffle on like a dummy would do in identifying words with an unfamiliar language: look for the given words in a dictionary or a word list. The catch with Myanmar text is that words don’t come separated by space. The good news is that I’ve already segmented the text into syllables and a “word” would just be a syllable or a combination of syllables depending on the text. I guess I could do like this:
(i)Take the first syllable in the text, and look for a word in the word-list/dictionary that starts with the given syllable,
(ii-a)if there is at least one match, move to next syllable (iii-a)add the current syllable to the previous syllable, look for a word in the word-list/dictionary that starts with this merged-syllable
(ii-b)if no match, take the current syllable as word (and mark it with “*” to show it is not in the dictionary) (iii-b)move to the next syllable and look for a word in the word-list/dictionary that starts with this syllable
Repeat (-a) or (-b) steps as appropriate until all is done.

The following is my R script for word segmentation:
# Kanaung WORD LIST downloaded Feb06, 2019 from raw at 
# https://raw.githubusercontent.com/kanaung/wordlists/master/wordlists.list
# saved file: wordlists.list.txt
# knWL_1 <- readLines(con = "wordlists.list.txt", encoding = "UTF-8")
# save(knWL_1,file = "knWL_12449.RData")
# wikitionary dict downloaded, feb 06, 2019 from raw at
# https://raw.githubusercontent.com/kanaung/wordlists/master/wikitionary/
##  mywiktionary20150901pagesarticlesmultistream.xml.out.list.sorted.only-mm.txt
# wikitionary_1 <- readLines(con = "mywiktionary20150901.txt", encoding = "UTF-8")
# save(wikitionary_1,file = "wikitionary_1.RData")
load("SYLL.RData")
load("knWL_12449.RData")
load("wikitionary_1.RData")
Run word segmentation; matching with Kanaung Word List (12,467 words)
word <- list()
dict <- knWL_1
for (k in 1:5){
    word.k <- list()
    L <- length(SYLL[[k]])
    j <- 1
    x <- grep(paste0("^",SYLL[[k]][1]),dict)
    if (length(x)==0){
        word.k[[j]] <- paste0("*",SYLL[[k]][1])
        j <- j+1
        TEMP.0 <- ""
    } else {
        TEMP.0 <- SYLL[[k]][1]
    }
    for (i in 2:L){
        x <- grep(paste0("^",SYLL[[k]][i]),dict)
        if (TEMP.0==""){
            # current char has 0 match in dict  
            if (length(x) == 0){
                word.k[[j]] <- paste0("*",SYLL[[k]][i])
                j <- j + 1
                TEMP.0 <- ""
            # current char has +1 match in dict
            }else{
                TEMP.0 <- SYLL[[k]][i]
            }
        # previous syllable cluster not empty
        } else {
            TEMP.1 <- paste0(TEMP.0,SYLL[[k]][i])
            y <- grep(paste0("^",TEMP.1),dict)
            # previous syll cluster+current, no match in dict
            if (length(y) == 0){
                word.k[[j]] <- TEMP.0
                j <- j + 1
                if (length(x) == 0){
                    word.k[[j]] <- paste0("*",SYLL[[k]][i])
                    j <- j + 1
                    TEMP.0 <- ""
                } else {
                TEMP.0 <- SYLL[[k]][i]
                }
            } else {
                TEMP.0 <- paste0(TEMP.0,SYLL[[k]][i])
            }
        }
    }
    if (i == L){
        word.k[[j]] <- TEMP.0
    }
    word[[k]] <- paste(unlist(word.k))
}
Show the results of word segmentation using Kanaung Word List
utf8::utf8_print(unlist(word))
  [1] "ဝန်ကြီးချုပ်"     "ဦး"           "ဖြိုး"          "မင်း"          "သိန်း"         
  [6] "*ခွင့်"          "ထပ်"           "တိုင်"           "ရန်ကုန်တိုင်း"      "လွှတ်တော်"       
 [11] "မှာ"           "*YBS"         "စနစ်"          "ပြုပြင်"        "ပြောင်းလဲ"     
 [16] "ဖို့"            "တင်"           "*သွင်း"         "တဲ့"            "အ"           
 [21] "ဆို"            "အတည်"          "ပြု"           "ဖို့"            "*၊"          
 [26] "မ"            "ပြု"           "ဖို့"            "ဆုံးဖြတ်"        "*မယ့်"         
 [31] "မနက်ဖြန်"       "လွှတ်တော်"        "အစည်းအဝေး"     "ကို"            "လည်း"         
 [36] "ဝန်ကြီးချုပ်"     "ဦး"           "ဖြိုး"          "မင်း"          "သိန်း"         
 [41] "က"            "*ခွင့်"          "ထပ်"           "တိုင်"           "ခဲ့"           
 [46] "ကြောင်း"       "သိ"            "ရ"            "ပါ"           "တယ်"          
 [51] "စီ"            "အိုင်"           "အေ"           "က"            "နှိပ်စက်"        
 [56] "စစ်"           "ဆေး"          "မှု"            "တွေ"           "လုပ်"          
 [61] "ခဲ့"            "အမေ"          "ရိ"            "ကန်"           "*-"          
 [66] "ဗဟို"           "ထောက်လှမ်း"      "ရေး"          "ဌာန"          "*CIA"        
 [71] "ဟာ"           "သမ္မတ"         "ဟောင်း"        "*ဂျော့ချ်"      "*ဘုရှ်"         
 [76] "လက်"           "ထက်"           "စက်တင်ဘာ"       "*၁၁"          "ရက်"          
 [81] "တိုက်"           "ခိုက်"           "ခံရ"           "မှု"            "နောက်"        
 [86] "ပိုင်း"          "စစ်"           "ဆေး"          "မှု"            "တွေ"          
 [91] "လုပ်"           "ရာ"           "မှာ"           "နှိပ်စက်"         "ညှင်း"         
 [96] "ပန်း"          "မှု"            "တွေ"           "ကျူးလွန်"        "ခဲ့"           
[101] "ဖူး"           "တယ်"           "*လို့"           "စီ"            "အိုင်"          
[106] "အေ"           "*ရဲ့"           "အ"            "ကြီး"          "အကဲ"          
[111] "ဟောင်း"        "*ဘတ်ဇ်"         "ခ"            "*ရောရှ့်"        "*ဂတ်"         
[116] "ကဘီ"           "ဘီ"            "စီ"            "ကို"            "ပြော"        
[121] "ခဲ့"            "ပါ"           "တယ်"           "*။"           ""            
[126] "တောင်"         "ကို"            "*ရီး"          "ယား"          "အခြေ"        
[131] "စိုက်"           "*PoscoDaewoo" "*နှင့်"          "*သြ"          "စ"           
[136] "*တြေး"        "လျ"           "အခြေ"         "စိုက်"           "*Woodside"   
[141] "တို့"            "အကျိုး"         "တူ"            "ပူး"           "ပေါင်း"       
[146] "ဆောင်ရွက်"       "နေ"           "*သည့်"          "ရခိုင်"          "ကမ်းလွန်"       
[151] "ရှိ"            "*AD-7"        ""             "*၂၀၁၈"        "ခုနှစ်"         
[156] "အာ"           "ရှ"            "အားက"         "စား"          "ပြိုင်"         
[161] "ပွဲ"            "တွင်"           "အားက"         "စား"          "နည်း"         
[166] "အရေ"          "အ"            "တွက်"           "တိုး"           "*မြင့်"        
[171] "လာ"           "ခဲ့"            "ပိ"            "*ဿာ"          "*ချိန်"        
[176] "*၁၀"          "သား"          "ရှိ"            "သော"          "ကြက်"         
[181] "သား"          "များ"         "*ချက်"         "ပြုတ်"          "ကျွေးမွေး"     
[186] "လှူဒါန်း"        "သွား"          "*သည့်"          "အ"            "တွက်"          
[191] "ကျေးဇူးတင်"     "ပါ"           "သည်"           "*။"           ""            
Run word segmentation; matching with words in Wikitionary (26,729 words)
The code is exactly the same as previous with only the dict changed to "wikitionary_1".

Show the results of word segmentation using Wikitionary data
utf8::utf8_print(unlist(wordWK))
  [1] "ဝန်ကြီးချုပ်"     "ဦး"           "ဖြိုး"          "မင်း"          "သိန်း"         
  [6] "ခွင့်"           "ထပ်"           "တိုင်"           "ရန်"           "ကုန်"          
 [11] "တိုင်း"          "လွှတ်တော်"        "မှာ"           "*YBS"         "စနစ်"         
 [16] "ပြုပြင်"        "ပြောင်းလဲ"      "ဖို့"            "တင်သွင်း"        "တဲ့"           
 [21] "အဆို"           "အတည်ပြု"        "ဖို့"            "*၊"           "မ"           
 [26] "ပြု"           "ဖို့"            "ဆုံးဖြတ်"        "*မယ့်"          "မနက်ဖြန်"      
 [31] "လွှတ်တော်"        "အစည်းအဝေး"     "ကို"            "လည်း"          "ဝန်ကြီးချုပ်"    
 [36] "ဦး"           "ဖြိုး"          "မင်း"          "သိန်း"          "က"           
 [41] "ခွင့်"           "ထပ်"           "တိုင်"           "ခဲ့"            "ကြောင်း"      
 [46] "သိရ"           "ပါ"           "တယ်"           "စီ"            "အိုင်"          
 [51] "အေ"           "က"            "နှိပ်စက်"         "စစ်ဆေး"        "မှု"           
 [56] "တွေ"           "လုပ်"           "ခဲ့"            "အမေ"          "ရိ"           
 [61] "ကန်"           "*-"           "ဗဟို"           "ထောက်လှမ်းရေး"   "ဌာန"         
 [66] "*CIA"         "ဟာ"           "သမ္မတ"         "ဟောင်း"        "*ဂျော့ချ်"     
 [71] "*ဘုရှ်"          "လက်ထက်"         "စက်တင်ဘာ"       "*၁၁"          "ရက်"          
 [76] "တိုက်ခိုက်"         "ခံရ"           "မှု"            "နောက်ပိုင်း"      "စစ်ဆေး"       
 [81] "မှု"            "တွေ"           "လုပ်"           "ရာ"           "မှာ"          
 [86] "နှိပ်စက်"         "ညှင်း"          "ပန်း"          "မှု"            "တွေ"          
 [91] "ကျူးလွန်"        "ခဲ့"            "ဖူး"           "တယ်"           "လို့"           
 [96] "စီ"            "အိုင်"           "အေ"           "ရဲ့"            "အကြီးအကဲ"      
[101] "ဟောင်း"        "*ဘတ်ဇ်"         "ခ"            "*ရောရှ့်"        "*ဂတ်"         
[106] "ကဘီ"           "ဘီစီ"           "ကို"            "ပြော"         "ခဲ့"           
[111] "ပါ"           "တယ်"           "*။"           ""             "တောင်"        
[116] "ကို"            "*ရီး"          "ယား"          "အခြေစိုက်"       "*PoscoDaewoo"
[121] "နှင့်"           "သြ"           "စ"            "*တြေး"        "လျ"          
[126] "အခြေစိုက်"       "*Woodside"    "တို့"            "အကျိုး"         "တူ"           
[131] "ပူးပေါင်း"      "ဆောင်ရွက်"       "နေ"           "သည့်"           "ရခိုင်"         
[136] "ကမ်းလွန်"        "ရှိ"            "*AD-7"        ""             "*၂၀၁၈"       
[141] "ခုနှစ်"          "အာရှ"          "အားကစား"      "ပြိုင်ပွဲ"         "တွင်"          
[146] "အားကစား"      "နည်း"          "အရေ"          "အတွက်"          "တိုး"          
[151] "မြင့်"          "လာ"           "ခဲ့"            "ပိ"            "*ဿာ"         
[156] "ချိန်"          "*၁၀"          "သား"          "ရှိ"            "သော"         
[161] "ကြက်သား"       "များ"         "ချက်ပြုတ်"       "ကျွေးမွေး"      "လှူဒါန်း"       
[166] "သွား"          "သည့်အတွက်"        "ကျေးဇူးတင်"     "ပါ"           "သည်"          
[171] "*။"           ""           
The above exercise tried to find out if naive coding is possible for word segmentation of Myanmar text. I guess the answer could be, timidly, yes.  And it seems obvious that the approach used here hinges entirely on the quality and completeness of the word lists. I have no knowledge of the field, but at the beginning I felt a word-list of only ten or twenty thousand would be quite inadequate. Yet their contribution to word boundary identification seems quite respectable and you could clearly see significant improvements in word identification attributable to the larger Wikitionary list against the smaller Kanaung list. In my word segmentation I've marked the base-syllable (first syllable) that could not be found in a given word list with a “*”. This may be quite helpful in understanding and locating the “incompleteness” for a given word list.
Remarks on the procedural aspects of the word segmentation itself: I’ve ignored the practice of leaving out stop-words, punctuation, blanks, and other less meaningful textual data from the original syllable data. I guess it could be called a conservative syllable list because of that. So also the words produced from it in turn became a conservative word list. They are ugly, but this way, they may be quite helpful for making adjustments and improvements.