R Academy · 课时

TF-IDF 与词频分析

使用 bind_tf_idf() 评估词语在各文档中的重要性

第 2 / 4 课13 个步骤

TF-IDF 与词频分析 是 CoddyKit 上的免费 R Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。

什么是 TF-IDF?

TF-IDF(词频–逆文档频率)用于衡量一个单词对于语料库中特定文档的独特程度。TF-IDF 分数较高,表示该词在该文档中出现频繁,但在所有文档中较为罕见,因此可以很好地指示文档的关键主题。

# TF-IDF formula
# TF(w, d)  = count of w in document d / total words in d
# IDF(w)    = log(N / number of docs containing w)
# TF-IDF(w, d) = TF(w, d) * IDF(w)

# Manual example
N <- 4  # total documents
docs_with_word <- 1  # 'neural' appears in only 1 doc
tf <- 5 / 100  # word appears 5 times in 100-word doc
idf <- log(N / docs_with_word)
tfidf <- tf * idf

cat('TF:     ', round(tf, 4), '\n')
cat('IDF:    ', round(idf, 4), '\n')
cat('TF-IDF: ', round(tfidf, 4), '\n')

准备词元计数

在计算 TF-IDF 之前,您需要一个包含以下列的数据框:document(文档标识符)、word(词元)和 n(计数)。请先使用 unnest_tokens(),再使用 count(document, word)。

library(tidytext)
library(dplyr)

# Simulate a 4-document corpus on tech topics
docs <- tibble::tibble(
  document = c(rep('ML', 3), rep('Stats', 3), rep('DB', 3), rep('Web', 3)),
  text = c(
    'machine learning neural networks training',
    'deep learning models gradient descent',
    'reinforcement learning reward policy agent',
    'probability distributions hypothesis testing',
    'bayesian inference regression analysis variance',
    'statistics sampling confidence intervals normal',
    'database sql joins indexes queries transactions',
    'relational tables primary keys foreign constraints',
    'nosql document store mongodb redis cassandra',
    'html css javascript frontend react components',
    'dom events browser api fetch requests',
    'responsive design flexbox grid layout'
  )
)

word_counts <- docs |>
  unnest_tokens(word, text) |>
  count(document, word, sort = TRUE)

cat('Document-word pairs:', nrow(word_counts), '\n')
print(head(word_counts, 8))

bind_tf_idf:计算 TF-IDF

bind_tf_idf(word, document, n) 接收一个整洁的计数数据框,并追加三列:tf(词频)、idf(逆文档频率)和 tf_idf(二者的乘积)。它会根据数据框中存在的文档计算 IDF。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c(rep('ML', 2), rep('Stats', 2), rep('DB', 2)),
  text = c(
    'machine learning neural gradient',
    'deep networks training epochs',
    'probability distributions variance covariance',
    'bayesian inference prior posterior',
    'sql database tables joins indexes',
    'queries transactions foreign primary'
  )
)

tfidf <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n)

cat('Columns added:', names(tfidf), '\n')
print(head(arrange(tfidf, desc(tf_idf)), 8))

arrange(desc(tf_idf)):高频术语

按 tf_idf 降序排列,可以查看哪些单词最能体现每个文档的特征。IDF 为零的单词(存在于每个文档中)无论频率多高,其 TF-IDF 都为零,因此无法区分文档。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c(rep('Python', 3), rep('R', 3), rep('Julia', 3)),
  text = c(
    'python pandas numpy scipy programming',
    'machine learning scikit tensorflow keras',
    'jupyter notebooks scripts debugging modules',
    'ggplot2 dplyr tidyverse statistics vectors',
    'shiny rmarkdown knitr cran bioconductor',
    'linear models factors dataframes packages',
    'julia multiple dispatch type system macros',
    'package ecosystem flux diffeq parallel',
    'scientific computing performance benchmarks'
  )
)

top_terms <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n) |>
  arrange(desc(tf_idf))

cat('Top distinctive terms per language:\n')
print(head(top_terms[, c('document', 'word', 'tf_idf')], 9))

每个文档的高 TF-IDF 术语

使用 group_by(document) |> slice_max(tf_idf, n = 5) 提取每个文档中最具独特性的前 N 个术语。这是概括语料库的有效方式——每个文档的独特词汇都能清晰呈现。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c(rep('Cooking', 3), rep('Finance', 3), rep('Sports', 3)),
  text = c(
    'recipe ingredients oven bake flour butter',
    'cooking temperature boil simmer saute',
    'kitchen knife chopping herbs spices garnish',
    'investment portfolio returns dividends stocks bonds',
    'market equity risk hedge fund derivatives',
    'inflation interest rate treasury balance sheet',
    'goal tackle pass dribble goalkeeper penalty',
    'tournament league championship trophy season',
    'athlete training stamina sprint endurance'
  )
)

top5 <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n) |>
  group_by(document) |>
  slice_max(tf_idf, n = 3) |>
  ungroup()

print(top5[, c('document', 'word', 'tf_idf')])

可视化每个文档的 TF-IDF

按文档分面的 TF-IDF 分数条形图,可以一目了然地展示每个文档的独特词汇。使用 facet_wrap(~document, scales = 'free_y'),让每个面板独立显示自身的高频术语。

library(tidytext)
library(dplyr)
library(ggplot2)

docs <- tibble::tibble(
  document = c(rep('AI', 3), rep('DB', 3), rep('Web', 3)),
  text = c(
    'neural networks gradient backpropagation',
    'deep learning transformer attention bert',
    'reinforcement policy reward exploration',
    'sql database indexes joins transactions',
    'relational schema normalization queries',
    'nosql mongodb redis cassandra document',
    'html css javascript react dom',
    'frontend api fetch async components',
    'responsive flexbox grid webpack bundle'
  )
)

plot_data <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n) |>
  group_by(document) |>
  slice_max(tf_idf, n = 4) |>
  ungroup()

ggplot(plot_data, aes(reorder(word, tf_idf), tf_idf, fill = document)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~document, scales = 'free_y') +
  coord_flip() +
  labs(x = NULL, y = 'TF-IDF', title = 'Top TF-IDF Terms per Topic') +
  theme_minimal()

IDF:出现在所有文档中的单词

出现在每个文档中的单词,其 IDF = log(N/N) = 0,因此 TF-IDF = 0。这样无需停用词列表,就能自动降低整个语料库中常见词的权重——不过对于非常小的语料库,删除停用词仍然有用。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c('A', 'B', 'C'),
  text = c(
    'data analysis statistics regression probability',
    'data engineering pipelines etl transformation',
    'data visualisation ggplot2 charts dashboards'
  )
)

tfidf <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n)

# 'data' appears in all 3 docs: tf_idf should be 0
data_rows <- filter(tfidf, word == 'data')
cat('TF-IDF for "data" across documents:\n')
print(data_rows[, c('document', 'word', 'idf', 'tf_idf')])

比较两个文档

TF-IDF 可以轻松比较文档:找出一个文档中 TF-IDF 较高、但在另一个文档中较低或为零的术语,从而了解每个文档的独特之处。请根据 word 对 TF-IDF 表进行内连接,然后比较分数。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c(rep('Doc1', 3), rep('Doc2', 3)),
  text = c(
    'bayesian statistics prior posterior mcmc',
    'markov chain monte carlo sampling',
    'probability distributions conjugate inference',
    'convolutional neural network image classification',
    'pooling activation relu softmax batch',
    'convolution filter feature map stride padding'
  )
)

tfidf <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n)

# Top 3 unique terms per document
tfidf |>
  group_by(document) |>
  slice_max(tf_idf, n = 3) |>
  select(document, word, tf_idf) |>
  print()

使用简·奥斯汀小说进行 TF-IDF 分析

janeaustenr 包提供了奥斯汀六部小说的完整文本。这是经典的 TF-IDF 基准示例:奥斯汀的读者可以识别每部小说的独特词汇——“wentworth”这个词几乎只出现在《劝导》中。

library(tidytext)
library(dplyr)

# Requires janeaustenr package
# library(janeaustenr)
# austen_books() returns: book, text

# Simulated mini-Austen corpus
mini_austen <- tibble::tibble(
  book = c(rep('Sense', 3), rep('Pride', 3), rep('Emma', 3)),
  text = c(
    'elinor marianne dashwood willoughby colonel',
    'edward ferrars barton cottage sister sense',
    'brandon feelings attachment sensibility heart',
    'darcy bennet bingley netherfield wickham',
    'jane lizzy lydia longbourn pemberley',
    'pride prejudice proposal marriage happiness',
    'emma woodhouse knightley harriet weston',
    'highbury match social governess niece',
    'frank jane fairfax box hill picnic'
  )
)

top <- mini_austen |>
  unnest_tokens(word, text) |>
  count(book, word) |>
  bind_tf_idf(word, book, n) |>
  group_by(book) |>
  slice_max(tf_idf, n = 3) |>
  select(book, word, tf_idf)

print(top)

使用 TF-IDF 进行特征工程

TF-IDF 分数可以用作机器学习分类器的特征。使用 cast_dtm() 或 cast_sparse() 将整洁的 TF-IDF 输出转换为文档–术语矩阵,然后将其传递给 glmnet、xgboost 或其他分类器。

library(tidytext)
library(dplyr)

docs <- tibble::tibble(
  document = c(rep('Positive', 4), rep('Negative', 4)),
  text = c(
    'excellent product love recommend quality',
    'amazing fast delivery five star perfect',
    'great value money satisfied happy return',
    'wonderful experience purchase outstanding best',
    'terrible waste money broken arrived damaged',
    'horrible quality slow delivery disappointed awful',
    'poor value cheap plastic flimsy broken',
    'worst experience refund needed useless junk'
  )
)

# TF-IDF as features
tfidf_wide <- docs |>
  unnest_tokens(word, text) |>
  count(document, word) |>
  bind_tf_idf(word, document, n) |>
  cast_dtm(document, word, tf_idf)

cat('DTM dimensions:', dim(tfidf_wide), '\n')
cat('(rows=documents, cols=unique words)\n')

局限性和替代方案

TF-IDF 存在一些局限性:它将单词视为相互独立,忽略词序和上下文,并且可能受到罕见拼写错误的影响。现代替代方案包括词嵌入(word2vec、GloVe)和上下文嵌入(BERT),但 TF-IDF 仍然非常适合作为文档检索和分类的基线方法。

# TF-IDF strengths and weaknesses summary
criteria <- data.frame(
  Criterion = c('Speed', 'Interpretability', 'Context', 'Word order',
                'Rare words', 'Scalability'),
  TF_IDF    = c('Fast', 'High', 'None', 'Ignored',
                'High IDF (inflated)', 'Excellent'),
  BERT      = c('Slow', 'Low', 'Rich', 'Captured',
                'Handled', 'Moderate')
)

print(criteria, row.names = FALSE)

快速检查

某个单词出现在语料库中的所有文档中。它的 TF-IDF 分数是多少?

回顾:TF-IDF 分析

要点:

  • TF-IDF = 词频 × 逆文档频率;分数越高,单词越独特
  • 流程:unnest_tokens() → count(document, word) → bind_tf_idf(word, document, n)
  • bind_tf_idf() 会追加 tf、idf 和 tf_idf 列
  • arrange(desc(tf_idf)) / slice_max(tf_idf, n = 5) 用于找出每个文档的高频术语
  • 出现在所有文档中的单词会自动得到 IDF = 0 和 TF-IDF = 0
  • cast_dtm(document, word, tf_idf) 将数据转换为可用于机器学习的文档–术语矩阵
  • 分面条形图是展示 TF-IDF 结果的标准可视化方式
library(tidytext)
library(dplyr)

# Minimal TF-IDF pipeline
tibble::tibble(
  doc = c('A', 'A', 'B', 'B'),
  text = c('neural networks deep', 'learning training', 'sql database query', 'joins indexes')
) |>
  unnest_tokens(word, text) |>
  count(doc, word) |>
  bind_tf_idf(word, doc, n) |>
  arrange(desc(tf_idf)) |>
  print()
免费开始

用 AI 导师学习 R — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
43
课程
159

常见问题解答

「TF-IDF 与词频分析」课时是免费的吗?

是的 — 「TF-IDF 与词频分析」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。

「TF-IDF 与词频分析」这节课中我会学到什么?

使用 bind_tf_idf() 评估词语在各文档中的重要性 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 R Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「TF-IDF 与词频分析」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 R Academy 课中编写并运行代码吗?

能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 分词与停用词移除
  2. TF-IDF 与词频分析
  3. 在 R 中进行情感分析
  4. 使用 LDA 进行主题建模
← 返回 R Academy