【譯文】100000個故事的情節分析:一個簡單案例
作者:David Robinson
譯者:錢亦欣
近來我從Mark Riedel那裡拿到了一個很棒的自然語言方面的數據集。它有從英語wiki下載的112000個故事作品的情節,故事作品包括了圖書,電影,電視劇和遊戲等任何包含情節的藝術形式。
這給我提供了量化分析故事結構的契機。本文我將做一些非常簡單的分析來檢驗在故事的特定節點,哪些詞會平凡的出現,比如指明了開端,中期和結局之類的辭彙。
根據我做文本挖掘的習慣,我會使用tidytext包,這個包由Julia Silge和我在去年開發。如果你想了解更多相關知識,可以看這本在線書Text Mining with R: A Tidy Approach。它很快就會被OReilly出版了。我會把文本挖掘用到的代碼展示出來,這樣看本文你也能跟上思路。為了文章簡介,關於可視化部分的代碼我基本都沒貼出來,但全套代碼可以在我的Github上找到。
準備工作
我從Github上下載並解壓了plots.zip文件 ,然後將其讀入R,再用dplyr合併。
library(readr)nlibrary(dplyr)nn## 圖和標題在不同文件nplots <- read_lines("~/Downloads/plots/plots", progress = FALSE)ntitles <- read_lines("~/Downloads/plots/titles", progress = FALSE)nn## 每個故事都由 <EOS> 結尾enplot_text <- data_frame(text = plots) %>%n mutate(story_number = cumsum(text == "<EOS>") + 1,n title = titles[story_number]) %>%n filter(text != "<EOS>")n
我們可以用tidytext包把情節的嵌套關係轉換成一個簡潔的結構,一個詞一行。
library(tidytext)nplot_words <- plot_text %>%n unnest_tokens(word, text)n
plot_wordsn
#### ## A tibble: 40,330,086 × 3n#### story_number title wordn#### <dbl> <chr> <chr>n#### 1 1 Animal Farm oldn#### 2 1 Animal Farm majorn#### 3 1 Animal Farm then#### 4 1 Animal Farm oldn#### 5 1 Animal Farm boarn#### 6 1 Animal Farm onn#### 7 1 Animal Farm then#### 8 1 Animal Farm manorn#### 9 1 Animal Farm farmn#### 10 1 Animal Farm summonsn#### ## ... with 40,330,076 more rowsn
這個數據集有超過4000萬個單詞,112000個故事。
開始和結尾部分的單詞
Joseph Campbell提出了名為「hero』s journey」分析方法。他認為所有的故事都有一致的結構,大部分故事不會在開頭引來激烈的對戰,也不會在結尾引入新的人物。
這個結構可以用單詞的量化結構來表現:一些詞應當高頻出現於開頭,另一些出現於結尾。
作為一個簡單的測量方法,我們將記錄每個單詞的位置的中值,同時也記錄它出現的次數。
word_averages <- plot_words %>%n group_by(title) %>%n mutate(word_position = row_number() / n()) %>%n group_by(word) %>%n summarize(median_position = median(word_position),n number = n())n
我們對只在少數情節中出現,存在感不強的單詞沒有興趣,所以我們篩選除了出現次數超過2500詞的單詞,只對它們做分析。
word_averages %>%n filter(number >= 2500) %>%n arrange(median_position)n
#### ## A tibble: 1,640 × 3n#### word median_position numbern#### <chr> <dbl> <int>n#### 1 fictional 0.1193618 2688n#### 2 year 0.2013554 18692n#### 3 protagonist 0.2029450 3222n#### 4 century 0.2096774 3583n#### 5 wealthy 0.2356817 5686n#### 6 opens 0.2408638 7319n#### 7 california 0.2423856 2656n#### 8 angeles 0.2580645 2889n#### 9 los 0.2661747 3110n#### 10 student 0.2692308 6961n#### ## ... with 1,630 more rowsn
舉個例子,「fictional」被使用了差不多2700次,其中半數情況在故事的前12%出現,說明它和故事開端有很大關聯。
下圖展示了與故事開頭和結尾關聯最大的一些詞。
與開頭相關的詞時常是描述一些設定:故事 發生(opens) 在(主人公)protagonist,一個 富有(wealthy), 年輕(young), 生活在19世紀(century) 的 學生(student)身上。它們大部分都是名詞和形容詞,可以用來描述並限定一個人,地點或者時期。
與之相反,與結尾相關的詞大部分與情感有關。有一些詞本身就是結尾的指代,如「ending」,"final"。但也有一些動詞反映了激烈刺激的情節,比如英雄射殺(shoots)並沖向(rushes)女英雄,並向其道歉(apologizes)。他倆破鏡重圓(reunited), 並且打了個kiss。
可視化單詞趨勢
下圖給我們展示關係辭彙出現位置的一些統計信息,我們來著重看幾個詞的位置分布。首先我們把每個故事10等分,然後計算每個詞在每個10分位的出現頻率。
decile_counts <- plot_words %>%n group_by(title) %>%n mutate(word_position = row_number() / n()) %>%n ungroup() %>%n mutate(decile = ceiling(word_position * 10) / 10) %>%n count(decile, word)n
上述工作讓我們可以繪製單詞在不同情節位置中的評率分布,我們可以看看在開頭結尾集中出現的哪些單詞。
沒有任何單詞僅在開頭結尾出現。像「happily」在全文都由穩定出現,但在結尾出現頻率暴增(他們幸福地生活在了一起。。。,俗套。。。)。其他的詞,比如「truth」或者「apologizes」,隨著故事開展出現頻率也穩步上升。類似的,「wealthy」這類描述設定的詞出現頻率會逐漸下降。
通過這個圖可以得到的一個有趣發現是大部分詞頻率的巔峰都在開頭或結尾,像「grabs」, 「rushes」, 和 「shoots」這樣的詞在故事的90%部分最常出現,說明故事的高潮一般在這裡。
出現在故事中部的詞
從之前對出現在故事高潮部分的單詞的分析的啟發,我們可以觀察哪些出現在故事中間特定位置的詞,而不是只瞄著開頭結尾。
peak_decile <- decile_counts %>%n inner_join(word_averages, by = "word") %>%n filter(number >= 2500) %>%n transmute(peak_decile = decile,n word,n number,n fraction_peak = n / number) %>%n arrange(desc(fraction_peak)) %>%n distinct(word, .keep_all = TRUE)nnpeak_decilen
#### ## A tibble: 1,640 × 4n#### peak_decile word number fraction_peakn#### <dbl> <chr> <int> <dbl>n#### 1 0.1 fictional 2688 0.4676339n#### 2 1.0 happily 2895 0.4601036n#### 3 1.0 ends 18523 0.4036603n#### 4 0.1 opens 7319 0.3913103n#### 5 1.0 reunited 2660 0.3853383n#### 6 0.1 protagonist 3222 0.3764742n#### 7 1.0 ending 4181 0.3721598n#### 8 0.1 year 18692 0.3578536n#### 9 0.1 century 3583 0.3530561n#### 10 0.1 story 37248 0.3257356n#### ## ... with 1,630 more rowsn
故事的每個10分位都有一些高頻詞(即這些詞在那個10分位出現頻率最高),哪些詞能很好地描述這些10分位呢?
可以發現開頭結尾的高頻詞模式最固定,像「fictional」一詞差不多有一半出現在故事的前10%。中部的詞頻率分布區別沒有這麼明顯,但這些高頻詞還是對分析故事結構有幫助的。
我們可以把其中代表性強的單詞的完整趨勢繪製出來看看。
試著分析上圖的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 %>%n inner_join(get_sentiments("afinn"), by = "word") %>%n group_by(decile) %>%n summarize(score = sum(score * n) / sum(n)) %>%n ggplot(aes(decile, score)) +n geom_line() +n scale_x_continuous(labels = percent_format()) +n expand_limits(y = 0) +n labs(x = "Position within a story",n y = "Average AFINN sentiment score")n
情節描述在故事的每個部分都計算得出了負的AFINN分值(這很有意義,因為故事就是聚焦於矛盾的)。但開頭可能比較平緩,然後矛盾逐步凸顯,在故事的80%-90%部分達到高潮)。最後,故事會有一個終結,一半會包含「happily」, 「rescues」和「reunited」這樣的詞,使得得分又變高。
健兒研製,如果我們必須總結除人類撰寫的平均的故事結構,大致就是「事情變得越來越糟糕,直到最後一分鐘才出現了轉機」。
後續
本文是對故事情節的簡單分析(需要深度挖掘案例的,請看這些研究)),得到的信息並不是非常齊全(除了我們了解到了很多角色會在故事中期喝醉)。
通過本文我希望你能掌握在大型文本數據集上快速量化分析故事結構的能力。未來的文章中我會深入挖掘這些情節來看看我們還能得到哪些信息。
註:原文刊載於Examining the arc of 100,000 stories: a tidy analysis
推薦閱讀:
※R語言中S3,S4,RC結構都分別是什麼?
※有哪些比較好的R語言網路視頻教程推薦?
※好看的數據可視化的圖片是怎麼樣做的?
※如何用R語言匹配兩個表的數據?
※R語言相關圖書?