如何在Golang中计算词频

Go词频教程展示了如何在Golang中计算词频。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

我们的示例仅适用于拉丁词,并且专门针对分析圣经。

$ wget https://raw.githubusercontent.com/janbodnar/data/main/the-king-james-bible.txt

我们使用钦定版圣经。

为了将文本切割成单词,我们使用Go的strings.FieldsFunc和正则表达式。

Go词频示例I

FieldsFunc函数在每次运行满足提供函数的Unicodecode点时拆分字符串,并返回一个切片数组。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "sort"
    "strings"
)

func main() {

    fileName := "the-king-james-bible.txt"

    bs, err := ioutil.ReadFile(fileName)

    if err != nil {

        log.Fatal(err)
    }

    text := string(bs)

    fields := strings.FieldsFunc(text, func(r rune) bool {

        return !('a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '\'')
    })

    wordsCount := make(map[string]int)

    for _, field := range fields {

        wordsCount[field]++
    }

    keys := make([]string, 0, len(wordsCount))

    for key := range wordsCount {

        keys = append(keys, key)
    }

    sort.Slice(keys, func(i, j int) bool {

        return wordsCount[keys[i]] > wordsCount[keys[j]]
    })

    for idx, key := range keys {

        fmt.Printf("%s %d\n", key, wordsCount[key])

        if idx == 10 {
            break
        }
    }
}

我们计算KingJames圣经中单词的出现频率。

fields := strings.FieldsFunc(text, func(r rune) bool {

    return !('a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '\'')
})

FieldsFunc按非字母和撇号的字符剪切文本。这也将忽略所有节号。

wordsCount := make(map[string]int)

for _, field := range fields {

    wordsCount[field]++
}

每个单词及其频率都存储在wordsCount映射中。

keys := make([]string, 0, len(wordsCount))

for key := range wordsCount {

    keys = append(keys, key)
}

sort.Slice(keys, func(i, j int) bool {

    return wordsCount[keys[i]] > wordsCount[keys[j]]
})

为了按频率对单词进行排序,我们创建了一个新的keys切片。我们将所有单词放在那里并按它们的频率值对它们进行排序。

for idx, key := range keys {

    fmt.Printf("%s %d\n", key, wordsCount[key])

    if idx == 10 {
        break
    }
}

我们打印出圣经中最常用的前十个单词。

$ go run word_freq.go
the 62103
and 38848
of 34478
to 13400
And 12846
that 12576
in 12331
shall 9760
he 9665
unto 8942
I 8854

Go词频例子二

在第二个示例中,我们使用正则表达式将文本分成单词。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "regexp"
    "sort"
)

type WordFreq struct {
    word string
    freq int
}

func (p WordFreq) String() string {
    return fmt.Sprintf("%s %d", p.word, p.freq)
}

func main() {

    fileName := "the-king-james-bible.txt"

    reg := regexp.MustCompile("[a-zA-Z']+")
    bs, err := ioutil.ReadFile(fileName)

    if err != nil {
        log.Fatal(err)
    }

    text := string(bs)
    matches := reg.FindAllString(text, -1)

    words := make(map[string]int)

    for _, match := range matches {
        words[match]++
    }

    var wordFreqs []WordFreq
    for k, v := range words {
        wordFreqs = append(wordFreqs, WordFreq{k, v})
    }

    sort.Slice(wordFreqs, func(i, j int) bool {

        return wordFreqs[i].freq > wordFreqs[j].freq
    })

    for i := 0; i < 10; i++ {

        fmt.Println(wordFreqs[i])
    }
}

我们将单词及其频率存储在WordFreq结构中。

reg := regexp.MustCompile("[a-zA-Z']+")

在我们的正则表达式中,一个或多个字母字符或撇号构成一个词。

matches := reg.FindAllString(text, -1)

FindAllString函数返回表达式的所有连续匹配项的一部分。

words := make(map[string]int)

for _, match := range matches {
    words[match]++
}

我们检查匹配项并计算它们在文件中的频率。单词及其出现次数存储在wordsmap中。

var wordFreqs []WordFreq
for k, v := range words {
    wordFreqs = append(wordFreqs, WordFreq{k, v})
}

我们从words映射中构建了一片WordFreq结构。

sort.Slice(wordFreqs, func(i, j int) bool {

    return wordFreqs[i].freq > wordFreqs[j].freq
})

我们按freq字段对wordFreqs切片进行排序。

for i := 0; i < 10; i++ {

    fmt.Println(wordFreqs[i])
}

我们打印前十个最常见的单词。

围棋词频例子三

在下一个示例中,我们还使用正则表达式。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "regexp"
    "sort"
)

type WordFreq struct {
    word string
    freq int
}

func (p WordFreq) String() string {
    return fmt.Sprintf("%s %d", p.word, p.freq)
}

type byFreq []WordFreq

func (a byFreq) Len() int           { return len(a) }
func (a byFreq) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byFreq) Less(i, j int) bool { return a[i].freq < a[j].freq }

func main() {

    fileName := "the-king-james-bible.txt"
    bs, err := ioutil.ReadFile(fileName)

    if err != nil {
        log.Fatal(err)
    }

    text := string(bs)

    re := regexp.MustCompile("[a-zA-Z']+")
    matches := re.FindAllString(text, -1)

    words := make(map[string]int)

    for _, match := range matches {
        words[match]++
    }

    var wordFreqs []WordFreq
    for k, v := range words {
        wordFreqs = append(wordFreqs, WordFreq{k, v})
    }

    sort.Sort(sort.Reverse(byFreq(wordFreqs)))

    for i := 0; i < 10; i++ {
        fmt.Printf("%v\n", wordFreqs[i])
    }
}

此示例还实现了自定义排序接口。

type byFreq []WordFreq

func (a byFreq) Len() int           { return len(a) }
func (a byFreq) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byFreq) Less(i, j int) bool { return a[i].freq < a[j].freq }

我们根据freq字段为[]WordFreq实现了sort.Interface

sort.Sort(sort.Reverse(byFreq(wordFreqs)))

要按降序对WordFreq结构进行排序,我们使用sort.Reverse函数。

在本教程中,我们计算了钦定版圣经中的词频。

列出所有Go教程。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏