Tokenise texts into n-grams (unigrams, bigrams, ...), drop
n-grams that contain a stop word, a digit, punctuation or a symbol, and
count the remaining ones by document (or any grouping). Stop words can be
tolerated inside the longer n-grams ("rate_of_growth"), see
inner_stop_words_from.
Usage
extract_ngrams(
data,
ngrams = 2L,
document_col = NULL,
text_col = "text",
min_nchar = 2L,
stop_words = get_stop_words("en"),
inner_stop_words_from = Inf,
inner_stop_words = NULL,
language_col = NULL,
compounds = NULL,
chunk_size = NULL,
check_duplicates = TRUE,
...
)Arguments
- data
A
data.frameordata.tablewith the texts. It is not modified.- ngrams
Integer. A scalar
ncomputes all sizes1:n; a vector (e.g.c(1L, 3L)) computes only those sizes.- document_col
Character vector of the column(s) identifying the documents (or any group to count by, e.g.
c("institution", "year")).NULLcounts over the whole corpus.- text_col
Character scalar, name of the text column.
- min_nchar
Integer scalar, minimal number of characters of each word of an n-gram (inner stop words excepted).
3Lremoves the one- and two-letter tokens of formulas and OCR ("kt", "mt") in academic texts, but also abbreviations such as "uk" or "eu".- stop_words
Stop words: a character vector, used for all texts (default: English,
get_stop_words()), or a named list of vectors by language (get_stop_words(c("en", "fr"))) used withlanguage_col.NULL(orcharacter(0)) uses no stop word list: only n-grams with digits, punctuation or symbols are dropped.- inner_stop_words_from
Integer scalar. From this n-gram size onwards, stop words are allowed inside an n-gram, but not as its first or last word: with
3L, "rate_of_growth" is kept but "of_growth_in" and "the_rate_of" are not. The defaultInfdrops every n-gram containing a stop word, and says so in a message (once per session) when n-grams of size 3 or more are asked for.- inner_stop_words
Optional character vector. With
inner_stop_words_from, only these stop words are allowed inside n-grams (e.g."of"), instead of every stop word.- language_col
Optional character scalar, a column of language codes (e.g. from
detect_language()). Each text is then filtered with the stop words of its language (stop_wordsmust be a named list); texts whose language has no list use all the lists together.- compounds
Optional n-grams to merge into single tokens before counting: the output of
select_collocations(), a table withtoken(words joined by_) and optionallyscore, or a character vector (words joined by_or by spaces). Each text is rewritten with its words in order: where a selected n-gram occurs, its words are replaced by one token ("monetary_policy"), so they are no longer counted on their own there; the same words elsewhere are still counted. When two selected n-grams overlap, the higherscorewins (the longer one without score). Withcompounds,ngramsmust be1L: the output has the single words and the compounds, with their size inngram.- chunk_size
Optional integer, number of rows tokenised at once. Use it on large corpora to bound memory.
NULLprocesses all rows at once.- check_duplicates
Logical: warn when texts are duplicates of one another (see
find_duplicate_texts()).- ...
Not used: an argument renamed in the development version of mintR gives an error naming its replacement.
Value
A data.table with document_col, token (words joined by _),
ngram (its size) and count (number of occurrences in the document).
Details
Texts are lower-cased and split into words once by
tokenizers::tokenize_words(). Curly apostrophes are made straight and a
final possessive 's is dropped ("bank's" and "bank’s" become "bank");
other apostrophes stay inside the word ("don't"). An n-gram is dropped
when any of its words is a stop word, is shorter than min_nchar, or
contains a character other than a letter or an apostrophe; n-grams do
not cross texts. The test is made once per distinct word (per language),
and only the n-grams kept are built, so the cost grows little with n.
Clean links, handles or OCR hyphenation first with clean_texts().
Stop words in longer n-grams
Stop words are useful to drop
from unigrams and bigrams ("of_the"), but they are part of many
multiword terms: "rate_of_growth", "balance_of_payments". Justeson and
Katz (1995) found, in samples of four technical dictionaries, that 97%
of multiword terms contain only nouns and adjectives, and more than 99%
only nouns, adjectives and the preposition "of"; terms with a
preposition were about 3% of the total. They therefore leave
prepositions out by default because candidates that contain them are
mostly not terms (5 or 6 valid terms among 58 candidates with
prepositions in their three test texts). Allowing inner stop words
is thus a trade-off: more coverage of terms like "rate_of_growth", more
noise from phrases like "growth_in_output". Restricting
inner_stop_words to "of" follows their finding, and filtering
the n-grams by a minimal count or by tf-idf (see
filter_group_quantile()) limits the noise. Constant et al. (2017)
survey the wider field of multiword expression discovery, where
association measures such as pointwise mutual information are the usual
tool for bigrams but are hard to extend to longer n-grams.
Languages
The stop words are English by default. When they are,
texts of at least 50 words in which English stop words make up less
than 10% of the words (against 40-50% in English prose) are reported
with a warning: they are probably in another language, and their stop
words are kept and will form topics of their own. Detect the languages
with detect_language(), then filter the corpus or use language_col
with get_stop_words().
References
Justeson, J. S. and Katz, S. M. (1995). Technical terminology: some linguistic properties and an algorithm for identification in text. Natural Language Engineering, 1(1), 9-27. doi:10.1017/S1351324900000048
Constant, M., Eryigit, G., Monti, J., van der Plas, L., Ramisch, C., Rosner, M. and Todirascu, A. (2017). Multiword expression processing: a survey. Computational Linguistics, 43(4), 837-892. doi:10.1162/COLI_a_00302
Examples
df <- data.frame(id = 1:2, text = c("Economic growth matters", "Growth and the bank's trade"))
extract_ngrams(df, ngrams = 2L, document_col = "id")
#> id token ngram count
#> <int> <char> <int> <int>
#> 1: 1 economic 1 1
#> 2: 1 growth 1 1
#> 3: 1 matters 1 1
#> 4: 2 growth 1 1
#> 5: 2 bank 1 1
#> 6: 2 trade 1 1
#> 7: 1 economic_growth 2 1
#> 8: 1 growth_matters 2 1
#> 9: 2 bank_trade 2 1
