我們分析了復(fù)仇者聯(lián)盟系列所有臺(tái)詞,看看英雄們都愛(ài)說(shuō)什么?沒(méi)有劇透!
大數(shù)據(jù)文摘出品
作者:蔣寶尚、魏子敏
《復(fù)仇者聯(lián)盟4》終于上映,這部匯集了10年回憶打造的電影,據(jù)看過(guò)的小伙伴們表示:3小時(shí)劇情,毫無(wú)尿點(diǎn),全程都是經(jīng)典回憶。
忙著工作還沒(méi)來(lái)得及看電影,又超怕被劇透的文摘菌這兩天的狀態(tài)基本是這樣👇
萬(wàn)般捉急的文摘菌在這周也去重新回憶了一下這個(gè)系列的作品。這部電影是復(fù)仇者系列的終結(jié)作品,能有如此成就,離不開(kāi)《鋼鐵俠》、《美國(guó)隊(duì)長(zhǎng)》,《雷神》、《綠巨人》等獨(dú)立敘事電影為其構(gòu)建的宏大的宇宙觀,也在全球觀眾心里種下同一種英雄情結(jié)。
復(fù)仇者系列火遍全球絕非意外,這部作品盡管出現(xiàn)了各種人物,但是每個(gè)英雄又都被塑造地各具特色,讓人一次就能記住。
而臺(tái)詞可以說(shuō)是最能塑造人物性格的部分了。因此,文摘菌希望用數(shù)據(jù)分析的方式,看看漫威宇宙的英雄喜歡用的詞匯可視化,并通過(guò)此分析他們的人物特點(diǎn),向這部偉大的作品致敬。
本次分析,我們主要使用了R語(yǔ)言進(jìn)行編程,目的是找出最能代表每位英雄的詞匯。數(shù)據(jù)選用了三個(gè)比較有代表性的漫威英雄交叉度極高的劇本,分別是:《復(fù)仇者聯(lián)盟》(就是打洛基的那一部)、《復(fù)仇者聯(lián)盟:奧創(chuàng)紀(jì)元》以及《美國(guó)隊(duì)長(zhǎng):內(nèi)戰(zhàn)》。
上代碼前,先來(lái)看看分析結(jié)果。
1. 美國(guó)隊(duì)長(zhǎng):以你的名字呼喚你-鋼鐵俠!
作為聯(lián)盟的老大哥,美國(guó)隊(duì)長(zhǎng)超愛(ài)喊別人的名字。并且我們發(fā)現(xiàn),他口中最經(jīng)常出現(xiàn)的名字就是鋼鐵俠。此外,還經(jīng)常點(diǎn)名的是Sam,和Strucker。
美國(guó)隊(duì)長(zhǎng)和鋼鐵俠可謂《復(fù)仇者聯(lián)盟》系列中兩大相愛(ài)相殺的主角了。兩人在電影中都是領(lǐng)導(dǎo)級(jí)別的角色,但是兩者的追求卻有很大的差異。在電影《美國(guó)隊(duì)長(zhǎng):內(nèi)戰(zhàn)》中,復(fù)仇者聯(lián)盟團(tuán)隊(duì)徹底分崩離析,分別從屬了美國(guó)隊(duì)長(zhǎng)和鋼鐵俠兩大陣營(yíng)。
一方面美國(guó)隊(duì)長(zhǎng)為了自己的好朋友冬兵戰(zhàn)斗,另一方鋼鐵俠為了維護(hù)世界的秩序和為自己的父母報(bào)仇戰(zhàn)斗。兩者即是好友,又是同級(jí)別的對(duì)手,這或許也就解釋了為什么美國(guó)隊(duì)長(zhǎng)總是叫鋼鐵俠的名字。
2. 黑豹:最喜歡談?wù)?ldquo;中二“話(huà)題的貴族
從分析結(jié)果來(lái)看,黑豹最喜歡說(shuō)的是父親、朋友,國(guó)王等聽(tīng)起來(lái)比較“中二“的詞語(yǔ)。
黑豹的父親前任黑豹特查卡,瓦坎達(dá)的國(guó)王!守護(hù)者振金,是黑豹的偶像,卻在電影中死于一場(chǎng)陰謀。而黑豹作為瓦坎達(dá)的年輕的王位繼承人,將他父親的遺志作為了追求的夢(mèng)想,守護(hù)著瓦坎達(dá)。國(guó)王身份,追求理想,這就是黑豹喜歡談?wù)撨@類(lèi)貴族話(huà)題的原因。
3. 蜘蛛俠:我還是個(gè)“寶寶“。
作為全隊(duì)的“小朋友“,蜘蛛俠在復(fù)仇者聯(lián)盟系列電影中的臺(tái)詞一直比較幼齒,他在電影中說(shuō)的最多的是詞是:“嗨”、“呃”、“嗯”。
在這三部電影中,蜘蛛俠只是一個(gè)十幾歲的孩子,在這么多大人物面前如果再不蹦蹦跳跳,那就更沒(méi)有存在感了☺。
4. 浩克和鷹眼:大家都愛(ài)黑寡婦。
通過(guò)可視化分析可以發(fā)現(xiàn),綠巨人和鷹眼都非常喜歡提到黑寡婦。
浩克喜歡和黑寡婦聊天原因很簡(jiǎn)單,因?yàn)楫?dāng)綠巨人發(fā)狂時(shí),黑寡婦用滿(mǎn)心關(guān)愛(ài)的眼神瞅著他那龐大的身軀,對(duì)他說(shuō)道:“嘿,大塊頭,太陽(yáng)快下山了!”然后慢慢地舉起了手,用她那柔軟的手指,伸向了綠巨人的手臂,輕輕滑了下來(lái)。這時(shí)候浩克就會(huì)平息他那滿(mǎn)腔的怒火!
電影中黑寡婦和鷹眼不是戀人或者情侶,他們的關(guān)系一直戀人未滿(mǎn)、曖昧不清。但是,因?yàn)閮扇嗽趶?fù)仇者聯(lián)盟之前就已經(jīng)發(fā)生了一系列故事。刀光劍影,愛(ài)恨情仇,即是老友又是戰(zhàn)友,或許兩人早已暗生情愫。
5. 幻世和緋紅女巫:惺惺相惜,在線(xiàn)發(fā)糖!
從數(shù)據(jù)可視化的結(jié)果中可以看到,幻視和緋紅女巫絕對(duì)是soulmate了,兩人的談?wù)搩?nèi)容都很一致,特別喜歡說(shuō)“恐懼、擔(dān)憂(yōu)“類(lèi)話(huà)題!
關(guān)于緋紅女巫,我們可以從她童年的經(jīng)歷和非人的待遇中找到原因。而幻世作為超級(jí)人工智能,能夠看到別的英雄看不到的“畫(huà)面”,可能對(duì)未來(lái)的擔(dān)憂(yōu)讓他心煩意亂。
6. 托爾:能力越大,責(zé)任越大,考慮深遠(yuǎn)
托爾作為雷神,擁有多種魔法能力,例如:操控風(fēng)暴,釋放或控制閃電,將閃電能量實(shí)體化為盔甲,瞬間改變天氣,利用雷神之錘飛行,召喚雷神之錘令其飛回托爾用閃電與敵人交戰(zhàn)。
雷神除去強(qiáng)大的戰(zhàn)斗力,托爾還掌握著星際級(jí)的知識(shí)。例如:寶石知識(shí)、各式星際飛船駕駛技術(shù)、格魯特語(yǔ)(格魯特所在種族的語(yǔ)言)、蟲(chóng)洞知識(shí)。
或許是能力越大,責(zé)任越大,他比其他英雄角色看的更遠(yuǎn)。在電影中,他對(duì)推動(dòng)劇情前進(jìn)的物品更加專(zhuān)注,例如洛基的權(quán)杖以及心靈寶石。
7. 洛基:追逐權(quán)力。
洛基從小和雷神托爾一起長(zhǎng)大。一直窺視眾神之王的寶座且不認(rèn)同雷神托爾會(huì)是一位合格的繼承人。他野心十足想當(dāng)老大,陰險(xiǎn)狡詐陷害兄長(zhǎng)、反逆父母,視天下生命如草芥,為了目的不擇手段。
總之一句話(huà),他非常想要權(quán)力!
8. 奧創(chuàng):更愛(ài)“詩(shī)和遠(yuǎn)方“。
奧創(chuàng)被制造出來(lái)的目的是為了守護(hù)和平,但是一誕生就發(fā)生錯(cuò)誤,認(rèn)為想要和平就要消滅人類(lèi)和復(fù)仇者聯(lián)盟,于是搶走洛基權(quán)杖(心靈寶石)從尤利西斯·克勞手中弄到大量的振金,操縱趙海倫利用再生搖籃幫其制造幻視身體,想要進(jìn)化得更強(qiáng)。
換句話(huà)說(shuō),奧創(chuàng)一出生就被訂上了守護(hù)和平的烙印,雖然他看問(wèn)題的角度不同,但是和復(fù)仇者聯(lián)盟有著共同的任務(wù)。所以,它更加向往詩(shī)與遠(yuǎn)方!
上面條條的長(zhǎng)度對(duì)應(yīng)的是超級(jí)英雄使用每個(gè)詞匯的程度。
可視化過(guò)程
最后,分析完全劇的角色,我們也來(lái)一起看看整個(gè)可視化過(guò)程。
導(dǎo)入R語(yǔ)言包:
- library(dplyr)
- library(grid)
- library(gridExtra)
- library(ggplot2)
- library(reshape2)
- library(cowplot)
- library(jpeg)
- library(extrafont)
清除R工作環(huán)境中的全部東西:
- rm(list = ls())
加載包含所有圖片的文件夾(根據(jù)你自己的情況修改代碼):
- dir_images <- "C:\\Users\\Matt\\Documents\\R\\Avengers"
- setwd(dir_images)
設(shè)置字體:
- windowsFonts(Franklin=windowsFont("Franklin Gothic Demi"))
英雄角色名字的簡(jiǎn)化版本:
- character_names <- c("black_panther","black_widow","bucky","captain_america",
- "falcon","hawkeye","hulk","iron_man",
- "loki","nick_fury","rhodey","scarlet_witch",
- "spiderman","thor","ultron","vision")
- image_filenames <- paste0(character_names, ".jpg")
將所有圖片讀入一個(gè)列表中。
- all_images <- lapply(image_filenames, read_image)
將角色名字分配給圖像列表,以便按名字對(duì)其進(jìn)行索引。
- names(all_images) <- character_names
例如:
- # clear the plot window
- grid.newpage()
- # draw to the plot window
- grid.draw(rasterGrob(all_images[['vision']]))
獲得文本數(shù)據(jù)
數(shù)據(jù)由計(jì)算機(jī)科學(xué)家Elle O'Brien收集的,使用文本挖掘技術(shù)對(duì)電影劇本分析。
更正專(zhuān)有名稱(chēng)的大寫(xiě):
- capitalize <- Vectorize(function(string){
- substr(string,1,1) <- toupper(substr(string,1,1))
- return(string)
- })
- proper_noun_list <- c("clint","hydra","steve","tony",
- "sam","stark","strucker","nat","natasha",
- "hulk","tesseract", "vision",
- "loki","avengers","rogers", "cap", "hill")
- # Run the capitalization function
- word_data <- word_data %>%
- mutate(word = ifelse(word %in% proper_noun_list, capitalize(word), word)) %>%
- mutate(word = ifelse(word == "jarvis", "JARVIS", word))
請(qǐng)注意,以前的簡(jiǎn)版角色名字與文本dataframe格式中的角色不匹配。
- unique(word_data$Speaker)
- ## [1] "Black Panther" "Black Widow" "Bucky"
- ## [4] "Captain America" "Falcon" "Hawkeye"
- ## [7] "Hulk" "Iron Man" "Loki"
- ## [10] "Nick Fury" "Rhodey" "Scarlet Witch"
- ## [13] "Spiderman" "Thor" "Ultron"
- ## [16] "Vision"
創(chuàng)建一個(gè)索引表,將文件名轉(zhuǎn)換為角色名。
- character_labeler <- c(`black_panther` = "Black Panther",
- `black_widow` = "Black Widow",
- `bucky` = "Bucky",
- `captain_america` = "Captain America",
- `falcon` = "Falcon", `hawkeye` = "Hawkeye",
- `hulk` = "Hulk", `iron_man` = "Iron Man",
- `loki` = "Loki", `nick_fury` = "Nick Fury",
- `rhodey` = "Rhodey",`scarlet_witch` ="Scarlet Witch",
- `spiderman`="Spiderman", `thor`="Thor",
- `ultron` ="Ultron", `vision` ="Vision")
有兩個(gè)不同版本的角色名,一個(gè)用于顯示(漂亮),一個(gè)用于索引(簡(jiǎn)單)。
- convert_pretty_to_simple <- Vectorize(function(pretty_name){
- # pretty_name = "Vision"
- simple_name <- names(character_labeler)[character_labeler==pretty_name]
- # simple_name <- as.vector(simple_name)
- return(simple_name)
- })
- # convert_pretty_to_simple(c("Vision","Thor"))
- # just for fun, the inverse of that function
- convert_simple_to_pretty <- function(simple_name){
- # simple_name = "vision"
- pretty_name <- character_labeler[simple_name] %>% as.vector()
- return(pretty_name)
- }
- # example
- convert_simple_to_pretty(c("vision","black_panther"))
- ## [1] "Vision" "Black Panther"
將簡(jiǎn)化的角色名稱(chēng)添加到文本數(shù)據(jù)框架中。
- word_data$character <- convert_pretty_to_simple(word_data$Speaker)
為每個(gè)角色指定主顏色:
- character_palette <- c(`black_panther` = "#51473E",
- `black_widow` = "#89B9CD",
- `bucky` = "#6F7279",
- `captain_america` = "#475D6A",
- `falcon` = "#863C43", `hawkeye` = "#84707F",
- `hulk` = "#5F5F3F", `iron_man` = "#9C2728",
- `loki` = "#3D5C25", `nick_fury` = "#838E86",
- `rhodey` = "#38454E",`scarlet_witch` ="#620E1B",
- `spiderman`="#A23A37", `thor`="#323D41",
- `ultron` ="#64727D", `vision` ="#81414F" )
繪制條形圖☟
- avengers_bar_plot <- word_data %>%
- group_by(Speaker) %>%
- top_n(5, amount) %>%
- ungroup() %>%
- mutate(word = reorder(word, amount)) %>%
- ggplot(aes(x = word, y = amount, fill = character))+
- geom_bar(stat = "identity", show.legend = FALSE)+
- scale_fill_manual(values = character_palette)+
- scale_y_continuous(name ="Log Odds of Word",
- breaks = c(0,1,2)) +
- theme(text = element_text(family = "Franklin"),
- # axis.title.x = element_text(size = rel(1.5)),
- panel.grid = element_line(colour = NULL),
- panel.grid.major.y = element_blank(),
- panel.grid.minor = element_blank(),
- panel.background = element_rect(fill = "white",
- colour = "white"))+
- # theme(strip.text.x = element_text(size = rel(1.5)))+
- xlab("")+
- coord_flip()+
- facet_wrap(~Speaker, scales = "free_y")
- avengers_bar_plot
這已經(jīng)非常漂亮了,但是還可以更漂亮。比如人物形象通過(guò)“線(xiàn)條”顯示出來(lái)。具體做法是將透明的條形圖全覆蓋,然后從端點(diǎn)向里繪制白色的條形圖,注意條形圖是能夠遮擋圖片的。
在數(shù)據(jù)框架中,用達(dá)到總值所需的余數(shù)來(lái)補(bǔ)充數(shù)值,這樣當(dāng)將值和余數(shù)組合在一起時(shí),就會(huì)形成長(zhǎng)度一致的線(xiàn)條組合。
- max_amount <- max(word_data$amount)
- word_data$remainder <- (max_amount - word_data$amount) + 0.2
每個(gè)英雄角色僅提取5個(gè)關(guān)鍵詞。
- word_data_top5 <- word_data %>%
- group_by(character) %>%
- arrange(desc(amount)) %>%
- slice(1:5) %>%
- ungroup()
將“amount”和“remaining”的格式進(jìn)行轉(zhuǎn)換:
確保每個(gè)角色有兩個(gè)長(zhǎng)條;一個(gè)用于顯示amount,另一個(gè)用于選擇結(jié)束位置。
這會(huì)將“amount”和“remaining”折疊成一個(gè)名為“variable”的列,指示它是哪個(gè)值,另一列“value”包含每個(gè)值中的數(shù)字。
- word_data_top5_m <- melt(word_data_top5, measure.vars = c("amount","remainder"))
將這些條形圖放在有序因素中,與在數(shù)據(jù)融合中相反。否則,“amount”和“remainder”將在圖上以相反的順序顯示。
- word_data_top5_m$variable2 <- factor(word_data_top5_m$variable,
- levels = rev(levels(word_data_top5_m$variable)))
每個(gè)角色僅僅顯示五個(gè)詞匯
注意角色名稱(chēng)的版本問(wèn)題,例如采用“black_panther”而不是“Black Panther”。
- plot_char <- function(character_name){
- # example: character_name = "black_panther"
- # plot details that we might want to fiddle with
- # thickness of lines between bars
- bar_outline_size <- 0.5
- # transparency of lines between bars
- bar_outline_alpha <- 0.25
- #
- # The function takes the simple character name,
- # but here, we convert it to the pretty name,
- # because we'll want to use that on the plot.
- pretty_character_name <- convert_simple_to_pretty(character_name)
- # Get the image for this character,
- # from the list of all images.
- temp_image <- all_images[character_name]
- # Make a data frame for only this character
- temp_data <- word_data_top5_m %>%
- dplyr::filter(character == character_name) %>%
- mutate(character = character_name)
- # order the words by frequency
- # First, make an ordered vector of the most common words
- # for this character
- ordered_words <- temp_data %>%
- mutate(word = as.character(word)) %>%
- dplyr::filter(variable == "amount") %>%
- arrange(value) %>%
- `[[`(., "word")
- # order the words in a factor,
- # so that they plot in this order,
- # rather than alphabetical order
- temp_data$word = factor(temp_data$word, levels = ordered_words)
- # Get the max value,
- # so that the image scales out to the end of the longest bar
- max_value <- max(temp_data$value)
- fill_colors <- c(`remainder` = "white", `value` = "white")
- # Make a grid object out of the character's image
- character_image <- rasterGrob(all_images[[character_name]],
- width = unit(1,"npc"),
- height = unit(1,"npc"))
- # make the plot for this character
- output_plot <- ggplot(temp_data)+
- aes(x = word, y = value, fill = variable2)+
- # add image
- # draw it completely bottom to top (x),
- # and completely from left to the the maximum log-odds value (y)
- # note that x and y are flipped here,
- # in prep for the coord_flip()
- annotation_custom(character_image,
- xmin = -Inf, xmax = Inf, ymin = 0, ymax = max_value) +
- geom_bar(stat = "identity", color = alpha("white", bar_outline_alpha),
- size = bar_outline_size, width = 1)+
- scale_fill_manual(values = fill_colors)+
- theme_classic()+
- coord_flip(expand = FALSE)+
- # use a facet strip,
- # to serve as a title, but with color
- facet_grid(. ~ character, labellerlabeller = labeller(character = character_labeler))+
- # figure out color swatch for the facet strip fill
- # using character name to index the color palette
- # color= NA means there's no outline color.
- theme(strip.background = element_rect(fill = character_palette[character_name],
- color = NA))+
- # other theme elements
- theme(strip.text.x = element_text(size = rel(1.15), color = "white"),
- text = element_text(family = "Franklin"),
- legend.position = "none",
- panel.grid = element_blank(),
- axis.text.x = element_text(size = rel(0.8)))+
- # omit the axis title for the individual plot,
- # because we'll have one for the entire ensemble
- theme(axis.title = element_blank())
- return(output_plot)
- }
單個(gè)角色是如何設(shè)置?
- sample_plot <- plot_char("black_panther")+
- theme(axis.title = element_text())+
- # x lab is still declared as y lab
- # because of coord_flip()
- ylab(plot_x_axis_text)
- sample_plot
橫軸為什么這么特殊?因?yàn)殡S著數(shù)值的增加,條形圖會(huì)變得越來(lái)越高,因此需要轉(zhuǎn)換刻度。
如下所示☟
- logit2prob <- function(logit){
- odds <- exp(logit)
- prob <- odds / (1 + odds)
- return(prob)
- }
…這就是這個(gè)軸的樣子:
- logit2prob(seq(0, 2.5, 0.5))
- ## [1] 0.5000000 0.6224593 0.7310586 0.8175745 0.8807971 0.9241418
注意該列表中連續(xù)項(xiàng)之間的遞減差異:
- diff(logit2prob(seq(0, 2.5, 0.5)))
- ## [1] 0.12245933 0.10859925 0.08651590 0.06322260 0.04334474
好了,可以進(jìn)行下一項(xiàng)了:探討一些細(xì)節(jié),并把上面設(shè)置的函數(shù)應(yīng)用到所有角色的列表中,并把所有的結(jié)果放入一個(gè)列表中。
- all_plots <- lapply(character_names, plot_char)
從圖片中提取標(biāo)題
- get_axis_grob <- function(plot_to_pick, which_axis){
- # plot_to_pick <- sample_plot
- tmp <- ggplot_gtable(ggplot_build(plot_to_pick))
- # tmp$grobs
- # find the grob that looks like
- # it would be the x axis
- axis_x_index <- which(sapply(tmp$grobs, function(x){
- # for all the grobs,
- # return the index of the one
- # where you can find the text
- # "axis.title.x" or "axis.title.y"
- # based on input argument `which_axis`
- grepl(paste0("axis.title.",which_axis), x)}
- ))
- axis_grob <- tmp$grobs[[axis_x_index]]
- return(axis_grob)
- }
提取軸標(biāo)題:
- px_axis_x <- get_axis_grob(sample_plot, "x")
- px_axis_y <- get_axis_grob(sample_plot, "y")
下面是如何使用提取出來(lái)的坐標(biāo)軸:
- grid.newpage()
- grid.draw(px_axis_x)
- # grid.draw(px_axis_y)
匯總所有的英雄:
- big_plot <- arrangeGrob(grobs = all_plots)
加入圖注,注意圖和坐標(biāo)軸的比例關(guān)系:
- big_plot_w_x_axis_title <- arrangeGrob(big_plot,
- px_axis_x,
- heights = c(10,1))
- grid.newpage()
- grid.draw(big_plot_w_x_axis_title)
因?yàn)樵~匯的長(zhǎng)度不同,這些圖表占用的頁(yè)面空間略有不同。
所以,這看起來(lái)有點(diǎn)亂。
一般來(lái)說(shuō),我們使用facet_grid()或facet_wrap()確保在繪圖的過(guò)程中保持整齊和對(duì)齊,這個(gè)項(xiàng)目中不再適用,因?yàn)槊總€(gè)都有自己的自定義背景圖像。
使用Cowplot而不是arrangebrob,讓圖片的軸垂直對(duì)齊:
- big_plot_aligned <- cowplot::plot_grid(plotlist = all_plots, align = 'v', nrow = 4)
增加X(jué)軸的標(biāo)題,和之前類(lèi)似,注意網(wǎng)格對(duì)齊:
- big_plot_w_x_axis_title_aligned <- arrangeGrob(big_plot_aligned,
- px_axis_x,
- heights = c(10,1))
然后,大功告成☟
然后,保存一下!
ggsave(big_plot_w_x_axis_title_aligned, file = "Avengers_Word_Usage.png", width = 12, height = 6.3)
相關(guān)報(bào)道:
- https://towardsdatascience.com/i-analyzed-marvel-movie-scripts-to-learn-what-each-avenger-says-most-2e5e7b6105bf
- http://mattwinn.com/Plot_Avengers_data.html
【本文是51CTO專(zhuān)欄機(jī)構(gòu)大數(shù)據(jù)文摘的原創(chuàng)文章,微信公眾號(hào)“大數(shù)據(jù)文摘( id: BigDataDigest)”】