---
title: "Basic NLP"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Basic NLP}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE,
comment = "#>"
)
```
Three `nlp_*` functions form the core processing pipeline: `nlp_split_sentences()` segments text into sentence rows; `nlp_tokenize_text()` tokenizes each sentence into a named list with optional character spans; `nlp_cast_tokens()` flattens that list into a long-format data frame ready for counting, filtering, or indexing. This vignette walks through each step on a Wikipedia corpus, then collapses them into a single pipe.
Load packages.
```{r libs}
library(textpress)
library(dplyr)
library(DT)
```
## Corpus
Fetch Wikipedia articles on the Strauss-Howe generational theory and scrape the article bodies.
```{r corpus}
wiki_urls <- textpress::fetch_wiki_urls("Strauss Howe generational theory", limit = 5)
wiki_text_list <- textpress::read_urls(wiki_urls, exclude_wiki_refs = TRUE)
corpus <- wiki_text_list$text
nrow(corpus)
```
## Sentence split
`nlp_split_sentences()` segments each text into individual sentences, adding `sentence_id` and `start`/`end` character offsets. Known abbreviations (*Mr., U.S., vs.*) and single-letter initials are protected from false boundary detection via a built-in `abbreviations` vector, which can be extended as needed.
```{r split-sentences}
web_ss <- corpus |>
textpress::nlp_split_sentences(by = c("doc_id", "node_id"))
web_ss |>
slice(1:5) |>
mutate(text = {
words <- strsplit(text, "\\s+")
sapply(words, function(w) paste(paste(w[seq_len(min(15, length(w)))],
collapse = " "), "..."))
}) |>
DT::datatable(rownames = FALSE)
```
## Tokenize
`nlp_tokenize_text()` tokenizes each sentence into a named list. With `include_spans = TRUE` the result carries two parallel lists -- `tokens` and `spans` -- keyed by a composite `uid` built from the `by` columns.
```{r tokenize}
tokens <- web_ss |>
textpress::nlp_tokenize_text(
by = c("doc_id", "node_id", "sentence_id"),
method = 'biber',
include_spans = TRUE
)
# tokens list: first unit
head(tokens$tokens[[1]])
# spans matrix: matching start/end positions
head(tokens$spans[[1]])
```
## Cast to data frame
`nlp_cast_tokens()` flattens the token list into a long-format data table: one row per token, with `id`, `token`, `start`, and `end`.
```{r cast}
token_df <- tokens |> textpress::nlp_cast_tokens()
token_df |>
slice(1:20) |>
DT::datatable(rownames = FALSE)
```
## Full pipeline
All three steps as a single pipe from the abstract corpus.
```{r pipeline}
corpus |>
textpress::nlp_split_sentences(by = c("doc_id", "node_id")) |>
textpress::nlp_tokenize_text(
by = c("doc_id", "node_id", "sentence_id"),
include_spans = TRUE
) |>
textpress::nlp_cast_tokens()
```
## Summary
`nlp_split_sentences()` → `nlp_tokenize_text()` → `nlp_cast_tokens()` is the standard processing path from raw text to a token-level data frame. The `start`/`end` spans thread through each step, keeping token positions recoverable relative to the source text.