Skip to contents

Splits each document into units at a selectable granularity using deterministic sentence-boundary rules: a word-boundary-anchored, case-insensitive abbreviation gazetteer (see abbreviations()), a decimal-number guard, and a parenthetical guard, plus shallow clause chunking at discourse connectives. No model call is involved, so segmentation works offline and is fully reproducible.

Usage

segment(
  text,
  level = c("clause", "sentence", "phrase"),
  merge_below = 0L,
  abbreviations = default_abbreviations(),
  cores = 1L,
  max_tokens = NULL,
  model = NULL,
  min_content = 0
)

Arguments

text

A character vector of documents. Names, when present, are carried into the document_name column.

level

"clause" (default), "sentence", or "phrase".

merge_below

Re-join segments shorter than this many words into their neighbor. 0 (default) disables merging and returns the pure segmentation.

abbreviations

Character vector of abbreviations (each ending in a period) whose periods never end a sentence. Defaults to the built-in gazetteer from abbreviations(); matching is case-insensitive.

cores

Number of forked worker processes used to split documents. Default 1 (serial). Values above one use parallel::mclapply on Unix-alikes and fall back to serial on Windows or for small inputs; the segmentation is identical regardless of the count.

max_tokens

Optional cap on segment length, so no segment overruns an encoder's context window and is silently truncated. NULL (default) leaves segments uncapped. When set, any segment over the budget is re-split at the finest logical boundaries — clause hinges, ";", ":", " - ", and commas (though a comma that would sever a name from its initials, as in "Tomlinson, C. A.", does not split) — and the pieces are packed back up to the budget, so a split lands on punctuation wherever possible. A run with no such boundary is chopped further, but even then the break is placed just before a function word (a coordinator, preposition, article, or relative pronoun) near the budget edge rather than mid-phrase, falling back to the raw edge only when the run has no function word either. With model = NULL the budget counts whitespace-delimited words, a deterministic offline proxy; set it below the model's true token limit (for example around 200 for a 256-token model), since a tokenizer emits somewhat more tokens than words.

model

Optional loaded sbert_model. When supplied with max_tokens, the budget counts that model's exact sub-word tokens instead of words, so max_tokens can be the model's real limit. Token-counted segmentation runs serially (the tokenizer is not forked), so cores is ignored in that case.

min_content

Minimum alphabetic-content ratio, in [0, 1], for a segment to be kept (see content_ratio()). Segments below the floor — citation fragments, page and reference bits, number lists — are dropped. Prose scores near 1 and reference noise far below, so a value around 0.5 removes the noise while keeping real clauses. 0 (default) keeps every segment.

Value

A base data frame with one row per segment and columns document_id (integer position in text), document_name (name of the input element, or ""), segment (integer position within the document), and text (the segment). Blank documents contribute no rows.

Details

Each finer level adds separators:

"sentence"

splits at ., ?, and ! only.

"clause"

additionally splits at ;, :, spaced dashes, and subordinating hinges (for example "which", "where", "in terms of"). Comma-separated enumerations stay whole. This is the default.

"phrase"

additionally splits at commas, for maximal granularity.

Before segmentation, each document is normalized: curly quotes become straight quotes, en and em dashes become spaced hyphens, and runs of whitespace collapse to single spaces. Letter case is preserved.

Examples

segment(
  "We propose a simulator which runs alongside the processor."
)
#>   document_id document_name segment                                text
#> 1           1                     1              We propose a simulator
#> 2           1                     2 which runs alongside the processor.

segment(
  c(intro = "See Fig. 3 for details. The next part follows."),
  level = "sentence"
)
#>   document_id document_name segment                    text
#> 1           1         intro       1 See Fig. 3 for details.
#> 2           1         intro       2  The next part follows.