{"id":43,"date":"2023-01-12T19:46:18","date_gmt":"2023-01-12T19:46:18","guid":{"rendered":"https:\/\/barnarderc.wordpress.com\/?p=43"},"modified":"2023-01-12T19:46:18","modified_gmt":"2023-01-12T19:46:18","slug":"twitter-sentiment-analysis-in-r","status":"publish","type":"post","link":"https:\/\/ercblog.barnard.edu\/?p=43","title":{"rendered":"Twitter Sentiment Analysis in R"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">By Lara Fernandez Musso<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal of this analysis is to understand the generalized feelings associated with the COVID19 pandemic. To do so, I perform a basic sentiment analysis on Twitter data. I use a <a href=\"https:\/\/github.com\/gabrielpreda\/covid-19-tweets\">dataset collected by Gabriel Preda<\/a>, which is prepared by running a query for the hashtag #covid19 using the Twitter API alongside Python scripts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">R has an abundance of premade packages that make data analysis infinitely easier. Below, I load in packages that will facilitate data manipulation, processing, and visualization:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" width=\"134.58823529411774\" height=\"147.61540717046518\" src=\"https:\/\/lh5.googleusercontent.com\/Xoy6sfN4X8OnY7HDmKlnIdP5HRDKxyWGbTJdUBYTZ8uO81YwrQIQzkL7Y-Scnkr_aRzTbeGTKcqx1C5d5i0jH3MGJlWFIuktsIkroW4yDvMBUg_oGkyUYvTV7f3MkKo8bUmbUeZy7tWN23BjRclNnxC9in6_nTLqq1GfPtMYEA6wXfGL5vToAnOkRpC5\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, we load in the data. When working with text analysis in R, it is important to set the stringsAsFactors argument of the read.csv function to FALSE, as this ensures that the text is maintained as a string, instead of being converted to a factor variable. The glimpse function allows us to quickly see what our data frame looks like. We have 13 variables (columns) and 179,108 observations\/tweets (rows). The variable that is going to be of greatest relevance to us here is \u201ctext\u201d, as this is the object that contains the actual tweet itself.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/C20qLN62CEM2MB2NeM63vU6q56q1kT7xN2v0uIlgDFK0RGC1Kuc_MUH-gkm_2QQL37vfOUMvJb9tdqJiU_4rcMxeA78e546NC9lL5cM6c9OV8RyOE8sHizYpcPbUBSlaZYkk-ai-UhTJILaG0r0R7d3rXW-csE4tTANIX3PnPBKFWTDUsgaYWHlnDuoN\" alt=\"\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Before getting started with the actual analysis, we need to process, or clean, the data. Text data, especially when coming from internet users, is often very inconsistent. In order for our computer programs to function properly, we need to clean the text for differences that are irrelevant to our analysis. For example, \u201c<em>Covid\u201d<\/em>, \u201c<em>COVID\u201d<\/em>, and \u201c<em>covid\u201d<\/em>, are all the same to us. Differing capitalization does not change what the user is tweeting about.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To standardize our data, we turn to R\u2019s \u201ctm&#8221; package. Before performing any operations, we must convert the tweets dataframe to the Corpus format\u2014the data structure used for managing documents in this specific package.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/SGXPgV7F4f35oFgGkTmPDMx8cxfg4Kn2-8pxZifN4EYy6NcoeH_2Ex3zQnO_T6R9IqHeGOVh41d0e1k-2NKN7uw2Mn2FGoITT8a5J4VZFB4SShMVglq6Uned5FCR6RpWHKmhT8-fwJqtEkXAHrvb7awJBhJmm6cIdChmJDeStoFt8X8MneXzt2LOZBlr\" alt=\"\" width=\"315\" height=\"47\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now that we have our Corpus, we can perform the following text manipulations:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/TQmDXQs0QBE7r3-uaI085mmnqOrn0jz0ZARam1OUbTCbK_H-mmYRPzjiyPOCR9w8Z5oRc02rLty0xmCpHTol_hEMJLEtrLhJ_a0nKtI4boYvD0_LB1nj66ZNyyZnuK-78nUHcjTDENVv-M9Yyo1IeuvbfcarpN7GTStV8AUAjmsxnb5quFQ6otdc1-w-\" alt=\"\" width=\"423\" height=\"316\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">While the first four operations are quite self-explanatory, the last two deserve a brief explanation. Stopwords are words that are commonly used to construct sentences but that don\u2019t really have much meaning by themselves. The following are examples of English stopwords:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/7bHPUDRlJS6MXulc8gr0ArQNwUNE8dDak0cSjSndwyVpERZUlEynISUBiyPtlTOcvPrjF32pMEgwuwZimnjtVhuCL5gZaDV1NOaKZnKCziWQh_Pi5sBh0zGrxuW0V7xHyKKSDfJexbpudqc94yYXj4mDxK2UmEch-DQIf7Q36fQgWP6p_nb7ztA50VwX\" alt=\"\" width=\"762\" height=\"194\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To understand stemming, think about the following words: <em>\u201cargue\u201d, \u201cargued\u201d, \u201cargues\u201d<\/em>, and <em>\u201carguing\u201d<\/em>. Although each of these words are used slightly differently in sentences, their core meaning can be represented by the stem <em>\u201cargu\u201d<\/em>. As with the capitalization example above, there is no reason for our program to differentiate between each of these words, which is why we reduce them all to their stem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now that we have cleaned up the text, there is one last necessary pre-processing step: tokenizing the data. In this context, tokenization is the breaking down of human-readable text into algorithm-readable components. Given that our algorithm will analyze sentiment from individual words, it is preferable to have each word in its own separate row in the dataframe. For example, the tweet <em>\u201cCOVID is scary\u201d<\/em> would be tokenized into three separate rows, one containing each word. Note that before tokenizing, we must convert the Corpus object back to a dataframe.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/bMayqUyY8XIfiVvUcmWEV-BDuGo5PJZG7-04G6T9f2gKaO-Y_aZIlxXBe3CCXgufzH9q1-H_-UX7CPP7xRvIfja-cic0Yh4hLoeVOxNgFQTJbmylik3fySsDa5xGIB1f9BcjThAuBOKC0AhkNtWxECUalxcREHVCJoLegbd6I4Nm3shQhuJ1GT8qYxB3\" alt=\"\" width=\"583\" height=\"124\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Using the head &nbsp;or glimpse functions, we can visualize our new dataframe\u2014one column of 2,041,828 rows, where each row is a singular word.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/7_9yJ7atv6qFCfrCLOUqWb7Dn0aLumgRPJEEFYkv4Ubmwx0uxf8R6oNjJ1uIyvEZQKNGIvP451ZEsvlJ1wVIX5ODX8BjgZyV_XZg9kKB0ffVU4CHvGm2nNlt-jZCZkHnvgXFDcioXZhRYzWJmJ98kjg6OHwalznc_pGCTSO0YdnOa7BGl4CTHWbKa7F0\" alt=\"\" width=\"172\" height=\"242\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/1lGr2xgrwRNKpgNOZF5_OVbK5g-mw1pXcS0BFRDRC8mvDq8sLahV180Gmjwr-WSXj9YC5sstuf4dNCQtL_rFBsnBhY69zSYiyMZWTiGNrjGMz_ff7hOST7BHzji5EogYSWp6-Kwdupv96_A-XO-JBr90Y5MpxcpcqTO2Yw9ARKm6cEDcurn8J4clmQsF\" alt=\"\" width=\"498\" height=\"126\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now that our data has been processed and is ready to go, we can start the analysis. First, I want to see what the most frequently COVID-related words are, as this will provide us with a \u201cbig picture\u201d view of what people are thinking regarding the pandemic. We can use the \u201cggplot2\u201d package to easily visualize the top 10 most frequently occurring word stems.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/oWdLRNleh7sterIoX1FzS39IsYGKYM1XCcOBnfo2PMgpiS-BHZxRJGSzRWdROrqKaHEfXxc7B8HYVvAmJ2m6opiUJ1Ej8OCLV_RQ65n1B1Bpa5ijrcwmzXK-87cU2yD6b1HyX4WsGvgxrHtp7R3-2EtRJS1q_PwW-SUsIj9jn4gsi7sOqO3LjC6rxsJL\" alt=\"\" width=\"366\" height=\"232\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/TaUAUhcrlEUhiGEtowCGtpzwgiQRwOq9AH_YcGV2CzmQ5I3x8sU0BblJ1iHBASZpx_HC3N6GDii5D-xhlOkv49ldQPoaI_tB_CTh5_cVty3CXm3k1lFIQS3OqgfbcrcKcLIbWo-cfDuCv6dCGLKktOzpGKCuDyKXJr6P-2ghulklYk6NFT_kjxQVVCmJ\" alt=\"\" width=\"365\" height=\"296\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The figure above does not really tell us much about how people are <em>feeling<\/em> regarding the pandemic. To get a better understanding of this, we can construct a positive-negative sentiment word cloud using lexicons. A lexicon is a dictionary or library that attaches a \u201csentiment\u201d score to words based on their semantic orientation. These dictionaries are prepared by individuals or built through crowd surfing initiatives. There are three popularly used lexicon libraries in R:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"http:\/\/www2.imm.dtu.dk\/pubdb\/pubs\/6010-full.html\">Affin<\/a>: ranks words on a -5 to 5 scare, where -5 is the most negative and 5 is the most positive.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.cs.uic.edu\/~liub\/FBS\/sentiment-analysis.html\">Bing<\/a>: categorizes words in a binary fashion, assigning a value of either positive or negative.<\/li>\n\n\n\n<li><a href=\"http:\/\/saifmohammad.com\/WebPages\/NRC-Emotion-Lexicon.htm\">NRC<\/a>: categorizes words as either positive or negative, and then assigns them to one of eight primary emotion categories\u2014anger, fear, anticipation, trust, surprise, sadness, joy, and disgust<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Since our word cloud will characterize words as either negative or positive, it makes sense to use the \u201cbing\u201d lexicon. The code below joins the tokenized data frame with the bing library, which we call using the get_sentiments function. The \u201cwordcloud\u201d package then allows us to create the beautiful visualization underneath, which shows us that the most frequently occurring negative words are \u201cdeath\u201d, \u201cvirus\u201d, and \u201cdie\u201d, while the most frequently occurring positive words are \u201ctrump\u201d, \u201cwork\u201d, and \u201clike\u201d. It is curious that \u201cTrump\u201d appears as a positive sentiment&#8230;however, we must remember that the lexicon library does not interpret the word \u201ctrump\u201d as <em>Donald<\/em> Trump. The algorithm uses the <em>dictionary<\/em> definition of \u201ctrump\u201d, which is in fact a positive thing: \u201ctrump (noun): beat (someone or something) by saying or doing something better\u201d.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/Ul2WRIpnLGP0O4Tq0lYGp2wqDsFmWVWqc22Ex6p_RePFW2A5XKK88t6ykYQIl4IcKgFx85jM1mGyu4iDTi6L1O6sHFlV30xDgGbxajb7GC0_XtjA2hzfUnX4qKfmugXSVH89SO4qgU-eWtxRGGuqb5ZJrGIK2q8Xi4xbqigxKbBoLABPm_FFeu0V3-7U\" alt=\"\" width=\"560\" height=\"570\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We take this a step further with the \u201cNRC\u201d lexicon, which allows us to count word frequencies by emotion\/sentiment category. Surprisingly, the most occurring sentiment is that of trust. I imagine that this is trust towards the medical and scientific community, which is very reassuring! The next most occurring emotions are anticipation, fear, and sadness. This is no surprise, as I can imagine most of us have felt these things over the past year.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/QBKNR3_UN3FfwC2txur8jzfcOJFRlCg4YC904I25I79PT2nPdU5op5_twS0shUTu12L_T14xpKmM8luTRjVjBP4UVBLRyyntnq1wmA-JMpgrxTE_u2RFEOppwZRGn-bFejeBISzy2j5Ec_mLmrKoDOeGRE5W_uXPLjFbv-2hNfzbK5rBDHgWREb-abJy\" alt=\"\" width=\"674\" height=\"636\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To conclude this analysis, we look within these sentiment categories to see which were the most frequently occurring words in each, once again using the \u201cNRC\u201d lexicon. It\u2019s interesting to see that many words, like \u201cdeath\u201d or \u201ccase\u201d, repeat over multiple sentiment categories.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/hW26aPuIrrrYyWS9duvoyr6YN82JT8Ka9FZe7yDCG5EoTk1VUrgYW_cXc8VQm4NYFHo3PWeuVTCRY88KCecBOFVgQLWik4SCoQg0fd_5Nxc_Ca9rr7pQHBL8UzoAohfjpUXAdPPNe1U5lrBjZRHmmUWvxw4ZZ1ljl0Imj9HBd9abyPgctgKe0EGd6f7A\" alt=\"\" width=\"567\" height=\"281\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/ocKS79maORaF-IfEzYOLu0uQCLy3450uMcqtUalJlOG7N-oDI-SsTSJr367-teB4mr7NLpR88mRMVVxkkuHbdaIVxMiUfZJYKx-XmsJY64RTxp0__ygcyJpHFvvOeiAqK4wjgKy5VISpVZfpyRoly4JjN1Ix764lnPQGq898AuoCamItm7Xj6_c5gXOt\" alt=\"\" width=\"562\" height=\"319\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This type of sentiment text analysis has many useful applications. For example, many businesses use this type of programming to understand how consumers are feeling about their product\/brand. I have also seen this being done on the speeches of politicians or on movie scripts. Text analysis is often the first step to a bigger project, like prediction models or recommendation engines.&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>R sentiment analysis on Twitter data using the hashtag #covid19 with Twitter API<\/p>\n","protected":false},"author":1,"featured_media":102,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,4],"tags":[9,16,19],"class_list":["post-43","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-project","category-tutorial","tag-covid19","tag-r","tag-twitterapi"],"_links":{"self":[{"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/posts\/43","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=43"}],"version-history":[{"count":0,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/posts\/43\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=\/wp\/v2\/media\/102"}],"wp:attachment":[{"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=43"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=43"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ercblog.barnard.edu\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=43"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}