granuscore 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,61 @@
1
+ import re
2
+
3
+
4
+ class TextCleaner:
5
+ """Utilities for cleaning and normalizing text."""
6
+
7
+ # Regex patterns
8
+ LATEX_PATTERN = re.compile(
9
+ r"<\s*l\s*a\s*t\s*e\s*x\s*i\s*t(?:\s+[^>]*)?>.*?<\s*/\s*l\s*a\s*t\s*e\s*x\s*i\s*t\s*>",
10
+ re.DOTALL | re.IGNORECASE,
11
+ )
12
+ ARXIV_META_PATTERN = re.compile(r"arXiv:\d+\.\d+v\d+", re.IGNORECASE)
13
+ FIGURE_CAPTION_PATTERN = re.compile(r"(Figure|Fig\.)\s*\d+[:].*", re.IGNORECASE)
14
+ BRACKETS_PATTERN = re.compile(r"\([^)]*\)|\[[^\]]*\]")
15
+ SINGLE_LETTER_NOISE_PATTERN = re.compile(r"(?:\b[A-Za-z0-9]\b\s+){2,}\b[A-Za-z0-9]\b")
16
+
17
+ @classmethod
18
+ def clean(
19
+ cls,
20
+ text: str,
21
+ *,
22
+ skip_brackets: bool = False,
23
+ skip_latex: bool = False,
24
+ skip_arxiv_meta: bool = False,
25
+ skip_figures: bool = False,
26
+ skip_pdf_noise: bool = False,
27
+ ) -> str:
28
+ """
29
+ Clean text by removing various unwanted patterns.
30
+
31
+ Args:
32
+ text: Input text to clean
33
+ skip_brackets: Remove content in brackets/parentheses
34
+ skip_latex: Remove LaTeX blocks
35
+ skip_arxiv_meta: Remove arXiv metadata
36
+ skip_figures: Remove figure captions
37
+ skip_pdf_noise: Remove PDF extraction noise
38
+ Returns:
39
+ Cleaned text
40
+ """
41
+ if skip_latex:
42
+ text = cls.LATEX_PATTERN.sub(" ", text)
43
+ if skip_arxiv_meta:
44
+ text = cls.ARXIV_META_PATTERN.sub(" ", text)
45
+ if skip_figures:
46
+ text = cls.FIGURE_CAPTION_PATTERN.sub(" ", text)
47
+ if skip_brackets:
48
+ text = cls.BRACKETS_PATTERN.sub(" ", text)
49
+ if skip_pdf_noise:
50
+ text = cls.SINGLE_LETTER_NOISE_PATTERN.sub(" ", text)
51
+
52
+ # Normalize spacing
53
+ text = re.sub(r"([.!?])([A-Za-z])", r"\1 \2", text)
54
+ text = re.sub(r"\s+", " ", text)
55
+ return text.strip()
56
+
57
+ @staticmethod
58
+ def normalize_spacing(text: str) -> str:
59
+ """Ensure proper spacing after sentence-ending punctuation."""
60
+ return re.sub(r"([.!?])([A-Za-z])", r"\1 \2", text)
61
+
granuscore/utils.py ADDED
@@ -0,0 +1,48 @@
1
+ import os
2
+ from multiprocessing.pool import ThreadPool
3
+ from typing import Sequence, Iterator, TypeVar, Callable, Any
4
+
5
+ import torch
6
+ from spacy.util import is_package
7
+ from tqdm import tqdm
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ def ensure_spacy_model(name: str = "en_core_web_sm") -> None:
13
+ if is_package(name):
14
+ return
15
+
16
+ from spacy.cli import download
17
+ download(name)
18
+
19
+
20
+ def iter_batches(items: Sequence[T], batch_size: int) -> Iterator[Sequence[T]]:
21
+ for i in range(0, len(items), batch_size):
22
+ yield items[i: i + batch_size]
23
+
24
+
25
+ def get_best_device():
26
+ if torch.cuda.is_available():
27
+ return torch.device("cuda")
28
+ if torch.backends.mps.is_available():
29
+ return torch.device("mps")
30
+ return torch.device("cpu")
31
+
32
+
33
+ def map_with_progress(
34
+ f: Callable,
35
+ xs: list[Any],
36
+ num_threads: int = os.cpu_count() or 10,
37
+ pbar: bool = False,
38
+ ):
39
+ """
40
+ Apply f to each element of xs, using a ThreadPool, and show progress.
41
+ """
42
+ pbar_fn = tqdm if pbar else lambda x, *args, **kwargs: x
43
+
44
+ if os.getenv("debug"):
45
+ return list(map(f, pbar_fn(xs, total=len(xs))))
46
+ else:
47
+ with ThreadPool(min(num_threads, len(xs))) as pool:
48
+ return list(pbar_fn(pool.imap(f, xs), total=len(xs)))
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: granuscore
3
+ Version: 1.0.0
4
+ Summary: Granularity scoring for natural language
5
+ Author-email: Lukas Ellinger <lukas.ellinger@tum.de>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/lukasellinger/granuscore
8
+ Project-URL: Issues, https://github.com/lukasellinger/granuscore/issues
9
+ Keywords: granularity,natural language processing,nlp,text analysis,semantic abstraction,specificity
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Classifier: Topic :: Text Processing :: Linguistic
18
+ Requires-Python: <3.13,>=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: faiss-cpu>=1.13.2
22
+ Requires-Dist: hierarchy_transformers>=0.1.1
23
+ Requires-Dist: lightgbm>=4.6.0
24
+ Requires-Dist: spacy>=3.8.11
25
+ Requires-Dist: torch>=2.9.1
26
+ Requires-Dist: numpy>=1.26.4
27
+ Requires-Dist: platformdirs>=3.10.0
28
+ Requires-Dist: requests>=2.31.0
29
+ Requires-Dist: tqdm>=4.66.0
30
+ Requires-Dist: openai>=2.21.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: optuna>=4.6.0; extra == "dev"
33
+ Requires-Dist: pygam>=0.12.0; extra == "dev"
34
+ Requires-Dist: python-dotenv>=1.2.1; extra == "dev"
35
+ Requires-Dist: ollama>=0.6.1; extra == "dev"
36
+ Requires-Dist: google-genai>=1.66.0; extra == "dev"
37
+ Requires-Dist: wget>=3.2; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ [![PyPI version](https://img.shields.io/pypi/v/granuscore.svg)](https://pypi.org/project/granuscore/)
41
+ [![GitHub](https://img.shields.io/badge/GitHub-Repository-black)](https://github.com/lukasellinger/granuscore)
42
+ # Granuscore
43
+
44
+ **Granuscore** is a Python library for measuring the *semantic granularity* of natural language text.
45
+
46
+ It provides an end-to-end pipeline that:
47
+
48
+ 1. splits text into referential units,
49
+ 2. assigns continuous granularity scores to each unit,
50
+ 3. aggregates these scores into document-level estimates.
51
+
52
+ Granuscore is designed for analyzing how *fine-grained* or *coarse-grained* textual expressions are in applications such as question answering, educational dialogue, summarization, and scientific writing.
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ Install from PyPI:
59
+
60
+ ```bash
61
+ pip install granuscore
62
+ ```
63
+
64
+ Or install the latest development version locally:
65
+
66
+ ```bash
67
+ git clone https://github.com/lukasellinger/granuscore.git
68
+ cd granuscore
69
+ pip install -e .
70
+ ```
71
+
72
+ Optional development dependencies:
73
+
74
+ ```bash
75
+ pip install -e ".[dev]"
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Quick Start
81
+
82
+ ```python
83
+ from granuscore import GranuScore
84
+
85
+ scorer = GranuScore()
86
+
87
+ text = """
88
+ Tony Hawk was born in San Diego.
89
+ """
90
+
91
+ score = scorer(text)
92
+
93
+ print(score)
94
+ ```
95
+
96
+ By default, Granuscore returns percentile scores, where higher values correspond to coarser-grained expressions.
97
+
98
+ ---
99
+
100
+ ## Default Configuration
101
+
102
+ The default configuration reproduces the setup used in the paper.
103
+
104
+ ```python
105
+ scorer = GranuScore()
106
+ ```
107
+
108
+ Equivalent to:
109
+
110
+ ```python
111
+ scorer = GranuScore(
112
+ predictor_type="hit",
113
+ )
114
+ ```
115
+
116
+ Default settings:
117
+
118
+ - `predictor_type="hit"`
119
+ - `model_name="Hierarchy-Transformers/HiT-MiniLM-L12-WordNetNoun"`
120
+ - `search_method="random_anchors"`
121
+ - `random_anchors_k=999`
122
+
123
+ Required artifacts such as:
124
+ - FAISS indices,
125
+ - anchor vectors,
126
+ - LightGBM models,
127
+ - and reference percentile distributions
128
+
129
+ are automatically downloaded and cached on first use.
130
+
131
+ ---
132
+
133
+ ## Important Compatibility Note
134
+
135
+ The default configuration works out of the box and is the recommended setup.
136
+
137
+ If you customize components such as:
138
+ - the embedding model,
139
+ - search method,
140
+ - FAISS index,
141
+ - anchor vectors,
142
+ - or LightGBM model,
143
+
144
+ you must ensure that all resources are compatible with each other.
145
+
146
+ For example, a LightGBM model trained using:
147
+
148
+ ```python
149
+ search_method="random_anchors"
150
+ ```
151
+
152
+ should not be combined with:
153
+
154
+ ```python
155
+ search_method="nearest_neighbor"
156
+ ```
157
+
158
+ Similarly, FAISS indices, anchor vectors, percentile reference distributions, and LightGBM models must originate from the same embedding space and training configuration.
159
+
160
+ Compatibility between custom resources is not validated automatically.
161
+
162
+ ---
163
+
164
+ ## Notebook Tutorial
165
+
166
+ An interactive introduction is available in:
167
+
168
+ ```text
169
+ notebooks/getting_started.ipynb
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Repository Structure
175
+
176
+ ```text
177
+ granuscore/
178
+ ├── src/
179
+ │ └── granuscore/
180
+ │ ├── pipeline.py
181
+ │ ├── granularity_predictor.py
182
+ │ ├── claim_splitter.py
183
+ │ ├── bucket_output.py
184
+ │ ├── cache.py
185
+ │ └── artifacts.py
186
+ ├── notebooks/
187
+ │ └── getting_started.ipynb
188
+ ├── training_scripts/
189
+ ├── evaluation/
190
+ ├── assets/
191
+ ├── data/ (needs to be externally downloaded)
192
+ ├── pyproject.toml
193
+ ├── LICENSE
194
+ └── README.md
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Reproducing Paper Experiments
200
+
201
+ The datasets and precomputed resources required to reproduce the experiments from the paper are available here:
202
+
203
+ https://drive.google.com/drive/folders/1mJdUENOxHEiuYn-_f1KRQ1PZggXJDnb4?usp=sharing
204
+
205
+ Download the archive and extract it into the repository root:
206
+
207
+ ```bash
208
+ unzip data.zip
209
+ ```
210
+
211
+ This will create the expected directory structure used by the training and evaluation scripts.
212
+
213
+ ---
214
+
215
+ ## Training Pipeline
216
+
217
+ Training uses precomputed `.pkl` feature files.
218
+
219
+ 1. Generate precomputed datasets:
220
+
221
+ ```text
222
+ training_scripts/build_precalc_data/
223
+ ```
224
+
225
+ 2. Train LightGBM models:
226
+
227
+ ```bash
228
+ python training_scripts/train_lgb_models.py
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Citation
234
+ Updated Citation information will be added after publication.
235
+
236
+ ```bibtex
237
+ @misc{ellinger2026granuscore,
238
+ title={Granuscore: A Reference-Free Measure of Granularity for Text Analysis and Question Answering},
239
+ author={Ellinger, Lukas and Fichtl, Alexander M. and Anschütz, Miriam and Groh, Georg},
240
+ year={2026}
241
+ }
242
+ ```
@@ -0,0 +1,16 @@
1
+ granuscore/__init__.py,sha256=nG10Jgicc_EngbTgxXyyNLL3M3GF6NOWMNmUYytKVdU,268
2
+ granuscore/artifcats.py,sha256=BgCatJuAgkERcfV6f_xS0NqyjsawOlhHrsrrm1Ce7vA,3973
3
+ granuscore/bucket_output.py,sha256=y9yiHOvhEXW-KwWtRIcdeal7uBj30rkLZIemYUZ8Sas,1633
4
+ granuscore/cache.py,sha256=HSPMMPDO0Dyi3dYwjXnuZ1P5szJeOpuaMXB6M15-2-c,6873
5
+ granuscore/claim_splitter.py,sha256=nub3fZj7E23Y4QvWrQcIC0hETO1ZlaaBVT-R7O9P5TQ,9785
6
+ granuscore/default_assets.py,sha256=A4zGxQ1AqheT3ja8naDR1f_RTaPfD93pOG82sRMfiNA,1287
7
+ granuscore/granularity_predictor.py,sha256=kzGGMHimedCTfHzVL4kz0lnDFvc7ZZvAqXm7_orWsro,25609
8
+ granuscore/loader.py,sha256=5I9Fd9phOszD-od7kVN7RiMycJXRIrFaGdyxmVs59jo,3059
9
+ granuscore/pipeline.py,sha256=apcFHLR2YGfZiXXThSsZqCeNNMDYd7rOADhmQb5-FTU,24396
10
+ granuscore/text_cleaner.py,sha256=nNfy2XUANgKhReM-occwut4LtxXfpe0s7fp-TGBZLwA,2109
11
+ granuscore/utils.py,sha256=h-yNEYA1zqazd0kAjjtERojH3vXHVZzAu8Jxh7B5rDM,1237
12
+ granuscore-1.0.0.dist-info/licenses/LICENSE,sha256=Sev5Z9X5Afo9ThLkUqBEevqX8ERAoFEn6rvEtQjtPwU,1075
13
+ granuscore-1.0.0.dist-info/METADATA,sha256=1cp9QnCHpwqLiStf41bpoOhl6hfegRtEfo7whDza_Mc,5882
14
+ granuscore-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
15
+ granuscore-1.0.0.dist-info/top_level.txt,sha256=BWgkEbJfbqBpAJdIrF2uXwelo7UuMlJkvyQc9xA0glg,11
16
+ granuscore-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Granuscore Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ granuscore