如何使用tm包在R中计算可读性

6

tm 库中是否有预先构建的函数,或者与之兼容的函数可用?

我的当前语料库已经加载到 tm 中,类似以下内容:

s1 <- "This is a long, informative document with real words and sentence structure:  introduction to teaching third-graders to read.  Vocabulary is key, as is a good book.  Excellent authors can be hard to find." 
s2 <- "This is a short jibberish lorem ipsum document.  Selling anything to strangers and get money!  Woody equal ask saw sir weeks aware decay. Entrance prospect removing we packages strictly is no smallest he. For hopes may chief get hours day rooms. Oh no turned behind polite piqued enough at. "
stuff <- rbind(s1,s2) 
d <- Corpus(VectorSource(stuff[,1]))

我尝试使用 koRpus , 但在不同的软件包中重新分词似乎很愚蠢,我还遇到了问题,无法将其返回对象向量化,以使我能够将结果重新合并到 tm 中。(即,由于错误,它通常会返回比我的集合中的文档数更多或更少的可读性评分。)
我知道我可以通过将元音解析为音节进行朴素计算,但想要一个更彻底的软件包,已经考虑了边缘情况(如处理静默e等)。
我选择的可读性评分是Flesch-Kincaid或Fry。
我最初尝试的方法是,其中d是我的100个文档的语料库:
f <- function(x) tokenize(x, format="obj", lang='en')
g <- function(x) flesch.kincaid(x)
x <- foreach(i=1:length(d), .combine='c',.errorhandling='remove') %do% g(f(d[[i]]))

很不幸,x返回少于100个文档,因此我无法将成功与正确的文档关联起来。(这部分是我对R中foreach和lapply的误解,但我发现文本对象的结构足够困难,以至于我无法适当地进行分词、应用flesch.kincaid,并在一系列的应用语句中成功检查错误。)
更新:
我尝试了另外两件事,试图将koRpus函数应用于tm对象...
  1. Pass arguments into the tm_map object, using the default tokenizer: tm_map(d,flesch.kincaid,force.lang="en",tagger=tokenize)

  2. Define a tokenizer, pass that in.

     f <- function(x) tokenize(x, format="obj", lang='en')
     tm_map(d,flesch.kincaid,force.lang="en",tagger=f)
    

这两个都返回:

   Error: Specified file cannot be found:

然后列出d[1]的完整文本。看起来已经找到了?我应该怎么做才能正确地通过函数?

更新2

当我尝试使用lapply直接映射koRpus函数时,出现了以下错误:

> lapply(d,tokenize,lang="en")
Error: Unable to locate
 Introduction to teaching third-graders to read.  Vocabulary is key, as is a good book.  Excellent authors can be hard to find. 

这似乎是一个奇怪的错误——我几乎不认为它意味着找不到文本,而是找不到一些空白的错误代码(比如“tokenizer”),然后转储定位到的文本。 更新3 使用koRpus重新标记化的另一个问题是,与tm标记器相比,重新标记化非常缓慢,并将其分词进度输出到stdout。无论如何,我尝试了以下操作:
f <- function(x) capture.output(tokenize(x, format="obj", lang='en'),file=NULL)
g <- function(x) flesch.kincaid(x)
x <- foreach(i=1:length(d), .combine='c',.errorhandling='pass') %do% g(f(d[[i]]))
y <- unlist(sapply(x,slot,"Flesch.Kincaid")["age",])

我想要做的是将上面的y对象重新绑定到我的tm(d)语料库作为元数据,meta(d, "F-KScore") <- y

不幸的是,应用于我的实际数据集时,出现了错误消息:

Error in FUN(X[[1L]], ...) : 
  cannot get a slot ("Flesch.Kincaid") from an object of type "character"

我认为我的语料库中必须有一个NA元素,或者是太长,或者是其他禁止因素 --- 由于嵌套的函数化,我很难确定具体是哪一个。目前看来,似乎没有预构建的函数可以很好地与tm库一起使用来读取分数。除非有人看到了一种简单的错误捕捉解决方案,我可以将其嵌入到我的函数调用中以处理无法标记某些明显错误的形式不规范的文档。

你不能使用koRpus中的flesh.kincaid和tm中的tm_map吗? - Tyler Rinker
我似乎无法做到。对于我能想到的每个tm_map(dd,flesch.kincaid)变体,例如tm_map(dd,flesch.kincaid, "en")等,它都会显示“错误:未指定语言!” - Mittenchops
因此,我参考了另一个关于如何将参数传递给嵌套函数的SO问题(https://dev59.com/Ymw15IYBdhLWcg3wGn9c)。我尝试了这个 tm_map(d,flesch.kincaid,force.lang="en",tagger=tokenize) 但是出现错误,无法找到“指定的文件”,然后输出文档1的内容。 - Mittenchops
3个回答

4
您会收到错误提示,因为koRpus函数无法处理corpus对象。最好创建一个kRp.tagged对象,然后在其上应用所有的koRpus功能。我将展示如何使用tm包中的ovid数据来执行此操作。
我使用list.files获取我的源文件列表。您只需要提供正确的路径即可访问源文本文件。
ll.files <- list.files(path = system.file("texts", "txt", 
                                    package = "tm"),
                 full.names=T)

然后我使用tokenize构建了一个kRp.tagged对象列表,这是koRpus包提供的默认标记器(推荐使用TreeTagger,但需要安装它)。

ll.tagged <- lapply(ll.files, tokenize, lang="en") ## tm_map is just a wrapper of `lapply`

当我有了我的“标记”对象列表后,我可以在其上应用可读性公式。由于flesch.kincaidreadability的包装器,因此我将直接应用后者:

ll.readability <- lapply(ll.tagged,readability)          ## readability
ll.freqanalysis <- lapply(ll.tagged,kRp.freq.analysis)   ## Conduct a frequency analysis
ll.hyphen <- lapply(ll.tagged,hyphen)                    ## word hyphenation

等等,...所有这些都会产生一个S4对象列表。 desc槽提供了访问此列表的简单方法:

lapply(lapply(ll.readability ,slot,'desc'),              ## I apply desc to get a list
         '[',c('sentences','words','syllables'))[[1]]    ## I subset to get some indexes
[[1]]
[[1]]$sentences
[1] 10

[[1]]$words
[1] 90

[[1]]$syllables
all  s1  s2  s3  s4 
196  25  32  25   8 

例如,您可以使用插槽hyphen获取一个包含两列(单词(连字号分隔的单词)和音节数)的数据框。在此示例中,我使用lattice将所有数据框绑定在一起,以便为每个文档绘制dotplot

library(lattice)
ll.words.syl <- lapply(ll.hyphen,slot,'hyphen')     ## get the list of data.frame
ll.words.syl <- lapply(seq_along(ll.words.syl),      ## add a  column to distinguish docs
       function(i)cbind(ll.words.syl[[i]],group=i))
dat.words.syl <- do.call(rbind,ll.words.syl)
dotplot(word~syll|group,dat.words.syl,
        scales=list(y=list(relation ='free')))

enter image description here


这是一个非常好的回答,很有帮助,但如果我拥有的格式不像ovid文本文件一样存储在本地,我仍然不明白如何进行标记化并将其与tm对象相关联。我已经使用该库进行了其他分析,因此即使我完全切换到koRpus进行此算法,我仍然需要一种将koRpus可读性分数与那些实体关联起来的方法。 - Mittenchops
@Mittenchops 谢谢!好的,但是它们在哪里?如果分数是按文档计算的,将其放入您的tm对象中作为元数据之类的东西很容易,例如 meta(doc[i],'scoree') <- score... - agstudy
谢谢,@agstudy。我在上面添加了一个更新。生成语料库的过程比我从数据源获取的要复杂得多,但我需要来回转换成那种格式。阻止我之前最早的解决方案的问题是,如果有空字符串文档,则阅读级别分数会将其删除,我无法正确地映射出tm,也无法正确地映射回去。d是我要开始处理的对象。 - Mittenchops
@Mittenchops,我还是不明白为什么你不能将 d <- Corpus(VectorSource(stuff[,1])) 保存在一个文件中,然后像上面那样继续进行呢? - agstudy
我得检查一下我是否知道如何做。=)这样做会丢失我的元数据吗?我一直在从csv文件中提取,所以以前没有使用过tm进行直接IO。如果是内存的话,保持在那里似乎更清晰,而不是只写出来再读回来,对吧? - Mittenchops
1
在理论上是可以的...但你假设这两个程序包必须以一种协调的方式一起工作..也许你期望太高了?话虽如此,您可以在连接(流)中保存。如果我有更多时间,我会详细说明。 - agstudy

3
抱歉,koRpus包与tm包的交互还不够顺畅。我已经思考了数月如何在两个对象类之间进行翻译,但尚未找到令人满意的解决方案。如果您有任何想法,请随时与我联系。
但是,我想向您介绍koRpus生成的可读性对象的summary()方法,它返回一个紧凑的相关结果的data.frame。这可能比通过相当复杂的S4对象进行爬行要容易得多;-) 您还可以尝试summary(x, flat=TRUE)
@agstudy:漂亮的图表 :-) 为节省时间,您应该在运行readability()之前运行hyphen(),以便可以通过“连字符”参数重复使用结果。或者,您可以在readability()结果之后简单地访问“连字符”插槽。如果需要,它会自动断字并保留结果。只有在需要更改hyphen()的输出以进行下一步操作时,才需要手动调用连字符。我要强调的是,0.05-1比其前身要快得多。

1
我之前没有看到你的评论!我猜想你是“koRpus”包的作者。加一! - agstudy

2
截至 qdap版本1.1.0,qdap有数量函数,以更好地与tm包兼容。以下是使用您提供的相同Corpus解决问题的方法(请注意,Fry最初是一种图形化测量方法,qdap保留了这一点;另外,通过您的Corpus和随机抽样,Fry建议您的样本Corpus不够大,无法计算Fry's):
library(qdap)
with(tm_corpus2df(d), flesch_kincaid(text, docs))

##   docs word.count sentence.count syllable.count FK_grd.lvl FK_read.ease
## 1   s1         33              1             54       16.6       34.904
## 2   s2         49              1             75       21.6       27.610

with(tm_corpus2df(d), fry(text, docs))

## To plot it
qheat(with(tm_corpus2df(d), flesch_kincaid(text, docs)), values=TRUE, high="red")

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接