报纸对于男性和女性的描述会有差别吗?快速回答这一问题的一个方式就是直接对比描述男女的语句中的词汇,本节将提供一个使用Python进行这种分析的案例。
我们选取了纽约时报 2013.02.27-2013.03.06 一周内的新闻,从 LexisNexis 数据库下载了除了修正和讣告外可获得的全部文章,总计 1379 篇,平均每天的新闻量在 200 篇左右。
在分析之前,我们利用 Python 对数据进行了如下预处理:移除元数据、将每篇文章的文本存储在单独的文件中,再将所有的文件存储在“ articles ”文件夹中。虽然这种处理方式不是最有效的,但是文本数据常以这种形式出现,所以有必要进行这一操作以便于后续处理方法的讲解。
首先要加载几个模块,而需要下载的工具包只有文本数据处理和分析的强大套件 NLTK 。在本例中,只使用 NLTK 函数将文本分为句子,此外,glob 是检索字典内容的重要模块;string.punctuation 则是一个包含所有ASCII标点符号的字符串,也就是常见的 !"#$%&'()*+,-/:;<=>?@[\]^_{|}~. 。
- from __future__ import division
- import glob
- import nltk
- from string import punctuation
- tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
本例分析的核心目的是:识别某段言论谈论的是男性、女性、男性和女性或者都不是。
作为初始判断,如果一句话出现类似“he”、“dad”或者“Mr.”的词汇,就认为该语句描述的是男性;如果一句话出现类似“she”、“mother”或者“Ms.”的词汇,就认为该语句描述的是女性。
这种分类方式虽然不是最精确的,但是出于文本分析的特性,用该方法进行分类十分有效。为了使这些基础词汇的选定更具科学性,我们参考了 Danielle Sucher 在其浏览器插件 Jailbreak the Patriarchy 提供的词汇列表。
- # 根据 Danielle Sucher(https://github.com/DanielleSucher/Jailbreak-the-
- # Patriarchy),描述男、女时会使用到下面两个分类词汇列表
- male_words=set(['guy','spokesman','chairman',"men's",'men','him',"he's",
- 'his','boy','boyfriend','boyfriends','boys','brother',
- 'brothers','dad','dads','dude','father','fathers','fiance',
- 'gentleman','gentlemen','god','grandfather','grandpa',
- 'grandson','groom','he','himself','husband','husbands',
- 'king','male','man','mr','nephew','nephews','priest',
- 'prince','son','sons','uncle','uncles','waiter','widower',
- 'widowers'])
- female_words=set(['heroine','spokeswoman','chairwoman',"women's",'actress',
- 'women',"she's",'her','aunt','aunts','bride','daughter',
- 'daughters','female','fiancee','girl','girlfriend',
- 'girlfriends','girls','goddess','granddaughter',
- 'grandma','grandmother','herself','ladies','lady','lady',
- 'mom','moms','mother','mothers','mrs','ms','niece',
- 'nieces','priestess','princess','queens','she','sister',
- 'sisters','waitress','widow','widows','wife','wives',
- 'woman'])
可以看到,在这里将分类词汇保存为集合(set)而不是列表,这是因为我们后续需要查看句子中的词汇是否与这些分类词汇重叠,Python会返回集合的交集。
接着我构造了下面的函数,用于定义语句性别分类。这个函数的输入为句子词汇的集合,它能够根据句子中目标词汇出现的频数初步判断句子的类别。
- def gender_the_sentence(sentence_words):
- mw_length=len(male_words.intersection(sentence_words))
- fw_length=len(female_words.intersection(sentence_words))
- if mw_length>0 and fw_length==0:
- gender='male'
- elif mw_length==0 and fw_length>0:
- gender='female'
- elif mw_length>0 and fw_length>0:
- gender='both'
- else:
- gender='none'
- return gender
本例有意忽略了对专有名词的判断,特别是人名(很难讲 'Boehner' 就一定是一个男性的名字),所以需要一个识别出这类词的方式,以把它们提取出来。采用的方法是:记录一个词汇首字母大写和非大写的次数。当我们拥有的文本数据足够多,且不考虑句子首字母一定为大写的情况时,以这种方法来识别专有名词的效果还是相当不错的。
- def is_it_proper(word):
- if word[0]==word[0].upper():
- case='upper'
- else:
- case='lower'
-
- word_lower=word.lower()
- try:
- proper_nouns[word_lower][case]
- = proper_nouns[word_lower].get(case,0)+1
- except Exception,e:
- #当找不到这个词时,下列语句会被触发
- proper_nouns[word_lower] = {case:1}
可以看到,上述函数利用了 .get( ) 来检索专有名词字典存储的值,这是为了避免当字典里不存在相应键值时返回错误信息。
- proper_nouns[word_lower].get(case,0) :当大小写同时存在时,返回 word_lower 的值,否则,返回0。
- 只有当词汇不存在时,except 语句才会被执行。
本例会同时使用两个计数器跟踪记录每句话的每个词汇,这一函数没有任何输出,但是改变了word_freq、 word_counter 和 sentence_counter 字典。
- def increment_gender(sentence_words,gender):
- sentence_counter[gender]+=1
- word_counter[gender]+=len(sentence_words)
- for word in sentence_words:
- word_freq[gender][word]=word_freq[gender].get(word,0)+1
接下来,我们开始准备对感兴趣的目标词汇进行计数统计。我们需要构建 increment_gender 函数中对应的字典,它们其中的一些可能在接下来的分析中并不重要(例如 word_counter 和 sentence_counter ),本例也一起将它们构造出来,并把其中的元素初始化为0,以方便时刻回忆起它们的具体意义。
- sexes=['male','female','none','both']
- sentence_counter={sex:0 for sex in sexes}
- word_counter={sex:0 for sex in sexes}
- word_freq={sex:{} for sex in sexes}
- proper_nouns={}
在做分析之前,将所有要用到的文本语料以 txt 格式存在了一个名为 articles 的文件夹中,为了导入数据,我们需要先获得它们的文件名。
- file_list=glob.glob('articles/*.txt')
我们的基本思路为:
- 逐个导入文件,拆分成句,然后对每一个句子进行分析。
- 具体到每一个句子上,首先处理的流程会包括分词和去掉标点符号。
- 在此基础上,将所有非句首的单词进行大小写检查,确认它是否是一个专属名词。接着,我们开始判断这个句子是否在讨论男人或女人,判断的依据为性别词列表中词汇的出现频数。
- 在分析的最后,将所有属于合适的男性或女性言论中的单词加入对应的词库——例如 "She is lovely." 这个句子被判断为谈论女性的言论,那么就把 'she'、'is' 和 'lovely' 三个词加入女性词库;同时,在上文提及的词汇大小写记录中,'is' 和 'lovely'这两个词对应的非大写次数也会增加。
- for file_name in file_list:
- #打开对应的文件
- text=open(file_name,'rb').read()
-
- #拆分成句
- sentences=tokenizer.tokenize(text)
-
- for sentence in sentences:
- #分词、剔除标点符号
- sentence_words=sentence.split()
- sentence_words=[w.strip(punctuation) for w in sentence_words
- if len(w.strip(punctuation))>0]
-
- #统计词汇大写次数
- [is_it_proper(word) for word in sentence_words[1:]]
- #将词汇转换为小写
- sentence_words=set([w.lower() for w in sentence_words])
-
- #根据性别词列表子集的长度确定句子谈及的性别
- gender=gender_the_sentence(sentence_words)
- #计数统计
- increment_gender(sentence_words,gender)
以上内容转自 数析学院,具体的词频分析过程可以直接查看原文