Components¶
StatsComponent(nlp, name)
The base of the components of spaCy that put the statistics of a text into an extension of Doc: a component registers the extension when added to a pipeline and puts the statistics of every document into doc._.<name>. Writing components in general is described in the documentation of spaCy.
The components of ruTS are subclasses of the StatsComponent of the anyTS core, one for every class of statistics.
Note
The examples below use the recommended pipeline on the ru_core_news_sm model, which is installed separately: python -m spacy download ru_core_news_sm (see installation).
import ruts
nlp = ruts.load_pipeline()
nlp.add_pipe("ruts_basic", name="basic")
nlp("мама мыла раму")._.basic.c_letters
nlp("?!")._.basic is None
{4: 3}
True
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
nlp |
Language |
- | Pipeline the component is added to |
name |
str |
- | Name of the component in the pipeline and of the extension |
spaCy sets both at nlp.add_pipe(factory, name=...); the other parameters of a component are passed in the config dictionary of the same call.
Names¶
The factories carry the prefix ruts_ (table below, and the component of sentence boundaries ruts_blank_lines). They are declared as entry points of spacy_factories, so spaCy finds them without import ruts, and a pipeline saved with them (nlp.to_disk(path)) loads with spacy.load(path).
The name of the pipe is the name of the extension: nlp.add_pipe(factory, name="basic") puts the statistics into doc._.basic. Without name both take the name of the factory. The extension follows the pipe when the pipeline renames it (nlp.rename_pipe). A name taken by an extension of another package raises ParameterError instead of replacing it: at add_pipe, or at the first document after rename_pipe, which renames the pipe before the component learns its name. A component taken from another pipeline (add_pipe(name, source=other)) is the same object in both, so under a new name it writes to that extension in the other pipeline too; a separate component comes from the factory, add_pipe(factory, name=...). The same component can be added twice under different names, with different parameters. A document with no words - an empty string, whitespace, punctuation alone - passes through a component untouched, its extension left at None.
In ruts_lexical a document of numbers alone passes untouched as well, and in ruts_verse a document without letters.
Serialization
A component keeps an object of statistics in doc._.<name>, and spaCy cannot serialize it: Doc.to_bytes(), DocBin(store_user_data=True) and nlp.pipe(..., n_process>1) fail with these components in the pipeline. To save a document, leave the user data out (doc.to_bytes(exclude=["user_data"])) or keep doc._.<name>.get_stats() separately; for multiprocessing compute the statistics in the main process after nlp.pipe, without the components.
Adding a component adds to the spaCy tokenizer the rules for the dialogue dashes glued to words (ruts.utils.add_dash_rules), so that a dialogue dash is split off a word as in a string; a pipeline without ruTS components needs this call of its own. The anySeg tokenizer of the recommended pipeline is left as it is.
ruTS removes stress marks from the words of a Doc, but the model parses such words worse: on a text after accentuate the tagger of ru_core_news_sm changes the part of speech of over half of the words, and the dependencies change in the recommended pipeline as well. Pass a text with stresses without them - nlp(ruts.utils.strip_marks(text)).
Recommended pipeline¶
load_pipeline(model='ru_core_news_sm', exclude=(), **kwargs)
A spaCy pipeline in which a Doc is annotated the same way as a string, except the hyphenated words such as муж-то and два-три: a string splits them into parts, a Doc keeps one word. The tokens and the sentences come from the anySeg segmenter, the parts of speech, features and lemmas from the ruMorph morphological analyzer in the context of the sentence, and the model gives the dependency parser and the named entities. A saved pipeline of this kind loads as it is. The same by hand:
nlp = spacy.load(
"ru_core_news_sm",
exclude=["morphologizer", "lemmatizer"],
config={"nlp": {"tokenizer": {"@tokenizers": "anyseg.Tokenizer.v1"}}},
)
nlp.add_pipe("anyseg_sentencizer", first=True)
nlp.add_pipe("rumorph", after="anyseg_sentencizer")
On UD SynTagRus this pipeline is more accurate than the model itself: the part of speech is right for 97.3 % of the words against 93.3 %, the dependency with its relation (LAS) for 82.7 % against 82.1 %. A plain spacy.load("ru_core_news_sm") is supported too, but then the parts of speech, features and sentences come from the model, and the statistics of a Doc may differ from those of the string. ruTS does not work around the errors of its tagger: the model tags connectors such as значит and т.е. as a verb and a punctuation mark, so they are not found (11 in 71 thousand words of TextsByGrade and SovChLit), and боишься as an infinitive, so the subordinate clause with it counts as an infinitive clause.
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
str |
"ru_core_news_sm" |
Name or path of the spaCy model: ru_core_news_sm, ru_core_news_md, ru_core_news_lg |
exclude |
str, list[str] |
() |
More components of the model not to load, for example ner |
**kwargs |
- | - | Other parameters of spacy.load; the tokenizer in config is replaced with anySeg |
from ruts import load_pipeline
pipeline = load_pipeline()
pipeline.pipe_names
[(token.text, token.pos_, token.lemma_) for token in pipeline("Нож из стали.")]
['anyseg_sentencizer', 'rumorph', 'tok2vec', 'parser', 'attribute_ruler', 'ner']
[('Нож', 'NOUN', 'нож'), ('из', 'ADP', 'из'), ('стали', 'NOUN', 'сталь'), ('.', 'PUNCT', '.')]
Components of the classes of statistics¶
| Component | Factory | Class of statistics | Parameters |
|---|---|---|---|
BasicStatsComponent |
ruts_basic |
BasicStats |
- |
MorphStatsComponent |
ruts_morph |
MorphStats |
- |
ReadabilityStatsComponent |
ruts_readability |
ReadabilityStats |
preset, basic |
UnadaptedReadabilityStatsComponent |
ruts_unadapted_readability |
UnadaptedReadabilityStats |
basic |
DiversityStatsComponent |
ruts_diversity |
DiversityStats |
window_len, mtld_threshold, mtld_min_len, hdd_sample_size, log_base |
StyleStatsComponent |
ruts_style |
StyleStats |
stopwords, top_n, nausea_stopwords, cliches |
PhonStatsComponent |
ruts_phon |
PhonStats |
window_len |
SyntaxStatsComponent |
ruts_syntax |
SyntaxStats |
- |
CohesionStatsComponent |
ruts_cohesion |
CohesionStats |
blank_lines |
LexicalStatsComponent |
ruts_lexical |
LexicalStats |
data_dir |
VerseStatsComponent |
ruts_verse |
VerseStats |
data_dir |
Basic statistics¶
BasicStatsComponent(nlp, name)
BasicStatsComponent puts the basic statistics of a document into its extension.
Morphological statistics¶
MorphStatsComponent(nlp, name)
MorphStatsComponent puts the morphological statistics into the extension; by default they need the parts of speech before it.
In ruTS the parts of speech and the features come from the annotation of the pipeline (token.pos_, token.morph), and without it from the ruMorph analysis by word.
nlp.add_pipe("ruts_morph", name="morph")
nlp("мама мыла раму")._.morph.case
('Nom', None, 'Acc')
Readability metrics¶
ReadabilityStatsComponent(nlp, name, preset=None, basic=None)
ReadabilityStatsComponent puts the readability metrics into the extension, computed from the document or, with basic, from the basic statistics another component put into it. A library sets basic_factory, the factory of its component of the basic statistics, which the error names when that extension holds none.
| Parameter | Type | Default | Description |
|---|---|---|---|
preset |
str |
None |
Preset of the coefficients, one of the presets of the class; the default of the class if None |
basic |
str |
None |
Extension of a component of the basic statistics to reuse instead of computing them again |
With preset left at None the config of a saved pipeline holds no preset, and the pipeline takes the default of the class when it is loaded; a library that wants its default written into the config gives it in the signature of its subclass.
Raises:
ParameterError- astats_classthat is not concrete, a preset that is not a string or not one of the class, an extension not named by a string;SourceTypeError- a class withoutbasic_stats_classand nobasic;SourceError- a document whose extensionbasicholds no basic statistics of the class.
The presets of ruTS are plainrussian (the default), fiction and academic. In ruTS the errors of stats_class and SourceTypeError do not occur, and the message of SourceError names the component ruts_basic to add before it.
nlp.add_pipe("ruts_readability", name="readability")
config = {"preset": "fiction", "basic": "basic"}
nlp.add_pipe("ruts_readability", name="readability_fiction", config=config)
doc = nlp("Кот сидел на окне и смотрел на птиц, которые летали над крышами домов.")
doc._.readability.flesch_kincaid_grade
doc._.readability_fiction.flesch_kincaid_grade
-0.1506153846153886
6.41769230769231
Formulas of other languages¶
UnadaptedReadabilityStatsComponent(nlp, name, basic=None)
UnadaptedReadabilityStatsComponent puts the readability formulas of other languages into the extension, computed from the document or, with basic, from the basic statistics another component put into it. A library sets basic_factory, as for ReadabilityStatsComponent.
| Parameter | Type | Default | Description |
|---|---|---|---|
basic |
str |
None |
Extension of a component of the basic statistics to reuse instead of computing them again |
Raises:
ParameterError- astats_classthat is not concrete, an extension not named by a string;SourceTypeError- a class withoutbasic_stats_classand nobasic;SourceError- a document whose extensionbasicholds no basic statistics of the class.
The formulas are those of UnadaptedReadabilityStats, with their original coefficients and without a calibration for Russian; the errors in ruTS are as for ReadabilityStatsComponent.
nlp.add_pipe("ruts_unadapted_readability", name="unadapted", config={"basic": "basic"})
doc = nlp("Кот сидел на окне и смотрел на птиц, которые летали над крышами домов.")
doc._.unadapted.gulpease, doc._.unadapted.forcast
doc._.unadapted.bs is doc._.basic
(69.0, 13.076923076923077)
True
Lexical diversity metrics¶
DiversityStatsComponent(nlp, name, window_len=50, mtld_threshold=0.72, mtld_min_len=10, hdd_sample_size=42, log_base=10)
DiversityStatsComponent puts the lexical diversity metrics into the extension, by a class of the library that reads a Doc. Its parameters are those of DiversityStats, with the same defaults.
Raises:
ParameterError- a wrong parameter (check_paramsof the diversity).
| Parameter | Type | Default | Description |
|---|---|---|---|
window_len |
int |
50 |
Window size for MATTR and segment size for MSTTR |
mtld_threshold |
float |
0.72 |
TTR threshold for MTLD, MA-MTLD and MTLD-W |
mtld_min_len |
int |
10 |
Minimum factor length for MTLD, MA-MTLD and MTLD-W |
hdd_sample_size |
int |
42 |
Sample size for HD-D |
log_base |
float |
10 |
Logarithm base for the Summer, Maas and Dugast metrics |
config = {"window_len": 100, "log_base": 2.718281828459045}
nlp.add_pipe("ruts_diversity", name="diversity", config=config)
nlp("мама мыла раму")._.diversity.rttr
1.7320508075688774
SEO style metrics¶
StyleStatsComponent(nlp, name, stopwords=None, top_n=10, nausea_stopwords=None, cliches=None)
StyleStatsComponent puts the style metrics into the extension.
| Parameter | Type | Default | Description |
|---|---|---|---|
stopwords |
list[str] |
None |
Stopwords of the water content; those of the class if None |
top_n |
int |
10 |
Number of the most frequent words of the academic nausea and of the naturalness by Zipf's law (NAUSEA_TOP_N) |
nausea_stopwords |
list[str] |
None |
Words left out of the most frequent words of the academic nausea; those of the class if None |
cliches |
list[str] |
None |
Cliches; those of the class (cliches_list) if None |
Raises:
SourceTypeError- stopwords, nausea stopwords or cliches that are not a list of strings;ParameterError- atop_nthat is not an integer of at least one.
Without the lists the water words and the stopwords of the nausea are determined by the ruMorph tags, and the cliches come from OFFICIALESE_CLICHES.
nlp.add_pipe("ruts_style", name="style", config={"stopwords": ["и", "в", "не"], "top_n": 5})
nlp("мама мыла раму")._.style.water
nlp.add_pipe("ruts_style", name="style_cliches", config={"cliches": ["в целях", "в рамках"]})
nlp("В целях экономии в рамках проекта мы закрыли склад.")._.style_cliches.cliches
0.0
22.22222222222222
Phonostatistics¶
PhonStatsComponent(nlp, name, window_len=3)
PhonStatsComponent puts the phonetic statistics into the extension; window_len (PHON_WINDOW_LEN by default) is the window in words of the alliteration and the assonance.
Raises:
ParameterError- awindow_lenthat is not an integer of at least 2.
nlp.add_pipe("ruts_phon", name="phon", config={"window_len": 5})
nlp("мама мыла раму")._.phon.p_open_syllables
1.0
Syntactic statistics¶
SyntaxStatsComponent(nlp, name)
SyntaxStatsComponent puts the syntactic statistics into the extension; they need a parser and the parts of speech before it.
The recommended pipeline and the ru_core_news_sm, ru_core_news_md and ru_core_news_lg models have a parser and the parts of speech.
Raises:
SourceError- a document without a dependency parse or parts of speech (rumorphof the recommended pipeline or themorphologizerof a model).
nlp.add_pipe("ruts_syntax", name="syntax")
nlp("Дом, построенный рабочими в прошлом году, был продан")._.syntax.tree_depth
4.0
Cohesion statistics¶
CohesionStatsComponent(nlp, name, blank_lines=False)
CohesionStatsComponent puts the cohesion statistics into the extension. It needs no parser: a Doc without sentence boundaries is split into sentences by SentsExtractor. The parts of speech come from the annotation, and the lemmas from ruMorph by the part of speech of the token; without annotation both come from the ruMorph analysis, as for a string.
| Parameter | Type | Default | Description |
|---|---|---|---|
blank_lines |
bool |
False |
Start the paragraphs after the blank lines alone, for a text whose lines are wrapped inside its paragraphs; a paragraph is a line by default |
Raises:
ParameterError- ablank_linesthat is not abool.
nlp.add_pipe("ruts_cohesion", name="cohesion")
nlp.add_pipe("ruts_cohesion", name="paragraphs", config={"blank_lines": True})
doc = nlp("Кот сидел на окне. Он смотрел на птиц. Птицы улетели,\nи кот уснул.")
(
doc._.cohesion.noun_overlap_adjacent,
doc._.cohesion.n_paragraphs,
doc._.paragraphs.n_paragraphs,
)
(0.5, 2, 1)
Lexical sophistication statistics¶
LexicalStatsComponent(nlp, name, data_dir=None)
LexicalStatsComponent puts the lexical sophistication statistics into the extension. The frequency dictionary metrics require a downloaded FreqDict; the bands and lexical density are computed without it.
| Parameter | Type | Default | Description |
|---|---|---|---|
data_dir |
str |
None |
Path to the frequency dictionary directory; if not given, the default directory is used |
Raises:
DatasetNotFoundError- the frequency dictionary metrics andget_stats()whenFreqDictis not downloaded.
nlp.add_pipe("ruts_lexical", name="lexical")
nlp("Кот сидел на окне и смотрел на птиц")._.lexical.p_top1000
0.75
Verse statistics¶
VerseStatsComponent(nlp, name, data_dir=None)
VerseStatsComponent puts the verse statistics into the extension; it requires a downloaded StressDict stress dictionary. The lines of a poem are the lines of the Doc text, so pass the poem to nlp without joining them. A text with letters but without Russian words gives empty statistics, as in VerseStats.
| Parameter | Type | Default | Description |
|---|---|---|---|
data_dir |
str |
None |
Path to the stress dictionary directory; if not given, the default directory is used |
Raises:
DatasetNotFoundError- the stress dictionary is not downloaded; the first document with letters raises it.
nlp.add_pipe("ruts_verse", name="verse")
poem = (
"Буря мглою небо кроет,\nВихри снежные крутя;\n"
"То, как зверь, она завоет,\nТо заплачет, как дитя"
)
doc = nlp(poem)
doc._.verse.meter, doc._.verse.n_feet
('хорей', 4)
Sentences after blank lines¶
start_sents_after_blank_lines(doc)
A component that starts a sentence at the first token after a blank line - a line break, optional whitespace and another line break - so that a heading, a list item or a paragraph without a final mark is a sentence of its own, as in the sentence extractor. It goes before the parser, senter or sentencizer, which keeps its starts and places the others. The whitespace before such a start stays with the sentence before it, and the marks that open a paragraph (a dash of a dialogue, ¡, «) stay with its first word. Alone, the component leaves the sentences within a paragraph unsplit and sets partial boundaries (has_sents), so the statistics take the sentences of such a document from its text. A parsed document, such as one passed through the pipeline again, passes untouched, since spaCy keeps its sentences. A language library registers the component under its prefix, Language.component("<prefix>_blank_lines", func=start_sents_after_blank_lines).
| Parameter | Type | Default | Description |
|---|---|---|---|
doc |
Doc |
- | Doc object |
In ruTS the component is registered as ruts_blank_lines. Without it the parser of the ru_core_news_sm model glues a paragraph without a final mark to the next sentence; the recommended pipeline does not need it.
import spacy
nlp = spacy.load("ru_core_news_sm")
nlp.add_pipe("ruts_blank_lines", before="parser")
[sent.text.strip() for sent in nlp("Что нового\n\nБыстрый импорт. Меньше ошибок").sents]
['Что нового', 'Быстрый импорт.', 'Меньше ошибок']