作为背景,我试图在球拍中做一个NLP应用程序,我到达了我必须阻止单词的部分(我也获得了它们的频率)。
我使用(planet dyoo/porter-stemmer)包来阻止,举个例子,我们可以写:
(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))。
我想出了一个办法,但我不喜欢我的解决方案:
(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函数可以变得更快,我也会很高兴)。
发布于 2021-10-27 00:01:03
您可以创建一个add-count过程,该过程接受计数列表和新计数作为参数,如果列表中没有类似标记的计数,则可以将计数添加到列表中,或者将新计数与现有计数相结合。
#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-tag和get-val只是访问存储在计数中的标记和值的方便过程。assoc过程用于提取cs中与要添加的新计数c匹配的第一个计数的副本。此计数存储在old-count中,其值用于创建一个新的计数,该新计数在从原始列表cs中删除old-count后添加到列表中。
定义了add-count过程后,可以定义一个过程reduce-counts,该过程可以遍历所有计数,并通过使用add-count将它们累加到空列表中。结果列表中的计数将加在一起。
(define (reduce-counts cs (acc '()))
(if (null? cs)
acc
(reduce-counts (rest cs) (add-count acc (first cs)))))下面是一个测试运行:
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收集列表中类似标记的计数,并在对值进行求和后将它们合并为新计数。在过滤输入以删除刚刚组合的标记之前,可以在累加器中收集合并的计数。可以递归地重复此过程,直到将所有计数组合、删除和收集为止。
;;; 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。
这和以前一样工作,尽管顺序已经改变了:
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))我怀疑这两种实现会有不同的性能特征,这取决于它们的输入数据的细节。我的直觉是,对于包含大量每个标记的大量输入,第二个结果会更好,但真正的答案将来自于对一些有代表性的数据样本的测试。
https://stackoverflow.com/questions/69730273
复制相似问题