anyts 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anyts-0.1.0/.gitignore +27 -0
- anyts-0.1.0/LICENSE.txt +17 -0
- anyts-0.1.0/PKG-INFO +131 -0
- anyts-0.1.0/README.md +96 -0
- anyts-0.1.0/anyts/__init__.py +49 -0
- anyts-0.1.0/anyts/cohesion.py +292 -0
- anyts-0.1.0/anyts/constants.py +84 -0
- anyts-0.1.0/anyts/corpus/__init__.py +49 -0
- anyts-0.1.0/anyts/corpus/collocations.py +315 -0
- anyts-0.1.0/anyts/corpus/compare.py +388 -0
- anyts-0.1.0/anyts/corpus/dispersion.py +334 -0
- anyts-0.1.0/anyts/corpus/keyness.py +438 -0
- anyts-0.1.0/anyts/corpus/stylometry.py +527 -0
- anyts-0.1.0/anyts/diversity_stats.py +1673 -0
- anyts-0.1.0/anyts/exceptions.py +76 -0
- anyts-0.1.0/anyts/extractors.py +549 -0
- anyts-0.1.0/anyts/py.typed +0 -0
- anyts-0.1.0/anyts/syntax.py +231 -0
- anyts-0.1.0/anyts/utils.py +285 -0
- anyts-0.1.0/pyproject.toml +160 -0
anyts-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.egg-info/
|
|
4
|
+
dist/
|
|
5
|
+
build/
|
|
6
|
+
target/
|
|
7
|
+
|
|
8
|
+
# Environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# Tools
|
|
14
|
+
.coverage
|
|
15
|
+
coverage.xml
|
|
16
|
+
.mypy_cache
|
|
17
|
+
.pytest_cache
|
|
18
|
+
.ruff_cache
|
|
19
|
+
.ipynb_checkpoints
|
|
20
|
+
|
|
21
|
+
# Documentation
|
|
22
|
+
site/
|
|
23
|
+
|
|
24
|
+
# Editors and OS
|
|
25
|
+
.vscode
|
|
26
|
+
.idea
|
|
27
|
+
.DS_Store
|
anyts-0.1.0/LICENSE.txt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2026 Sergey Shkarin
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
The above copyright notice and this permission notice shall be included in all
|
|
10
|
+
copies or substantial portions of the Software.
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
12
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
13
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
14
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
15
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
17
|
+
SOFTWARE.
|
anyts-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: anyts
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Any Texts Statistics: the language-independent core of text statistics libraries
|
|
5
|
+
Project-URL: Homepage, https://github.com/SergeyShk/anyTS
|
|
6
|
+
Project-URL: Repository, https://github.com/SergeyShk/anyTS
|
|
7
|
+
Project-URL: Documentation, https://sergeyshk.github.io/anyTS/
|
|
8
|
+
Project-URL: Issues, https://github.com/SergeyShk/anyTS/issues
|
|
9
|
+
Author-email: Sergey Shkarin <kouki.sergey@gmail.com>
|
|
10
|
+
Maintainer-email: Sergey Shkarin <kouki.sergey@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE.txt
|
|
13
|
+
Keywords: CL,NLP,analytics,computational,language,linguistics,natural,processing,statistics,text
|
|
14
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Education
|
|
17
|
+
Classifier: Intended Audience :: Information Technology
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Topic :: Text Processing
|
|
27
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.11
|
|
30
|
+
Requires-Dist: numpy>=1.26
|
|
31
|
+
Requires-Dist: pandas>=2.1
|
|
32
|
+
Requires-Dist: scipy>=1.11
|
|
33
|
+
Requires-Dist: spacy>=3.7
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Any Texts Statistics (anyTS)
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/anyts/)
|
|
39
|
+
[](https://pypi.org/project/anyts/)
|
|
40
|
+
[](https://github.com/SergeyShk/anyTS/actions/workflows/ci.yml)
|
|
41
|
+
[](https://github.com/astral-sh/ruff)
|
|
42
|
+
[](https://github.com/SergeyShk/anyTS/blob/master/LICENSE.txt)
|
|
43
|
+
|
|
44
|
+
**anyTS** is the language-independent core of the text statistics libraries [ruTS](https://github.com/SergeyShk/ruTS) (Russian) and [esTS](https://github.com/SergeyShk/esTS) (Spanish). It holds the code that does not depend on a language, so that it is written, tested and documented once; a language library adds its tokenizer, lemmatizer, [spaCy](https://github.com/explosion/spaCy) model and rules and re-exports the core under its own names.
|
|
45
|
+
|
|
46
|
+
[Documentation](https://sergeyshk.github.io/anyTS/) · [PyPI](https://pypi.org/project/anyts/)
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
* **[Extraction](https://sergeyshk.github.io/anyTS/extractors/sentences/)** - sentence, word and character N-gram extractors whose tokenizer, lemmatizer and number pattern are hooks a language library overrides
|
|
51
|
+
* **[Lexical diversity](https://sergeyshk.github.io/anyTS/stats/diversity_stats/)** - 32 metrics: TTR and its variants, MATTR, MSTTR, MTLD, MA-MTLD, MTLD-W, HD-D, the indices of Simpson and Yule, entropy, the laws of Zipf and Heaps, over the whole text or over windows with confidence intervals
|
|
52
|
+
* **[Cohesion helpers](https://sergeyshk.github.io/anyTS/stats/cohesion/)** - the overlap of sentences, adjacent and over all pairs, Dice, givenness and repetition
|
|
53
|
+
* **[Dependency tree helpers](https://sergeyshk.github.io/anyTS/stats/syntax/)** - dependency distances, tree depth, valency and coordination chains on the labels shared by Universal Dependencies and ClearNLP
|
|
54
|
+
* **[Corpus measures](https://sergeyshk.github.io/anyTS/corpus/keyness/)** - keywords against a corpus or a frequency dictionary, collocations, the dispersion of a word, Burrows's Delta and its variants, Zeta, Kilgarriff's chi-square, the Mendenhall curve and the comparison of corpora with effect sizes, a bootstrap by texts and the Holm correction
|
|
55
|
+
* **[Exceptions](https://sergeyshk.github.io/anyTS/exceptions/)** - one hierarchy whose classes are also built-in exceptions
|
|
56
|
+
|
|
57
|
+
The reference of every function is a named section that the libraries include in their own documentation, so a formula is described in one place.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Requires Python 3.11 or newer.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install anyts
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv add anyts
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The dependencies are numpy, pandas, scipy and spaCy; no trained spaCy model is needed.
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
>>> from anyts import DiversityStats, SentsExtractor, WordsExtractor
|
|
79
|
+
|
|
80
|
+
>>> text = "The cat sat on the mat. The dog sat on the log! The cat and the dog slept."
|
|
81
|
+
|
|
82
|
+
>>> SentsExtractor().extract(text)
|
|
83
|
+
('The cat sat on the mat.', 'The dog sat on the log!', 'The cat and the dog slept.')
|
|
84
|
+
|
|
85
|
+
>>> ds = DiversityStats(WordsExtractor(lowercase=True).extract(text))
|
|
86
|
+
>>> ds.ttr, round(ds.entropy, 3)
|
|
87
|
+
(0.5, 2.864)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A language library subclasses the extractors and overrides their hooks:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
>>> class Words(WordsExtractor):
|
|
94
|
+
... def lemmatize(self, word):
|
|
95
|
+
... return {"sat": "sit", "slept": "sleep"}.get(word.lower(), word)
|
|
96
|
+
|
|
97
|
+
>>> Words(lowercase=True, use_lexemes=True).extract("The cat sat. The dog slept.")
|
|
98
|
+
('the', 'cat', 'sit', 'the', 'dog', 'sleep')
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The corpus measures take lists of words:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
>>> from anyts.corpus import keyness
|
|
105
|
+
|
|
106
|
+
>>> cats = WordsExtractor(lowercase=True).extract("The cat sat on the mat. The cat saw a bird. The cat slept.")
|
|
107
|
+
>>> dogs = WordsExtractor(lowercase=True).extract("The dog sat on the log. The dog saw a cat. The dog barked.")
|
|
108
|
+
>>> [(keyword.word, round(keyword.log_ratio, 2)) for keyword in keyness(cats, dogs, min_freq=2)]
|
|
109
|
+
[('cat', 1.58)]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
The project uses [uv](https://docs.astral.sh/uv/) for dependencies and [ruff](https://docs.astral.sh/ruff/) for linting and formatting.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/SergeyShk/anyTS.git
|
|
118
|
+
cd anyTS
|
|
119
|
+
|
|
120
|
+
make deps # create the environment and install all dependencies
|
|
121
|
+
uv run pre-commit install # hooks: linters on commit, tests on push
|
|
122
|
+
make lint # ruff check, ruff format --check, mypy
|
|
123
|
+
make test-cov # pytest with doctests and full coverage
|
|
124
|
+
make docs-build # mkdocs build --strict
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The full list of commands is in `make help`. Contribution guidelines are in [CONTRIBUTING.md](https://github.com/SergeyShk/anyTS/blob/master/CONTRIBUTING.md).
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
[MIT](https://github.com/SergeyShk/anyTS/blob/master/LICENSE.txt)
|
anyts-0.1.0/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Any Texts Statistics (anyTS)
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/anyts/)
|
|
4
|
+
[](https://pypi.org/project/anyts/)
|
|
5
|
+
[](https://github.com/SergeyShk/anyTS/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](https://github.com/SergeyShk/anyTS/blob/master/LICENSE.txt)
|
|
8
|
+
|
|
9
|
+
**anyTS** is the language-independent core of the text statistics libraries [ruTS](https://github.com/SergeyShk/ruTS) (Russian) and [esTS](https://github.com/SergeyShk/esTS) (Spanish). It holds the code that does not depend on a language, so that it is written, tested and documented once; a language library adds its tokenizer, lemmatizer, [spaCy](https://github.com/explosion/spaCy) model and rules and re-exports the core under its own names.
|
|
10
|
+
|
|
11
|
+
[Documentation](https://sergeyshk.github.io/anyTS/) · [PyPI](https://pypi.org/project/anyts/)
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
* **[Extraction](https://sergeyshk.github.io/anyTS/extractors/sentences/)** - sentence, word and character N-gram extractors whose tokenizer, lemmatizer and number pattern are hooks a language library overrides
|
|
16
|
+
* **[Lexical diversity](https://sergeyshk.github.io/anyTS/stats/diversity_stats/)** - 32 metrics: TTR and its variants, MATTR, MSTTR, MTLD, MA-MTLD, MTLD-W, HD-D, the indices of Simpson and Yule, entropy, the laws of Zipf and Heaps, over the whole text or over windows with confidence intervals
|
|
17
|
+
* **[Cohesion helpers](https://sergeyshk.github.io/anyTS/stats/cohesion/)** - the overlap of sentences, adjacent and over all pairs, Dice, givenness and repetition
|
|
18
|
+
* **[Dependency tree helpers](https://sergeyshk.github.io/anyTS/stats/syntax/)** - dependency distances, tree depth, valency and coordination chains on the labels shared by Universal Dependencies and ClearNLP
|
|
19
|
+
* **[Corpus measures](https://sergeyshk.github.io/anyTS/corpus/keyness/)** - keywords against a corpus or a frequency dictionary, collocations, the dispersion of a word, Burrows's Delta and its variants, Zeta, Kilgarriff's chi-square, the Mendenhall curve and the comparison of corpora with effect sizes, a bootstrap by texts and the Holm correction
|
|
20
|
+
* **[Exceptions](https://sergeyshk.github.io/anyTS/exceptions/)** - one hierarchy whose classes are also built-in exceptions
|
|
21
|
+
|
|
22
|
+
The reference of every function is a named section that the libraries include in their own documentation, so a formula is described in one place.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Requires Python 3.11 or newer.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install anyts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or with [uv](https://docs.astral.sh/uv/):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv add anyts
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The dependencies are numpy, pandas, scipy and spaCy; no trained spaCy model is needed.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
>>> from anyts import DiversityStats, SentsExtractor, WordsExtractor
|
|
44
|
+
|
|
45
|
+
>>> text = "The cat sat on the mat. The dog sat on the log! The cat and the dog slept."
|
|
46
|
+
|
|
47
|
+
>>> SentsExtractor().extract(text)
|
|
48
|
+
('The cat sat on the mat.', 'The dog sat on the log!', 'The cat and the dog slept.')
|
|
49
|
+
|
|
50
|
+
>>> ds = DiversityStats(WordsExtractor(lowercase=True).extract(text))
|
|
51
|
+
>>> ds.ttr, round(ds.entropy, 3)
|
|
52
|
+
(0.5, 2.864)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
A language library subclasses the extractors and overrides their hooks:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
>>> class Words(WordsExtractor):
|
|
59
|
+
... def lemmatize(self, word):
|
|
60
|
+
... return {"sat": "sit", "slept": "sleep"}.get(word.lower(), word)
|
|
61
|
+
|
|
62
|
+
>>> Words(lowercase=True, use_lexemes=True).extract("The cat sat. The dog slept.")
|
|
63
|
+
('the', 'cat', 'sit', 'the', 'dog', 'sleep')
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The corpus measures take lists of words:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
>>> from anyts.corpus import keyness
|
|
70
|
+
|
|
71
|
+
>>> cats = WordsExtractor(lowercase=True).extract("The cat sat on the mat. The cat saw a bird. The cat slept.")
|
|
72
|
+
>>> dogs = WordsExtractor(lowercase=True).extract("The dog sat on the log. The dog saw a cat. The dog barked.")
|
|
73
|
+
>>> [(keyword.word, round(keyword.log_ratio, 2)) for keyword in keyness(cats, dogs, min_freq=2)]
|
|
74
|
+
[('cat', 1.58)]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
The project uses [uv](https://docs.astral.sh/uv/) for dependencies and [ruff](https://docs.astral.sh/ruff/) for linting and formatting.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git clone https://github.com/SergeyShk/anyTS.git
|
|
83
|
+
cd anyTS
|
|
84
|
+
|
|
85
|
+
make deps # create the environment and install all dependencies
|
|
86
|
+
uv run pre-commit install # hooks: linters on commit, tests on push
|
|
87
|
+
make lint # ruff check, ruff format --check, mypy
|
|
88
|
+
make test-cov # pytest with doctests and full coverage
|
|
89
|
+
make docs-build # mkdocs build --strict
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The full list of commands is in `make help`. Contribution guidelines are in [CONTRIBUTING.md](https://github.com/SergeyShk/anyTS/blob/master/CONTRIBUTING.md).
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
[MIT](https://github.com/SergeyShk/anyTS/blob/master/LICENSE.txt)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Any Texts Statistics (anyTS)
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026
|
|
4
|
+
# Author: Sergey Shkarin <kouki.sergey@gmail.com>
|
|
5
|
+
# URL: <https://github.com/SergeyShk/anyTS>
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
9
|
+
|
|
10
|
+
from .diversity_stats import DiversityStats
|
|
11
|
+
from .exceptions import (
|
|
12
|
+
AnyTSError,
|
|
13
|
+
DataFileError,
|
|
14
|
+
DatasetNotFoundError,
|
|
15
|
+
DownloadError,
|
|
16
|
+
ParameterError,
|
|
17
|
+
SourceError,
|
|
18
|
+
SourceTypeError,
|
|
19
|
+
UnknownStatError,
|
|
20
|
+
)
|
|
21
|
+
from .extractors import CharNgramsExtractor, SentsExtractor, WordsExtractor
|
|
22
|
+
|
|
23
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
__version__ = version("anyts")
|
|
27
|
+
except PackageNotFoundError:
|
|
28
|
+
__version__ = "0.0.0"
|
|
29
|
+
__description__ = (
|
|
30
|
+
"The language-independent core of text statistics libraries. Requires Python 3.11+"
|
|
31
|
+
)
|
|
32
|
+
__author__ = "Sergey Shkarin"
|
|
33
|
+
__author_email__ = "kouki.sergey@gmail.com"
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"AnyTSError",
|
|
37
|
+
"CharNgramsExtractor",
|
|
38
|
+
"DataFileError",
|
|
39
|
+
"DatasetNotFoundError",
|
|
40
|
+
"DiversityStats",
|
|
41
|
+
"DownloadError",
|
|
42
|
+
"ParameterError",
|
|
43
|
+
"SentsExtractor",
|
|
44
|
+
"SourceError",
|
|
45
|
+
"SourceTypeError",
|
|
46
|
+
"UnknownStatError",
|
|
47
|
+
"WordsExtractor",
|
|
48
|
+
"__version__",
|
|
49
|
+
]
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from collections import Counter
|
|
2
|
+
from collections.abc import Collection, Iterable, Sequence
|
|
3
|
+
from itertools import pairwise
|
|
4
|
+
from math import fsum, nan
|
|
5
|
+
from typing import NamedTuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from .utils import check_sequence, check_words, safe_divide
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _check_sents(sents: Iterable[Iterable[object]], what: str) -> None:
|
|
13
|
+
"""Checking a list of sentences given as collections of their elements"""
|
|
14
|
+
check_sequence(sents, "sentences")
|
|
15
|
+
for sent in sents:
|
|
16
|
+
check_words(sent, what)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Overlap(NamedTuple):
|
|
20
|
+
"""
|
|
21
|
+
Overlaps of the sentences of a text
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
adjacent (float): Share of adjacent pairs of sentences with a shared element
|
|
25
|
+
all (float): Share of all pairs of sentences with a shared element
|
|
26
|
+
prop_adjacent (float): Mean share of shared elements in adjacent pairs
|
|
27
|
+
prop_all (float): Mean share of shared elements in all pairs
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
adjacent: float
|
|
31
|
+
all: float
|
|
32
|
+
prop_adjacent: float
|
|
33
|
+
prop_all: float
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def calc_overlap(sets: Sequence[Collection[str]], adjacent: bool = True) -> float:
|
|
37
|
+
"""
|
|
38
|
+
Computing the share of pairs of sentences with a shared element
|
|
39
|
+
|
|
40
|
+
Description:
|
|
41
|
+
The binary overlap of Coh-Metrix (CRFNO1, CRFAO1, CRFSO1 over the
|
|
42
|
+
adjacent pairs, CRFNOa, CRFAOa, CRFSOa over all): a pair of sentences is
|
|
43
|
+
cohesive when they share at least one element; all the pairs are counted
|
|
44
|
+
over bit masks of the sentences, without going through them
|
|
45
|
+
|
|
46
|
+
Arguments:
|
|
47
|
+
sets (list[set[str]]): Elements of every sentence
|
|
48
|
+
adjacent (bool): Count the adjacent pairs only, otherwise all the pairs
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
float: Share of the pairs with a shared element, nan for a text shorter
|
|
52
|
+
than two sentences
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
SourceTypeError: If the sentences are a string, a Doc or an iterator, or a sentence
|
|
56
|
+
is not a list of strings (check_words)
|
|
57
|
+
"""
|
|
58
|
+
_check_sents(sets, "elements of a sentence")
|
|
59
|
+
frozen = [frozenset(elements) for elements in sets]
|
|
60
|
+
n_sents = len(frozen)
|
|
61
|
+
if n_sents < 2:
|
|
62
|
+
return nan
|
|
63
|
+
if adjacent:
|
|
64
|
+
return sum(1 for first, second in pairwise(frozen) if not first.isdisjoint(second)) / (
|
|
65
|
+
n_sents - 1
|
|
66
|
+
)
|
|
67
|
+
return _count_sharing_pairs(frozen) / (n_sents * (n_sents - 1) // 2)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def calc_proportional_overlap(sets: Sequence[Collection[str]], adjacent: bool = True) -> float:
|
|
71
|
+
"""
|
|
72
|
+
Computing the mean share of shared elements in pairs of sentences
|
|
73
|
+
|
|
74
|
+
Description:
|
|
75
|
+
The proportional overlap of Coh-Metrix (CRFCWO1, CRFCWOa): the Dice
|
|
76
|
+
coefficient of a pair of sentences (dice) averaged over the pairs; over all
|
|
77
|
+
the pairs the sum comes from the histograms of the lengths of the sentences
|
|
78
|
+
holding every element (see calc_overlaps), without going through them
|
|
79
|
+
|
|
80
|
+
Arguments:
|
|
81
|
+
sets (list[set[str]]): Elements of every sentence
|
|
82
|
+
adjacent (bool): Count the adjacent pairs only, otherwise all the pairs
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
float: Mean share of shared elements, nan for a text shorter than two sentences
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
SourceTypeError: If the sentences are a string, a Doc or an iterator, or a sentence
|
|
89
|
+
is not a list of strings (check_words)
|
|
90
|
+
"""
|
|
91
|
+
_check_sents(sets, "elements of a sentence")
|
|
92
|
+
frozen = [frozenset(elements) for elements in sets]
|
|
93
|
+
n_sents = len(frozen)
|
|
94
|
+
if n_sents < 2:
|
|
95
|
+
return nan
|
|
96
|
+
if adjacent:
|
|
97
|
+
return sum(dice(first, second) for first, second in pairwise(frozen)) / (n_sents - 1)
|
|
98
|
+
return _sum_dice(frozen) / (n_sents * (n_sents - 1) // 2)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def dice(first: frozenset[str], second: frozenset[str]) -> float:
|
|
102
|
+
"""
|
|
103
|
+
Computing the Dice coefficient of two sets
|
|
104
|
+
|
|
105
|
+
Arguments:
|
|
106
|
+
first (frozenset[str]): First set
|
|
107
|
+
second (frozenset[str]): Second set
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
float: The coefficient 2·|A ∩ B| / (|A| + |B|), 0 for two empty sets
|
|
111
|
+
"""
|
|
112
|
+
return safe_divide(2 * len(first & second), len(first) + len(second))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def calc_overlaps(sets: Sequence[Collection[str]], proportional: bool = True) -> Overlap:
|
|
116
|
+
"""
|
|
117
|
+
Computing the binary and the proportional overlap over adjacent and all pairs
|
|
118
|
+
|
|
119
|
+
Description:
|
|
120
|
+
Gives the values of calc_overlap and calc_proportional_overlap over the
|
|
121
|
+
adjacent and all the pairs at once
|
|
122
|
+
|
|
123
|
+
Arguments:
|
|
124
|
+
sets (list[set[str]]): Elements of every sentence
|
|
125
|
+
proportional (bool): Compute the proportional overlap as well; without it
|
|
126
|
+
prop_adjacent and prop_all are nan
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Overlap: Shares of the pairs with a shared element and the mean Dice
|
|
130
|
+
coefficients, nan for a text shorter than two sentences
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
SourceTypeError: If the sentences are a string, a Doc or an iterator, or a sentence
|
|
134
|
+
is not a list of strings (check_words)
|
|
135
|
+
"""
|
|
136
|
+
_check_sents(sets, "elements of a sentence")
|
|
137
|
+
frozen = [frozenset(elements) for elements in sets]
|
|
138
|
+
n_sents = len(frozen)
|
|
139
|
+
if n_sents < 2:
|
|
140
|
+
return Overlap(nan, nan, nan, nan)
|
|
141
|
+
n_all = n_sents * (n_sents - 1) // 2
|
|
142
|
+
return Overlap(
|
|
143
|
+
sum(1 for first, second in pairwise(frozen) if not first.isdisjoint(second))
|
|
144
|
+
/ (n_sents - 1),
|
|
145
|
+
_count_sharing_pairs(frozen) / n_all,
|
|
146
|
+
sum(dice(first, second) for first, second in pairwise(frozen)) / (n_sents - 1)
|
|
147
|
+
if proportional
|
|
148
|
+
else nan,
|
|
149
|
+
_sum_dice(frozen) / n_all if proportional else nan,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _count_sharing_pairs(sets: Sequence[frozenset[str]], block_size: int = 4096) -> int:
|
|
154
|
+
"""
|
|
155
|
+
Number of pairs of sentences with a shared element, over bit masks of the occurrences
|
|
156
|
+
|
|
157
|
+
Description:
|
|
158
|
+
Every element gets a mask of the sentences of a block it occurs in; the
|
|
159
|
+
union of the masks of sentence i gives the sentences it shares an
|
|
160
|
+
element with, and the bits after i are counted
|
|
161
|
+
"""
|
|
162
|
+
total = 0
|
|
163
|
+
for start in range(0, len(sets), block_size):
|
|
164
|
+
end = min(start + block_size, len(sets))
|
|
165
|
+
masks: dict[str, int] = {}
|
|
166
|
+
for j in range(start, end):
|
|
167
|
+
bit = 1 << (j - start)
|
|
168
|
+
for element in sets[j]:
|
|
169
|
+
masks[element] = masks.get(element, 0) | bit
|
|
170
|
+
for i in range(end):
|
|
171
|
+
union = 0
|
|
172
|
+
for element in sets[i]:
|
|
173
|
+
union |= masks.get(element, 0)
|
|
174
|
+
if i >= start:
|
|
175
|
+
union >>= i - start + 1
|
|
176
|
+
total += union.bit_count()
|
|
177
|
+
return total
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _sum_dice(sets: Sequence[frozenset[str]]) -> float:
|
|
181
|
+
"""
|
|
182
|
+
Sum of the Dice coefficients over all the pairs, over histograms of the lengths
|
|
183
|
+
|
|
184
|
+
Description:
|
|
185
|
+
A pair of sentences of lengths k and l gives 2/(k + l) for every shared
|
|
186
|
+
element, so the sum over the pairs holding an element is
|
|
187
|
+
(h·W·h - h·diag(W)) / 2, where h is the histogram of the lengths of its
|
|
188
|
+
sentences and W[k, l] = 2/(k + l); the sums of the elements are added
|
|
189
|
+
by fsum, so the result does not depend on their order
|
|
190
|
+
"""
|
|
191
|
+
sizes = sorted({len(elements) for elements in sets if elements})
|
|
192
|
+
columns = {size: column for column, size in enumerate(sizes)}
|
|
193
|
+
postings: dict[str, list[int]] = {}
|
|
194
|
+
for elements in sets:
|
|
195
|
+
if not elements:
|
|
196
|
+
continue
|
|
197
|
+
column = columns[len(elements)]
|
|
198
|
+
for element in elements:
|
|
199
|
+
postings.setdefault(element, []).append(column)
|
|
200
|
+
rows = [row for row in postings.values() if len(row) > 1]
|
|
201
|
+
if not rows:
|
|
202
|
+
return 0.0
|
|
203
|
+
histograms = np.zeros((len(rows), len(sizes)))
|
|
204
|
+
for index, row in enumerate(rows):
|
|
205
|
+
np.add.at(histograms[index], row, 1)
|
|
206
|
+
lengths = np.array(sizes, dtype=float)
|
|
207
|
+
weights = 2 / (lengths[:, None] + lengths[None, :])
|
|
208
|
+
full = np.einsum("ik,kl,il->i", histograms, weights, histograms)
|
|
209
|
+
diagonal = (histograms * np.diag(weights)).sum(axis=1)
|
|
210
|
+
return fsum(full - diagonal) / 2
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def count_given(sents: Sequence[Sequence[str]]) -> int:
|
|
214
|
+
"""
|
|
215
|
+
Counting the given elements - the ones used before in the text
|
|
216
|
+
|
|
217
|
+
Description:
|
|
218
|
+
An element is given when the same lemma was used earlier in any sentence,
|
|
219
|
+
the current one included; the first occurrence is new
|
|
220
|
+
|
|
221
|
+
Arguments:
|
|
222
|
+
sents (list[list[str]]): Lemmas of every sentence in the order of the text
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
int: Number of given elements
|
|
226
|
+
|
|
227
|
+
Raises:
|
|
228
|
+
SourceTypeError: If the sentences are a string, a Doc or an iterator, or a sentence
|
|
229
|
+
is not a list of strings (check_words)
|
|
230
|
+
"""
|
|
231
|
+
_check_sents(sents, "lemmas of a sentence")
|
|
232
|
+
seen: set[str] = set()
|
|
233
|
+
given = 0
|
|
234
|
+
for sent in sents:
|
|
235
|
+
for lemma in sent:
|
|
236
|
+
if lemma in seen:
|
|
237
|
+
given += 1
|
|
238
|
+
seen.add(lemma)
|
|
239
|
+
return given
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def dominant(values: Sequence[str]) -> str | None:
|
|
243
|
+
"""
|
|
244
|
+
Getting the dominant value
|
|
245
|
+
|
|
246
|
+
Description:
|
|
247
|
+
The most frequent value, the first one of the equally frequent
|
|
248
|
+
|
|
249
|
+
Arguments:
|
|
250
|
+
values (list[str]): Values
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
str|None: Dominant value, None for an empty list
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
SourceTypeError: If the values are not a list of strings (check_words)
|
|
257
|
+
"""
|
|
258
|
+
check_words(values, "values")
|
|
259
|
+
if not values:
|
|
260
|
+
return None
|
|
261
|
+
return Counter(values).most_common(1)[0][0]
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def calc_repetition(sents: Sequence[Sequence[str]]) -> float:
|
|
265
|
+
"""
|
|
266
|
+
Computing the share of adjacent pairs of sentences with the same dominant value
|
|
267
|
+
|
|
268
|
+
Description:
|
|
269
|
+
The repetition of the tense and of the mood of Coh-Metrix (SMTEMP): a
|
|
270
|
+
pair of adjacent sentences is cohesive when the dominant values of the
|
|
271
|
+
feature of their verbs are equal; a pair where one of the sentences has
|
|
272
|
+
no verb with the feature is skipped
|
|
273
|
+
|
|
274
|
+
Arguments:
|
|
275
|
+
sents (list[list[str]]): Values of the feature of the verbs of every sentence
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
float: Share of the cohesive pairs, nan without a single pair with the feature
|
|
279
|
+
|
|
280
|
+
Raises:
|
|
281
|
+
SourceTypeError: If the sentences are a string, a Doc or an iterator, or a sentence
|
|
282
|
+
is not a list of strings (check_words)
|
|
283
|
+
"""
|
|
284
|
+
_check_sents(sents, "values of a sentence")
|
|
285
|
+
pairs = [
|
|
286
|
+
(first, second)
|
|
287
|
+
for first, second in pairwise(dominant(sent) for sent in sents)
|
|
288
|
+
if first and second
|
|
289
|
+
]
|
|
290
|
+
if not pairs:
|
|
291
|
+
return nan
|
|
292
|
+
return sum(1 for first, second in pairs if first == second) / len(pairs)
|