首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何总结单词频率后,堵塞在拍子?

如何总结单词频率后,堵塞在拍子?
EN

Stack Overflow用户
提问于 2021-10-26 21:38:29
回答 1查看 95关注 0票数 1

作为背景,我试图在球拍中做一个NLP应用程序,我到达了我必须阻止单词的部分(我也获得了它们的频率)。

我使用(planet dyoo/porter-stemmer)包来阻止,举个例子,我们可以写:

代码语言:javascript
复制
(map (λ(x) (list (stem (first x)) (second x)))
     '(("cryed" 1)
       ("racketeer" 2)
       ("crying" 3)
       ("playing" 4)
       ("racketing" 5)
       ("plays" 6)
       ("Racket" 7)))

生产:'(("cry" 1) ("racket" 2) ("cry" 3) ("plai" 4) ("racket" 5) ("plai" 6) ("racket" 7))

现在我的目标是总结每个学期的频率,也就是到达:'(("cry" 4) ("racket" 14) ("plai" 10))

我想出了一个办法,但我不喜欢我的解决方案:

代码语言:javascript
复制
(define (frequency string)
  (map (λ(x) (list (first x) (length x)))
       (group-by (λ(x) x) (string-split string))))

(define (recalculate lst)
  (frequency
   (string-join
    (flatten
     (map (λ(x) (make-list (second x) (first x))) lst)))))

基本上,我重复输入每个单词的次数和频率一样多,然后生成一个包含所有单词的字符串,最后再计算频率。有更简单(更快)的方法来实现这一点吗?

也许我应该补充一点,这个顺序并不重要(“柏拉图”可以出现在“哭泣”之前,等等)。另外,我正在寻找一种更简单的解决方案,因为我需要使用更大的数据集,并且我想让它更快(即使frequency函数可以变得更快,我也会很高兴)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-27 00:01:03

您可以创建一个add-count过程,该过程接受计数列表和新计数作为参数,如果列表中没有类似标记的计数,则可以将计数添加到列表中,或者将新计数与现有计数相结合。

代码语言:javascript
复制
#lang racket

(define (get-tag c) (first c))

(define (get-val c) (second c))

(define (add-count cs c)
  (let* ((k (get-tag c))
         (v (get-val c))
         (old-count (assoc k cs)))
    (if old-count
        (cons (list k (+ v (get-val old-count)))
              (remove old-count cs))
        (cons c cs))))

这里,get-tagget-val只是访问存储在计数中的标记和值的方便过程。assoc过程用于提取cs中与要添加的新计数c匹配的第一个计数的副本。此计数存储在old-count中,其值用于创建一个新的计数,该新计数在从原始列表cs中删除old-count后添加到列表中。

定义了add-count过程后,可以定义一个过程reduce-counts,该过程可以遍历所有计数,并通过使用add-count将它们累加到空列表中。结果列表中的计数将加在一起。

代码语言:javascript
复制
(define (reduce-counts cs (acc '()))
  (if (null? cs)
      acc
      (reduce-counts (rest cs) (add-count acc (first cs)))))

下面是一个测试运行:

代码语言:javascript
复制
reduce-counts.rkt> (define test-counts '(("cry" 1) ("racket" 2) ("cry" 3) ("play" 4) ("racket" 5) ("play" 6) ("racket" 7)))
reduce-counts.rkt> (reduce-counts test-counts)
'(("racket" 14) ("play" 10) ("cry" 4))

作为另一种方法,您可以使用filter收集列表中类似标记的计数,并在对值进行求和后将它们合并为新计数。在过滤输入以删除刚刚组合的标记之前,可以在累加器中收集合并的计数。可以递归地重复此过程,直到将所有计数组合、删除和收集为止。

代码语言:javascript
复制
;;; An alternate solution
(define (combine-like-counts cs)
  (list (get-tag (first cs))
        (foldl (lambda (c x) (+ x (get-val c))) 0 cs)))

(define (reduce-counts cs (acc '()))
  (if (null? cs)
      acc
      (let* ((k (get-tag (first cs)))
             (k-tag? (lambda (c) (equal? k (get-tag c))))
             (like (filter k-tag? cs))
             (remaining (filter (negate k-tag?) cs)))
        (reduce-counts remaining
                       (cons (combine-like-counts like) acc)))))

在这里,combine-like-counts过程假设输入列表中的所有计数共享相同的标记,因此通过将标记和所有值的和合并到一个列表中形成一个新的计数。

新的reduce-counts过程返回输入为空列表时放置在累加器中的所有内容,否则将保存第一个计数的标记并使用它创建k-tag?谓词,然后与filter一起使用该谓词创建匹配计数列表和删除所有匹配计数的剩余计数列表。匹配计数列表与combine-like-counts组合成单个计数,并添加到累加器,累加器与remaining一起递归地传递给reduce-counts

这和以前一样工作,尽管顺序已经改变了:

代码语言:javascript
复制
reduce-counts.rkt> (define test-counts '(("cry" 1) ("racket" 2) ("cry" 3) ("play" 4) ("racket" 5) ("play" 6) ("racket" 7)))
reduce-counts.rkt> (reduce-counts test-counts)
'(("play" 10) ("racket" 14) ("cry" 4))

我怀疑这两种实现会有不同的性能特征,这取决于它们的输入数据的细节。我的直觉是,对于包含大量每个标记的大量输入,第二个结果会更好,但真正的答案将来自于对一些有代表性的数据样本的测试。

如果您真正关心大量数据的性能,可以考虑将数据转换为哈希表,并使用一些内置的字典程序来获得类似的解决方案。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69730273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档