如何站在大資料的角度看100000個故事

騰訊雲加社群發表於2018-06-15

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~

本文來自雲+社群翻譯社,作者HesionBlack

最近我從馬克·裡德爾 那拿到了很棒的自然語言方面的資料集 :從WIKI下載了112000個故事作品的情節。其中包括了書籍、電影、電視劇集、視訊遊戲等有“情節”的任何內容。

這為我定量分析故事結構提供了一個很好的契機。在這篇文章中,我將會進行一個簡單的分析來檢驗在故事中的特定情節上,哪些詞會頻繁出現,比如一些提示了故事開端開始,中間情節或結局的詞。

根據我對文字挖掘的習慣,我將使用Julia Silge和我在去年開發的tidytext軟體包。如果你想要了解更多相關知識,請參閱我們的這本線上書Text Mining with R: A Tidy Approach,它將很快被O'Reilly出版了。我將為您提供部分程式碼,以便您可以繼續跟上我的思路。為了保持文章簡潔,關於視覺化部分的程式碼我基本都沒貼出來。但所有的文章和程式碼都可以在GitHub上找到。

建立

我從GitHub上下載並解壓縮了plots.zip檔案。然後我們將這些檔案讀入R,然後將它們與dplyr使用結合。

library(readr)
library(dplyr)

# Plots and titles are in separate files
plots <- read_lines("~/Downloads/plots/plots", progress = FALSE)
titles <- read_lines("~/Downloads/plots/titles", progress = FALSE)

# Each story ends with an <EOS> line
plot_text <- data_frame(text = plots) %>%
  mutate(story_number = cumsum(text == "<EOS>") + 1,
         title = titles[story_number]) %>%
  filter(text != "<EOS>")
複製程式碼

然後,我們可以使用tidytext將情節整理為一個簡潔的結構,一個詞一行。

library(tidytext)
plot_words <- plot_text %>%
  unnest_tokens(word, text)
複製程式碼
plot_words
複製程式碼
## # A tibble: 40,330,086 × 3
##    story_number       title    word
##           <dbl>       <chr>   <chr>
## 1             1 Animal Farm     old
## 2             1 Animal Farm   major
## 3             1 Animal Farm     the
## 4             1 Animal Farm     old
## 5             1 Animal Farm    boar
## 6             1 Animal Farm      on
## 7             1 Animal Farm     the
## 8             1 Animal Farm   manor
## 9             1 Animal Farm    farm
## 10            1 Animal Farm summons
## # ... with 40,330,076 more rows
複製程式碼

該資料集包含了超過4000萬個單詞,112000個故事。

故事開頭和結尾部分的單詞

約瑟夫坎貝爾提出了名為“hero‘s journey”的分析方法,他認為每個故事都有一致的結構。無論你是否對他的理論買賬,假如一個故事在一開頭就是高潮開始的或者到了結束卻引入了新角色,你可能也會覺得這是很令人詫異的。

這種結構可以用單詞的量化結構來表現-- 有些詞彙應該被期望在開始時出現,而一些詞詞則在應該在結尾出現。

一個簡單的測量方法,我們將記錄每個單詞的位置的中值,同時也記錄它出現的次數。

word_averages <- plot_words %>%
  group_by(title) %>%
  mutate(word_position = row_number() / n()) %>%
  group_by(word) %>%
  summarize(median_position = median(word_position),
            number = n())
複製程式碼

我們對在少數情節出現並且出現頻率比較小的詞語不感興趣,所以我們將篩選出至少出現了2500次的單詞,並只對他們進行分析。

word_averages %>%
  filter(number >= 2500) %>%
  arrange(median_position)
複製程式碼
## # A tibble: 1,640 × 3
##           word median_position number
##          <chr>           <dbl>  <int>
## 1    fictional       0.1193618   2688
## 2         year       0.2013554  18692
## 3  protagonist       0.2029450   3222
## 4      century       0.2096774   3583
## 5      wealthy       0.2356817   5686
## 6        opens       0.2408638   7319
## 7   california       0.2423856   2656
## 8      angeles       0.2580645   2889
## 9          los       0.2661747   3110
## 10     student       0.2692308   6961
## # ... with 1,630 more rows
複製程式碼

例如,我們可以看到,“fictional”這個詞大約用了2700次,其中半數出現在故事的前12% - 這表明這個詞與開端有很大的關係。

下圖展示了與故事開頭和結尾關聯最大的一些詞。

img

與開頭相關的詞時常是描述一些設定:“這個故事主角(protagonist),是一個年輕(young),富有(weathy)的 19**世紀(century)的學生(student),最近(rencently)從在美國加利福尼亞州洛杉磯編造的大學(university College)**畢業。它們大部分都是名詞和形容詞,可以用來描述並限定一個人,一個地點或者一個時期。

相比之下,在故事結尾處的單詞就充滿情感!有些詞本身就有結尾的意思。比如“ending”和“final”,但也有一些動詞反映了激烈刺激的情節,比如“英雄向惡棍射擊(shoots)衝向(rushes)女主角,並道歉(apologizes)。兩人團聚(reunited),他們吻了(kiss)。“

視覺化詞彙趨勢

中值的方法為我們提供了一個有用的彙總統計資訊,讓我們仔細研究下統計資訊的內容。首先,我們將每個故事分成幾個十分位數(前10%,後10%等),並計算每個單詞在每個十分位數內的次數。

decile_counts <- plot_words %>%
  group_by(title) %>%
  mutate(word_position = row_number() / n()) %>%
  ungroup() %>%
  mutate(decile = ceiling(word_position * 10) / 10) %>%
  count(decile, word)
複製程式碼

上述工作使我們可以通過繪製不同單詞在不同情節的位置中的頻率分佈。我們想看看哪些單詞集中在開始/結束的:

img

沒有任何單詞會中在故事的開始或結束。像“高興地(happily)”,在全文都穩定出現,但在最後結尾頻率飆升(“從此他們過上幸福快樂(happily)的生活”)。其他的詞,如“真相(truth)”或“道歉(apologize)”,在故事情節發展的過程中頻率不斷上升,這很合理。一個角色通常不會在故事開始時就“道歉(apologize)”或“意識到真相(realize the truth)”。類似的,“wealthy”這類描述設定的詞出現頻率會逐漸下降,就像劇情發展到後面就越不可能引入新的角色一樣。

上圖種有一個有趣特徵,大多數單詞出現頻率最高的時候是在開始或結束時,但在90%的點上,像“grabs”, “rushes”, 和 “shoots”這樣的詞在故事的90%部分最常出現,說明故事的高潮一般在這裡。

在故事中出現的詞語

受到對出現在故事高潮時出現的單詞的分析的啟發,我們可以觀察哪些單詞出現在故事情節的中間部分,而不是一直盯著開頭和結尾不放。

peak_decile <- decile_counts %>%
  inner_join(word_averages, by = "word") %>%
  filter(number >= 2500) %>%
  transmute(peak_decile = decile,
            word,
            number,
            fraction_peak = n / number) %>%
  arrange(desc(fraction_peak)) %>%
  distinct(word, .keep_all = TRUE)

peak_decile
複製程式碼
## # A tibble: 1,640 × 4
##    peak_decile        word number fraction_peak
##          <dbl>       <chr>  <int>         <dbl>
## 1          0.1   fictional   2688     0.4676339
## 2          1.0     happily   2895     0.4601036
## 3          1.0        ends  18523     0.4036603
## 4          0.1       opens   7319     0.3913103
## 5          1.0    reunited   2660     0.3853383
## 6          0.1 protagonist   3222     0.3764742
## 7          1.0      ending   4181     0.3721598
## 8          0.1        year  18692     0.3578536
## 9          0.1     century   3583     0.3530561
## 10         0.1       story  37248     0.3257356
## # ... with 1,630 more rows
複製程式碼

故事的每個十分位數(起點,終點,30%點等)都有一次單詞出現的頻率很高。哪些詞更能代表這些十分位呢?

img

我們觀察到,開頭和結尾的高頻詞相對固定。例如,“fictionnal”一詞出現在故事的前10%。中間部分的詞彙分佈的相對分散(比如,在該部分中出現的比例為14%,而不是預期的10%),但它們仍然是故事結構中很有意義的詞彙。

我們可以把其中代表性強的單詞的完整趨勢繪製出來看看。

img

試著分析上圖的24個詞,我們的主人公被“attracted”, then “suspicious”, followed by “jealous”, “drunk”, and ultimately “furious”. A shame that once they “confront” the problem, they run into a “trap” and are “wounded”.如果你忽略掉哪些重複的詞和語法的確實,你可以發現整個故事的趨勢可以用這些關鍵詞複述出來。

情感分析

我們關於故事情節中不斷上升的緊張局勢和衝突的這一假設,得到了證實。可以用情感分析來發現每個故事不同10分位的平均情感得分。

decile_counts %>%
  inner_join(get_sentiments("afinn"), by = "word") %>%
  group_by(decile) %>%
  summarize(score = sum(score * n) / sum(n)) %>%
  ggplot(aes(decile, score)) +
  geom_line() +
  scale_x_continuous(labels = percent_format()) +
  expand_limits(y = 0) +
  labs(x = "Position within a story",
       y = "Average AFINN sentiment score")
複製程式碼

img

情節描述在故事中的每個部分都計算出了負AFINN分值(這是很有意義的,因為故事是聚焦於矛盾的)。但開頭相對平緩一點,然後矛盾開始逐步凸顯出來,在80-90%的高潮時。然後通常會有一半的結束,一半包含“快樂(happily)”,“救助(rescues)”和“團聚(reunited)”等詞彙,導致得分又變高了。

總而言之,如果我們必須總結出人類撰寫的平均的故事結構,那麼大致都是“事情會變得越來越糟,直到最後一分鐘才出現轉機,變得越來越好”這樣的情況。

後續

這是對故事情節的簡單的分析(需要深入挖掘的例子,參見這些研究),並沒有得到齊全的資訊,(除了角色可能在故事中期被灌醉。我們如何深入洞悉這些情節)

通過本文我希望你能掌握這些在大型文字據數集上快速量化分析(計數,採用中位數)故事結構的能力。接下來的文章中我會深入挖掘這些情節,來看看我們還能得到哪些資訊。


問答

如何使用樣本資料或Web服務對NLTK python進行情感分析?

相關閱讀

豆瓣電影資料分析和視覺化

使用snownlp進行評論情感分析

拓撲資料分析在機器學習中的應用


此文已由作者授權騰訊雲+社群釋出,原文連結:https://cloud.tencent.com/developer/article/1142199?fromSource=waitui

歡迎大家前往騰訊雲+社群或關注雲加社群微信公眾號(QcloudCommunity),第一時間獲取更多海量技術實踐乾貨哦~

相關文章