Style metrics¶
StyleStats(source, words_extractor=None, stopwords=None, top_n=10, cliches=None, nlp=None, nausea_stopwords=None)
The style metrics of a text: the SEO metrics of Advego and Text.ru and the markers of the officialese style. The services publish no formulas, so the common definitions are taken. The data source is a text or a Doc object of spaCy.
The SEO metrics are counted on the words of the word extractor, in lower case by default, or on the tokens of a Doc; an extractor passed explicitly is used on the text of a Doc. The markers are counted on the word forms in lower case (forms), whatever the extractor.
Raises:
SourceTypeError- a source that is neither a string nor aDoc, an extractor of another type, a list that is not a list of strings;SourceError- a source without words;ParameterError- atop_nthat is not an integer of at least one.
The class extends the StyleStats of the anyTS core with the hooks of Spanish, given in the cards of the metrics: the stopwords, the compound prepositions, parenthetical expressions and clichés that the Spanish-language guides to plain language warn about, found with their contractions and the forms of their verbs, and the nouns derived from a verb among the nouns of the spaCy model. The words of a string are extracted by the Spanish WordsExtractor and are not lemmatized, so the forms of one word count as different words, as in Advego; to compute by lemmas, pass a WordsExtractor with use_lexemes=True and lowercase=True. The print_stats table takes the descriptions of ests.constants.STYLE_STATS_DESC.
The verbal nouns need the parts of speech and the lemmas: a Doc gives its own annotation, also when an extractor takes its text, and a string is parsed with es_core_news_sm or with the pipeline passed in nlp the first time verbal_nouns or markers() is read - the other metrics of a string need no model. A string longer than the max_length of the pipeline is parsed in parts of whole sentences. Besides the errors above, a pipeline that is not a spaCy Language raises SourceTypeError and a string with a character without a UTF-8 form raises SourceError from the Spanish tokenizer; reading verbal_nouns or markers() raises SourceError for a Doc without the parts of speech or the lemmas and for a string with a sentence longer than the max_length of the pipeline.
The examples of this page take a notice written in the administrative style:
from ests import StyleStats
official = (
"En el marco del procedimiento de referencia, y a efectos de dar cumplimiento a lo "
"dispuesto en la normativa vigente, se procedió a la revisión de la documentación "
"presentada. No obstante, en virtud de la resolución de la comisión, se llevará a cabo "
"la notificación de la misma a la mayor brevedad, en tiempo y forma."
)
ss = StyleStats(official)
ss.print_stats()
Metric | Value
-----------------------------------------------------
Classic nausea | 2.83
Academic nausea (%) | 17.86
Water content (%) | 57.14
Spam score (%) | 37.50
Naturalness by Zipf's law (%) | 53.57
Verbal nouns (% of nouns) | 47.06
Compound prepositions (per 100 words) | 5.36
Parenthetical expressions (per 100 words) | 1.79
Officialese clichés (per 100 words) | 8.93
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
source |
str / Doc |
- | Data source |
words_extractor |
WordsExtractor |
None |
Word extraction tool; for a Doc it is applied to its text |
stopwords |
list[str] / set[str] |
None |
Words of water; is_stopword if not given |
top_n |
int |
10 |
Number of the most frequent words of the academic nausea and the naturalness by Zipf's law (NAUSEA_TOP_N) |
cliches |
list[str] / set[str] |
None |
Clichés; OFFICIALESE_CLICHES if not given |
nlp |
Language |
None |
spaCy pipeline that parses a string for the verbal nouns; the model es_core_news_sm of ests.utils.get_nlp if not given |
nausea_stopwords |
list[str] / set[str] |
None |
Stopwords left out of the most frequent words of the academic nausea; is_stopword if not given |
A set of words is taken in sorted order. NAUSEA_TOP_N is a constant of anyts.constants.
check_params(top_n, *, messages=MESSAGES)
Checks the number of the most frequent words: one that is not an integer of at least one raises ParameterError.
Attributes¶
| Attribute | Type | Description |
|---|---|---|
words |
tuple[str] |
Words of the SEO metrics |
forms |
tuple[str] |
Word forms in lower case, of the markers |
The metrics are properties computed on each access.
The pipeline of the parameter nlp is kept in the attribute nlp, None if not given.
Metrics¶
get_stats gives all the metrics. A metric is computed by the function of its card on words or forms with the hooks of the class.
| Metric | Measures | Range | Higher means |
|---|---|---|---|
classic_nausea |
Repetition of the most frequent word | 0 and above, grows with the text | One word pressed harder |
academic_nausea |
Share of the most frequent significant words | 0-100 % | A narrower vocabulary |
water |
Share of the words without content | 0-100 % | Less content per word |
spam |
Share of the repeated words | 0-100 % | More repetition |
zipf_naturalness |
Agreement of the top frequencies with Zipf's law | 0-100 %, nan without ranks to compare |
Closer to a natural text |
verbal_nouns |
Share of the verbal nouns among the nouns | 0-100 %, nan without nouns |
A more nominal style |
compound_prepositions |
Compound prepositions per 100 words | 0-100 | More officialese |
parentheticals |
Parenthetical expressions per 100 words | 0-100 | More insertions |
cliches |
Cliches per 100 words | 0-100 | More cliches |
The functions of the cards are imported from ests.style_stats. The cards give the norms of Advego, Text.ru, pr-cy and megaindex; the norms of the services are meant for texts of several hundred words, and on short texts nausea and spam are uninformative. esTS gives the metrics no scales, so describe gives None; the markers of the officialese have no norms.
Classic nausea¶
calc_classic_nausea(text)
How much one word is pressed: the square root of the occurrences \(f_1\) of the most frequent word. The value is not corrected for the length of the text, so it grows with the text; no words give 0.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
The norm of Advego is at most 7, 1-5 in practice.
Academic nausea¶
calc_academic_nausea(text, top_n=10, stopwords=None)
The share of the occurrences of the top_n most frequent significant words among all the words, in percent. The exact formula of Advego is not published. With the stopwords the top ten of almost any text would be de, la, que, el, and its nausea above the norm. The norm of Advego is 5-15%, for texts of several hundred words: in a short text the top ten is a large share of the words whatever the style.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
top_n |
int |
10 |
Number of the most frequent words (NAUSEA_TOP_N) |
stopwords |
list[str] / set[str] |
None |
Stopwords left out of the most frequent words; is_stopword if not given |
Raises:
SourceTypeError- words that are not a list of strings, stopwords that are not a list or a set of strings;ParameterError- atop_nthat is not an integer of at least one.
find_nausea_words(text, top_n=10, stopwords=None)
The top_n most frequent words without the stopwords, those the academic nausea counts, with their occurrences: the most frequent first, words of equal frequency in the order of the text. A stopword is a word of stopwords, in any case, or, without the list, a word of is_stopword. The parameters and the errors are those of calc_academic_nausea.
is_stopword(word)
Whether a word is a stopword, in any case: the 265 forms of ests.constants.STOPWORDS - the closed classes of the grammar: articles and the other determiners (este, cada, mucho), pronouns (él, cuyo, nadie), prepositions, conjunctions and interjections, with the adverbs that point, relate or ask (aquí, así, dónde) and the ones that negate, affirm or focus (no, solo, también) - and the one-word parenthetical expressions of PARENTHETICALS (finalmente, naturalmente). The forms of the old orthography are included (á, ó, tí). StyleStats takes it for the academic nausea without nausea_stopwords and for the water content without stopwords.
| Parameter | Type | Default | Description |
|---|---|---|---|
word |
str |
- | Word |
Raises:
SourceTypeError- a word that is not a string.
from ests.style_stats import find_nausea_words, is_stopword
is_stopword("Aquel"), is_stopword("sin"), is_stopword("trigo")
find_nausea_words(["gato", "y", "gato", "y", "perro"], top_n=1)
(True, True, False)
{'gato': 2}
Water content¶
calc_water(text, stopwords=None)
The share of the words without content, in percent: the words of stopwords, in any case, or, without the list, the stopwords of is_stopword. The norms of Text.ru: up to 15% natural, 15-30% excessive, above 30% high.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
stopwords |
list[str] / set[str] |
None |
Words of water; is_stopword if not given |
Raises:
SourceTypeError- words that are not a list of strings, stopwords that are not a list or a set of strings.
The water content of Spanish
The norms of Text.ru are set for Russian, which has no articles; a Spanish text has more water by its grammar alone. Compare the water content of texts with each other, not with the norm.
Spam score¶
calc_spam(text)
The share of the repeated words in percent: every occurrence of a word after the first.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
To compute it by lemmas, extract the words with lemmatization. The norms of Text.ru: up to 30% natural, 30-60% SEO-optimized, above 60% spammed.
Naturalness by Zipf's law¶
calc_zipf_naturalness(text, top_n=10, *, messages=MESSAGES)
The agreement of the frequencies of the most frequent words with the ideal distribution \(f_r = f_1 / r\), where \(r\) is the rank. The first rank is ideal by construction, and above \(f_1\) the ideal frequency falls below one, so the ranks run from 2 to \(R = \min(\textrm{top\_n}, V, f_1)\). Negative values are cut to 0. Without ranks to compare - all the words hapaxes, one lexeme or top_n below 2 - the value is nan.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
top_n |
int |
10 |
Number of the most frequent words (NAUSEA_TOP_N) |
messages |
dict[str, str] |
MESSAGES |
Messages of the errors by key |
A top_n that is not an integer of at least one raises ParameterError.
The ideal distribution is that of Zipf's law; the norm of pr-cy and megaindex is at least 50%.
Verbal nouns¶
calc_verbal_nouns(nouns)
The share of the nouns derived from a verb among the lemmas of the nouns, in percent; nan without nouns. A noun is derived from a verb by its suffix - -ción, -sión, -miento, -anza, -encia, -ancia, -aje, -dura, -azgo (revisión, nombramiento, aprendizaje) - or is one of the nouns whose derivation leaves no suffix behind (uso, pago, envío), as in the split predicates of SyntaxStats. The rule also catches the nouns of other origins with the same endings (ciencia, distancia). The function takes the lemmas of the tokens tagged NOUN, not the words: a noun and a verb form may be spelt alike (uso, viaje).
| Parameter | Type | Default | Description |
|---|---|---|---|
nouns |
list[str] |
- | Lemmas of the nouns |
Raises:
SourceTypeError- nouns that are not a list of strings.
noun_lemmas(doc)
The lemmas of the tokens of a Doc tagged NOUN. StyleStats takes the nouns of a Doc the same way, and those of a string from its parse by the pipeline of nlp or get_nlp, run without the parser and the named entities.
| Parameter | Type | Default | Description |
|---|---|---|---|
doc |
Doc |
- | Doc object |
Raises:
SourceTypeError- a source that is not aDoc;SourceError- aDocwithout the parts of speech or the lemmas.
from ests.style_stats import calc_verbal_nouns, noun_lemmas
from ests.utils import get_nlp
calc_verbal_nouns(["revisión", "proyecto", "nombramiento", "casa"])
lemmas = noun_lemmas(get_nlp()("Se procedió a la revisión de los proyectos"))
lemmas, calc_verbal_nouns(lemmas)
50.0
(['revisión', 'proyecto'], 50.0)
Compound prepositions and clichés¶
calc_phrase_density(text, phrases)
The occurrences of the phrases of a list per 100 words, with the contractions and the forms of the verbs of expand_phrases. At every position the longest phrase is taken, and the phrases found do not overlap. StyleStats counts this way, over the word forms (forms), the compound prepositions of COMPOUND_PREPOSITIONS (en el marco de, a efectos de, en virtud de) and the clichés of OFFICIALESE_CLICHES (dar cumplimiento, proceder a, a la mayor brevedad) or of the parameter cliches.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
phrases |
list[str] / set[str] |
- | Phrases, words separated by spaces |
Raises:
SourceTypeError- words that are not a list of strings, phrases that are not a list or a set of strings.
expand_phrases(text, phrases)
The phrases spelled out in the forms of a text, each with the phrase of the list it spells out. A phrase ending in a or de also takes the contraction with the article (a efectos del, conforme al), and a phrase whose first word is an infinitive takes the forms of the text with that lemma (proceder a - procedió a, ser de aplicación - es de aplicación). The lemma is the one of simplemma, and a pronominal lemma counts for its verb (llévese - llevar); the forms simplemma does not lemmatize - some participles (ha dado, ha hecho) and the imperative dese - are given by IRREGULAR_VERB_FORMS. The words after the verb rule out the readings as a noun (el hecho, el puesto). When two phrases spell out the same words, a phrase written so wins over the forms of another (a efectos del stays itself next to a efectos de), and then the first phrase of a list, or of a set in sorted order.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
phrases |
list[str] / set[str] |
- | Phrases, words separated by spaces |
Raises:
SourceTypeError- words that are not a list of strings, phrases that are not a list or a set of strings.
from ests.constants import COMPOUND_PREPOSITIONS
from ests.style_stats import calc_phrase_density, expand_phrases
expand_phrases(["se", "procedió", "al", "cierre"], ["proceder a"])
words = "se procedió a la revisión en el marco del proyecto".split()
calc_phrase_density(words, COMPOUND_PREPOSITIONS)
{'proceder a': 'proceder a',
'proceder al': 'proceder a',
'procedió a': 'proceder a',
'procedió al': 'proceder a'}
10.0
The compound prepositions (42) and the clichés (74) are the ones the Spanish-language guides to plain language and style manuals of the administrations flag:
Left out are the forms the guides recommend (sobre la base de) or accept (de acuerdo a) and the phrases with frequent neutral uses (en este sentido), but for proceder a and llevar a cabo. A few compound prepositions have neutral uses too (a través de, en caso de, con respecto a), so the density of a neutral text is not zero: compare texts with each other.
Parenthetical expressions¶
calc_parentheticals(text)
The parenthetical expressions of PARENTHETICALS (sin embargo, es decir, por ejemplo, finalmente) per 100 words: the phrases of the list with their contractions (expand_phrases) and, outside them, the one-word expressions of the list. The punctuation is not looked at, so the list holds the expressions set off by commas or standing at the edge of a sentence in most of their occurrences in the corpus of literature, with the series of order (en primer lugar, en segundo lugar). The adverbs of doubt (tal vez, quizá), sobre todo, sin duda and además are not in the list.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
Raises:
SourceTypeError- words that are not a list of strings.
find_parentheticals(text)
The bounds of the parenthetical expressions of calc_parentheticals as slices of the list of words, in order. The parameter and the errors are those of calc_parentheticals.
is_parenthetical(word)
Whether a word is one of the one-word expressions of PARENTHETICALS (finalmente, naturalmente, verbigracia), in any case.
| Parameter | Type | Default | Description |
|---|---|---|---|
word |
str |
- | Word |
Raises:
SourceTypeError- a word that is not a string.
from ests.style_stats import calc_parentheticals, find_parentheticals, is_parenthetical
words = ["sin", "embargo", "el", "gato", "finalmente", "duerme"]
find_parentheticals(words), calc_parentheticals(words)
is_parenthetical("Finalmente")
([(0, 2), (4, 5)], 33.33333333333333)
True
Keyword density¶
calc_keyword_density(text, keywords, normalize=str.lower)
The occurrences of every keyword per 100 words. A keyword of several words separated by spaces is a sequence of words whose occurrences may overlap. The words and the keywords are compared by their normalized forms, and the keywords of a set come in sorted order.
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
list[str] |
- | List of words |
keywords |
list[str] |
- | Keywords or phrases |
normalize |
Callable[[str], str] |
str.lower |
Normalized form of a word and of a keyword |
To compare by lemmas, extract the words with lemmatization and pass lemmas.
from ests import StyleStats
text = "Tres tristes tigres tragaban trigo en un trigal, en tres tristes trastos"
StyleStats(text).keyword_density("tres tristes", "trigo")
{'tres tristes': 16.666666666666668, 'trigo': 8.333333333333334}
The functions of the core cards check their words too: words and keywords that are not a list of strings, and a normalize that cannot be called, raise SourceTypeError.
Methods¶
keyword_density(*keywords) gives the density of keywords and phrases in percent (calc_keyword_density with normalize of the class), nausea_words() the most frequent significant words of the academic nausea with their occurrences (find_nausea_words).
markers() returns the markers of the officialese found by metric - verbal_nouns, compound_prepositions, parentheticals, cliches - as the forms write them, in the order of the text.
A phrase is found with its contractions and the forms of its verb (expand_phrases). The verbal nouns need the parts of speech and the lemmas, as their metric does, so a string is parsed by the model and a Doc without them raises SourceError.
ss.markers()
{'verbal_nouns': ('procedimiento',
'referencia',
'cumplimiento',
'revisión',
'documentación',
'resolución',
'comisión',
'notificación'),
'compound_prepositions': ('en el marco del', 'a efectos de', 'en virtud de'),
'parentheticals': ('no obstante',),
'cliches': ('dar cumplimiento',
'procedió a',
'llevará a cabo',
'a la mayor brevedad',
'en tiempo y forma')}
The notice of 56 words has three compound prepositions, five clichés and one parenthetical expression, and almost half of its nouns are derived from a verb. The anaphoric de la misma, which the guides also advise against, is not counted.
The other methods - get_stats, to_dataframe, print_stats and describe - are those of the statistics interface of the anyTS core; the style metrics of esTS have no scales, so describe gives None for the metrics of get_stats. The same notice in plain language:
plain = (
"Hemos revisado los documentos que nos envió, como pide la ley. La comisión ha "
"decidido su caso y le avisaremos lo antes posible."
)
StyleStats(plain).get_stats()
ss.describe("water"), ss.describe("cliches")
{'classic_nausea': 1.4142135623730951,
'academic_nausea': 43.47826086956522,
'water': 43.47826086956522,
'spam': 4.3478260869565215,
'zipf_naturalness': 100.0,
'verbal_nouns': 25.0,
'compound_prepositions': 0.0,
'parentheticals': 0.0,
'cliches': 0.0}
(None, None)
The plain version has no compound prepositions, parenthetical expressions or clichés, and a quarter of its nouns are derived from a verb. The academic nausea says nothing of the style here: neither notice repeats a significant word, so it is ten words over the length of the text, 10/56 and 10/23.