-
Notifications
You must be signed in to change notification settings - Fork 0
0010 stop word list provenance
Status: accepted · Date: 2026-08-05
DataNet.Text ships one stop-word list: scikit-learn's 318-word English set,
for stop_words="english" parity. Six Snowball stemmers now cover English,
French, German, Italian, Portuguese and Spanish, so the missing lists are the
obvious follow-up (issue #13).
Two sources are candidates, and they are not interchangeable.
nltk is the parity reference for the stemmers (0008),
which makes nltk.corpus.stopwords the natural target. Its README states the
lists were obtained from PostgreSQL's copy of the Snowball stop words, with
Romanian from arlc.ro and later community edits (English augmented, German
corrected, Kazakh, Nepali, Azerbaijani, Greek and Indonesian added). But the
nltk_data repository classifies the stopwords package under "Unclarified,
Unknown, Ambiguous, or Citation-Only" in DATASET-LICENSES.md: the package
carries no license attribute in index.xml, and its webpage attribute reads
ftp://ftp.cs.cornell.edu/pub/smart/english.stop and http://snowball.tartarus.org/ and others. LICENSE-OVERVIEW.md is explicit that the repository-wide
Apache-2.0 licence governs the repository, not the individual data packages,
and warns against redistribution of the unclarified ones. So the Apache-2.0
licence on nltk — recorded in THIRD-PARTY-NOTICES.md for oracle generation —
does not extend to this corpus.
Snowball publishes its own stop-word lists at
https://snowballstem.org/algorithms/<language>/stop.txt, and the site's licence
page reads: "Except where explicitly noted, all the software given out on this
Snowball site is covered by the 3-clause BSD License", © 2001 Dr Martin Porter,
© 2002 Richard Boulton. BSD-3-Clause is compatible with Apache-2.0 and needs only
that the copyright notice and licence terms travel with the redistribution.
This is also the upstream of the nltk lists, so the two are close but not
equal. Measured, word for word:
| Language | Snowball | nltk |
Only in Snowball | Only in nltk
|
|---|---|---|---|---|
| French | 154 | 157 | 13 | 16 |
| German | 231 | 232 | 4 | 5 |
| Italian | 279 | 279 | 0 | 0 |
| Portuguese | 203 | 207 | 0 | 4 |
| Spanish | 308 | 313 | 2 | 7 |
| English | 174 | 198 | 15 | 39 |
The gap is a snapshot gap, not a disagreement: nltk froze PostgreSQL's copy —
which is itself a download of these same files — and Snowball has since added
words (ceci, cela, quel, sans in French), while nltk accepted edits of
its own (dass in German, estar/haver/ser/é in Portuguese, the
sentir forms in Spanish). English is the outlier: nltk's list was
deliberately augmented with contractions and negation forms
(nltk_data issue #22), and its
recorded origin includes the Cornell SMART list, whose terms are not stated.
- Ship the Snowball lists, for the five non-English languages that already have a Snowball stemmer: French, German, Italian, Portuguese, Spanish. They are BSD-3-Clause, from a named copyright holder, and are the upstream that the alternative merely mirrors.
-
Do not vendor the
nltkcorpus, in whole or in part — not the augmented English list, not the corrected German one, not the Romanian list fromarlc.ro. This is a licensing conclusion, not a quality judgement. -
StopWords.Englishstays scikit-learn's 318-word list. It is BSD-3-Clause and it is whatstop_words="english"gives a migrating user; replacing it with Snowball's 174-word English list would break a documented parity guarantee to gain nothing. Snowball English is therefore not shipped: the language is already served. -
Pin the source. The five files were retrieved on 2026-08-05 and their
SHA-256 recorded in
tools/fetch_stopwords.py, which regeneratesStopWords.Snowball.csfrom them. Vendoring is reproducible and auditable rather than typed in by hand. -
Attribute. Snowball is added to
NOTICEand to the shipped-components table ofTHIRD-PARTY-NOTICES.md— it is the first resource the packages redistribute, as opposed to a dependency they reference.
- For French, German, Portuguese and Spanish,
DataNet.Textremoves a slightly different set of tokens thannltkwould.docs/equivalence.mdrecords this as a known divergence, with the per-language counts above. Italian is identical. This is the one place where the library deliberately does not matchnltk, and0008is the reason it needs saying out loud: parity is the contract everywhere else. - A caller who needs exactly
nltk's behaviour keeps a clean route —StopWordsaccepts anyIReadOnlyCollection<string>, so the corpus can be loaded from the caller's own machine, under whatever terms the caller accepts. That decision is theirs to make; redistributing it is what we cannot do. - The remaining languages of the
nltkcorpus (Arabic, Russian, Dutch, …) stay out until either a stemmer justifies them or a clean source is found. Adding a language means finding it on snowballstem.org, not copying it fromnltk. - If Snowball updates a list, the SHA-256 check in
tools/fetch_stopwords.pyfails loudly instead of silently producing a different library. Refreshing is then a deliberate act: new checksum, regenerated file, updated counts in the tests.
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels