本文概述
文本挖掘中的一组重要指标与某个文本文档语料库中单词(或任何标记)的出现频率有关。但是, 如果每个文档都有一个描述文档某些属性的关联数值, 则还可以使用一组额外的指标。
一些例子:
- 推文及其各自的参与数量。
- 网址及其浏览量和跳出。
- 电影标题及其总收入。
- 关键字及其印象, 点击和转化。
在本教程中,
- 你将首先完成创建简单函数的过程, 该函数计算并比较文档语料库中单词的绝对出现次数和加权出现次数。有时, 通过查看前十个左右的值, 可以发现不一定清晰的隐藏趋势和汇总。它们通常也可能与绝对词频不同。
- 然后, 你将看到真实的数据集(电影标题和总收入), 并希望发现隐藏的趋势。一个预告片:爱情会以某种方式出现!
- 你将使用Python作为编程语言, 并使用collections模块的defaultdict数据结构进行繁重的工作, 并使用pandas DataFrames管理最终输出。
绝对加权词频:简介
假设你有两条推文, 它们的内容和印象数(视图)如下:
推文 | 观看次数 |
---|---|
西班牙 | 800 |
法国 | 200 |
进行基本分析很容易, 发现你的单词在” france”和” spain”之间以50:50的比例分开。在许多情况下, 这就是你所拥有的, 你只能测量单词的绝对频率, 并尝试推断某些关系。在这种情况下, 你具有有关每个文档的一些数据。
此处的加权频率明显不同, 并且分割为80:20。换句话说, 尽管”西班牙”和”法国”在你的推文中都出现过一次, 但从读者的角度来看, 前者出现了800次, 而后者出现了200次。有很大的不同!
使用defaultdict的简单单词频率
现在考虑一组类似的文档的示例:
文献 | 观看次数 |
---|---|
法国 | 200 |
西班牙 | 180 |
西班牙海滩 | 170 |
法国海滩 | 160 |
西班牙最好的海滩 | 160 |
现在, 你遍历文档, 将它们拆分为单词, 并计算每个单词的出现次数:
from collections import defaultdict
import pandas as pd
text_list = ['france', 'spain', 'spain beaches', 'france beaches', 'spain best beaches']
word_freq = defaultdict(int)
for text in text_list:
for word in text.split():
word_freq[word] += 1
pd.DataFrame.from_dict(word_freq, orient='index') \
.sort_values(0, ascending=False) \
.rename(columns={0: 'abs_freq'})
abs_freq | |
---|---|
西班牙 | 3 |
海滩 | 3 |
法国 | 2 |
最好 | 1 |
在上面的循环中, 第一行逐个循环通过text_list。第二行(在每个文档中)循环浏览每个项目的单词, 并用空格字符(可以是其他任何字符(‘-‘, ‘, ‘, ‘_’等)分隔)。
当你尝试为word_freq [word]分配值时, 有两种可能的情况:
- 关键字存在:在这种情况下, 分配已完成(加一)
- 关键字不在word_freq中, 在这种情况下defaultdict调用它在首次定义时分配给它的默认函数, 在这种情况下为int。
调用int时, 它返回零。现在, 该键已存在, 其值为零, 并且可以为其分配一个附加的1。
尽管在第一张表中最重要的单词是” france”, 但在对每个文档中的所有单词进行计数之后, 我们可以看到” spain”和” beaches”并列第一。这对于发现隐藏趋势非常重要, 尤其是当你要处理的文档列表为数以万计时。
加权词频
现在你已经计算了文档语料库中每个单词的出现次数, 现在你想要查看加权频率。也就是说, 你希望查看单词在读者中出现的次数, 与你使用它们的次数相比。
在第一个表格中, 单词的绝对频率在”西班牙”和”法国”之间平均分配, 但是”西班牙”的权重明显更高, 因为它的价值是800, 而” 200″或”法国”。
但是第二张稍微复杂一点的表的加权单词频率是多少?
让我们找出答案!
你可以重复使用上面使用的一些代码, 但要增加一些内容:
# default value is now a list with two ints
word_freq = defaultdict(lambda: [0, 0])
# the `views` column you had in the first DataFrame
num_list = [200, 180, 170, 160, 160]
# looping is now over both the text and the numbers
for text, num in zip(text_list, num_list):
for word in text.split():
# same as before
word_freq[word][0] += 1
# new line, incrementing the numeric value for each word
word_freq[word][1] += num
columns = {0: 'abs_freq', 1: 'wtd_freq'}
abs_wtd_df = pd.DataFrame.from_dict(word_freq, orient='index') \
.rename(columns=columns) \
.sort_values('wtd_freq', ascending=False) \
.assign(rel_value=lambda df: df['wtd_freq'] / df['abs_freq']) \
.round()
abs_wtd_df.style.background_gradient(low=0, high=.7, subset=['rel_value'])
abs_freq | wtd_freq | rel_value | |
---|---|---|---|
西班牙 | 3 | 510 | 170 |
海滩 | 3 | 490 | 163 |
法国 | 2 | 360 | 180 |
最好 | 1 | 160 | 160 |
一些观察:
- 尽管” france”是总体上最高的词组, 但是当你使用加权频率时, ” spanish”和” beaches”似乎更加突出。
- rel_value是一个简单的除法, 用于获取每个单词每次出现时的值。
- 查看rel_value, 你还可以看到, 即使wtd_freq指标上的’france’值非常低, 但似乎仍有潜力, 因为每次发生的值都很高。例如, 这可能暗示你增加了”法国”的内容覆盖率。
你可能还想添加一些其他指标, 以显示每种频率类型的百分比和累积百分比, 以便更好地了解构成总数的单词中有多少个单词(如果有):
abs_wtd_df.insert(1, 'abs_perc', value=abs_wtd_df['abs_freq'] / abs_wtd_df['abs_freq'].sum())
abs_wtd_df.insert(2, 'abs_perc_cum', abs_wtd_df['abs_perc'].cumsum())
abs_wtd_df.insert(4, 'wtd_freq_perc', abs_wtd_df['wtd_freq'] / abs_wtd_df['wtd_freq'].sum())
abs_wtd_df.insert(5, 'wtd_freq_perc_cum', abs_wtd_df['wtd_freq_perc'].cumsum())
abs_wtd_df.style.background_gradient(low=0, high=0.8)
abs_freq | abs_perc | abs_perc_cum | wtd_freq | wtd_freq_perc | wtd_freq_perc_cum | rel_value | |
---|---|---|---|---|---|---|---|
西班牙 | 3 | 0.333333 | 0.333333 | 510 | 0.335526 | 0.335526 | 170 |
海滩 | 3 | 0.333333 | 0.666667 | 490 | 0.322368 | 0.657895 | 163 |
法国 | 2 | 0.222222 | 0.888889 | 360 | 0.236842 | 0.894737 | 180 |
最好 | 1 | 0.111111 | 1 | 160 | 0.105263 | 1 | 160 |
可以分析的内容更多, 而有了更多的数据, 你通常会得到更多的惊喜。
那么, 在具有一些真实数据的真实环境中, 这看起来如何呢?
你将看电影的标题, 查看标题中使用最多的单词-这是绝对频率-哪些单词与最多的收入或加权频率相关。
Boxoffice Mojo列出了超过15, 000部电影以及相关的总收入和排名。首先使用请求和BeautifulSoup抓取数据-如果你愿意, 你可以在此处探索Boxoffice Mojo:
import requests
from bs4 import BeautifulSoup
final_list = []
for i in range(1, 156):
if not i%10:
print(i)
page = 'http://www.boxofficemojo.com/alltime/domestic.htm?page=' + str(i) + '&p=.htm'
resp = requests.get(page)
soup = BeautifulSoup(resp.text, 'lxml')
# trial and error to get the exact positions
table_data = [x.text for x in soup.select('tr td')[11:511]]
# put every 5 values in a row
temp_list = [table_data[i:i+5] for i in range(0, len(table_data[:-4]), 5)]
for temp in temp_list:
final_list.append(temp)
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
boxoffice_df = pd.DataFrame.from_records(final_list)
boxoffice_df.head(10)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | 1 | 星球大战:原力觉醒 | BV | $936, 662, 225 | 2015 |
1 | 2 | 头像 | 狐狸 | $760, 507, 625 | 2009^ |
2 | 3 | 黑豹 | BV | $681, 084, 109 | 2018 |
3 | 4 | 铁达尼号 | 依 | $659, 363, 944 | 1997^ |
4 | 5 | 侏罗纪世界 | 团结。 | $652, 270, 625 | 2015 |
5 | 6 | 漫威的复仇者联盟 | BV | $623, 357, 910 | 2012 |
6 | 7 | 星球大战:最后的绝地武士 | BV | $620, 181, 382 | 2017 |
7 | 8 | 黑暗骑士 | WB | $534, 858, 444 | 2008^ |
8 | 9 | 侠盗一号:星球大战外传 | BV | $532, 177, 324 | 2016 |
9 | 10 | Beauty and the Beast (2017) | BV | $504, 014, 165 | 2017 |
boxoffice_df.tail(15)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
15485 | 15486 | 黑暗时刻 | 不适用 | $423 | 2005 |
15486 | 15487 | 2:22 | 量。 | $422 | 2017 |
15487 | 15488 | 国家公园 | ATL | $421 | 1988 |
15488 | 15489 | The Magician (2010) | Reg。 | $406 | 2010 |
15489 | 15490 | 无皮的 | PPF | $400 | 2014 |
15490 | 15491 | Cinemanovels | 蒙特 | $398 | 2014 |
15491 | 15492 | 汉娜:佛教的不朽旅程 | KL | $396 | 2016 |
15492 | 15493 | 143号公寓 | 量。 | $383 | 2012 |
15493 | 15494 | 沼泽 | 所有。 | $336 | 2007 |
15494 | 15495 | 女仆 | FM | $315 | 2015 |
15495 | 15496 | 来自火星的消息 | KL | $310 | 2016 |
15496 | 15497 | 特洛伊战争 | WB | $309 | 1997 |
15497 | 15498 | !小日记 | 分发。 | $287 | 2015 |
15498 | 15499 | 介入 | 所有。 | $279 | 2007 |
15499 | 15500 | 回放 | 量。 | $264 | 2012 |
你将看到一些数字值具有一些特殊字符($ 、、和^), 而某些值实际上是N / A。因此, 你需要更改以下内容:
na_year_idx = [i for i, x in enumerate(final_list) if x[4] == 'n/a'] # get the indexes of the 'n/a' values
new_years = [1998, 1999, 1960, 1973] # got them by checking online
print(*[(i, x) for i, x in enumerate(final_list) if i in na_year_idx], sep='\n')
print('new year values:', new_years)
(8003, ['8004', 'Warner Bros. 75th Anniversary Film Festival', 'WB', '$741, 855', 'n/a'])
(8148, ['8149', 'Hum Aapke Dil Mein Rahte Hain', 'Eros', '$668, 678', 'n/a'])
(8197, ['8198', 'Purple Moon (Re-issue)', 'Mira.', '$640, 945', 'n/a'])
(10469, ['10470', 'Amarcord', 'Jan.', '$125, 493', 'n/a'])
new year values: [1998, 1999, 1960, 1973]
for na_year, new_year in zip(na_year_idx, new_years):
final_list[na_year][4] = new_year
print(final_list[na_year], new_year)
['8004', 'Warner Bros. 75th Anniversary Film Festival', 'WB', '$741, 855', 1998] 1998
['8149', 'Hum Aapke Dil Mein Rahte Hain', 'Eros', '$668, 678', 1999] 1999
['8198', 'Purple Moon (Re-issue)', 'Mira.', '$640, 945', 1960] 1960
['10470', 'Amarcord', 'Jan.', '$125, 493', 1973] 1973
现在, 通过使用适当的名称命名列并将其转换为所需的数据类型, 将列表变成pandas DataFrame。
import re
regex = '|'.join(['\$', ', ', '\^'])
columns = ['rank', 'title', 'studio', 'lifetime_gross', 'year']
boxoffice_df = pd.DataFrame({
'rank': [int(x[0]) for x in final_list], # convert ranks to integers
'title': [x[1] for x in final_list], # get titles as is
'studio': [x[2] for x in final_list], # get studio names as is
'lifetime_gross': [int(re.sub(regex, '', x[3])) for x in final_list], # remove special characters and convert to integer
'year': [int(re.sub(regex, '', str(x[4]))) for x in final_list], # remove special characters and convert to integer
})
print('rows:', boxoffice_df.shape[0])
print('columns:', boxoffice_df.shape[1])
print('\ndata types:')
print(boxoffice_df.dtypes)
boxoffice_df.head(15)
rows: 15500
columns: 5
data types:
lifetime_gross int64
rank int64
studio object
title object
year int64
dtype: object
life_gross | 秩 | 工作室 | 标题 | 年 | |
---|---|---|---|---|---|
0 | 936662225 | 1 | BV | 星球大战:原力觉醒 | 2015 |
1 | 760507625 | 2 | 狐狸 | 头像 | 2009 |
2 | 681084109 | 3 | BV | 黑豹 | 2018 |
3 | 659363944 | 4 | 依 | 铁达尼号 | 1997 |
4 | 652270625 | 5 | 团结。 | 侏罗纪世界 | 2015 |
5 | 623357910 | 6 | BV | 漫威的复仇者联盟 | 2012 |
6 | 620181382 | 7 | BV | 星球大战:最后的绝地武士 | 2017 |
7 | 534858444 | 8 | WB | 黑暗骑士 | 2008 |
8 | 532177324 | 9 | BV | 侠盗一号:星球大战外传 | 2016 |
9 | 504014165 | 10 | BV | Beauty and the Beast (2017) | 2017 |
10 | 486295561 | 11 | BV | 海底总动员2 | 2016 |
11 | 474544677 | 12 | 狐狸 | 星球大战:第一集-幻影威胁 | 1999 |
12 | 460998007 | 13 | 狐狸 | 星球大战 | 1977 |
13 | 459005868 | 14 | BV | 复仇者联盟:奥创纪元 | 2015 |
14 | 448139099 | 15 | WB | 黑暗骑士崛起 | 2012 |
“星”一词是最热门的电影之一, 它出现在前15部电影中的五部中, 而且你也知道《星球大战》系列有更多电影, 其中有几部也位于电影中。
现在, 让我们利用你开发的代码, 看看它如何在该数据集上工作。下面的代码没有新内容, 你只需将它们全部放在一个函数中:
def word_frequency(text_list, num_list, sep=None):
word_freq = defaultdict(lambda: [0, 0])
for text, num in zip(text_list, num_list):
for word in text.split(sep=sep):
word_freq[word][0] += 1
word_freq[word][1] += num
columns = {0: 'abs_freq', 1: 'wtd_freq'}
abs_wtd_df = (pd.DataFrame.from_dict(word_freq, orient='index')
.rename(columns=columns )
.sort_values('wtd_freq', ascending=False)
.assign(rel_value=lambda df: df['wtd_freq'] / df['abs_freq']).round())
abs_wtd_df.insert(1, 'abs_perc', value=abs_wtd_df['abs_freq'] / abs_wtd_df['abs_freq'].sum())
abs_wtd_df.insert(2, 'abs_perc_cum', abs_wtd_df['abs_perc'].cumsum())
abs_wtd_df.insert(4, 'wtd_freq_perc', abs_wtd_df['wtd_freq'] / abs_wtd_df['wtd_freq'].sum())
abs_wtd_df.insert(5, 'wtd_freq_perc_cum', abs_wtd_df['wtd_freq_perc'].cumsum())
return abs_wtd_df
word_frequency(boxoffice_df['title'], boxoffice_df['lifetime_gross']).head()
abs_freq | abs_perc | abs_perc_cum | wtd_freq | wtd_freq_perc | wtd_freq_perc_cum | rel_value | |
---|---|---|---|---|---|---|---|
的 | 3055 | 0.068747 | 0.068747 | 67518342498 | 0.081167 | 0.081167 | 22100930.0 |
的 | 1310 | 0.029479 | 0.098227 | 32973100860 | 0.039639 | 0.120806 | 25170306.0 |
of | 1399 | 0.031482 | 0.129709 | 30180592467 | 0.036282 | 0.157087 | 21572975.0 |
和 | 545 | 0.012264 | 0.141973 | 12188113847 | 0.014652 | 0.171739 | 22363512.0 |
2 | 158 | 0.003556 | 0.145529 | 9032673058 | 0.010859 | 0.182598 | 57168817.0 |
毫不奇怪, “停用词”是最重要的, 对于大多数文档集合而言, 它们几乎是相同的。你还需要复制它们, 其中一些使用大写字母, 而有些则不使用大写字母。因此, 你需要注意两件事:
- 删除所有停用词:你可以通过向函数添加新参数并提供自己的停用词列表来实现。
- 处理所有小写单词以删除重复项
这是对函数的简单更新(新的rm_words参数以及第6, 7和8行):
# words will be expanded
def word_frequency(text_list, num_list, sep=None, rm_words=('the', 'and', 'a')):
word_freq = defaultdict(lambda: [0, 0])
for text, num in zip(text_list, num_list):
for word in text.split(sep=sep):
# This should take care of ignoring the word if it's in the stop words
if word.lower() in rm_words:
continue
# .lower() makes sure we are not duplicating words
word_freq[word.lower()][0] += 1
word_freq[word.lower()][1] += num
columns = {0: 'abs_freq', 1: 'wtd_freq'}
abs_wtd_df = (pd.DataFrame.from_dict(word_freq, orient='index')
.rename(columns=columns )
.sort_values('wtd_freq', ascending=False)
.assign(rel_value=lambda df: df['wtd_freq'] / df['abs_freq']).round())
abs_wtd_df.insert(1, 'abs_perc', value=abs_wtd_df['abs_freq'] / abs_wtd_df['abs_freq'].sum())
abs_wtd_df.insert(2, 'abs_perc_cum', abs_wtd_df['abs_perc'].cumsum())
abs_wtd_df.insert(4, 'wtd_freq_perc', abs_wtd_df['wtd_freq'] / abs_wtd_df['wtd_freq'].sum())
abs_wtd_df.insert(5, 'wtd_freq_perc_cum', abs_wtd_df['wtd_freq_perc'].cumsum())
abs_wtd_df = abs_wtd_df.reset_index().rename(columns={'index': 'word'})
return abs_wtd_df
from collections import defaultdict
word_freq_df = word_frequency(boxoffice_df['title'], boxoffice_df['lifetime_gross'], rm_words=['of', 'in', 'to', 'and', 'a', 'the', 'for', 'on', '&', 'is', 'at', 'it', 'from', 'with'])
word_freq_df.head(15).style.bar(['abs_freq', 'wtd_freq', 'rel_value'], color='#60DDFF') # E6E9EB
字 | abs_freq | abs_perc | abs_perc_cum | wtd_freq | wtd_freq_perc | wtd_freq_perc_cum | rel_value | |
---|---|---|---|---|---|---|---|---|
0 | 2 | 158 | 0.00443945 | 0.00443945 | 9032673058 | 0.0137634 | 0.0137634 | 5.71688e+07 |
1 | 星 | 45 | 0.0012644 | 0.00570385 | 5374658819 | 0.00818953 | 0.0219529 | 1.19437e+08 |
2 | 男人 | 195 | 0.00547907 | 0.0111829 | 3967037854 | 0.0060447 | 0.0279976 | 2.03438e+07 |
3 | 部分 | 41 | 0.00115201 | 0.0123349 | 3262579777 | 0.00497129 | 0.0329689 | 7.95751e+07 |
4 | 电影 | 117 | 0.00328744 | 0.0156224 | 3216050557 | 0.00490039 | 0.0378693 | 2.74876e+07 |
5 | 3 | 63 | 0.00177016 | 0.0173925 | 3197591193 | 0.00487227 | 0.0427415 | 5.07554e+07 |
6 | ii | 67 | 0.00188255 | 0.0192751 | 3077712883 | 0.0046896 | 0.0474311 | 4.5936e+07 |
7 | 战争: | 6 | 0.000168587 | 0.0194437 | 2757497155 | 0.00420168 | 0.0516328 | 4.59583e+08 |
8 | 持续 | 133 | 0.003737 | 0.0231807 | 2670229651 | 0.00406871 | 0.0557015 | 2.00769e+07 |
9 | 哈里 | 27 | 0.00075864 | 0.0239393 | 2611329714 | 0.00397896 | 0.0596805 | 9.67159e+07 |
10 | me | 140 | 0.00393369 | 0.027873 | 2459330128 | 0.00374736 | 0.0634279 | 1.75666e+07 |
11 | 波特 | 10 | 0.000280978 | 0.028154 | 2394811427 | 0.00364905 | 0.0670769 | 2.39481e+08 |
12 | 黑色 | 71 | 0.00199494 | 0.0301489 | 2372306467 | 0.00361476 | 0.0706917 | 3.34128e+07 |
13 | – | 49 | 0.00137679 | 0.0315257 | 2339484878 | 0.00356474 | 0.0742564 | 4.77446e+07 |
14 | 故事 | 107 | 0.00300646 | 0.0345322 | 2231437526 | 0.00340011 | 0.0776565 | 2.08546e+07 |
让我们看一下基于abs_freq排序的相同DataFrame:
(word_freq_df.sort_values('abs_freq', ascending=False)
.head(15)
.style.bar(['abs_freq', 'wtd_freq', 'rel_value'], color='#60DDFF'))
字 | abs_freq | abs_perc | abs_perc_cum | wtd_freq | wtd_freq_perc | wtd_freq_perc_cum | rel_value | |
---|---|---|---|---|---|---|---|---|
26 | 爱 | 211 | 0.00592863 | 0.069542 | 1604206885 | 0.00244438 | 0.111399 | 7.60288e+06 |
2 | 男人 | 195 | 0.00547907 | 0.0111829 | 3967037854 | 0.0060447 | 0.0279976 | 2.03438e+07 |
24 | my | 191 | 0.00536668 | 0.0618994 | 1629540498 | 0.00248298 | 0.106478 | 8.53163e+06 |
15 | 一世 | 168 | 0.00472043 | 0.0392526 | 2203439786 | 0.00335745 | 0.081014 | 1.31157e+07 |
0 | 2 | 158 | 0.00443945 | 0.00443945 | 9032673058 | 0.0137634 | 0.0137634 | 5.71688e+07 |
10 | me | 140 | 0.00393369 | 0.027873 | 2459330128 | 0.00374736 | 0.0634279 | 1.75666e+07 |
31 | 生活 | 134 | 0.0037651 | 0.0764822 | 1534647732 | 0.00233839 | 0.123297 | 1.14526e+07 |
8 | 持续 | 133 | 0.003737 | 0.0231807 | 2670229651 | 0.00406871 | 0.0557015 | 2.00769e+07 |
23 | you | 126 | 0.00354032 | 0.0565327 | 1713758853 | 0.00261131 | 0.103995 | 1.36013e+07 |
4 | 电影 | 117 | 0.00328744 | 0.0156224 | 3216050557 | 0.00490039 | 0.0378693 | 2.74876e+07 |
14 | 故事 | 107 | 0.00300646 | 0.0345322 | 2231437526 | 0.00340011 | 0.0776565 | 2.08546e+07 |
33 | 晚 | 104 | 0.00292217 | 0.0814555 | 1523917360 | 0.00232204 | 0.127946 | 1.46531e+07 |
19 | 美国的 | 93 | 0.00261309 | 0.0478505 | 1868854192 | 0.00284763 | 0.0932591 | 2.00952e+07 |
117 | 女孩 | 90 | 0.0025288 | 0.151644 | 842771661 | 0.00128416 | 0.26663 | 9.36413e+06 |
16 | 天 | 87 | 0.00244451 | 0.0416971 | 2164198760 | 0.00329766 | 0.0843116 | 2.48758e+07 |
现在, 让我们进行可视化以比较两者并查看隐藏的趋势:
import matplotlib.pyplot as plt
plt.figure(figsize=(20, 8))
plt.subplot(1, 2, 1)
word_freq_df_abs = word_freq_df.sort_values('abs_freq', ascending=False).reset_index()
plt.barh(range(20), list(reversed(word_freq_df_abs['abs_freq'][:20])), color='#288FB7')
for i, word in enumerate(word_freq_df_abs['word'][:20]):
plt.text(word_freq_df_abs['abs_freq'][i], 20-i-1, s=str(i+1) + '. ' + word + ': ' + str(word_freq_df_abs['abs_freq'][i]), ha='right', va='center', fontsize=14, color='white', fontweight='bold')
plt.text(0.4, -1.1, s='Number of times the word was used in a movie title; out of 15500 movies.', fontsize=14)
plt.text(0.4, -1.8, s='Data: boxofficemojo.com Apr. 2018. Feedback: @eliasdabbas', fontsize=14)
plt.vlines(range(0, 210, 10), -1, 20, colors='gray', alpha=0.1)
plt.hlines(range(0, 20, 2), 0, 210, colors='gray', alpha=0.1)
plt.yticks([])
plt.xticks([])
plt.title('Words Most Used in Movie Titles', fontsize=22, fontweight='bold')
# =============
plt.subplot(1, 2, 2)
# plt.axis('off')
plt.barh(range(20), list(reversed(word_freq_df['wtd_freq'][:20])), color='#288FB7')
for i, word in enumerate(word_freq_df['word'][:20]):
plt.text(word_freq_df['wtd_freq'][i], 20-i-1, s=str(i+1) + '. ' + word + ': ' + '$' + str(round(word_freq_df['wtd_freq'][i] / 1000_000_000, 2)) + 'b', ha='right', va='center', fontsize=14, color='white', fontweight='bold')
plt.text(0.4, -1.1, s='Alltime boxoffice revenue of all movies whos title contained the word. (Top word is "2") ', fontsize=14)
plt.text(0.4, -1.8, s='Data collection & methodology: http://bit.ly/word_freq', fontsize=14)
plt.vlines(range(0, 9_500_000_000, 500_000_000), -1, 20, colors='gray', alpha=0.1)
plt.hlines(range(0, 20, 2), 0, 10_000_000_000, colors='gray', alpha=0.1)
plt.xlim((-70_000_000, 9_500_000_000))
plt.yticks([])
plt.xticks([])
plt.title('Words Most Associated With Boxoffice Revenue', fontsize=22, fontweight='bold')
plt.tight_layout(pad=0.01)
plt.show()
似乎至少在制作人和作家的心中, 爱确实征服了所有人!它是所有电影标题中使用最多的单词。但是, 在加权频率(票房收入)方面, 它并不高。
换句话说, 如果你看电影的所有标题, “爱”一词将是你最容易找到的电影。但是估算一下哪个词在观看者眼中出现得最多(使用总收入作为衡量标准), 那么” 2″, ” star”和” man”将是观看次数最多或与收入最高相关的内容。
只是要清楚:这是非常简单的计算。当你说”爱”一词的加权频率为1, 604, 106, 767时, 仅表示标题中包括”爱”一词的所有电影的总生存期总和就是那个数量。
有趣的是, ” 2″是最高单词。显然, 这不是一个字, 而是表明电影系列的第二部分的总和很大。位于第五位的” 3″也是如此。请注意, ” part”和” ii”也在前十名中, 证实了相同的事实。
“美国人”和”电影”具有较高的相对价值。
关于此功能中使用的停用词的快速说明
通常, 与此处的停用词相比, 你会提供更全面的停用词列表, 尤其是在处理文章或社交媒体帖子时。例如, nltk软件包提供了几种语言的停用词列表, 可以下载和使用这些列表。
在对热门电影进行几次检查后, 才选择了此处的字词。其中许多通常被视为停用词, 但对于电影标题而言, 保留其中一些是有意义的, 因为它们可能会提供一些见解。例如, “我”, “我”, “你”一词可能暗示了一些社会动态。另一个原因是电影标题是很短的短语, 因此我们正在努力使它们尽可能地有意义。
你绝对可以用自己的单词来尝试一下, 然后看到稍微不同的结果。
回顾电影标题的原始列表, 我们发现某些热门单词甚至没有出现在前十名中, 而这正是我们试图通过这种方法发现的洞察力。
boxoffice_df.head(10)
life_gross | 秩 | 工作室 | 标题 | 年 | |
---|---|---|---|---|---|
0 | 936662225 | 1 | BV | 星球大战:原力觉醒 | 2015 |
1 | 760507625 | 2 | 狐狸 | 头像 | 2009 |
2 | 681084109 | 3 | BV | 黑豹 | 2018 |
3 | 659363944 | 4 | 依 | 铁达尼号 | 1997 |
4 | 652270625 | 5 | 团结。 | 侏罗纪世界 | 2015 |
5 | 623357910 | 6 | BV | 漫威的复仇者联盟 | 2012 |
6 | 620181382 | 7 | BV | 星球大战:最后的绝地武士 | 2017 |
7 | 534858444 | 8 | WB | 黑暗骑士 | 2008 |
8 | 532177324 | 9 | BV | 侠盗一号:星球大战外传 | 2016 |
9 | 504014165 | 10 | BV | Beauty and the Beast (2017) | 2017 |
接下来, 我认为进一步探索有趣的热门单词将是有意义的。让我们过滤包含” 2″的电影, 然后看:
(boxoffice_df[boxoffice_df['title']
.str
.contains('2 | 2', case=False)] # spaces used to exclude words like '2010'
.head(10))
life_gross | 秩 | 工作室 | 标题 | 年 | |
---|---|---|---|---|---|
15 | 441226247 | 16 | DW | 怪物史莱克2 | 2004 |
30 | 389813101 | 31 | BV | 银河护卫队2 | 2017 |
31 | 381011219 | 32 | WB | 哈利·波特与死亡圣器第二部分 | 2011 |
35 | 373585825 | 36 | 了索尼 | 蜘蛛侠2 | 2004 |
38 | 368061265 | 39 | 团结。 | 卑鄙的我2 | 2013 |
64 | 312433331 | 65 | 依 | 钢铁侠2 | 2010 |
79 | 292324737 | 80 | LG / S | 暮光之城:破晓(第二部分) | 2012 |
87 | 281723902 | 88 | LGF | 饥饿游戏:Mockingjay-第2部分 | 2015 |
117 | 245852179 | 118 | BV | 玩具总动员2 | 1999 |
146 | 226164286 | 147 | NL | 高峰2 | 2001 |
我们还来看看顶级的”明星”电影:
boxoffice_df[boxoffice_df['title'].str.contains('star | star', case=False)].head(10)
life_gross | 秩 | 工作室 | 标题 | 年 | |
---|---|---|---|---|---|
0 | 936662225 | 1 | BV | 星球大战:原力觉醒 | 2015 |
6 | 620181382 | 7 | BV | 星球大战:最后的绝地武士 | 2017 |
8 | 532177324 | 9 | BV | 侠盗一号:星球大战外传 | 2016 |
11 | 474544677 | 12 | 狐狸 | 星球大战:第一集-幻影威胁 | 1999 |
12 | 460998007 | 13 | 狐狸 | 星球大战 | 1977 |
33 | 380270577 | 34 | 狐狸 | 星球大战:第三集-西斯的复仇 | 2005 |
65 | 310676740 | 66 | 狐狸 | 星球大战:第二集-克隆人的攻击 | 2002 |
105 | 257730019 | 106 | 依 | 星际迷航 | 2009 |
140 | 228778661 | 141 | 依 | 星际迷航:进入黑暗 | 2013 |
301 | 158848340 | 302 | 依 | 星际迷航 | 2016 |
最后, 最重要的”男人”电影:
boxoffice_df[boxoffice_df['title'].str.contains('man | man', case=False)].head(10)
life_gross | 秩 | 工作室 | 标题 | 年 | |
---|---|---|---|---|---|
18 | 423315812 | 19 | BV | 加勒比海盗:死人的胸膛 | 2006 |
22 | 409013994 | 23 | BV | 钢铁侠3 | 2013 |
35 | 373585825 | 36 | 了索尼 | 蜘蛛侠2 | 2004 |
48 | 336530303 | 49 | 了索尼 | 蜘蛛侠3 | 2007 |
53 | 330360194 | 54 | WB | 蝙蝠侠对超人:正义曙光 | 2016 |
59 | 318412101 | 60 | 依 | 钢铁侠 | 2008 |
64 | 312433331 | 65 | 依 | 钢铁侠2 | 2010 |
82 | 291045518 | 83 | WB | 钢铁之躯 | 2013 |
176 | 206852432 | 177 | WB | 蝙蝠侠侠影之谜 | 2005 |
182 | 202853933 | 183 | 了索尼 | 超凡蜘蛛侠2 | 2014 |
后续步骤和改进
第一步, 你可能会尝试获得更多的单词:电影标题非常短, 很多时候没有传达这些单词的字面意思。例如, 一个教父应该是一个怀有孩子洗礼仪式的人, 并承诺要照顾那个孩子(或者可能是为娱乐而杀死的黑手党成员?!)。
除电影标题外, 进一步的练习可能是获得更详细的描述。例如:
“一名计算机黑客从神秘的叛乱者那里了解到他的现实的真实本质以及他在与控制器的战争中所扮演的角色。”
告诉我们有关电影的更多信息, 而不是《黑客帝国》。
另外, 你也可以采用以下方法之一来丰富你的分析:
- 更好的统计分析:使用其他指标来处理极值/离群值。
- 文本挖掘:将相似的单词和主题分组在一起(“快乐”, “幸福”, “快乐”等)
- 粒度分析:在不同的年份/十年或某些生产工作室运行相同的功能。
你可能会对自己以及其他数据进行探索:
- 票房数据
- 古腾堡(Gutenberg)下载的书籍排在前1000名, 邀请你提出以下问题:
- 书名中最常用的词是?
- 哪些词与图书下载最相关?
- iPhone搜索关键字(通过SERP获得)
- 人们会与” iphone”一起搜索什么?
- 什么是最加权的频率?
好的, 所以现在你已经探索了电影标题中的单词计数, 并看到了绝对频率和加权频率之间的差异, 以及放任者如何错过图片的很大一部分。你还已经看到了这种方法的局限性, 并对如何改善分析提出了一些建议。
你经历了创建一个特殊功能的过程, 可以轻松运行该功能来分析带有数字的任何相似文本数据集, 并知道这如何改善你对这种数据集的理解。
通过分析你的推文的性能, 网站的URL, Facebook帖子或其他可能遇到的类似数据集来进行尝试。
仅用代码克隆存储库并自己尝试可能会更容易。
word_frequency函数是advertools软件包的一部分, 你可以下载该软件包并尝试在工作/研究中使用。
检查出来, 让我知道! @eliasdabbas
评论前必须登录!
注册