Lexical sophistication¶
LexicalStats(source, freq_dict=None)
Lexical sophistication in the manner of TAALES (Kyle, Crossley and Berger, 2018) - how rare the words of a text are: statistics by a frequency dictionary and its unigram model, the shares of the frequency bands of a list of the most frequent lemmas and the lexical density. The statistics by the dictionary are computed when first read, so the bands and the lexical density need no dictionary data.
Raises:
SourceTypeError- a dictionary of another class than that of the library or, withoutfreq_dict_class, one withoutlookupandipmor none at all; ananalyzethat does not giveLexicalWordobjects;SourceError- a source without words.
from typing import NamedTuple
from anyts.lexical_stats import LexicalStats, LexicalWord
class Entry(NamedTuple):
ipm: float
range: int
dispersion: float
class Frequencies:
min_ipm = 0.5
entries = {"the": Entry(50000.0, 40, 0.99), "cat": Entry(100.0, 38, 0.9)}
def lookup(self, lemma):
return self.entries.get(lemma)
def ipm(self, lemma):
entry = self.lookup(lemma)
return entry.ipm if entry is not None else 0.0
class Lexical(LexicalStats):
freq_dict_class = Frequencies
def analyze(self, source):
return [LexicalWord(word, word.lower(), word.lower() != "the") for word in source.split()]
def rank(self, lemma):
return {"the": 1, "cat": 1500}.get(lemma)
stats = Lexical("The cat sees the dog")
stats.print_stats()
Statistic | Value
------------------------------------------------------------
Share of words found in the frequency dictionary | 0.60
Mean frequency (ipm) | 33366.67
Mean frequency of content words (ipm) | 100.00
Mean log frequency (lg ipm) | 3.80
Mean log frequency of content words | 2.00
Mean range | 39.33
Mean dispersion (D) | 0.96
Mean surprisal (bits) | 12.76
Unigram perplexity | 6931.45
Share of words in the top 1000 | 0.40
Share of words in the top 2000 | 0.60
Share of words in the top 5000 | 0.60
Share of words in the top 10000 | 0.60
Share of words beyond the top 10000 | 0.40
Lexical density | 0.60
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
source |
str / Doc |
- | Data source |
freq_dict |
FrequencyDictionary |
None |
Frequency dictionary; freq_dict_class() by default |
Attributes¶
| Attribute | Type | Description |
|---|---|---|
freq_dict |
FrequencyDictionary |
Frequency dictionary |
words, lemmas |
tuple[str] |
Words and their lemmas; the bands are counted by the lemmas |
keys |
tuple[str] |
Keys of the words in the dictionary |
entries |
tuple[FrequencyEntry / None] |
Entries of the dictionary, None for a word out of it |
ranks |
tuple[int / None] |
Ranks of the lemmas, None out of the list |
n_words, n_content_words, n_found |
int |
Number of words, of content words and of words found in the dictionary |
Frequency dictionary¶
The dictionary is any object of the protocol FrequencyDictionary: lookup(lemma) gives the entry of a lemma or None, ipm(lemma) its frequency per million words or 0, and min_ipm the lowest frequency of the dictionary, above zero. An entry (FrequencyEntry) has the frequency ipm, above zero, the range range (the parts of the corpus with the lemma) and the dispersion dispersion. Without freq_dict_class a dictionary without lookup and ipm raises SourceTypeError.
FrequencyEntry is the entry of a lemma in a FrequencyDictionary: ipm, range and dispersion.
Metrics¶
Every metric is an attribute or a property of the object, and get_stats gives them all. A mean is over the words found in the dictionary, and without them it is nan.
| Metric | Measures | Range | Higher means |
|---|---|---|---|
coverage |
Share of the words found in the dictionary | 0-1 | More words known to the dictionary |
mean_ipm, mean_ipm_content |
Mean frequency per million words of the words found, of the content words found | Above 0 | More frequent words |
mean_log_ipm, mean_log_ipm_content |
Mean decimal logarithm of the frequency of the words found, of the content words found | Up to 6 | More frequent words |
mean_range |
Mean range of the words found | 0 and above | Words used in more parts of the corpus |
mean_dispersion |
Mean dispersion of the words found | That of the dictionary | By the measure of the dictionary |
surprisal |
Mean surprisal of the words by the unigram model of the dictionary, in bits | 0 and above | Rarer words |
perplexity |
2 to the power of the surprisal | 1 and above | Rarer words |
p_top1000, p_top2000, p_top5000, p_top10000 |
Share of the words with a lemma in the top 1000, 2000, 5000 and 10000 of the list | 0-1, nan for a band beyond top_size |
More frequent words |
p_beyond_top10000 |
Share of the words with a lemma beyond the top 10000 | 0-1, nan for a list shorter than 10000 |
Rarer words |
lexical_density |
Share of the content words | 0-1 | More content words |
Surprisal and perplexity¶
calc_surprisal(lemmas, freq_dict)
The mean surprisal of the words by the unigram model of a frequency dictionary, in bits; a word out of the dictionary gets its lowest frequency min_ipm. The perplexity is 2 to its power.
| Parameter | Type | Default | Description |
|---|---|---|---|
lemmas |
list[str] |
- | Keys of the words in the dictionary |
freq_dict |
FrequencyDictionary |
- | Frequency dictionary |
No words give nan; keys that are not a list of strings and a dictionary without ipm and min_ipm raise SourceTypeError, a lowest frequency that is not above zero ParameterError.
Frequency bands¶
The share of the words whose lemma has a rank (rank) within the top \(N\) of the list, for the bounds 1000, 2000, 5000 and 10000 of FREQUENCY_BANDS; the share beyond the top 10000 is the rest, the words out of the list included. A band beyond the size of the list (top_size) is nan, and so is the share beyond the top 10000 for a list shorter than 10000.
Methods¶
band_coverage(bands, unique) gives the share of the words with a lemma in the top N of the list for every bound N of bands (FREQUENCY_BANDS by default), or with unique=True the share of the distinct lemmas. Bounds that are not a list of integers or a bound out of 1 and top_size() raise ParameterError.
stats.band_coverage([1000, 2000])
stats.band_coverage([1000, 2000], unique=True)
{1000: 0.4, 2000: 0.6}
{1000: 0.25, 2000: 0.5}
The other methods - get_stats, to_dataframe, print_stats and describe - are those of the statistics interface.
For library developers¶
Language hooks of a subclass
A language library subclasses LexicalStats:
| Hook | Kind | Description |
|---|---|---|
analyze(source) |
method | The words of a source as LexicalWord: the word, its lemma and whether it is a content word; an abstract method. Numbers (is_number of anyts.utils) are not words |
freq_dict_class |
class attribute | Class of the frequency dictionary, made without arguments when none is given; a dictionary of another class is refused, and without the class a dictionary must be given |
dictionary_keys() |
method | Keys of the words in the dictionary; the lemmas by default, or for instance a proper noun by its form |
rank(lemma) |
method | Rank of a lemma in the list of the most frequent lemmas, None out of it; no list by default |
top_size() |
method | Size of the list, the upper bound of band_coverage; 10000 by default |
stats_desc, stats_headers |
class attributes | Statistics of get_stats with their descriptions (anyts.constants.LEXICAL_STATS_DESC by default), headers of the columns for print_stats |
messages |
class attribute | Messages of the errors by key; MESSAGES by default |
A library takes its own parameters, such as a pipeline or a word extractor, in its __init__ and passes the source and the dictionary on. An analyze that does not give LexicalWord objects raises SourceTypeError.