【譯文】用R語言做網頁爬蟲和文本分析-Part2
作者 Florent Buisson
譯者 錢亦欣
在閱讀完本系列的第一篇文章之後,我們可以對手頭的數據做些探索性分析以對其形成初步了解,這個過程有助於後期分析中的特徵構建。
數據預處理
我們先載入相關的包並把上次爬來的數據讀入環境。
library(data.table)nlibrary(dplyr)nlibrary(ggplot2)nlibrary(stringr)nlibrary(tm)nlibrary(magrittr)nlibrary(textcat)nlibrary(tidytext)nlibrary(RTextTools)nndata <- read.csv("GoodReadsData.csv", stringsAsFactors = FALSE)ndata <- data.table(data)n
快速瀏覽一遍後,去除那些不是用英語撰寫的評論。
data$language <- as.factor(textcat(data$review))ndata <- data[language == "english"]n
此處採用的語言檢測演算法效果並不是特別好,會把一些評論歸類成奇奇怪怪的語言,不過對於目前而言夠用了。
之後我們把沒有打星評級的評論和太短的評論刪除。
data <- data[rating %in% c(did not like it,n it was ok,n liked it,n really liked it,n it was amazing)]ndata <- data[length(data$review) >= 5]n
最後,我們把評級改用數字編碼,把用做篩選條件的language和reviewer列刪除,再新增一列review_id作為識別碼。
data$rating[data$rating == did not like it] <- 1ndata$rating[data$rating == it was ok ] <- 2ndata$rating[data$rating == liked it ] <- 3ndata$rating[data$rating == really liked it] <- 4ndata$rating[data$rating == it was amazing ] <- 5ndata$rating <- as.integer(data$rating)nndata$language <- NULLndata$reviewer <- NULLndata$review.id <- 1:nrow(data)n
萬事俱備,開始EDA吧!
探索性數據分析
我們先來看看評級的分布,如下圖所示,評級分布很不均勻,這對後續分析有指導意義。
barplot(table(as.factor(data$rating)),n ylim = c(0,5000), n main = "Distribution of ratings")n
評級分布圖:
讓我們再來看看評論長度的分布:
data$review.length = nchar(data$review)nhist(data$review.length, n ylim = c(0,5000), n main = "Distribution of review length" )n
評論長度分布圖:
圖中有一個長尾,掐指一算我們發現只有45條評論超過了8000詞。讓我們刪除它們使得該數據的偏度降低(如果這些論評論出現了某些高頻詞,會扭曲該詞的權重)。
n <- nrow(data[data$review.length >= 8000])ndata <- data[data$review.length <= 8000]nhist(data$review_length, ylim = c(0,3000), main = "Distribution of review length" )n
修正後的圖:
現在看起來好多了,最後讓我們看看不同評級的評論長度分布。
with(data, boxplot(review.length~rating, n main = "Distribution of review length by rating"))n
不同評級的評論長度分布:
直觀看來,好評的長度會比差評短,但趨勢並不是太明顯。讓我們用情感分析法試試,利用tidytest包中的mutatis mutandis方法,詳情參看the analyses of David Robinson on Yelp』s reviews。
情感分析
這一部分,我們會研究詞語的褒義和貶義是否和評級有關。為達成這一目的,我們需要一部包含每個詞語褒貶程度的詞典,此處使用tidytext包中的sentiments數據集。
# 載入第一部情感詞典AFINNnAFINN <- sentiments %>%n filter(lexicon == "AFINN") %>%n select(word, afinn_score = score)nhead(AFINN)nn# 第二部詞典BingnBing <- sentiments %>%n filter(lexicon == "bing") %>%n select(word, bing_sentiment = sentiment)nhead(Bing)n
然後,我們把數據集揉成一個長面板(每個詞一行)並添加每個詞的褒貶程度得分。
# 把數據清洗成長面板並添加每個詞的情感得分nreview_words <- data %>%n unnest_tokens(word, review) %>%n select(-c(book, review_length)) %>%n left_join(AFINN, by = "word") %>%n left_join(Bing, by = "word")n
現在我們的數據集長這樣:
我們可以根據每條評論的平均褒貶程度得分來給它們定性,無論用AFINN詞典還是Bing詞典得到的結果都一致。
# 按照觀測的得分進行分組nreview_mean_sentiment <- review_words %>%n group_by(review_id, rating) %>%n summarize(mean_sentiment = mean(afinn_score, na.rm = TRUE))n
輸出是這個樣子的:
那麼,評級和評論的平均褒貶得分相關性如何呢?
theme_set(theme_bw())nggplot(review.mean.sentiment, aes(rating, mean.sentiment, group = rating)) +n geom_boxplot() +n ylab("Average sentiment score")n
如圖:
我們走上正道了,從這幅圖我們可以明顯的發現不同評級的評論褒貶得分有差異。1星評論的得分往往是負的,而5星評論則不部分為正。讓我們生成一個新的數據框來存儲這一特徵,方便後續分析使用。(注意:部分評論太短或者使用的詞不再詞典內,所以它們沒有分數)
review.mean.sentiment <- review.mean.sentiment %>%n select(-rating) %>% # 移除評級以防萬一n data.table()nclean.data <- data %>%n left_join(review.mean.sentiment, by = "review.id")n
如果我們用每個詞得分的中位數代替平均數作為特徵,評級和它的關係就更明顯了。
review.median.sentiment <- review.words %>%n group_by(review.id, rating) %>%n summarize(median.sentiment = median(afinn.score, na.rm = TRUE))ntheme_set(theme_bw())nggplot(review.median.sentiment, aes(rating, median.sentiment, group = rating)) +n geom_boxplot() +n ylab("Median sentiment score")n
如圖:
我們把這一特徵也保留在數據集里。
review.median.sentiment <- review.median.sentiment %>%n select(-rating) %>%n data.table()nclean.data <- clean.data %>%n left_join(review.median.sentiment, by = "review.id")n
最後,我們來計算下每條評論里褒義詞和貶義詞的個數,位後續的機器學習演算法做準備。
# 根據AFINN計算每條評論的貶義詞數量nreview.count.afinn.negative <- review.words %>%n filter(afinn.score < 0) %>%n group_by(review.id, rating) %>%n summarize(count.afinn.negative = n())n# 把結果轉移到我們的數據集中nreview.count.afinn.negative <- review.count.afinn.negative %>%n select(-rating) %>%n data.table()nclean.data <- clean.data %>%n left_join(review.count.afinn.negative, by = "review.id")nn# 根據AFINN計算每條評論的褒義詞數量nreview.count.afinn.positive <- review.words %>%n filter(afinn.score > 0) %>%n group_by(review.id, rating) %>%n summarize(count.afinn.positive = n())n# 把結果轉移到我們的數據集中nreview.count.afinn.positive <- review.count.afinn.positive %>%n select(-rating) %>%n data.table()nclean.data <- clean.data %>%n left_join(review.count.afinn.positive, by = "review.id")nn# 根據Bing計算每條評論的貶義詞數量nreview.count.bing.negative <- review.words %>%n filter(bing.sentiment == "negative") %>%n group_by(review.id, rating) %>%n summarize(count.bing.negative = n())n# 把結果轉移到我們的數據集中nreview.count.bing.negative <- review.count.bing.negative %>%n select(-rating) %>%n data.table()nclean.data <- clean.data %>%n left_join(review.count.bing.negative, by = "review.id")nn# 根據Bing計算每條評論的褒義詞數量nreview.count.bing.positive <- review.words %>%n filter(bing.sentiment == "positive") %>%n group_by(review.id, rating) %>%n summarize(count.bing.positive = n())n# 把結果轉移到我們的數據集中nreview.count.bing.positive <- review.count.bing.positive %>%n select(-rating) %>%n data.table()nclean.data <- clean.data %>%n left_join(review.count.bing.positive, by = "review.id")n
最後,把所有數據保存在一個文件里方便日後調用。
write.csv(clean.data, "GoodReadsCleanData.csv", row.names = FALSE)n
處於探索性分析的目的,我們可以換一種角度,把每個詞而不是每條評論作為分析單位。首先,我們統計每個詞在所有評論中出現的次數和包含該詞的評論個數與其平均星級。然後我們只保留在3條以上的評論中出現過的詞來避免一些生僻詞的影響。
word.mean.summaries <- review.words %>%n count(review.id, rating, word) %>%n group_by(word) %>%n summarize(reviews = n(),n uses = sum(n),n average.rating = mean(rating)) %>%n filter(reviews >= 3) %>%n arrange(average.rating)n
輸出如下:
之後,我們對每個詞的平均評級和AFINN中的得分進行比較。
word.mean.afinn <- word.mean.summaries %>%n inner_join(AFINN)nnggplot(word.mean.afinn, aes(afinn.score, average.rating, group = afinn.score)) +n geom_boxplot() +n xlab("AFINN score of word") +n ylab("Mean rating of reviews with this word")n
本系列代碼可從我的github下載.
註:原文刊載於Datescience+網站
原文地址:GoodReads: Exploratory data analysis and sentiment analysis (Part 2)
推薦閱讀:
※R 學習筆記: Par 函數
※【R語言基礎】02. 基本數據結構
※翻譯:用R語言進行數據清洗
※如何在Quora上獲得更多的贊——來自10393個回答的實證
※R語言可視化——圖表美化與套用主題(上)