NLP中的圣经(上)

2023-01-02 12:47 601 阅读 ID:653
磐创AI
磐创AI

我想到了一个有趣的数据科学项目,将NLP技术应用于圣经。圣经可以从Gutenberg项目免费下载文本格式https://www.gutenberg.org/cache/epub/1581/pg1581.txt(Gutenberg许可证)

杜埃·莱姆的《圣经》是从拉丁文俗语翻译成英语的,共有74本书。圣经的第一部分是《旧约》,由46本书组成,讲述了以色列人的历史(《摩西五经》)、《智慧书》和《先知书》。第二部分是《新约》,28本书,讲述了基督及其门徒的历史以及直到天启的地球未来。这部分的主要书籍是马可福音、马太福音、路加福音、约翰福音和启示录。

这个项目希望成为NLP世界的一个温和的介绍,带领读者完成每一步,从清理原始输入数据到获取和理解书籍的显著特征。

在第一部分中,我们将讨论:数据清理、语料库数据提取和Zipf定律。

环境

这是两篇文章中使用的所有软件包:

import re
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords

import glob
import string
import gensim

from gensim.corpora import Dictionary
from gensim.utils import simple_preprocess
from gensim.models import CoherenceModel, Phrases, LdaModel
from gensim.models.ldamulticore import LdaMulticore

import pandas as pd
from num2words import num2words
import numpy as np
from wordcloud import WordCloud
from sklearn.feature_extraction.text import CountVectorizer

import seaborn as sbn 
from matplotlib.pyplot as plt

from scipy.stats import hmean
from scipy.stats import norm

预处理

预处理是第一件事——我们应该永远记住!在这里,原始输入文件被清理,书籍被细分为单个文件。

我做了一个简单的手工清理,取出了每本书的介绍、标题,以及最后的Gutenberg结尾免责声明。

下面显示了我如何将所有书籍细分为单个文件。使用.readlines()将输入文件作为单个字符串读取。《旧约全书》的书都在old_books中,multinames列表表示所有有多个版本的书(如The First Book of Kings, The First Book of Paralipomenon等)。

import os 
output = "books"
if not os.path.exists(output):
    os.makedirs(output)

lines = open("cleaned_bible.txt", "r").readlines()

books = [] 
books_idx = {}
# there are books which are First of, second of etc 
# we need to pick up their names 
multinames = ["Kings", "Paralipomenon", "Esdras", "Machabees",
              "Corinthians","Thessalonians", "Timothy", "Peter", "John"]
# then collect the name of the old testament books
old_books = ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy","Josue", 
             "Judges", "Ruth", "1Kings", "2Kings", "3Kings", "4Kings", 
             "1Paralipomenon","2Paralipomenon", "1Esdras", "2Esdras", 
             "Tobias", "Judith", "Esther", "Job", "Psalms", "Proverbs", 
             "Ecclesiastes", "Canticle", "Wisdom", "Ecclesiasticus", "Isaias", 
             "Jeremias", "Lamentations", "Baruch", "Ezechiel", 
             "Daniel", "Osee", "Joel", "Amos", "Abdias", "Joans", "Micheas",
             "Nahum", "Habacuc", "Sophonias", "Aggeus", "Zacharias", "Malachias", 
             "1Machabees", "2Machabees"]

for i, val in enumerate(lines, 0):
    # retireve all the chapters 
    if "Chapter" in val:
        book_name = val.split()[0]
        possible_further_name = val.split()[1]

        if possible_further_name in multinames: 
            current_book_name = book_name + possible_further_name 
        else: 
            current_book_name = book_name

        if not current_book_name in books:        
            print(f"Adding {current_book_name} to books, starting idx {i}")
            if i==0:
                tmp_book = current_book_name 
            else:
                books_idx[tmp_book].append(i)

            tmp_book = current_book_name
            books.append(current_book_name)
            books_idx[current_book_name] = [i]

对于以Chapter开头的每一行,我们提取book_name。对于multinames书籍,我们在每一章的开头都有模式book_name Chapter 1,因此我们从行中提取下一个单词val.split()[1]获取名字。

Book_Name Chapter 1的第一次出现标记了该书的起始索引。当current_book_name与存储的tmp_book名称不同时,为结束索引。因此,字典books_idx包含书名、起始索引和结束索引(例如{"Genesis":[0, 5689], "Exodus":[5689, 10258], … })。

然后,我们可以从索引中选择原始行中的所有书籍,并将它们保存到txt文件中

如果没有不同的说明,输入文本已经以这种方式清理,并作为字符串存储在数据列表中:

data = [] 
ifiles = glob.glob("books/*.txt")
for ifile in ifiles: 
    book = open(ifile, "r").read().strip() 
    data.append(book)

stop_words = stopwords.words('english')
stop_words.extend(["thy","thou","thee", "hath", "upon", "me", "him", "them", "shall","ye", "one", "unto", "us"])


def remove_stopwords(text, stop_words):
    outtext = ' '.join([word for word in text.split() if word not in stop_words])
    return outtext


for i, book in enumerate(data, 0):
    # remove NUMBER:NUMBER. pattern at the beginning
    data[i] = re.sub(r"\d{1,}\:\d{1,}\.", "",data[i])

    # remove NAME Chapter NUMBER 
    data[i] = re.sub(r"\w{1,} Chapter \d{1,}","",data[i] )

    #lower case 
    data[i] = data[i].lower() 

    # remove punctuation 
    data[i] = data[i].translate(str.maketrans('', '', string.punctuation))

    # remove new lines 
    data[i] = re.sub('\s+', " ", data[i]) 

    # remove new line
    data[i] = re.sub(r"\\n", " ", data[i])

    # remove stopwords 
    data[i] = ' '.join([word for word in data[i].split() if word not in stop_words]) #remove_stopwords(data[i], stop_words)

对于每本书,用re和正则表达式r"\d{1,}\:\d{1,}\.删除相关数字。最后,所有文本都转换为小写,标点符号和停用词都被删除。

获得初步想法:wordcloud

wordcloud是对圣经书中的主题和反复出现的单词有初步了解的完美起点。

Wordcloud以人类可理解的方式显示文本中出现的最频繁的单词。所有圣经的书都可以压缩成一个big_string,并生成一个词云:

for book in data:
    big_string += book + " "

wordcloud = WordCloud(width=1600, height=800,max_font_size=200).generate(big_string)

plt.figure(figsize=(12,10))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")

plt.show()

下面显示了所有书籍的WordClouds。可以立即找到主题

我们可以做得更多,为每本书提取一个单词云。下面显示了《旧约》前三本书《创世纪》、《出埃及记》和《利未记》中的文字云对比。

我们可以清楚地认识到主要因素和差异。《创世纪》以地球和上帝的主题开始,上帝对亚伯拉罕说不要吃树上的果实。从那里我们可以定义亚伯拉罕的其他后裔,如约瑟夫、以撒。

在《出埃及记》中,主要人物是摩西以及埃及法老。最后,在《利未记》中主要主题是大屠杀,所以我们会有诸如祭祀、血、牺牲品、肉和献祭等词汇

我将离开这里,让你分析和看看福音书中的词云的差异,并欣赏福音书、《创世纪》和《启示录》之间的语调差异(这是最后一本圣经的书,可能是在基督100年后写成的)。

数字符号

因为圣经来自口头传统,所以有很多重复的模式。此外,还有很多象征意义。每个词在特定的上下文中都有其含义。这同样适用于数字。数字用来表达一个概念,例如“七倍多”或“七十七倍”通常用来表示“很多”,并构成一个独特而有趣的特征。此外,在文本中查找数字是学习正则表达式的好方法,也是获取数字的小技巧。

任务是在文本中找到简单的英文数字。第一种简单的方法是将整数转换为字符串,将文本拆分为空格以形成拆分列表,并计算每个拆分列表的实际出现次数。

下面总结了这种方法。首先,我们将数字转换为带有2个单词的字符串,并将它们存储在列表中。数据清理后(如上所述),我们可以统计每本书中出现的每个数字。

在GoogleColab笔记本上,这个过程大约需要12秒来处理100k个数字。然而,到目前为止,我们可以立即看到,最终字典中的大多数数字都出现了0次。

此外,找到的大多数数字都在0到20之间。这是由于我们如何拆分字符串,以及一些数字如何转换为字符串

numbers = [] 
for i in range(0,100001): 
    # convert a number to string
    numbers.append(num2words(i))

# clean the book and obtain the data list

# for each book collect the number occurrences
numb_dict = {}
for i, book in enumerate(data, 0):

    # split the string from spaces
    splitter = book.split() 
    book_name = book_list[i]
    for number in numbers:

        # count the occurences for a specific number 
        occurrences = splitter.count(number)

        # add this to a counter in the final dictionary
        if book_name in numb_dict:
            numb_dict[book_name][number] = occurrences
        else:
            numb_dict[book_name] = {number:occurrences}

我们可以使用Python模块re和构建正则表达式来改进这种方法。我不想教你如何创建正则表达式,有大量的源代码,背诵如何创建正则函数通常是无用的。我想把我们的注意力集中在如何处理这个过程上。

这个想法是创建一个正则表达式,它可以匹配我们在给定书中的所有数字。正则表达式的核心部分可能类似于\b(numb1|numb2|numb3)\b。

下面显示了一个字符串的测试,该字符串包含以下数字:1、20000、3034、3545。因此,我们可以构建一个包含数字的正则表达式,并使用re.findall(regex, test_text)对测试字符串运行。

超级简单,但结果却大错特错,因为找到的数字是1、20、3、34、3、5、45。正如你所看到的,一旦找到匹配,regex就停止了。这不是我们想要的。我们可以做一些更隐蔽的事情,颠倒正则表达式的顺序,在开头使用“罕见”数字,在结尾使用小的“频繁”数字。正如你现在看到的,结果是正确的,返回了我们想要的数字。

# this is a test string with numbers to be found
test_text = "this is a string with one number and then twenty thousand numbers and three thousand thirty four and three thousand five hundred forty five numbers"

# firstly we could think of a simple regex to match numbers
regex = r"\b(three thousand five hundred forty five|three thousand thirty four|twenty thousand|three thousand|forty five|thirty four|twenty|five|four|three|two|one)\b"
re.findall(regex, test_text)
# the result is not we were expecting 

# recalibrate the order from "rare" numbers to "frequent" ones
regex = r"\b(three thousand five hundred forty five|three thousand thirty four|twenty thousand|three thousand|forty five|thirty four|twenty|five|four|three|two|one)\b"
re.findall(regex, test_text)

# better result, we got what expected, can we do even better?

有了这个方案,我们现在可以在圣经中查找数字,对从num2words转换而来的数字添加一点预处理,如图8所示。正则表达式在2秒内创建,而运行匹配位需要1分钟30秒。在这种情况下,最终的字典match_dict将包含比我们以前的方法更重要和有用的信息,因为每本书都会记录正确的数字,避免了大量的0值。

然而,创建像这样的正则表达式是很痛苦的,而且当我们必须运行匹配过程时效率不高。这里有个网站:https://www.rexegg.com/regex-trick-numbers-in-english.html来帮忙

regex = r"\b("

remaining_numbers = []
remaining_numbers2 = []
for i in range(10000, 1000, -1):
    if i%10==0:
        # these are numbers that could cause false matching 
        # these numbers ends with zero but we may have something more in the text 
        # three thousand forty (3040) vs three thousand forty one
        remaining_numbers.append(i)
    elif (i%100 > 1) and (i%100<10):
        # these are numbers that could cause false matching
        # e.g. three thousand four (3004) vs three thousand four hundred
        remaining_numbers2.append(i)
    else:
        current_number = num2words(i)
        current_number = re.sub(" and ", " ", current_number)
        splitter = current_number.split(",")
        numb = ""
        for splitt in splitter: 
            dash_split = splitt.split("-")
            for elem in dash_split:
                for subelem in elem.split():
                    if subelem==" ":
                        continue 
                    else:
                        numb+= rf"\s{subelem.strip()}"

        regex+=f"{numb}"
        regex+=r"|"

# sort the number in ascending order, from Rare to Frequent
remaining_numbers.sort(reverse=True)
for val in remaining_numbers:
    current_number = num2words(val)
    current_number = re.sub(" and ", " ", current_number)
    splitter = current_number.split(",")
    numb = ""
    for splitt in splitter: 
        dash_split = splitt.split("-")
        for elem in dash_split:
            for subelem in elem.split():
                if subelem==" ":
                    continue 
                else:
                    numb+= rf"\s{subelem.strip()}"

    regex+=f"{numb}"
    regex+=r"|"

# add the remaining nubmers which ends in 1-9
remaining_numbers2.sort(reverse=True)
for val in remaining_numbers2:
    current_number = num2words(val)
    current_number = re.sub(" and ", " ", current_number)
    splitter = current_number.split(",")
    numb = ""
    for splitt in splitter: 
        dash_split = splitt.split("-")
        for elem in dash_split:
            for subelem in elem.split():
                if subelem==" ":
                    continue 
                else:
                    numb+= rf"\s{subelem.strip()}"

    regex+=f"{numb}"
    regex+=r"|"


# add the final numbers 
for i in range(1000, -1, -1):
    current_number = num2words(i)
    current_number = re.sub(" and ", " ", current_number)
    splitter = current_number.split(",")
    numb = ""
    for splitt in splitter: 
        dash_split = splitt.split("-")
        for elem in dash_split:
            for subelem in elem.split():
                numb+= rf"\s{subelem.strip()}"

    regex+=f"{numb}"

    if i==0:
        regex+=r")\b"
    else:
        regex+=r"|"

# run the query - pick the book name from the books
match_dict = {}
for i, val in enumerate(glob.glob("books/*.txt"),0):
    book_name = val.split("/")[-1].split(".")[0]
    match_dict[book_name] = {}
    matches = re.findall(regex, data[i])
    for numb in matches:
        if numb.strip() in match_dict[book_name]:
            match_dict[book_name][numb.strip()]+=1 
        else:
            match_dict[book_name][numb.strip()] = 1

下面显示了我们可以创建一个正则表达式来匹配简单的英文数字,只需简单地使用字面数字结构,例如f(?:ive|our),并定义从0到万亿的数字,而无需在内存或其他进程中存储任何内容。正则表达式创建过程大约需要300微秒,而匹配过程大约需要4秒!

one_to_9 = r"(?:f(?:ive|our)|s(?:even|ix)|t(?:hree|wo)|(?:ni|o)ne|eight)" # end one_to_9 definition

ten_to_19 = r"(?:(?:(?:s(?:even|ix)|f(?:our|if)|nine)te|e(?:ighte|lev))en|t(?:(?:hirte)?en|welve))" # end ten_to_19 definition

two_digit_prefix = r"(?:(?:s(?:even|ix)|t(?:hir|wen)|f(?:if|or)|eigh|nine)ty)" # end two_digit_prefix definition

one_to_99 = fr"(?:{two_digit_prefix}(?:[-\s]{one_to_9})?|{ten_to_19}|{one_to_9})" # end one_to_99 definition

one_to_999 = fr"(?:{one_to_9}\shundred(?:\s(?:and\s)?{one_to_99})?|{one_to_99})" # end one_to_999 definition

one_to_999_999 = fr"(?:{one_to_999}\sthousand(?:\s{one_to_999})?|{one_to_999})" # end one_to_999_999 definition

one_to_999_999_999 = fr"(?:{one_to_999}\smillion(?:\s{one_to_999_999})?|{one_to_999_999})" # end one_to_999_999_999 definition

one_to_999_999_999_999 = fr"(?:{one_to_999}\sbillion(?:\s{one_to_999_999_999})?|{one_to_999_999_999})" # end one_to_999_999_999_999 definition

one_to_999_999_999_999_999 = fr"(?:{one_to_999}\strillion(?:\s{one_to_999_999_999_999})?|{one_to_999_999_999_999})" # end one_to_999_999_999_999_999 definition

bignumber = fr"(?:zero|{one_to_999_999_999_999_999})" # end bignumber definition

zero_to_9 = fr"(?:{one_to_9}|zero)" # end zero to 9 definition

decimals = fr"point(?:\s{zero_to_9})+" # end decimals definition

numeral_pattern = fr"{bignumber}(?:\s{decimals})?"

re.findall(numeral_pattern, data)

正如你所看到的,最后一种方法是获胜的方法。它更加便携,不需要额外的数字处理,而且比我们的第一种方法更高效,可以返回完全正确的信息。

如果我们分析最后一本匹配的字典,我们可以看到《数字》是——不出所料——数字最多的一本书,632。第二本是《创世纪》,508,而《启示录》有323个数字,比《新约》中的任何一本书都多,其中7个重复了76次,1个重复了72次。圣经中最高的数字是800000,出现在《撒母耳记》第二卷,24:9(旧约):

And Joab gave up the sum of the number of the people to the king,
and there were found of Israel eight hundred thousand valiant men that
drew the sword: and of Juda five hundred thousand fighting men.

从输出字典中,我们可以通过pandas比较所有结果。DataFrame并将其显示在直方图中:

df = pd.DataFrame.from_dict(match_dict)

#Extract a subset from the books we want to analyse
books = ['Genesis','Exodus','1Kings','2Kings','Psalms','Isaias','Mark','Matthew','Luke','John','Apocalypse']

# drop NaN, so where occurrences = 0, so we can have a comparison of the common set of numbers
# transpose the dataframe so  numbers will be the name of the columns
subset = df[book].dropna().T

# melt the dataframe
subset = subset.reset_index().melt(id_vars='index')

# rename columns 
subset.rename(columns={"index":"Book", "variable":"Number","value":"Occurrence"},inplace=True)

# display 
fig, ax = plt.subplots(figsize=(15,15))
sns.set_theme(style="white", context="talk")
sns.barplot(data=subset, x="Occurrence", y="Number", hue="Book",palette="deep", ax=ax)

我们可以比较圣经的主要书籍,即《创世纪》、《出埃及记》、《撒母耳记》的第一和第二本(《列王纪》、《圣经》)、《诗篇》、《以赛亚书》、《福音书》:马可福音、马太福音、路加福音、约翰福音和最后一本《启示录》

10、1、7、2、3、4和6是主要圣经书籍中常用的数字。特别是,有趣的是注意到《创世记》(第一本)和《启示录》(最后一本)中七个字母的用法

齐普夫定律

以美国语言学家乔治·金斯利·齐普夫命名的齐普夫定律将单词的频率与其等级联系起来。特别是,在文本语料库中,任何单词的频率与其频率等级成反比。或多或少,最频繁的单词出现的频率是第二频繁单词的两倍,是第三频繁单词的三倍,以此类推。将这一规律转化为对数域,结果是一个线性图,它是单词等级和单词频率的函数。从这里我们来看看圣经语料库是如何出现在齐夫定律中的。

图12显示了如何计算每本圣经的词频和排名。首先,我们启动sklearn.feature_extraction.text。CountVectorizer()计算每本书中出现的所有单词。最后的矩阵计数all_df可以转换为pandas.DataFrame,将所有出现的单词相加。最终的数据帧term_freq_df将包含单词作为索引和列,这是语料库中单词的总数。从那里,可以在对数域上根据Zipf定律比较单词的分布。

# preprocessing on data
# data is a list of all the Bible's books 

# call the CountVectorizer
cvec = CountVectorizer()

# fit transform as we're working directly on all the corpus
cvec.fit_transform(data)

# np matrix sparse
all_df = cvec.transform(data)

# create a dataframe: sum on all the term occurrences
tf = np.sum(all_df,axis=0)

# remove an axis from the tf
tf2 = np.squeeze(np.asarray(tf))

# thus we can transform it as a Dataframe
term_freq_df = pd.DataFrame([tf2],columns=cvec.get_feature_names()).transpose()

# create the plot
# 0 is the counts
counts = term_freq_df[0]

# index the words
tokens = term_freq_df.index

# ranks is the position of the word 
ranks = np.arange(1, len(counts)+1)
indices = np.argsort(-counts)

# grab the frequencies
frequencies = counts[indices]

# plot figure
plt.figure(figsize=(15,15))

# set limits
plt.ylim(1,10**4.1)
plt.xlim(1,10**4.1)

# log log plot
plt.loglog(ranks, frequencies, marker=".")

# draw a line to highligh zipf's expected behaviour
plt.plot([1,frequencies[0]],[frequencies[0],1],color='r')
plt.xlabel("Frequency rank of token", fontsize=20)
plt.ylabel("Absolute frequency of token", fontsize=20)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.grid(True)

# add the text
for n in list(np.logspace(-0.5, np.log10(len(counts)-2), 25).astype(int)):
    dummy = plt.text(ranks[n], frequencies[n],
                     " " + tokens[indices[n]],
                     verticalalignment="bottom",
                     horizontalalignment="left",
                     fontsize=20)

下图显示了最终绘图结果。圣经稍微偏离了Zipf的线性。这不是一个大惊喜。事实上,在圣经中有许多重复的单词,在自然语言中很少使用。有趣的是,在《撒母耳记》第二卷20:9中,“chin”一词只使用过一次:

And Joab said to Amasa: God save thee, my brother. And he took
Amasa by the chin with his right hand to kiss him.

从这里,我们可以深入了解圣经中的单词用法,以便看到主要书籍之间的差异。首先,整本圣经中经常出现的主要词汇是:

  • lord出现了8377次
  • god出现了5720次
  • man出现2999次

有趣的是,大卫被使用的次数比基督或耶稣多,分别为1162、992和1028次,而children一词被使用了1915次。

接下来,我们可以检查新约和旧约之间的词语差异。表1显示了旧约圣经(旧T%)和新约圣经(新T%)中单词出现的百分比。我们可以看到主、以色列、国王、人在旧约中的主要用法,以及他们在新约中对上帝、耶稣、基督、父亲和人的翻译/转化。

我们可以通过研究单词的出现情况,从术语上探讨这种差异。下面的代码显示了如何将新旧约的单词出现率与总比率和新旧类比率的正态累积分布函数的调和平均值相关联。这是什么意思?我们可以立即找到一些独特的词语,这些词语主要是旧约书和新约书的特点:

  • 首先,我们计算单词出现率,即单词出现率termfreq_df[0]表示出现的总次数term_freq_df['total'] → old_rate
  • 类似地,我们计算特定类的单词率,即用termfreqdf[0]细分单词的出现。term_freq_df[0].sum() → old_freq_pct
  • 我们计算old_rate和old_freq_pct之间的类调和平均值
  • 从这里我们计算old_rate和old_freq_pct的正态累积分布函数→ old_rate_normcdf和old_freq_pct_normcdf
  • 最后,我们计算oldratenormcdf和oldfreqpctnormcdf之间的调和平均值,以便为给定类的唯一和特定单词赋予更多权重

这种方法主要来自RickyKim的文章:https://medium.com/@rickykim78

def normcdf(x):
    return norm.cdf(x, x.mean(), x.std())

# compute the rate of a word: word_occurrence_old_test/total
term_freq_df.loc[:,'old_rate'] = term_freq_df[0] * 1./term_freq_df['total']

# rate the word appear in a class, in this case old testament word_occurrence_old_test/total_old_test
term_freq_df.loc[:,'old_freq_pct'] = term_freq_df[0] * 1./term_freq_df[0].sum()

# combine the total rate and the class rate with the harmonic mean, to weight over most unique and specific words
term_freq_df.loc[:,'old_hmean'] = term_freq_df.apply(lambda x: (hmean([x['old_rate'], x['old_freq_pct']]) if x['old_rate'] > 0 and x['old_freq_pct'] > 0 else 0), axis=1)

# where old_rate or old_freq_pct lies in the distribution in terms of cumulative manner. 
term_freq_df.loc[:,'old_rate_normcdf'] = normcdf(term_freq_df['old_rate'])
term_freq_df.loc[:,'old_freq_pct_normcdf'] = normcdf(term_freq_df['old_freq_pct'])

# take the harmonic mean of the cumulative distribution of both to get the real different words
term_freq_df.loc[:,'old_normcdf_hmean'] = hmean([term_freq_df['old_rate_normcdf'], term_freq_df['old_freq_pct_normcdf']])


term_freq_df.loc[:,'new_rate'] = term_freq_df[1] * 1./term_freq_df['total']
term_freq_df.loc[:,'new_freq_pct'] = term_freq_df[1] * 1./term_freq_df[1].sum()
term_freq_df.loc[:,'new_hmean'] = term_freq_df.apply(lambda x: (hmean([x['new_rate'], x['new_freq_pct']]) if x['new_rate'] > 0 and x['new_freq_pct'] > 0 else 0), axis=1)                                      
term_freq_df.loc[:,'new_rate_normcdf'] = normcdf(term_freq_df['new_rate'])
term_freq_df.loc[:,'new_freq_pct_normcdf'] = normcdf(term_freq_df['new_freq_pct'])
term_freq_df.loc[:,'new_normcdf_hmean'] = hmean([term_freq_df['new_rate_normcdf'], term_freq_df['new_freq_pct_normcdf']])

如果你画出old_normcdf_hmean和new_normcdf _hmeman,你会看到下图,它在左上角突出显示了所有独特的单词,这些单词主要用于《旧约》,而在右下角则专门用于《新约》。

从这里,我们可以立即看到新约和旧约之间的语调对比,表2。在旧约中,更流行的“战争”术语,如勇敢,故事的特点是“农村”社会。新约强调新信仰,强调天使、慈善和信仰的概念

作为最后一件事,上面的分析可以对所有4本福音书进行,突出每个福音书的特征词。福音书在不同的时代写成,最古老的是马可福音,其次是马特福音、路加福音和约翰福音。接下来会有不同的写作风格,以及每一种语言中使用的更具体的词语。约翰似乎是最“天主教”的福音,用词指代当今的教会:肉身、荣耀、圣洁、真理。我们将在下一篇文章中回到这个轨道:)

第1部分结论

让我们回顾一下我们今天所做的:

  • 我们学习了如何预处理原始文本输入,为NLP分析做好准备,并将其保存到不同的文件中
  • 为了初步了解我们的数据,我们运行了WordCloud。从这里,我们能够识别圣经书中的不同主题和主题
  • 我们看到了如何解决正则表达式问题,用简单的英语找到一系列数字,以及如何优化最终的解决方案
  • 最后,我们对照《圣经》的语料库测试了齐普夫定律,并扩展了这一分析,为四部福音书中的每一部定义了独特的词汇

下次我们将运行主题建模、单词嵌入和文本嵌入以及文本相似性。

免责声明:作者保留权利,不代表本站立场。如想了解更多和作者有关的信息可以查看页面右侧作者信息卡片。
反馈
to-top--btn