PsychiatryNLPKit 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.
- psychiatrynlpkit-0.1.0/LICENSE +21 -0
- psychiatrynlpkit-0.1.0/PKG-INFO +570 -0
- psychiatrynlpkit-0.1.0/README.md +524 -0
- psychiatrynlpkit-0.1.0/pyproject.toml +94 -0
- psychiatrynlpkit-0.1.0/setup.cfg +4 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/__init__.py +34 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Density.py +164 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Graph.py +302 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/ImageSimilarity.py +227 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Lexicon.py +116 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Perplexity.py +356 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Similarity.py +139 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/Syntax.py +727 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/__init__.py +108 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/analysis/batch.py +429 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/config.py +76 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/data/Audio.py +7 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/data/Image.py +36 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/data/Text.py +550 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/data/__init__.py +23 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/data/_resources.py +96 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/model/LLM.py +150 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/model/ViT.py +74 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/model/__init__.py +24 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/py.typed +0 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/resources/__init__.py +6 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/resources/filler_words.json +4 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit/resources/word2vec.db +0 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit.egg-info/PKG-INFO +570 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit.egg-info/SOURCES.txt +41 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit.egg-info/dependency_links.txt +1 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit.egg-info/requires.txt +25 -0
- psychiatrynlpkit-0.1.0/src/PsychiatryNLPKit.egg-info/top_level.txt +1 -0
- psychiatrynlpkit-0.1.0/tests/test_batch.py +215 -0
- psychiatrynlpkit-0.1.0/tests/test_data.py +110 -0
- psychiatrynlpkit-0.1.0/tests/test_density.py +48 -0
- psychiatrynlpkit-0.1.0/tests/test_graph.py +50 -0
- psychiatrynlpkit-0.1.0/tests/test_image_similarity.py +12 -0
- psychiatrynlpkit-0.1.0/tests/test_lexicon.py +31 -0
- psychiatrynlpkit-0.1.0/tests/test_model_wrappers.py +158 -0
- psychiatrynlpkit-0.1.0/tests/test_perplexity.py +50 -0
- psychiatrynlpkit-0.1.0/tests/test_similarity.py +50 -0
- psychiatrynlpkit-0.1.0/tests/test_syntax.py +147 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rukun Dou, Tiana Wei, Alban Elias Voppel
|
|
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,570 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PsychiatryNLPKit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Computational linguistics toolkit for psychosis risk assessment and thought disorder analysis
|
|
5
|
+
Author-email: Rukun Dou <rukun.dou2004@gmail.com>, Tiana Wei <hsi.wei@mail.mcgill.ca>, Alban Elias Voppel <alban.voppel@mail.mcgill.ca>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rukun-dou/PsychiatryNLPKit
|
|
8
|
+
Project-URL: Repository, https://github.com/rukun-dou/PsychiatryNLPKit
|
|
9
|
+
Project-URL: Documentation, https://rukun-dou.github.io/PsychiatryNLPKit/
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/rukun-dou/PsychiatryNLPKit/issues
|
|
11
|
+
Keywords: nlp,psychiatry,clinical-nlp,thought-disorder,psychosis-risk
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
19
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: benepar>=0.2.0
|
|
24
|
+
Requires-Dist: numpy>=2.0.0
|
|
25
|
+
Requires-Dist: nltk>=3.9.0
|
|
26
|
+
Requires-Dist: pandas>=2.2.0
|
|
27
|
+
Requires-Dist: stanza>=1.8.0
|
|
28
|
+
Requires-Dist: torch>=2.5.0
|
|
29
|
+
Requires-Dist: transformers>=4.40.0
|
|
30
|
+
Requires-Dist: sentence-transformers>=5.0.0
|
|
31
|
+
Requires-Dist: scikit-learn>=1.3.0
|
|
32
|
+
Requires-Dist: scikit-dimension>=0.3.2
|
|
33
|
+
Requires-Dist: matplotlib>=3.8.0
|
|
34
|
+
Requires-Dist: zstandard>=0.22.0
|
|
35
|
+
Provides-Extra: graph
|
|
36
|
+
Requires-Dist: networkx>=3.0; extra == "graph"
|
|
37
|
+
Provides-Extra: image
|
|
38
|
+
Requires-Dist: pillow>=10.0.0; extra == "image"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pyright>=1.1; extra == "dev"
|
|
42
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
43
|
+
Requires-Dist: sphinx>=7.0; extra == "dev"
|
|
44
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "dev"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# PsychiatryNLPKit
|
|
48
|
+
|
|
49
|
+
[](https://github.com/rukun-dou/PsychiatryNLPKit/actions)
|
|
50
|
+
[](https://pypi.org/project/PsychiatryNLPKit/)
|
|
51
|
+
[](https://python.org/downloads/release/python-3120/)
|
|
52
|
+
[](https://github.com/rukun-dou/PsychiatryNLPKit/blob/main/LICENSE)
|
|
53
|
+
[](https://rukun-dou.github.io/PsychiatryNLPKit/)
|
|
54
|
+
|
|
55
|
+
A scientific Python package for computational linguistics analysis of clinical data in psychiatry. It provides a curated set of linguistic metrics across seven analytical categories, each grounded in peer-reviewed research on psychosis risk assessment and thought disorder characterization.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Overview
|
|
60
|
+
|
|
61
|
+
PsychiatryNLPKit is designed for researchers and clinicians who need to extract validated computational linguistics features from spoken or written clinical text. The package implements analysis functions derived from the scientific literature on language markers of psychosis, including formal thought disorder, disorganization, and cognitive impairment.
|
|
62
|
+
|
|
63
|
+
The toolkit supports **English** and **French** text and provides metrics spanning:
|
|
64
|
+
|
|
65
|
+
| Category | What it measures |
|
|
66
|
+
|---|---|
|
|
67
|
+
| **Syntax** | POS ratios, clause structure, syntax tree depth, sentence complexity |
|
|
68
|
+
| **Similarity** |Semantic coherence via adjacent word/sentence cosine similarity |
|
|
69
|
+
| **Perplexity** | Language model perplexity at paragraph and sentence levels (generative + masked LM) |
|
|
70
|
+
| **Graph** | Network metrics from structural word-transition graphs (nodes, edges, diameter, z-scores) |
|
|
71
|
+
| **Density** | Semantic space dimensionality via PCA explained variance and intrinsic dimension estimation |
|
|
72
|
+
| **Lexicon** | Disfluency and filler word frequency |
|
|
73
|
+
| **ImageSimilarity** | Cross-modal cosine similarity between images and text sections (ViT-based) |
|
|
74
|
+
|
|
75
|
+
Each function accepts pre-computed linguistic data from the `TextData` container and returns a dictionary mapping section names to numeric metric values. This modular design lets you compose analyses flexibly or run them all at once via the batch API.
|
|
76
|
+
|
|
77
|
+
### Design principles
|
|
78
|
+
|
|
79
|
+
- **Scientific grounding** -- analysis functions are based on the psychiatry research literature. This package implements speech metrics that have been shown to correlate with clinical rating scales (PANSS, TLC, TLI).
|
|
80
|
+
- **Batch efficiency** -- expensive computations (tokenization, embedding, constituency parsing) are lazy-loaded and cached on the `TextData` container. Running multiple analyses over the same text incurs no redundant work.
|
|
81
|
+
- **Hardware acceleration** -- all deep learning pipelines run on CUDA, MPS (Apple Silicon), or Intel XPU when available, with automatic fallback to CPU.
|
|
82
|
+
- **Composable architecture** -- individual analysis functions can be called standalone, or orchestrated together via `BatchAnalyzer`. The same `TextData` object serves all analyses.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Quick Start
|
|
87
|
+
|
|
88
|
+
### Batch API (recommended for most use cases)
|
|
89
|
+
|
|
90
|
+
The high-level batch API processes an entire corpus in a few lines:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import logging
|
|
94
|
+
import PsychiatryNLPKit as pnlp
|
|
95
|
+
from PsychiatryNLPKit.data import Section, TextData
|
|
96
|
+
from PsychiatryNLPKit.model import HFEmbeddingLLM, HFGenerativeLLM
|
|
97
|
+
|
|
98
|
+
# 1. Configure logging (optional but recommended)
|
|
99
|
+
pnlp.configure_logging(level=logging.INFO)
|
|
100
|
+
|
|
101
|
+
# 2. Load your text data from a file or database. Organize the dataset into different sections for separate analysis.
|
|
102
|
+
sections = [
|
|
103
|
+
Section(text="I work in a factory that produces humanoid robots.", name="p1"),
|
|
104
|
+
Section(text="I just came back from a vacation in the mountains.", name="p2"),
|
|
105
|
+
]
|
|
106
|
+
data = TextData(sections=sections, lang="en")
|
|
107
|
+
|
|
108
|
+
# 3. Attach models (lazy-loaded on first use)
|
|
109
|
+
data.embedding_model = HFEmbeddingLLM("unsloth/embeddinggemma-300m")
|
|
110
|
+
data.generative_model = HFGenerativeLLM("unsloth/Llama-3.2-1B")
|
|
111
|
+
|
|
112
|
+
# 4. Run analyses (exclude analyses that require additional models)
|
|
113
|
+
result = pnlp.BatchAnalyzer(
|
|
114
|
+
data,
|
|
115
|
+
excluded_analyses=[
|
|
116
|
+
"paragraph_level_pseudo_perplexity", # requires mask-filling model
|
|
117
|
+
"sentence_level_pseudo_perplexity",
|
|
118
|
+
"structural_graph", # requires networkx extra
|
|
119
|
+
"image_text_similarity", # requires pillow extra and image paths
|
|
120
|
+
],
|
|
121
|
+
).run()
|
|
122
|
+
|
|
123
|
+
# 5. Inspect results
|
|
124
|
+
print(result.sections) # ['p1', 'p2']
|
|
125
|
+
print(result.analyses_run) # list of successfully executed function names
|
|
126
|
+
print(result.results["p1"]) # {'sentence_length': 12.5, 'adverb_ratio': 0.08, ...}
|
|
127
|
+
|
|
128
|
+
# 6. Export to CSV for downstream statistical analysis
|
|
129
|
+
import csv
|
|
130
|
+
with open("results.csv", "w", newline="") as f:
|
|
131
|
+
writer = csv.writer(f)
|
|
132
|
+
writer.writerow(["section"] + list(result.results["p1"].keys()))
|
|
133
|
+
for section in result.sections:
|
|
134
|
+
writer.writerow([section] + [result.results[section].get(k, float("nan"))
|
|
135
|
+
for k in result.results["p1"].keys()])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Low-level API (fine-grained control)
|
|
139
|
+
|
|
140
|
+
For targeted analysis or custom pipelines, call individual functions directly:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from PsychiatryNLPKit import analysis as pnlp_a
|
|
144
|
+
from PsychiatryNLPKit.data import Section, TextData
|
|
145
|
+
from PsychiatryNLPKit.model import HFEmbeddingLLM, HFGenerativeLLM
|
|
146
|
+
|
|
147
|
+
sections = [Section(text="The quick brown fox jumps over the lazy dog.", name="p1")]
|
|
148
|
+
data = TextData(sections=sections, lang="en")
|
|
149
|
+
|
|
150
|
+
# Attach models (lazy-loaded on first use)
|
|
151
|
+
data.embedding_model = HFEmbeddingLLM("unsloth/embeddinggemma-300m")
|
|
152
|
+
data.generative_model = HFGenerativeLLM("unsloth/Llama-3.2-1B")
|
|
153
|
+
|
|
154
|
+
# Syntax: sentence length
|
|
155
|
+
lengths = pnlp_a.sentence_length(data.pos_tags)
|
|
156
|
+
# → {"p1": {"sentence_length": 10.0}}
|
|
157
|
+
|
|
158
|
+
# Semantic coherence: adjacent sentence similarity
|
|
159
|
+
coherence = pnlp_a.sentence_level_cosine_similarity(data.sentence_embedding_vectors)
|
|
160
|
+
# → {"p1": {"sentence_level_cosine_similarity": 0.73}}
|
|
161
|
+
|
|
162
|
+
# Perplexity: generative model on paragraphs
|
|
163
|
+
perplexity = pnlp_a.paragraph_level_perplexity(
|
|
164
|
+
data.paragraph_generative_tokens, data.generative_model
|
|
165
|
+
)
|
|
166
|
+
# → {"p1": {"paragraph_level_perplexity": 42.5}}
|
|
167
|
+
|
|
168
|
+
# Graph: structural word-transition network metrics
|
|
169
|
+
graph_metrics = pnlp_a.structural_graph(data.content_words)
|
|
170
|
+
# → {"p1": {"nodes_count": 8.0, "edges_count": 9.0, ...}}
|
|
171
|
+
|
|
172
|
+
# Density: PCA-based semantic space compression (uses token-level embeddings)
|
|
173
|
+
density = pnlp_a.pca_density_metrics(data.token_embedding_vectors)
|
|
174
|
+
# → {"p1": {"Ncomp_90": 3.0, "Pcomp_90": 0.375, "ExVar_2": 0.45}}
|
|
175
|
+
|
|
176
|
+
# Lexicon: filler word disfluency count
|
|
177
|
+
fillers = pnlp_a.filler_words_count(data.pos_tags, language="en")
|
|
178
|
+
# → {"p1": {"filler_words_count": 0, "filler_words_ratio": 0.0}}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Installation
|
|
184
|
+
|
|
185
|
+
### From PyPI
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pip install PsychiatryNLPKit # core package
|
|
189
|
+
pip install PsychiatryNLPKit[graph] # + networkx (for structural_graph)
|
|
190
|
+
pip install PsychiatryNLPKit[image] # + pillow (for image_text_similarity)
|
|
191
|
+
pip install PsychiatryNLPKit[dev] # + pytest, pyright, sphinx (for development)
|
|
192
|
+
|
|
193
|
+
# All extras at once:
|
|
194
|
+
pip install PsychiatryNLPKit[graph,image,dev]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### From source
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
git clone https://github.com/rukun-dou/PsychiatryNLPKit.git
|
|
201
|
+
cd PsychiatryNLPKit
|
|
202
|
+
pip install -e ".[graph,image,dev]"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Requirements:** Python 3.12+, [Hugging Face token](https://huggingface.co/settings/tokens) (for gated models). Set the `HF_TOKEN` environment variable before loading any model that requires authentication.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Configuration
|
|
210
|
+
|
|
211
|
+
### Device detection
|
|
212
|
+
|
|
213
|
+
PsychiatryNLPKit automatically detects and uses the fastest available hardware:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
CUDA (NVIDIA) → MPS (Apple Silicon) → Intel XPU → CPU
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from PsychiatryNLPKit import device, hf_token
|
|
221
|
+
|
|
222
|
+
print(device) # torch.device('cuda') or 'mps' or 'cpu'
|
|
223
|
+
print(hf_token) # str | None (read from HF_TOKEN env var)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Models are loaded with `bfloat16` precision on GPUs for memory efficiency and faster inference. All deep learning computations (tokenization, embedding generation, perplexity scoring, image-text similarity) run on the detected device.
|
|
227
|
+
|
|
228
|
+
### Logging
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
import logging
|
|
232
|
+
import PsychiatryNLPKit as pnlp
|
|
233
|
+
|
|
234
|
+
pnlp.configure_logging(level=logging.DEBUG) # default is INFO
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The package uses a dedicated logger (`PsychiatryNLPKit`) with `propagate=False`, so messages won't interfere with your application's root logger. Calling `configure_logging` multiple times is safe (duplicate handlers are guarded against).
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Data Model
|
|
242
|
+
|
|
243
|
+
### TextData
|
|
244
|
+
|
|
245
|
+
The central container for all analysis. It holds raw text sections and computes linguistic properties lazily on first access, caching results to avoid redundant computation:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from PsychiatryNLPKit.data import Section, TextData
|
|
249
|
+
|
|
250
|
+
sections = [
|
|
251
|
+
Section(text="First paragraph.", name="p1"),
|
|
252
|
+
Section(text="Second paragraph.", name="p2"),
|
|
253
|
+
]
|
|
254
|
+
data = TextData(sections=sections, lang="en")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
| Property | Requires model | Returns | Description |
|
|
258
|
+
|---|---|---|---|
|
|
259
|
+
| `data` | No | `dict[str, str]` | Raw section text keyed by name |
|
|
260
|
+
| `sentences` | No | `dict[str, list[str]]` | Sentences per section (regex-split) |
|
|
261
|
+
| `pos_tags` | No | `dict[str, list[list[tuple[str,str,str]]]]` | POS-tagged sentences: `(word, lemma, tag)` tuples |
|
|
262
|
+
| `syntax_trees` | No | `dict[str, list[benepar.Tree]]` | Constituency parse trees per sentence |
|
|
263
|
+
| `token_embedding_vectors` | `embedding_model` | `dict[str, torch.Tensor]` | Token-level embeddings, shape `(n_tokens, dim)` |
|
|
264
|
+
| `attention_scores` | `embedding_model` | `dict[str, torch.Tensor]` | Aggregated attention scores, shape `(n_tokens, n_tokens)` |
|
|
265
|
+
| `sentence_embedding_vectors` | `embedding_model` | `dict[str, torch.Tensor]` | Sentence-level embeddings, shape `(n_sentences, dim)` |
|
|
266
|
+
| `content_words` | No | `dict[str, list[str]]` | Content words (nouns, verbs, adjectives, adverbs) per section |
|
|
267
|
+
| `content_word_embedding_vectors` | No | `dict[str, torch.Tensor]` | Word2Vec embeddings for content words, shape `(n_words, dim)` |
|
|
268
|
+
| `paragraph_generative_tokens` | `generative_model` | `dict[str, dict[str, torch.Tensor]]` | Tokenized paragraphs (input_ids + attention_mask) |
|
|
269
|
+
| `sentence_generative_tokens` | `generative_model` | `dict[str, dict[str, torch.Tensor]]` | Tokenized sentences |
|
|
270
|
+
|
|
271
|
+
### Section
|
|
272
|
+
|
|
273
|
+
A named text segment:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from PsychiatryNLPKit.data import Section
|
|
277
|
+
|
|
278
|
+
section = Section(text="Clinical interview transcript...", name="session_01")
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Each section has a `text` attribute (the raw string) and an optional `name` used as the dictionary key in all analysis outputs. If no name is provided, sections are auto-named `section_0`, `section_1`, etc.
|
|
282
|
+
|
|
283
|
+
### ImageData
|
|
284
|
+
|
|
285
|
+
For image-text similarity analysis:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from PsychiatryNLPKit.data import ImageData
|
|
289
|
+
|
|
290
|
+
image = ImageData(path="patient_response.jpg", name="p1")
|
|
291
|
+
# Access via image.image → PIL.Image.Image (lazy-loaded)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Batch Analysis
|
|
297
|
+
|
|
298
|
+
`BatchAnalyzer` is the high-level orchestrator. It runs selected analyses on a `TextData` object and collects all results into a single structured container:
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
from PsychiatryNLPKit.analysis import BatchAnalyzer, AnalysisResult
|
|
302
|
+
|
|
303
|
+
# Run every analysis (requires all models)
|
|
304
|
+
result = BatchAnalyzer(
|
|
305
|
+
data,
|
|
306
|
+
mask_filling_model=mask_lm, # for pseudo-perplexity
|
|
307
|
+
vit_model=vit, # for image-text similarity
|
|
308
|
+
image_paths={"p1": "img1.jpg", "p2": "img2.jpg"}, # per-section images
|
|
309
|
+
).run()
|
|
310
|
+
|
|
311
|
+
# Run a subset
|
|
312
|
+
result = BatchAnalyzer(
|
|
313
|
+
data,
|
|
314
|
+
included_analyses=["sentence_length", "adverb_ratio", "filler_words_count"],
|
|
315
|
+
).run()
|
|
316
|
+
|
|
317
|
+
# Exclude specific analyses from the full set
|
|
318
|
+
result = BatchAnalyzer(
|
|
319
|
+
data,
|
|
320
|
+
included_analyses="all",
|
|
321
|
+
excluded_analyses=[
|
|
322
|
+
"paragraph_level_pseudo_perplexity", # requires mask-filling model
|
|
323
|
+
"sentence_level_pseudo_perplexity",
|
|
324
|
+
"structural_graph", # requires networkx extra
|
|
325
|
+
"image_text_similarity", # requires pillow extra and image paths
|
|
326
|
+
],
|
|
327
|
+
).run()
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Parameters
|
|
331
|
+
|
|
332
|
+
| Parameter | Type | Description |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| `text_data` | `TextData` | The data container with sections and optional model references |
|
|
335
|
+
| `included_analyses` | `"all"` or `list[str]` | Default `"all"` runs every registered analysis. Pass an explicit list for a subset |
|
|
336
|
+
| `excluded_analyses` | `list[str] \| None` | Function names to remove from the inclusion set. Every excluded name must be in the resolved inclusion list; otherwise an `AssertionError` is raised |
|
|
337
|
+
| `mask_filling_model` | `HFMaskFillingModel \| None` | Required if any pseudo-perplexity function remains after filtering |
|
|
338
|
+
| `vit_model` | `HuggingFaceViTModel \| None` | Required if `"image_text_similarity"` is in the analysis set |
|
|
339
|
+
| `image_paths` | `dict[str, str] \| None` | Maps section name → image file path. Must cover every section in `text_data.section_names` when image analysis is requested |
|
|
340
|
+
|
|
341
|
+
### AnalysisResult
|
|
342
|
+
|
|
343
|
+
| Attribute | Type | Description |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| `results` | `dict[str, dict[str, float]]` | Merged per-section metrics keyed by section name, then metric name |
|
|
346
|
+
| `sections` | `list[str]` | Section names in order (from `TextData.section_names`) |
|
|
347
|
+
| `analyses_run` | `list[str]` | Names of analyses that executed successfully |
|
|
348
|
+
| `errors` | `dict[str, str]` | Per-function failures: `{function_name: error_message}` |
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Analysis Functions Reference
|
|
353
|
+
|
|
354
|
+
All functions accept a `sections` parameter (`list[str] \| None`) to restrict processing to specific sections. When `None`, all available sections are processed. Empty or missing sections receive `float("nan")`. Language-dependent functions support `"en"` and `"fr"`.
|
|
355
|
+
|
|
356
|
+
### Syntax (`pnlp.analysis.Syntax`) — 13 functions
|
|
357
|
+
|
|
358
|
+
Measures of sentence structure, part-of-speech distributions, and syntax tree complexity derived from clinical research on thought disorder.
|
|
359
|
+
|
|
360
|
+
| Function | Required input | Returns | Clinical basis |
|
|
361
|
+
|---|---|---|---|
|
|
362
|
+
| `sentence_length` | `pos_tags` | `{section: {"sentence_length": float}}` | Poverty of content (Bilgrami et al., 2022) |
|
|
363
|
+
| `syntax_depth` | `syntax_trees` | `{section: {"syntax_depth": float}}` | Reduced complexity predicts psychosis onset (Morice & Ingram, 1983) |
|
|
364
|
+
| `unique_pos_tags` | `pos_tags` | `{section: {"unique_pos_tags": int}}` | Linguistic diversity marker |
|
|
365
|
+
| `adverb_ratio` | `pos_tags`, `lang` | `{section: {"adverb_ratio": float, "adverb_count": int}}` | Associated with negative symptoms (Haas et al., 2020) |
|
|
366
|
+
| `coordinating_conjunction_ratio` | `pos_tags`, `lang` | `{section: {"coordinating_conjunction_ratio": float}}` | Negative symptom correlation (Haas et al., 2020) |
|
|
367
|
+
| `adjective_ratio` | `pos_tags`, `lang` | `{section: {"adjective_ratio": float}}` | — |
|
|
368
|
+
| `pronoun_ratio` | `pos_tags`, `lang` | `{section: {"pronoun_ratio": float}}` | Predicts semantic similarity patterns (He et al., 2024) |
|
|
369
|
+
| `determiner_ratio` | `pos_tags`, `lang` | `{section: {"determiner_ratio": float}}` | Negative thought disorder marker (Bilgrami et al., 2022) |
|
|
370
|
+
| `modal_auxiliary_verb_ratio` | `pos_tags`, `lang` | `{section: {"modal_auxiliary_verb_ratio": float}}` | — |
|
|
371
|
+
| `stop_words_ratio` | `pos_tags`, `lang` | `{section: {"stop_words_ratio": float}}` | Baseline lexical measure |
|
|
372
|
+
| `clause_count` | `syntax_trees` | `{section: {"clause_count": int}}` | Psychosis onset prediction (Bilgrami et al., 2022) |
|
|
373
|
+
| `noun_group_count` | `syntax_trees` | `{section: {"noun_group_count": int}}` | — |
|
|
374
|
+
| `adjective_sentence_length` | `syntax_trees` | `{section: {"adjective_sentence_length": float}}` | — |
|
|
375
|
+
|
|
376
|
+
POS tag aliases support both Penn Treebank (`JJ`, `NN`, `RB`) and Universal Dependencies (`ADJ`, `NOUN`, `ADV`) schemes.
|
|
377
|
+
|
|
378
|
+
### Similarity (`pnlp.analysis.Similarity`) — 2 functions
|
|
379
|
+
|
|
380
|
+
Cosine similarity between adjacent embeddings to quantify semantic coherence:
|
|
381
|
+
|
|
382
|
+
| Function | Required input | Returns | Clinical basis |
|
|
383
|
+
|---|---|---|---|
|
|
384
|
+
| `word_level_cosine_similarity` | `content_word_embedding_vectors` | `{section: {"word_level_cosine_similarity": float}}` | Correlated with tangentiality, circumstantiality, derailment (Bilgrami et al., 2022; Elvevag et al., 2007; He et al., 2024) |
|
|
385
|
+
| `sentence_level_cosine_similarity` | `sentence_embedding_vectors` | `{section: {"sentence_level_cosine_similarity": float}}` | Detects incoherent speech in formal thought disorder (same references) |
|
|
386
|
+
|
|
387
|
+
### Perplexity (`pnlp.analysis.Perplexity`) — 4 functions
|
|
388
|
+
|
|
389
|
+
Language model perplexity at paragraph and sentence levels using both generative (causal LM) and masked language models:
|
|
390
|
+
|
|
391
|
+
| Function | Model type | Required input | Returns | Clinical basis |
|
|
392
|
+
|---|---|---|---|---|
|
|
393
|
+
| `paragraph_level_perplexity` | Generative (causal LM) | `paragraph_generative_tokens`, `generative_model` | `{section: {"paragraph_level_perplexity": float}}` | High perplexity predicts delusion and unusual thought content (Alqahtani et al., 2022; He et al., 2024) |
|
|
394
|
+
| `sentence_level_perplexity` | Generative (causal LM) | `sentence_generative_tokens`, `generative_model` | `{section: {"sentence_level_perplexity": float}}` | Same references |
|
|
395
|
+
| `paragraph_level_pseudo_perplexity` | Masked LM | `data.data` (raw text), `mask_filling_model` | `{section: {"paragraph_level_pseudo_perplexity": float}}` | Same references |
|
|
396
|
+
| `sentence_level_pseudo_perplexity` | Masked LM | `data.data` (raw text), `mask_filling_model` | `{section: {"sentence_level_pseudo_perplexity": float}}` | Same references |
|
|
397
|
+
|
|
398
|
+
### Graph (`pnlp.analysis.Graph`) — 1 function
|
|
399
|
+
|
|
400
|
+
Constructs a directed, weighted word-transition graph from lemmatized content words and computes network metrics:
|
|
401
|
+
|
|
402
|
+
| Function | Required input | Returns | Clinical basis |
|
|
403
|
+
|---|---|---|---|
|
|
404
|
+
| `structural_graph` | `content_words` | `{section: {nodes_count, edges_count, average_degree, density, diameter, average_shortest_path_length, largest_connected_component, largest_strongly_connected_component, lcc/n, lsc/n, edge_weight_repetition_index, lcc_z_score, lsc_z_score, aspl_z_score, degree_distribution_z_score}}` | Schizophrenia patients show smaller connected components and lower ASPL (Nikzad et al., 2022) |
|
|
405
|
+
|
|
406
|
+
Parameters: `directed=True`, `weighted=True`, `n_random_graphs=1000` (for z-score computation against a null distribution).
|
|
407
|
+
|
|
408
|
+
### Density (`pnlp.analysis.Density`) — 2 functions
|
|
409
|
+
|
|
410
|
+
Measures of semantic space dimensionality derived from token embeddings:
|
|
411
|
+
|
|
412
|
+
| Function | Required input | Returns | Clinical basis |
|
|
413
|
+
|---|---|---|---|
|
|
414
|
+
| `pca_density_metrics` | `token_embedding_vectors` | `{section: {Ncomp_90, Pcomp_90, ExVar_2}}` | Schizophrenia patients show altered semantic compressibility (Palominos et al., 2025) |
|
|
415
|
+
| `intrinsic_dimensionality_density` | `token_embedding_vectors` | `{section: {ID_MLE}}` | Lower intrinsic dimensionality indicates more redundant speech (same reference) |
|
|
416
|
+
|
|
417
|
+
PCA is applied in **token space** (features = tokens, i.e. `X.T`) so metrics reflect how many token directions are needed to explain semantic variance. Total paragraph length must be controlled during statistical analysis.
|
|
418
|
+
|
|
419
|
+
### Lexicon (`pnlp.analysis.Lexicon`) — 1 function
|
|
420
|
+
|
|
421
|
+
| Function | Required input | Returns | Clinical basis |
|
|
422
|
+
|---|---|---|---|
|
|
423
|
+
| `filler_words_count` | `pos_tags`, `lang` | `{section: {filler_words_count, filler_words_ratio}}` | Disfluencies correlate with symptom severity and PANSS negative scores (Vail et al., 2018; Liebenthal et al., 2022) |
|
|
424
|
+
|
|
425
|
+
Filler word lists are loaded from the packaged `resources/filler_words.json` resource. The `averaging_method` parameter controls computation: `"macro"` averages per-sentence filler ratios, `"micro"` computes total fillers / total words across the section (default: `"macro"`).
|
|
426
|
+
|
|
427
|
+
### ImageSimilarity (`pnlp.analysis.ImageSimilarity`) — 1 function
|
|
428
|
+
|
|
429
|
+
| Function | Required input | Returns | Clinical basis |
|
|
430
|
+
|---|---|---|---|
|
|
431
|
+
| `image_text_similarity` | `image`, `text`, `vit_model` | `{section: {"image_text_similarity": float}}` | Lower CLIP scores predict higher conceptual disorganization (He et al., 2024) |
|
|
432
|
+
|
|
433
|
+
The image argument accepts a PIL Image object or a file path. Text is split into chunks that fit the model's context window, encoded at the token level alongside patch-level visual embeddings, and pairwise cosine similarity is aggregated with mean pooling.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Model Wrappers
|
|
438
|
+
|
|
439
|
+
All models use lazy loading: the underlying Hugging Face model is downloaded and loaded only when first accessed. Models are unloaded after each analysis pass to free GPU memory.
|
|
440
|
+
|
|
441
|
+
| Class | Purpose | Example model ID |
|
|
442
|
+
|---|---|---|
|
|
443
|
+
| `HFEmbeddingLLM` | Sentence/word embedding models | `unsloth/embeddinggemma-300m` |
|
|
444
|
+
| `HFGenerativeLLM` | Autoregressive causal LMs | `unsloth/Llama-3.2-1B` |
|
|
445
|
+
| `HFMaskFillingModel` | Masked language models | `google-bert/bert-base-multilingual-uncased` |
|
|
446
|
+
| `HuggingFaceViTModel` | Vision-language (SigLIP/CLIP-style) | `openai/clip-vit-base-patch16` |
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
from PsychiatryNLPKit.model import HFEmbeddingLLM, HFGenerativeLLM, HFMaskFillingModel, HuggingFaceViTModel
|
|
450
|
+
|
|
451
|
+
embedding = HFEmbeddingLLM("unsloth/embeddinggemma-300m")
|
|
452
|
+
embedding.load()
|
|
453
|
+
# embedding.model.encode(...) # use the underlying model directly
|
|
454
|
+
embedding.unload() # frees GPU memory
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Data Formats
|
|
460
|
+
|
|
461
|
+
### Input: Text sections
|
|
462
|
+
|
|
463
|
+
The primary input is a list of `Section` objects, each containing raw text and an optional name. Sections can represent individual interview responses, paragraphs from clinical notes, or any other text unit.
|
|
464
|
+
|
|
465
|
+
For batch processing from files, a typical workflow looks like:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
import csv
|
|
469
|
+
from PsychiatryNLPKit.data import Section, TextData
|
|
470
|
+
|
|
471
|
+
# Read from CSV: columns "id", "text"
|
|
472
|
+
sections = []
|
|
473
|
+
with open("clinical_corpus.csv", newline="") as f:
|
|
474
|
+
reader = csv.DictReader(f)
|
|
475
|
+
for row in reader:
|
|
476
|
+
sections.append(Section(text=row["text"], name=row["id"]))
|
|
477
|
+
|
|
478
|
+
data = TextData(sections=sections, lang="en")
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Output: AnalysisResult
|
|
482
|
+
|
|
483
|
+
The `AnalysisResult` container provides three access patterns:
|
|
484
|
+
|
|
485
|
+
```python
|
|
486
|
+
# By section → metric
|
|
487
|
+
result.results["p1"]["sentence_length"] # 12.5
|
|
488
|
+
|
|
489
|
+
# Export to pandas DataFrame for statistical analysis
|
|
490
|
+
import pandas as pd
|
|
491
|
+
records = []
|
|
492
|
+
for section in result.sections:
|
|
493
|
+
record = {"section": section}
|
|
494
|
+
record.update({k: v for k, v in result.results[section].items()})
|
|
495
|
+
records.append(record)
|
|
496
|
+
df = pd.DataFrame(records)
|
|
497
|
+
|
|
498
|
+
# Check for failures
|
|
499
|
+
if result.errors:
|
|
500
|
+
print("Failed analyses:", result.errors)
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Project Structure
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
PsychiatryNLPKit/
|
|
509
|
+
├── config.py # Device detection, logging setup, HF token
|
|
510
|
+
├── data/
|
|
511
|
+
│ ├── Text.py # Section, TextData (lazy-computed properties)
|
|
512
|
+
│ ├── Image.py # ImageData container
|
|
513
|
+
│ └── _resources.py # Packaged resource paths
|
|
514
|
+
├── model/
|
|
515
|
+
│ ├── LLM.py # HFEmbeddingLLM, HFGenerativeLLM, HFMaskFillingModel
|
|
516
|
+
│ └── ViT.py # HuggingFaceViTModel (SigLIP/CLIP)
|
|
517
|
+
├── analysis/
|
|
518
|
+
│ ├── Syntax.py # 13 syntax functions
|
|
519
|
+
│ ├── Similarity.py # 2 similarity functions
|
|
520
|
+
│ ├── Perplexity.py # 4 perplexity functions
|
|
521
|
+
│ ├── Graph.py # structural_graph (network metrics + z-scores)
|
|
522
|
+
│ ├── Density.py # PCA density + intrinsic dimensionality
|
|
523
|
+
│ ├── Lexicon.py # Filler word disfluency analysis
|
|
524
|
+
│ ├── ImageSimilarity.py # ViT-based image-text similarity
|
|
525
|
+
│ └── batch.py # BatchAnalyzer, AnalysisResult
|
|
526
|
+
├── resources/
|
|
527
|
+
│ ├── filler_words.json # EN/FR filler word lists
|
|
528
|
+
│ └── word2vec.model # Gensim KeyedVectors (pre-trained)
|
|
529
|
+
└── tests/ # Unit and integration tests
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## BibTeX Citation
|
|
535
|
+
|
|
536
|
+
If you use PsychiatryNLPKit in your research, please cite:
|
|
537
|
+
|
|
538
|
+
```bibtex
|
|
539
|
+
@software{psychiatrynlpkit2026,
|
|
540
|
+
author = {Dou, Rukun and Wei, Tiana and Voppel, Alban Elias},
|
|
541
|
+
title = {PsychiatryNLPKit: Computational linguistics toolkit for psychosis risk assessment and thought disorder analysis},
|
|
542
|
+
year = {2026},
|
|
543
|
+
url = {https://github.com/rukun-dou/PsychiatryNLPKit},
|
|
544
|
+
version = {0.1.0},
|
|
545
|
+
license = {MIT}
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## References
|
|
552
|
+
|
|
553
|
+
All analysis functions include their theoretical basis and primary references in their docstrings. Key publications underpinning this toolkit:
|
|
554
|
+
|
|
555
|
+
- Alqahtani, A., Kayi, E. S., Hamidian, S., Compton, M., & Diab, M. (2022). A quantitative and qualitative analysis of schizophrenia language. *Proceedings of the 13th International Workshop on Health Text Mining and Information Analysis (LOUHI)*, 173–183.
|
|
556
|
+
- Bilgrami, Z. R., et al. (2022). Construct validity for computational linguistic metrics in individuals at clinical risk for psychosis. *Schizophrenia Research*, 245, 90–96.
|
|
557
|
+
- Elvevag, B., Palmer, B. W., Gold, J. M., & Jeste, D. V. (2007). Semantic coherence of speech in schizophrenia and bipolar disorder. *Schizophrenia Research*, 93(1-3), 304–316.
|
|
558
|
+
- Haas, S. S., et al. (2020). POS ratios and negative symptom correlation in psychosis.
|
|
559
|
+
- He, R., Palominos, C., Zhang, H., Alonso-Sánchez, M. F., Palaniyappan, L., & Hinzen, W. (2024). Navigating the semantic space: Unraveling the structure of meaning in psychosis using different computational language models. *Psychiatry Research*, 333, 115752.
|
|
560
|
+
- Liebenthal, E., et al. (2022). Linguistic and non-linguistic markers of disorganization in psychotic illness. *Schizophrenia Research*, 259, 111–120.
|
|
561
|
+
- Morice, R., & Ingram, J. C. I. (1983). Verbal fluency as a screening test for schizophrenia. *Neuropsychobiology*, 10(3), 158–161.
|
|
562
|
+
- Nikzad, A. H., et al. (2022). Who does what to whom? graph representations of action-predication in speech relate to psychopathological dimensions of psychosis. *Schizophrenia*, 8(1), 58.
|
|
563
|
+
- Palominos, C., et al. (2025). Lexical meaning is lower dimensional in psychosis. *Scientific Reports*, 16(1), 859.
|
|
564
|
+
- Vail, A. K., Liebson, E., Baker, J. T., & Morency, L.-P. (2018). Toward objective, multifaceted characterization of psychotic disorders: Lexical, structural, and disfluency markers of spoken language. *Proceedings of the 20th ACM International Conference on Multimodal Interaction*, 170–178.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## License
|
|
569
|
+
|
|
570
|
+
[MIT License](LICENSE) -- Copyright (c) 2026 Rukun Dou, Tiana Wei, Alban Elias Voppel
|