Text Modules¶
taters.text.analyze_with_archetypes ¶
analyze_with_archetypes ¶
analyze_with_archetypes(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
archetype_csvs,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
model_name="sentence-transformers/all-roberta-large-v1",
device="auto",
mean_center_vectors=True,
fisher_z_transform=False,
rounding=4
)
Compute archetype scores for text rows and write a wide, analysis-ready features CSV.
This function supports three input modes:
analysis_csv— Use a prebuilt CSV with exactly two columns:text_idandtext.csv_path— Gather text from an arbitrary CSV by specifyingtext_cols(and optionallyid_colsandgroup_by) to construct an analysis-ready CSV on the fly.txt_dir— Gather text from a folder of.txtfiles.
Archetype scoring is delegated to a middle layer that embeds text with a Sentence-Transformers
model and evaluates cosine similarity to one or more archetype CSVs. If out_features_csv is
omitted, the default path is ./features/archetypes/<analysis_ready_filename>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str or Path
|
Source CSV for gathering. Mutually exclusive with |
None
|
txt_dir
|
str or Path
|
Folder of |
None
|
analysis_csv
|
str or Path
|
Precomputed analysis-ready CSV containing exactly the columns |
None
|
gathered_csv
|
str or Path
|
Where to write the intermediate "analysis-ready" table built from
By default it lands beside the source -- which means analyzing a
spreadsheet in someone's Downloads folder writes a file into their
Downloads folder. Pass this to keep the intermediate with the rest of a
run's output instead. Ignored when |
None
|
on_progress
|
callable
|
Called as |
None
|
out_features_csv
|
str or Path
|
Output path for the features CSV. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
archetype_csvs
|
Sequence[str or Path]
|
One or more archetype CSVs (name → seed phrases). Directories are allowed and expanded
recursively to all |
required |
encoding
|
str
|
Text encoding for CSV I/O. |
"utf-8-sig"
|
delimiter
|
str
|
Field delimiter for CSV I/O. |
","
|
text_cols
|
Sequence[str]
|
When gathering from a CSV: column(s) that contain text. Used only if |
("text",)
|
id_cols
|
Sequence[str]
|
When gathering from a CSV: optional ID columns to carry into grouping (e.g., |
None
|
mode
|
(concat, separate)
|
Gathering behavior when multiple |
"concat"
|
group_by
|
Sequence[str]
|
Optional grouping keys used during gathering (e.g., |
None
|
joiner
|
str
|
Separator used when concatenating multiple text chunks. |
" "
|
num_buckets
|
int
|
Number of temporary hash buckets used for scalable CSV gathering. |
512
|
max_open_bucket_files
|
int
|
Maximum number of bucket files to keep open concurrently during gathering. |
64
|
tmp_root
|
str or Path
|
Root directory for temporary files used by gathering. |
None
|
recursive
|
bool
|
When gathering from a text folder, whether to recurse into subdirectories. |
True
|
pattern
|
str
|
Filename glob used when gathering from a text folder. |
"*.txt"
|
id_from
|
(stem, name, path)
|
How to derive the |
"stem"
|
include_source_path
|
bool
|
Whether to include the absolute source path as an additional column when gathering from a text folder. |
True
|
device
|
(auto, cuda, cpu)
|
Where to run the embedding model. "auto" uses the GPU when torch reports one that works and falls back to the CPU when it does not; "cuda" insists and raises if it cannot; "cpu" never touches the GPU. |
"auto"
|
model_name
|
str
|
Sentence-Transformers model used to embed text for archetype scoring. |
"sentence-transformers/all-roberta-large-v1"
|
mean_center_vectors
|
bool
|
If |
True
|
fisher_z_transform
|
bool
|
If |
False
|
workers
|
int
|
Parallel processes for reading documents. |
0
|
rounding
|
int
|
Number of decimal places to round numeric outputs. Use |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written features CSV. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If an input file or folder does not exist, or an archetype CSV path is invalid. |
ValueError
|
If required arguments are incompatible or missing (e.g., no input mode chosen),
or if the analysis-ready CSV lacks |
Examples:
Run on a transcript CSV, grouped by speaker:
>>> analyze_with_archetypes(
... csv_path="transcripts/session.csv",
... text_cols=["text"],
... id_cols=["speaker"],
... group_by=["speaker"],
... archetype_csvs=["dictionaries/archetypes"],
... model_name="sentence-transformers/all-roberta-large-v1",
... )
PosixPath('.../features/archetypes/session.csv')
Notes
If out_features_csv exists and overwrite_existing=False, the existing path is returned
without recomputation. Directories passed in archetype_csvs are expanded recursively to
all .csv files and deduplicated before scoring.
Source code in src\taters\text\analyze_with_archetypes.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | |
taters.text.analyze_with_dictionaries ¶
analyze_with_dictionaries ¶
analyze_with_dictionaries(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
dict_paths,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
relative_freq=True,
drop_punct=True,
rounding=4,
retain_captures=False,
wildcard_mem=True
)
Compute LIWC-style dictionary features for text rows and write a wide features CSV.
The function supports exactly one of three input modes:
analysis_csv— Use a prebuilt file with columnstext_idandtext.csv_path— Gather text from an arbitrary CSV usingtext_cols(and optionalid_cols/group_by) to produce an analysis-ready file.txt_dir— Gather text from a folder of.txtfiles.
If out_features_csv is omitted, the default output path is
./features/dictionary/<analysis_ready_filename>. Multiple dictionaries are supported;
passing a directory discovers all .dic, .dicx, and .csv dictionary files
recursively in a stable order. Global columns (e.g., word counts, punctuation) are emitted
once (from the first dictionary) and each dictionary contributes a namespaced block.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str or Path
|
Source CSV to gather from. Mutually exclusive with |
None
|
txt_dir
|
str or Path
|
Folder containing |
None
|
analysis_csv
|
str or Path
|
Prebuilt analysis-ready CSV with exactly two columns: |
None
|
gathered_csv
|
str or Path
|
Where to write the intermediate "analysis-ready" table built from
By default it lands beside the source -- which means analyzing a
spreadsheet in someone's Downloads folder writes a file into their
Downloads folder. Pass this to keep the intermediate with the rest of a
run's output instead. Ignored when |
None
|
on_progress
|
callable
|
Called as |
None
|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
dict_paths
|
Sequence[str or Path]
|
One or more dictionary inputs (files or directories). Supported extensions:
|
required |
encoding
|
str
|
Text encoding used for reading/writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns to carry into grouping when gathering from CSV. |
None
|
mode
|
(concat, separate)
|
Gathering behavior when multiple text columns are provided. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g., |
None
|
delimiter
|
str
|
Delimiter for reading/writing CSV files. |
","
|
joiner
|
str
|
Separator used when concatenating multiple text chunks in |
" "
|
num_buckets
|
int
|
Number of temporary hash buckets used during scalable CSV gathering. |
512
|
max_open_bucket_files
|
int
|
Maximum number of bucket files kept open concurrently during gathering. |
64
|
tmp_root
|
str or Path or None
|
Root directory for temporary gathering artifacts. |
None
|
recursive
|
bool
|
When gathering from a text folder, recurse into subdirectories. |
True
|
pattern
|
str
|
Glob pattern for selecting text files when gathering from a folder. |
"*.txt"
|
id_from
|
(stem, name, path)
|
How to derive |
"stem"
|
include_source_path
|
bool
|
If |
True
|
relative_freq
|
bool
|
Emit relative frequencies instead of raw counts, when supported by the dictionary engine. |
True
|
drop_punct
|
bool
|
Drop punctuation prior to analysis (dictionary-dependent). |
True
|
workers
|
int
|
Parallel processes for reading documents and scoring texts. |
0
|
rounding
|
int
|
Decimal places to round numeric outputs. Use |
4
|
retain_captures
|
bool
|
Pass-through flag to the underlying analyzer to retain capture groups, if applicable. |
False
|
wildcard_mem
|
bool
|
Pass-through optimization flag for wildcard handling in the analyzer. |
True
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written features CSV. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If input files/folders or any dictionary file cannot be found. |
ValueError
|
If input modes are misconfigured (e.g., multiple sources provided or none), required columns are missing from the analysis-ready CSV, or unsupported dictionary extensions are encountered. |
Examples:
Run on a transcript CSV, grouped by speaker:
>>> analyze_with_dictionaries(
... csv_path="transcripts/session.csv",
... text_cols=["text"], id_cols=["speaker"], group_by=["speaker"],
... dict_paths=["dictionaries/liwc/LIWC-22 Dictionary (2022-01-27).dicx"]
... )
PosixPath('.../features/dictionary/session.csv')
Notes
If overwrite_existing is False and the output exists, the existing file path
is returned without recomputation.
Source code in src\taters\text\analyze_with_dictionaries.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | |
taters.text.analyze_entropy ¶
Entropy and diversity, one row per document.
Lexical richness (:mod:analyze_lexical_richness) already answers "how varied
is the vocabulary" with a dozen indices. This answers the same family of
questions from information theory instead, which buys three things those
indices do not give you.
One family rather than a dozen names. Type-token ratio, Simpson's D and Yule's K are not unrelated measures; they are points on one curve, the Rényi entropies, indexed by an order q that says how much weight to put on common words versus rare ones. At q = 0 the answer is the number of types, at 1 it is Shannon, at 2 it is Simpson, and as q grows it approaches the commonest word's share alone. So the order is reported as a setting of the measure rather than buried in a formula named after somebody, and you can read the profile across orders instead of picking one index and hoping.
An honest answer on short texts. Plug-in entropy is biased downward, and the bias depends on how many tokens you had -- worse than type-token ratio, which is the usual cautionary example. A 50-word answer and a 5,000-word essay are not comparable on the plug-in number even when their vocabularies are equally varied. Four bias corrections are computed beside it (Miller-Madow, Chao-Shen, Grassberger, NSB), so the difference between them is visible rather than assumed away. Where they disagree, the text was too short to say.
Structure as well as variety. Entropy over single tokens measures how varied the vocabulary is. Conditional entropy over pairs and triples measures how predictable the next token is given the last one or two, which is a different construct -- a text can have a wide vocabulary and be highly formulaic. The compression ratios are a crude estimate of the same quantity that makes no assumption about tokenization at all.
Everything is computed over two units: words, and characters. Character-level measures need no tokenizer and survive languages the word tokenizer handles badly.
References
- Hill, M. O. (1973). Diversity and evenness: a unifying notation and its consequences. Ecology, 54(2), 427-432.
- Rényi, A. (1961). On measures of entropy and information. Berkeley Symposium on Mathematical Statistics and Probability.
- Tsallis, C. (1988). Possible generalization of Boltzmann-Gibbs statistics. Journal of Statistical Physics, 52, 479-487.
- Miller, G. A. (1955). Note on the bias of information estimates. Information Theory in Psychology.
- Chao, A., & Shen, T.-J. (2003). Nonparametric estimation of Shannon's index of diversity when there are unseen species. Environmental and Ecological Statistics, 10, 429-443.
- Grassberger, P. (2003). Entropy estimates from insufficient samplings. arXiv:physics/0307138.
- Nemenman, I., Shafee, F., & Bialek, W. (2002). Entropy and inference, revisited. NIPS 14.
- Pielou, E. C. (1966). The measurement of diversity in different types of biological collections. Journal of Theoretical Biology, 13, 131-144.
analyze_entropy ¶
analyze_entropy(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
overwrite_existing=False,
lowercase=True,
strip_punctuation=True,
strip_digits=True,
max_order=DEFAULT_MAX_ORDER,
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
encoding="utf-8-sig",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=False,
pass_through_cols=None,
workers=0,
on_progress=None,
verbose=True
)
Entropy and diversity measures for each text, over words and characters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The usual input contract: a spreadsheet of texts, a folder of documents, or a table somebody already gathered. |
None
|
txt_dir
|
Optional[PathLike]
|
The usual input contract: a spreadsheet of texts, a folder of documents, or a table somebody already gathered. |
None
|
analysis_csv
|
Optional[PathLike]
|
The usual input contract: a spreadsheet of texts, a folder of documents, or a table somebody already gathered. |
None
|
gathered_csv
|
Optional[PathLike]
|
The usual input contract: a spreadsheet of texts, a folder of documents, or a table somebody already gathered. |
None
|
out_features_csv
|
str or Path
|
Where to write. Default |
None
|
overwrite_existing
|
bool
|
Rebuild the table if it is already there. |
False
|
lowercase
|
bool
|
Fold case before counting. Off, "The" and "the" are two types. |
True
|
strip_punctuation
|
bool
|
Applied to the word units only, matching what
|
True
|
strip_digits
|
bool
|
Applied to the word units only, matching what
|
True
|
max_order
|
int
|
How far the block and conditional entropies go. Order 3 over characters already wants a few thousand characters to be worth reading; higher orders on short texts measure the sample rather than the text. |
3
|
text_cols
|
sequence of str
|
Which spreadsheet columns hold the text, when one has to be gathered. |
``("text",)``
|
id_cols
|
sequence of str
|
Columns that compose each row's identifier. |
None
|
mode
|
('concat', 'separate')
|
Measure several text columns joined together, or one at a time. |
"concat"
|
group_by
|
sequence of str
|
Combine rows sharing these columns before measuring. |
None
|
delimiter
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
encoding
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
joiner
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
num_buckets
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
max_open_bucket_files
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
tmp_root
|
str
|
The gatherer's own settings: how to read the spreadsheet, what to join combined texts with, and how much to spill to disk on a large one. |
','
|
recursive
|
bool
|
Search subfolders when the input is a folder of documents. |
True
|
pattern
|
str
|
Which files in that folder count as documents. |
DOCUMENT_PATTERN
|
id_from
|
('stem', 'name', 'path')
|
What to call each document, when the input is a folder. |
"stem"
|
include_source_path
|
bool
|
Carry each document's path into the gathered table. |
False
|
pass_through_cols
|
sequence of str
|
Columns of the gathered table to copy into the output. |
None
|
workers
|
int
|
Processes. 0 picks a sensible number for the job's size. |
0
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Notes
Read coverage before anything else. It says what share of the
distribution the text actually showed you, and when it is low the four
Shannon estimates will disagree -- that disagreement is the honest width
of the answer, not noise to average away.
Every entropy is reported in bits and again as an effective number of types, which is the same number in units people can hold in their head.
A conditional entropy can come out slightly negative on a short text. That is impossible in truth and is the estimate telling on itself: the block entropy above it is more undersampled than the one below, so the difference goes the wrong way. It is left as it falls rather than clamped at zero, because a negative number is a visible sign that the text was too short for that order and a zero is not.
Source code in src\taters\text\analyze_entropy.py
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | |
berger_parker ¶
berger_parker(counts)
The commonest unit's share -- dominance, the q -> infinity end.
Source code in src\taters\text\analyze_entropy.py
375 376 377 378 | |
chao1 ¶
chao1(counts)
How many types the text would have had if you had kept reading.
The bias-corrected Chao1 estimator: observed types plus a term built from how many appeared once and twice. Reported in its own right -- it is the q = 0 diversity you cannot see -- and used as the alphabet size NSB needs.
Source code in src\taters\text\analyze_entropy.py
264 265 266 267 268 269 270 271 272 273 274 275 | |
chao_shen_bits ¶
chao_shen_bits(counts)
Coverage-adjusted, after Chao and Shen (2003).
Estimates what share of the distribution you actually saw -- from how many words appeared exactly once -- and reweights accordingly. The one that holds up best on the short, Zipfian samples that text usually is.
Source code in src\taters\text\analyze_entropy.py
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
coverage ¶
coverage(counts)
Good-Turing sample coverage: the share of the distribution the text actually showed you. Low coverage is the signal that the entropy numbers below it are estimates rather than measurements.
Source code in src\taters\text\analyze_entropy.py
364 365 366 367 368 369 370 371 372 | |
grassberger_bits ¶
grassberger_bits(counts)
Grassberger (2003), which corrects each count by a digamma term.
Source code in src\taters\text\analyze_entropy.py
249 250 251 252 253 254 255 256 257 258 259 260 261 | |
hill_number ¶
hill_number(counts, order)
The Rényi entropy as an effective number of types.
Reported alongside every entropy because "4.2 bits" is not a quantity anybody has intuitions about and "18 equally common words" is.
Source code in src\taters\text\analyze_entropy.py
344 345 346 347 348 349 350 351 | |
miller_madow_bits ¶
miller_madow_bits(counts)
Plug-in plus (V - 1) / 2N: the leading term of the bias.
The cheapest correction and the least effective on a badly undersampled text, because it only knows how many types you saw.
Source code in src\taters\text\analyze_entropy.py
207 208 209 210 211 212 213 214 215 216 217 218 | |
nsb_bits ¶
nsb_bits(counts, alphabet=None)
Nemenman-Shafee-Bialek: Bayesian, integrated over the Dirichlet prior.
The only estimator here that needs to be told how many types the text could have used, because its prior is over distributions on a known alphabet. Text has no such number, and the answer moves by three bits across plausible guesses -- more than any two other estimators differ -- so guessing badly is worse than not using it.
So the guess is not left to anybody: alphabet defaults to
:func:chao1, which estimates the unseen types from the seen ones. That
makes it self-tuning and, on samples where the truth is known, accurate to
within a tenth of a bit.
Source code in src\taters\text\analyze_entropy.py
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | |
renyi_bits ¶
renyi_bits(counts, order)
Rényi entropy of the given order, in bits. Order 1 is Shannon.
Source code in src\taters\text\analyze_entropy.py
329 330 331 332 333 334 335 336 337 338 339 340 341 | |
shannon_bits ¶
shannon_bits(counts)
Plug-in (maximum likelihood) Shannon entropy, in bits.
Source code in src\taters\text\analyze_entropy.py
196 197 198 199 200 201 202 203 204 | |
tsallis ¶
tsallis(counts, order)
Tsallis entropy: the same family under a different, non-logarithmic way of combining independent parts.
Source code in src\taters\text\analyze_entropy.py
354 355 356 357 358 359 360 361 | |
taters.text.analyze_lexical_richness ¶
analyze_lexical_richness ¶
analyze_lexical_richness(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
msttr_window=100,
mattr_window=100,
mtld_threshold=0.72,
hdd_draws=42,
vocd_ntokens=50,
vocd_within_sample=100,
vocd_iterations=3,
vocd_seed=42,
pass_through_cols=None
)
Compute lexical richness/diversity metrics for each text row and write a features CSV. Draws heavily from https://github.com/LSYS/lexicalrichness but makes several key changes with the goals of minimizing dependencies, attempting to make some speed optimizations with grid search instead of precise curve specifications, and making some principled decisions around punctuation/hyphenization that differ from the original Note that these decisions are not objectively "better" than the original but, instead, reflect my own experiences/intuitions about what makes sense.
This function accepts (a) an analysis-ready CSV (with columns text_id,text), (b) a
raw CSV plus instructions for gathering/aggregation, or (c) a folder of .txt files.
For each resulting row of text, it tokenizes words and computes a suite of classical
lexical richness measures (e.g., TTR, Herdan's C, Yule's K, MTLD, MATTR, HDD, VOCD).
Results are written as a wide CSV whose rows align with the rows in the analysis-ready
table (or the gathered group_by rows), preserving any non-text metadata columns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str or Path
|
Source CSV to gather from. Use with |
None
|
txt_dir
|
str or Path
|
Folder of |
None
|
analysis_csv
|
str or Path
|
Existing analysis-ready CSV with columns |
None
|
gathered_csv
|
str or Path
|
Where to write the intermediate "analysis-ready" table built from
By default it lands beside the source -- which means analyzing a
spreadsheet in someone's Downloads folder writes a file into their
Downloads folder. Pass this to keep the intermediate with the rest of a
run's output instead. Ignored when |
None
|
on_progress
|
callable
|
Called as
Injected automatically by the pipeline runner for any step function that declares this parameter. |
None
|
out_features_csv
|
str or Path
|
Output CSV path. If omitted, defaults to
|
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading/writing CSVs. |
"utf-8-sig"
|
text_cols
|
sequence of str
|
Text column(s) to use when |
("text",)
|
id_cols
|
sequence of str
|
Columns to carry through unchanged into the analysis-ready CSV prior to analysis
(e.g., |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple |
"concat"
|
group_by
|
sequence of str
|
If provided, texts are grouped by these columns before analysis (e.g.,
|
None
|
delimiter
|
str
|
Column separator of the input spreadsheet. The gathered table and the output are always comma-separated. |
","
|
joiner
|
str
|
String used to join text fields when |
" "
|
num_buckets
|
int
|
Internal streaming/gather parameter to control temporary file bucketing (passed through to the gatherer). |
512
|
max_open_bucket_files
|
int
|
Maximum number of temporary files simultaneously open during gathering. |
64
|
tmp_root
|
str or Path
|
Temporary directory root for the gatherer. Defaults to a system temp location. |
None
|
recursive
|
bool
|
When |
True
|
pattern
|
str
|
Glob pattern for discovering text files under |
"*.txt"
|
id_from
|
('stem', 'name', 'path')
|
How to construct |
"stem"
|
include_source_path
|
bool
|
When |
True
|
msttr_window
|
int
|
Window size for MSTTR (Mean Segmental TTR). Must be smaller than the number of tokens in the text to produce a value. |
100
|
mattr_window
|
int
|
Window size for MATTR (Moving-Average TTR). Must be smaller than the number of tokens. |
100
|
mtld_threshold
|
float
|
MTLD threshold for factor completion. A higher threshold yields shorter factors and typically lower MTLD values; the default follows common practice. |
0.72
|
hdd_draws
|
int
|
Sample size |
42
|
vocd_ntokens
|
int
|
Maximum sample size used to estimate VOCD (D). For each |
50
|
vocd_within_sample
|
int
|
Number of random samples drawn per |
100
|
vocd_iterations
|
int
|
Repeat-estimate count for VOCD. The best-fit D from each repetition is averaged. |
3
|
vocd_seed
|
int
|
Seed for the VOCD random sampler (controls reproducibility across runs). |
42
|
pass_through_cols
|
Sequence[str] or None
|
Extra input columns to copy into the output beside |
None
|
workers
|
int
|
Parallel processes for reading documents. |
0
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written features CSV. |
Output shape
The features CSV starts with::
text_id, <pass-through columns...>, ttr, rttr, cttr, ...
Pass-through behavior:
- If
pass_through_colsis provided, those columns are included in that order. - Otherwise, if
id_colswere used during gathering, they are included in that order. - Otherwise (backward compatible), all non-
textcolumns from the analysis-ready CSV are passed through.
Metrics emitted per row (None if the text is too short):
ttr, rttr, cttr, herdan_c, summer_s, dugast, maas, yule_k, yule_i, herdan_vm, simpson_d,
msttr_{msttr_window}, mattr_{mattr_window}, mtld_{mtld_threshold}, hdd_{hdd_draws}, vocd_{vocd_ntokens}.
Notes
Tokenization and preprocessing.
Texts are lowercased, digits are removed, and punctuation characters are
replaced with spaces prior to tokenization. As a result, hyphenated forms such
as "state-of-the-art" will be split into separate tokens ("state", "of",
"the", "art"). This choice yields robust behavior across corpora but can
produce different numeric results than implementations that remove hyphens
(treating "state-of-the-art" as a single token). If you require strict parity
with a hyphen-removal scheme, adapt the internal preprocessing accordingly.
Metrics.
The following measures are emitted per row (values are None when a text is
too short to support the computation):
- ttr: Type-Token Ratio (|V| / N)
- rttr: Root TTR (|V| / sqrt(N))
- cttr: Corrected TTR (|V| / sqrt(2N))
- herdan_c: Herdan's C (log |V| / log N)
- summer_s: Summer's S (log log |V| / log log N)
- dugast: Dugast's U ((log N)^2 / (log N − log |V|))
- maas: Maas a^2 ((log N − log |V|) / (log N)^2)
- yule_k: Yule's K (dispersion of frequencies; higher = less diverse)
- yule_i: Yule's I (inverse of K, scaled)
- herdan_vm: Herdan's Vm
- simpson_d: Simpson's D (repeat-probability across tokens)
- msttr_{msttr_window}: Mean Segmental TTR over fixed segments
- mattr_{mattr_window}: Moving-Average TTR over a sliding window
- mtld_{mtld_threshold}: Measure of Textual Lexical Diversity (bidirectional)
- hdd_{hdd_draws}: HD-D (expected proportion of types in a sample of size hdd_draws)
- vocd_{vocd_ntokens}: VOCD (D) estimated by fitting TTR(N) to a theoretical curve
VOCD estimation. VOCD is fit without external optimization libraries: the function performs a coarse grid search over candidate D values (minimizing squared error between observed mean TTRs and a theoretical TTR(N; D) curve) for multiple repetitions, then averages the best D across repetitions. This generally tracks SciPy-based curve fits closely; you can widen the search grid or add a fine local search if tighter agreement is desired.
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If |
ValueError
|
If none or more than one of |
Examples:
Analyze an existing analysis-ready CSV (utterance-level):
>>> analyze_lexical_richness(
... analysis_csv="transcripts_all.csv",
... out_features_csv="features/lexical-richness.csv",
... overwrite_existing=True,
... )
Gather from a transcript CSV and aggregate per (source, speaker):
>>> analyze_lexical_richness(
... csv_path="transcripts/session.csv",
... text_cols=["text"],
... id_cols=["source", "speaker"],
... group_by=["source", "speaker"],
... mode="concat",
... out_features_csv="features/lexical-richness.csv",
... )
See Also
analyze_readability : Parallel analyzer producing readability indices.
Source code in src\taters\text\analyze_lexical_richness.py
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 | |
hdd ¶
hdd(tokens, draws=42)
HD-D (McCarthy & Jarvis): sum over types of (1 - P(X=0)) / draws, where X ~ Hypergeom(N, K, n) with N=len(tokens), K=freq(term), n=draws.
Source code in src\taters\text\analyze_lexical_richness.py
232 233 234 235 236 237 238 239 240 241 242 243 244 245 | |
vocd ¶
vocd(
tokens,
ntokens=50,
within_sample=100,
iterations=3,
seed=42,
)
Estimate D by: - for N in 35..ntokens: * sample 'within_sample' subsets of size N, compute TTR, average - grid search D over a reasonable range to minimize squared error to _ttr_nd - repeat 'iterations' times and average the best D
Source code in src\taters\text\analyze_lexical_richness.py
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
taters.text.analyze_readability ¶
analyze_readability ¶
analyze_readability(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
pass_through_cols=None
)
Compute per-row readability metrics using textstat and write a wide features CSV.
The function supports exactly one of three input modes:
analysis_csv— Use a prebuilt file with at least columnstext_idandtext.csv_path— Gather text from an arbitrary CSV usingtext_cols(and optionalid_cols/group_by) to produce an analysis-ready file.txt_dir— Gather text from a folder of.txtfiles.
If out_features_csv is omitted, the default output path is
./features/readability/<analysis_ready_filename>. All metrics below are computed
for every row. Non-numeric metrics (e.g., text_standard) are retained as strings.
Metrics (columns)
The following metrics are emitted as columns (subject to textstat availability):
flesch_reading_easesmog_indexflesch_kincaid_gradecoleman_liau_indexautomated_readability_indexdale_chall_readability_scoredifficult_wordslinsear_write_formulagunning_fogtext_standard(string label)spache_readability(for shorter/children texts; may be None)syllable_count(on entire text)lexicon_count(word count)sentence_countchar_countavg_sentence_lengthavg_syllables_per_wordavg_letter_per_word
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str or Path
|
Source CSV to gather from. Mutually exclusive with |
None
|
txt_dir
|
str or Path
|
Folder containing |
None
|
analysis_csv
|
str or Path
|
Prebuilt analysis-ready CSV with columns |
None
|
gathered_csv
|
str or Path
|
Where to write the intermediate "analysis-ready" table built from
By default it lands beside the source -- which means analyzing a
spreadsheet in someone's Downloads folder writes a file into their
Downloads folder. Pass this to keep the intermediate with the rest of a
run's output instead. Ignored when |
None
|
on_progress
|
callable
|
Called as
Injected automatically by the pipeline runner for any step function that declares this parameter. |
None
|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Text encoding used for reading/writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns to carry into grouping when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g., |
None
|
delimiter
|
str
|
Column separator of the input spreadsheet. The gathered table and the output are always comma-separated. |
","
|
joiner
|
str
|
Separator used when concatenating multiple text chunks in |
" "
|
num_buckets
|
int
|
Number of temporary hash buckets used during scalable CSV gathering. |
512
|
max_open_bucket_files
|
int
|
Maximum number of bucket files kept open concurrently during gathering. |
64
|
tmp_root
|
str or Path or None
|
Root directory for temporary gathering artifacts. |
None
|
recursive
|
bool
|
When gathering from a text folder, recurse into subdirectories. |
True
|
pattern
|
str
|
Glob pattern for selecting text files when gathering from a folder. |
"*.txt"
|
id_from
|
('stem', 'name', 'path')
|
How to derive |
"stem"
|
include_source_path
|
bool
|
If |
True
|
pass_through_cols
|
Sequence[str] or None
|
Extra input columns to copy into the output beside |
None
|
workers
|
int
|
Parallel processes for reading documents. |
0
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written features CSV. |
Output layout
The output CSV starts with:
text_id,
Pass-through behavior:
- If pass_through_cols is provided, those columns are included immediately
after text_id in that order.
- Else if id_cols were supplied during gathering, they are included in that order.
- Else (backward compatible), all non-text columns in the analysis-ready CSV
are copied through (e.g., source, speaker, etc.).
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If an input is missing. |
ValueError
|
If input modes are misconfigured or required columns are absent. |
RuntimeError
|
If |
Notes
- All rows are processed; blank or missing text yields benign defaults (metrics may be 0 or None).
- Additional columns present in the analysis-ready CSV (beyond
text) are copied through to the output (e.g.,source,speaker,group_count), aiding joins/aggregation.
Source code in src\taters\text\analyze_readability.py
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | |
taters.text.extract_sentence_embeddings ¶
extract_sentence_embeddings ¶
extract_sentence_embeddings(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
model_name="sentence-transformers/all-roberta-large-v1",
device="auto",
batch_size=32,
normalize_l2=True,
rounding=None,
show_progress=False,
on_progress=None,
pass_through_cols=None,
verbose=True
)
Average sentence embeddings per row of text and write a wide features CSV.
Supports three mutually exclusive input modes:
analysis_csv— Use a prebuilt file with columnstext_idandtext.csv_path— Gather from a CSV usingtext_cols(and optionalid_cols/group_by) to build an analysis-ready CSV.txt_dir— Gather from a folder of.txtfiles.
For each row, the text is split into sentences (NLTK if available; otherwise a regex fallback). Each sentence is embedded with a Sentence-Transformers model and the vectors are averaged into one row-level embedding. Optionally, vectors are L2-normalized. The output CSV schema is:
text_id[, <pass_through_cols...>], e0, e1, ..., e{D-1}
If out_features_csv is omitted, the default is
./features/sentence-embeddings/<analysis_ready_filename>. When
overwrite_existing is False and the output exists, the function
returns the existing path without recomputation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str or Path
|
Source CSV to gather from. Mutually exclusive with |
None
|
txt_dir
|
str or Path
|
Folder of |
None
|
analysis_csv
|
str or Path
|
Prebuilt analysis-ready CSV containing exactly |
None
|
gathered_csv
|
str or Path
|
Where to write the intermediate "analysis-ready" table built from
|
None
|
workers
|
int
|
Parallel processes for reading documents. By default it lands beside the source -- which means analyzing a
spreadsheet in someone's Downloads folder writes a file into their
Downloads folder. Pass this to keep the intermediate with the rest of a
run's output instead. Ignored when |
0
|
out_features_csv
|
str or Path
|
Output features CSV path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
verbose
|
bool
|
Print incidental notices -- which model is loading, which requested pass-through columns the input did not have. The pipeline runner passes False when a live display owns the screen. |
True
|
pass_through_cols
|
Sequence[str]
|
Column names from the analysis-ready CSV to copy into the output
alongside |
None
|
encoding
|
str
|
CSV I/O encoding. |
"utf-8-sig"
|
delimiter
|
str
|
CSV field delimiter. |
","
|
text_cols
|
Sequence[str]
|
When gathering from a CSV: column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str]
|
When gathering from a CSV: optional ID columns to carry through. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior if multiple |
"concat"
|
group_by
|
Sequence[str]
|
Optional grouping keys used during CSV gathering (e.g., |
None
|
joiner
|
str
|
Separator used when concatenating text in |
" "
|
num_buckets
|
int
|
Number of temporary hash buckets for scalable gathering. |
512
|
max_open_bucket_files
|
int
|
Maximum number of bucket files kept open concurrently during gathering. |
64
|
tmp_root
|
str or Path
|
Root directory for temporary gathering artifacts. |
None
|
recursive
|
bool
|
When gathering from a text folder, recurse into subdirectories. |
True
|
pattern
|
str
|
Glob pattern for selecting text files. |
"*.txt"
|
id_from
|
('stem', 'name', 'path')
|
How to derive |
"stem"
|
include_source_path
|
bool
|
Whether to include the absolute source path as an additional column when gathering from a text folder. |
True
|
model_name
|
str
|
Sentence-Transformers model name or path. |
"sentence-transformers/all-roberta-large-v1"
|
device
|
('auto', 'cuda', 'cpu')
|
Where to run the embedding model. "auto" uses the GPU when torch reports one that works and falls back to the CPU when it does not; "cuda" insists and raises if it cannot; "cpu" never touches the GPU. Previously there was no way to ask: sentence-transformers takes the GPU whenever torch reports one, which is fine until it is the third model in a pipeline to do so. |
"auto"
|
batch_size
|
int
|
Batch size for model encoding. |
32
|
normalize_l2
|
bool
|
If |
True
|
rounding
|
int or None
|
If provided, round floats to this many decimals (useful for smaller files). |
None
|
show_progress
|
bool
|
Print the model's own encoding progress bar. Suppressed whenever
|
False
|
on_progress
|
callable
|
Called as |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written features CSV. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If an input file or directory does not exist. |
ImportError
|
If |
ValueError
|
If input modes are misconfigured (e.g., multiple or none provided),
or if the analysis-ready CSV lacks |
Examples:
Compute row-level embeddings from a transcript CSV, grouped by speaker:
>>> analyze_with_sentence_embeddings(
... csv_path="transcripts/session.csv",
... text_cols=["text"], id_cols=["speaker"], group_by=["speaker"],
... model_name="sentence-transformers/all-roberta-large-v1",
... normalize_l2=True
... )
PosixPath('.../features/sentence-embeddings/session.csv')
Notes
- Rows with no recoverable sentences produce empty feature cells (not zeros).
- The embedding dimensionality
Dis taken from the model and used to construct header columnse0..e{D-1}.
Source code in src\taters\text\extract_sentence_embeddings.py
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | |
taters.text.finetune_predictor ¶
Fine-tune a transformer to predict outcomes from text -- one outcome or several at once -- with the same cross-validated honesty as the ridge.
A ridge over features asks "which measures predict this outcome?"; a fine-tuned encoder asks "how well can the text itself predict it?", with the encoder's own weights adjusted to the task. Every outcome gets a head of its own on one shared encoder: a regression head (mean squared error on the standardized outcome) for a numeric column, a classification head (cross-entropy) for a categorical one, and a row missing one outcome still trains the others -- so a sheet with a personality score and a diagnosis column trains one model for both (multi-task learning, Caruana 1997), which is the usual way to get more out of a small corpus.
- Devlin, J., et al. (2019). BERT: Pre-training of deep bidirectional transformers for language understanding. NAACL 2019.
- Caruana, R. (1997). Multitask learning. Machine Learning, 28, 41–75.
The discipline is the ridge's: every headline number is out of fold. The rows are dealt into folds (balanced on the first outcome, stratified when it is categorical); each fold trains a fresh model on the rest, with a slice of the training rows held out for early stopping, and predicts the rows it never saw; the metrics are computed over those predictions, per fold for the standard error and pooled for the headline. A final model is then trained on every row for the median best epoch and saved -- encoder and heads -- as a model any pipeline can apply to new text, whose class predictions carry the data's own labels.
Predicted classes are written as labels, never indices, and every
label can be renamed per model in Settings (class_names); so can the
settings that govern how the model is applied (apply).
apply_text_predictor ¶
apply_text_predictor(
*,
model_json,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
device="auto",
rounding=4,
batch_size=None,
max_length=None,
precision=None,
emit_probabilities=None
)
Score new texts with a fine-tuned text predictor.
The model's own apply settings (batch size, max_length, precision,
whether to write the per-class probabilities) are used unless the call
gives its own. Predicted classes are written as the model's labels --
the data's own, or whatever they were renamed to in Settings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_json
|
PathLike
|
A predictor manifest written by :func: |
required |
csv_path
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
out_features_csv
|
str or Path
|
Default |
None
|
overwrite_existing
|
bool
|
If False and the output exists, return it untouched. |
False
|
workers
|
int
|
Parallel processes for the gather and CPU threads for torch. |
0
|
text_cols
|
sequence of str
|
When gathering from a CSV, the column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row when gathering from a CSV. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join or treat separately. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before scoring. |
None
|
pattern
|
str
|
Which files to read from a folder of documents. |
every document type
|
device
|
('auto', 'cuda', 'cpu')
|
Where the model runs. |
"auto"
|
rounding
|
int
|
Decimal places written. |
4
|
batch_size
|
Optional[int]
|
Overrides for the model's own apply settings. |
None
|
max_length
|
Optional[int]
|
Overrides for the model's own apply settings. |
None
|
precision
|
Optional[int]
|
Overrides for the model's own apply settings. |
None
|
emit_probabilities
|
Optional[int]
|
Overrides for the model's own apply settings. |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\finetune_predictor.py
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 | |
finetune_text_predictor ¶
finetune_text_predictor(
*,
csv_path=None,
analysis_csv=None,
gathered_csv=None,
out_dir="stats_results",
out_models_dir=None,
name=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
outcome_cols=(),
categorical_outcomes=(),
task_weights="",
base_model=CURATED_ENCODERS[0][0],
layers="last",
pooling="mean",
max_length=256,
train_layers=0,
gradient_checkpointing=False,
n_folds=5,
stratify=True,
epochs=3,
learning_rate=2e-05,
batch_size=16,
grad_accum=1,
weight_decay=0.01,
warmup_fraction=0.06,
early_stopping=True,
val_fraction=0.1,
device="auto",
precision="auto",
seed=42,
rounding=4
)
Fine-tune an encoder to predict one or more outcome columns from text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The spreadsheet: the text in |
None
|
analysis_csv
|
Optional[PathLike]
|
The spreadsheet: the text in |
None
|
gathered_csv
|
Optional[PathLike]
|
The spreadsheet: the text in |
None
|
out_dir
|
str or Path
|
Where the metrics, fold, epoch and prediction tables, the report section and the figures go. |
"stats_results"
|
out_models_dir
|
str or Path
|
Where the model lands, default |
None
|
name
|
str
|
The model's name in menus and its file stem; default
|
None
|
overwrite_existing
|
bool
|
If False and the metrics table exists, return it untouched. |
False
|
workers
|
int
|
Parallel processes for the gather, and the CPU threads torch may use. 0 means automatic. |
0
|
text_cols
|
sequence of str
|
The column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join them into one text per row, or treat each as its own text. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before training (one text per group); an outcome then has to be constant within a group. |
None
|
outcome_cols
|
sequence of str
|
The columns to predict. A numeric column is a regression (mean
squared error on the standardized value); a column of labels, or
one named in |
()
|
categorical_outcomes
|
sequence of str
|
Which of |
()
|
task_weights
|
str
|
|
''
|
base_model
|
str
|
The encoder to start from: a Hugging Face name, a checkpoint folder, a Taters text encoder file, or a Taters fine-tuned predictor file -- in which case its encoder is the starting point and its heads are reused for outcomes with the same name and type (a warm start on new data), with fresh heads for new outcomes. |
CURATED_ENCODERS[0][0]
|
layers
|
str
|
Which hidden layers feed the heads ( |
"last"
|
pooling
|
('mean', 'cls', 'max')
|
How a text's token vectors become one. |
"mean"
|
max_length
|
int
|
The most tokens read per text; longer texts are cut (the share cut is reported). |
256
|
train_layers
|
int
|
Train only the top this-many encoder layers and the heads; 0 trains everything. Two is a good CPU compromise. |
0
|
gradient_checkpointing
|
bool
|
Trade compute for memory on a small card. |
False
|
n_folds
|
int
|
Cross-validation folds. Every headline number is out of fold. |
5
|
stratify
|
bool
|
Deal the folds balanced on the first outcome (stratified when it is a category) rather than at random. |
True
|
epochs
|
int
|
The most passes over the training rows per fold; with early stopping the epoch with the lowest validation loss is kept. |
3
|
learning_rate
|
float
|
The peak learning rate of AdamW, after warm-up. |
2e-5
|
batch_size
|
int
|
Texts per forward pass; halved automatically if the GPU runs out of memory (accumulation doubled to compensate). |
16
|
grad_accum
|
int
|
Batches accumulated per optimizer step. |
1
|
weight_decay
|
float
|
AdamW's weight decay; the share of steps spent warming up. |
0.01
|
warmup_fraction
|
float
|
AdamW's weight decay; the share of steps spent warming up. |
0.01
|
early_stopping
|
bool
|
Keep, per fold, the epoch with the lowest loss on a validation
slice of the training rows ( |
True
|
val_fraction
|
float
|
The share of each fold's training rows held out for early stopping. |
0.1
|
device
|
('auto', 'cuda', 'cpu')
|
Where training runs. |
"auto"
|
precision
|
('auto', 'fp32', 'fp16')
|
Half precision on a GPU (auto), always full, or always half. |
"auto"
|
seed
|
int
|
Seeds the folds, the shuffles, the validation slices and the heads. |
42
|
rounding
|
int
|
Decimal places written. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\finetune_predictor.py
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | |
parse_task_weights ¶
parse_task_weights(spec, outcomes)
"age: 1, condition: 2" -> weights per outcome, 1.0 where unsaid.
An unknown outcome is refused by name.
Source code in src\taters\text\finetune_predictor.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
taters.text.adapt_encoder ¶
Domain-adaptive pretraining: continue a language model's own training on your texts, so it speaks their dialect before it is asked to embed or predict anything.
A pre-trained encoder learned from web text and books. A corpus of
bereavement-forum posts, clinical notes or adolescents' diaries uses words
the encoder rarely saw and uses familiar words differently; adaptation
continues the masked-language-model objective -- hide fifteen percent of
the tokens, predict them -- on the corpus itself, with no labels, and
leaves an encoder whose held-out perplexity on that corpus has fallen. That
encoder is then the base for embeddings (:mod:transformer_embeddings) or
for fine-tuning a predictor (:mod:finetune_predictor).
- Gururangan, S., et al. (2020). Don't stop pretraining: Adapt language models to domains and tasks. ACL 2020.
The loop is plain torch: AdamW with linear warm-up and decay, gradient clipping, half precision on a GPU, dynamic masking from transformers' collator, a held-out split by text whose loss and perplexity are measured before and after with the same masking seed, so the two numbers are comparable. A GPU that runs out of memory halves the batch and doubles the accumulation, keeping the effective batch the same.
What it writes: <name>.json (a taters-encoder manifest), the
checkpoint folder <name>.encoder beside it, and <name>_report.md
with everything a methods section needs.
adapt_encoder ¶
adapt_encoder(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_model_json=None,
out_report_md=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
base_model=CURATED_ENCODERS[0][0],
name=None,
epochs=3,
max_length=256,
batch_size=16,
grad_accum=2,
learning_rate=5e-05,
warmup_fraction=0.06,
weight_decay=0.01,
mlm_probability=0.15,
heldout_fraction=0.1,
train_layers=0,
gradient_checkpointing=False,
device="auto",
precision="auto",
seed=42
)
Continue an encoder's masked-language-model training on these texts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_model_json
|
str or Path
|
The encoder manifest; the checkpoint lands beside it as
|
None
|
out_report_md
|
str or Path
|
The training report, default |
None
|
overwrite_existing
|
bool
|
If False and the manifest exists, return it untouched. |
False
|
workers
|
int
|
Parallel processes for the gather, and the CPU threads torch may use. 0 means automatic. |
0
|
text_cols
|
sequence of str
|
When gathering from a CSV, the column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row when gathering from a CSV. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join them into one text per row, or treat each as its own text. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before training (one text per group). |
None
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
base_model
|
str
|
The encoder to start from: a Hugging Face name, a checkpoint folder, or a Taters text encoder file (adapting twice is allowed). |
CURATED_ENCODERS[0][0]
|
name
|
str
|
The encoder's name in menus; default the manifest's file stem. |
None
|
epochs
|
int
|
Passes over the corpus. One to three is usual for adaptation; the report's held-out loss per epoch shows when more stopped helping. |
3
|
max_length
|
int
|
Tokens per training window. Longer texts are cut into windows so every token is trained on; 256 is a good trade of context for speed. |
256
|
batch_size
|
int
|
Windows per forward pass; halved automatically if the GPU runs out
of memory, with |
16
|
grad_accum
|
int
|
Batches accumulated per optimizer step. The effective batch is
|
2
|
learning_rate
|
float
|
The peak learning rate of AdamW, after warm-up. |
5e-5
|
warmup_fraction
|
float
|
The share of optimizer steps spent warming the learning rate up linearly from zero; it then decays linearly to zero. |
0.06
|
weight_decay
|
float
|
AdamW's weight decay. |
0.01
|
mlm_probability
|
float
|
The share of tokens masked in each window, freshly drawn each pass. |
0.15
|
heldout_fraction
|
float
|
The share of texts set aside, never trained on, to measure the loss and perplexity before and after. |
0.1
|
train_layers
|
int
|
Train only the top this-many layers (and the prediction head); 0 trains everything. Two layers is a good CPU compromise. |
0
|
gradient_checkpointing
|
bool
|
Trade compute for memory on a small card. |
False
|
device
|
('auto', 'cuda', 'cpu')
|
Where training runs. |
"auto"
|
precision
|
('auto', 'fp32', 'fp16')
|
Half precision on a GPU (auto), always full, or always half. |
"auto"
|
seed
|
int
|
Seeds the held-out split, the shuffles, the masking and the initialization of anything new. |
42
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\adapt_encoder.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | |
split_heldout ¶
split_heldout(text_ids, fraction, seed)
Indices of the training and held-out texts: a seeded shuffle, split by text, never by chunk, so no held-out sentence has a neighbor from the same document in the training set inflating the after-score. At least one text is held out whenever there are two or more.
Source code in src\taters\text\_mlm_train.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
taters.text.pretrain_encoder ¶
Pretrain a transformer encoder from scratch on your own texts.
Adapting (:mod:adapt_encoder) starts from a model somebody else pretrained
and continues its training on your corpus. This starts from nothing: random
weights, and a tokenizer built from your texts rather than borrowed from web
text -- so a corpus of clinical notes, forum posts or eighteenth-century
letters gets a vocabulary of its own words, whole, instead of one that
shatters them into pieces.
It is heavy-duty, and the step says so before it runs. A language model learns language from quantity: below a few million words of text the result will be worse at everything than any pretrained model you could have adapted, and even a modest model takes hours on one GPU and days on a CPU. The corpus size is left to your judgment -- testing the machinery on a small one is a legitimate thing to do -- but the report says what it was trained on, so nobody mistakes a toy for a tool.
The objective is masked-language modeling (Devlin et al., 2019) in the
RoBERTa style (Liu et al., 2019): byte-level BPE, dynamic masking, no
next-sentence task. The loop is the one adaptation uses
(:mod:_mlm_train), with two things pretraining needs and adaptation does
not: early stopping on the held-out loss, keeping the best epoch, and a
learning-rate schedule sized for a model that knows nothing yet.
- Devlin, J., Chang, M.-W., Lee, K., & Toutanova, K. (2019). BERT: Pre-training of deep bidirectional transformers for language understanding. NAACL 2019.
- Liu, Y., et al. (2019). RoBERTa: A robustly optimized BERT pretraining approach. arXiv:1907.11692.
What it writes: <name>.json (a taters-encoder manifest, the same
kind adaptation writes, so the library, the embeddings step and fine-tuning
treat it like any other encoder), the checkpoint folder <name>.encoder
beside it, and <name>_report.md.
pretrain_encoder ¶
pretrain_encoder(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_model_json=None,
out_report_md=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
name=None,
preset="small",
vocab_size=None,
layers=None,
hidden_size=None,
attention_heads=None,
max_length=256,
epochs=40,
patience=3,
batch_size=32,
grad_accum=2,
learning_rate=0.0005,
warmup_fraction=0.1,
weight_decay=0.01,
mlm_probability=0.15,
heldout_fraction=0.1,
gradient_checkpointing=False,
device="auto",
precision="auto",
seed=42
)
Train a transformer encoder, and its tokenizer, from nothing but these texts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as every other text step: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_model_json
|
str or Path
|
The encoder manifest; the checkpoint lands beside it as
|
None
|
out_report_md
|
str or Path
|
The training report, default |
None
|
overwrite_existing
|
bool
|
If False and the manifest exists, return it untouched. |
False
|
workers
|
int
|
Parallel processes for the gather, and the CPU threads torch may use. 0 means automatic. |
0
|
text_cols
|
Sequence[str]
|
Gather options, as in every text step. |
('text',)
|
id_cols
|
Sequence[str]
|
Gather options, as in every text step. |
('text',)
|
mode
|
Sequence[str]
|
Gather options, as in every text step. |
('text',)
|
group_by
|
Sequence[str]
|
Gather options, as in every text step. |
('text',)
|
pattern
|
Sequence[str]
|
Gather options, as in every text step. |
('text',)
|
name
|
str
|
The encoder's name in menus; default the manifest's file stem. |
None
|
preset
|
('small', 'base', 'custom')
|
The architecture. |
"small"
|
vocab_size
|
int
|
Symbols in the tokenizer. Default follows the preset (8,000 for small, 30,000 for base). At least 300. |
None
|
layers
|
int
|
The numbers behind a |
None
|
hidden_size
|
int
|
The numbers behind a |
None
|
attention_heads
|
int
|
The numbers behind a |
None
|
max_length
|
int
|
Tokens per training window, and the longest input the finished model will read. Longer texts are cut into windows for training. |
256
|
epochs
|
int
|
The most passes over the corpus. Training stops earlier when the
held-out loss has not fallen for |
40
|
patience
|
int
|
How many epochs without improvement end the run. |
3
|
batch_size
|
int
|
Windows per forward pass; halved automatically if the GPU runs out
of memory, with |
32
|
grad_accum
|
int
|
Batches accumulated per optimizer step. |
2
|
learning_rate
|
float
|
The peak learning rate of AdamW -- ten times adaptation's, because there is nothing here worth preserving yet. |
5e-4
|
warmup_fraction
|
float
|
The share of optimizer steps warming the learning rate up from zero. |
0.1
|
weight_decay
|
float
|
AdamW's weight decay. |
0.01
|
mlm_probability
|
float
|
The share of tokens masked in each window, freshly drawn each pass. |
0.15
|
heldout_fraction
|
float
|
The share of texts never trained on -- and never shown to the tokenizer -- whose loss decides when to stop. |
0.1
|
gradient_checkpointing
|
bool
|
Trade compute for memory on a small card. |
False
|
device
|
('auto', 'cuda', 'cpu')
|
Where training runs. With more than one GPU visible, all of them
are used from this one process ( |
"auto"
|
precision
|
('auto', 'fp32', 'fp16')
|
Half precision on a GPU (auto), always full, or always half. |
"auto"
|
seed
|
int
|
Seeds the weights, the held-out split, the shuffles and the masks. |
42
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\pretrain_encoder.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | |
train_tokenizer ¶
train_tokenizer(texts, *, vocab_size, min_frequency=2)
A byte-level BPE tokenizer learned from texts alone.
Byte-level, so no text can contain a character it cannot represent -- every byte is a symbol before any merge is learned -- and RoBERTa-style, so the model's config and the collator's masking agree with it without special handling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
texts
|
sequence of str
|
The texts to learn merges from. Pass the training texts only: a tokenizer that has seen the held-out texts has leaked a little of them into every measurement made on them. |
required |
vocab_size
|
int
|
How many symbols, byte symbols and special tokens included. |
required |
min_frequency
|
int
|
A merge has to occur this often to be kept. |
2
|
Returns:
| Type | Description |
|---|---|
RobertaTokenizerFast
|
|
Source code in src\taters\text\pretrain_encoder.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
taters.text.transformer_embeddings ¶
Embeddings from any transformer encoder, sentence by sentence, averaged per text.
Conceptually the sentence-transformers step: each text is split into
sentences with the same splitter, every sentence is encoded, and the
text's vector is the mean of its sentence vectors. What this step adds is
the choice of encoder and of reading: any Hugging Face encoder, an
encoder adapted to your corpus in Taters, or the encoder inside a
fine-tuned predictor; which hidden layers are read and how token vectors
are pooled. A sentence-transformers model has been trained to put a
sentence's meaning in one place; a plain encoder has not, so the defaults
here -- the second-to-last layer, mean pooling -- are the ones that
transfer best as frozen features (the last layer is specialized to
predicting masked words; [CLS] means little in a model never trained to
use it).
Long sentences are windowed with overlap and their windows averaged, never cut; sentences from many texts are batched together for throughput; a GPU that runs out of memory halves the batch and carries on. A text with no sentence gets blank cells, not zeros.
extract_transformer_embeddings ¶
extract_transformer_embeddings(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
delimiter=",",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
pass_through_cols=None,
model_name_or_path=DEFAULT_ENCODER,
layers="second_to_last",
pooling="mean",
sentence_weighting="equal",
max_length=512,
batch_size=32,
device="auto",
precision="auto",
normalize_l2=False,
rounding=6
)
Embed every text with a transformer encoder: sentence vectors, averaged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_features_csv
|
str or Path
|
Default |
None
|
overwrite_existing
|
bool
|
If False and the output exists, return it untouched. |
False
|
workers
|
int
|
Parallel processes for reading documents during the gather, and the CPU threads torch may use. 0 means automatic. |
0
|
text_cols
|
sequence of str
|
When gathering from a CSV, the column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row when gathering from a CSV. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join them into one text per row, or treat each as its own text. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before analyzing (one text per group). |
None
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
pass_through_cols
|
sequence of str
|
Columns of the source carried into the output beside |
None
|
model_name_or_path
|
str
|
A Hugging Face encoder name ( |
DEFAULT_ENCODER
|
layers
|
str
|
Which hidden layers become the token vectors: |
"second_to_last"
|
pooling
|
('mean', 'cls', 'max')
|
How a sentence's token vectors become one: the mean over its real
tokens, the |
"mean"
|
sentence_weighting
|
('equal', 'tokens')
|
How a text's sentence vectors become one: each sentence equally (what the sentence-transformers step does) or weighted by length in tokens. |
"equal"
|
max_length
|
int
|
The most tokens the encoder reads at once. A longer sentence is windowed with overlap and its windows averaged, never cut. |
512
|
batch_size
|
int
|
Sentences per forward pass; halved automatically if the GPU runs out of memory. |
32
|
device
|
('auto', 'cuda', 'cpu')
|
Where the encoder runs. |
"auto"
|
precision
|
('auto', 'fp32', 'fp16')
|
Half precision on a GPU (auto), always full, or always half. |
"auto"
|
normalize_l2
|
bool
|
Scale each text's vector to unit length, for cosine comparisons. |
False
|
rounding
|
int
|
Decimal places written. |
6
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\transformer_embeddings.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | |
taters.text.word_vectors ¶
Word vectors: train them on your own texts, bring them from elsewhere, and turn them into features.
A word vector model learns, from nothing but co-occurrence, that grave sits near death and far from picnic. Trained on the corpus under study it captures how these writers use words -- what "home" means in a bereavement forum is not what it means in a real-estate listing -- and a pre-trained set (GloVe, word2vec, fastText) brings a general-purpose sense of the language to a corpus too small to train on. Either way the model is a matrix: one row per word, one column per dimension.
Three things are done with it here:
- The mean vector per text --
wv_1 .. wv_k-- the classic bag-of- vectors representation, a compact numeric fingerprint that a ridge or classifier can learn from. - Similarity to concepts --
sim_mydict__death,sim_mydict__work-- the cosine between a text's vector and the weighted mean vector of a category of a LIWC-22 dictionary (.dic,.dicx,.csv: one column per category, wildcards and phrases as LIWC reads them, cells as weights), so a hypothesis ("these texts dwell on mortality") becomes one column per category. See :mod:taters.text._concept_dicts. - Nearest neighbors of chosen words, as a table and as word clouds, so the model can be inspected and reported: the neighbors are the evidence that it learned what you think it learned.
Two commitments shape the module:
- A fitted model is a reusable instrument. The manifest (
.json) records the vocabulary, the text settings that produced it, how it was trained and how it should be applied; the matrix travels beside it as<stem>.npy(a payload, in the library's terms -- moved, renamed and deleted with the manifest). Applying the model to its own training texts reproduces the training features exactly. - Memory-safe on an ordinary laptop. The matrix is memory-mapped, never copied whole; nearest neighbors are computed in row chunks; training streams the tokenized corpus from a scratch file (gensim keeps only the vocabulary and the matrix in memory). gensim is needed only to train and to read the binary word2vec/fastText formats; applying, describing and importing text formats need numpy alone.
fastText's subword vectors are not kept (the bucket matrix is hundreds of
megabytes and mostly noise for feature extraction); a word outside the
vocabulary is skipped for both families, and the in_vocab_count column
says how many words each text lost that way.
apply_word_vectors ¶
apply_word_vectors(
*,
model_json,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
device="auto",
rounding=4,
weighting=None,
normalize_words=None,
concept_dicts=None
)
Score new texts with a saved word-vector model.
The model's own settings (its apply block: weighting, word
normalization, concepts) are used unless the call gives its own, so a
pipeline that applies a model gets the settings the model was saved
with -- and changed in Settings afterwards -- without knowing them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_json
|
PathLike
|
A word-vector manifest written by :func: |
required |
csv_path
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers. |
None
|
out_features_csv
|
str or Path
|
Default |
None
|
overwrite_existing
|
bool
|
If False and the output files exist, return them untouched. |
False
|
workers
|
int
|
Parallel processes for reading and tokenizing texts. 0 means automatic: three-quarters of the logical cores. |
0
|
text_cols
|
sequence of str
|
When gathering from a CSV, the column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row when gathering from a CSV. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join them into one text per row, or treat each as its own text. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before analyzing (one text per group). |
None
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, if the stanza engine is used -- a runtime choice, deliberately not stored in the model. |
"auto"
|
rounding
|
int
|
Decimal places written. |
4
|
weighting
|
Optional[Literal['tokens', 'types', 'sif']]
|
Overrides for the model's own apply settings; see
:func: |
None
|
normalize_words
|
Optional[Literal['tokens', 'types', 'sif']]
|
Overrides for the model's own apply settings; see
:func: |
None
|
concept_dicts
|
Optional[Literal['tokens', 'types', 'sif']]
|
Overrides for the model's own apply settings; see
:func: |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\word_vectors.py
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | |
describe_word_vectors ¶
describe_word_vectors(
model_json,
out_neighbors_csv=None,
*,
probes="",
top_neighbors=20,
device="auto",
rounding=4,
encoding="utf-8-sig",
overwrite_existing=False,
verbose=True,
on_progress=None
)
Write the nearest-neighbors table of a saved model.
probes are comma-separated words; empty means the model's most
frequent words. Every concept category the model carries is listed
too, as the words closest to its vector. The table has one row
per (probe, neighbor): probe, rank, word, similarity; a probe not
in the vocabulary gets one row with blanks, so its absence is visible.
Source code in src\taters\text\word_vectors.py
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 | |
import_word_vectors ¶
import_word_vectors(
vectors_path,
out_model_json,
*,
format="auto",
max_vocab=200000,
name=None,
concept_dicts=(),
weighting="tokens",
normalize_words=False,
lemmatize=False,
keep_punctuation=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
probes="",
top_neighbors=20,
out_report_md=None,
overwrite_existing=False,
encoding="utf-8",
verbose=True,
on_progress=None
)
Bring pre-trained vectors (GloVe, word2vec, fastText) in as a model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vectors_path
|
PathLike
|
The vectors file: GloVe text ( |
required |
out_model_json
|
PathLike
|
Where the manifest goes; the matrix lands beside it as |
required |
format
|
Literal['auto', 'glove', 'word2vec_text', 'word2vec_bin', 'fasttext_bin']
|
|
'auto'
|
max_vocab
|
int
|
The most words kept, from the top of the file (these files list words by frequency). 200,000 x 300 floats is 240 MB, the most an ordinary laptop should be asked to hold. Case is folded, keeping the first occurrence of a word, because every tokenizer here lower-cases text: a vocabulary with both Cat and cat would never see the first. |
200000
|
lemmatize
|
bool
|
How texts will be tokenized when the model is applied. Off for lemmatizing by default: pre-trained vectors were learned on inflected words, and "ran" is in the vocabulary while "run" alone would miss it. |
False
|
keep_punctuation
|
bool
|
How texts will be tokenized when the model is applied. Off for lemmatizing by default: pre-trained vectors were learned on inflected words, and "ran" is in the vocabulary while "run" alone would miss it. |
False
|
engine
|
bool
|
How texts will be tokenized when the model is applied. Off for lemmatizing by default: pre-trained vectors were learned on inflected words, and "ran" is in the vocabulary while "run" alone would miss it. |
False
|
tokenizer
|
bool
|
How texts will be tokenized when the model is applied. Off for lemmatizing by default: pre-trained vectors were learned on inflected words, and "ran" is in the vocabulary while "run" alone would miss it. |
False
|
stanza_lang
|
bool
|
How texts will be tokenized when the model is applied. Off for lemmatizing by default: pre-trained vectors were learned on inflected words, and "ran" is in the vocabulary while "run" alone would miss it. |
False
|
name
|
Optional[str]
|
As for :func: |
None
|
concept_dicts
|
Optional[str]
|
As for :func: |
None
|
weighting
|
Optional[str]
|
As for :func: |
None
|
normalize_words
|
Optional[str]
|
As for :func: |
None
|
probes
|
Optional[str]
|
As for :func: |
None
|
top_neighbors
|
Optional[str]
|
As for :func: |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\word_vectors.py
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 | |
nearest_neighbors ¶
nearest_neighbors(
model_json, words, *, top_n=20, device="auto"
)
The top_n most similar vocabulary words to each word, by cosine.
A word is looked up as the model tokenizes it (lower-cased, lemmatized if the model was); a word not in the vocabulary maps to an empty list. The probe itself is never among its own neighbors. Computed in row chunks, so a large model is searched without a second copy in memory.
Source code in src\taters\text\word_vectors.py
401 402 403 404 405 406 407 408 409 410 411 412 413 414 | |
train_word_vectors ¶
train_word_vectors(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
out_features_csv=None,
out_model_json=None,
out_neighbors_csv=None,
out_report_md=None,
overwrite_existing=False,
workers=0,
on_progress=None,
verbose=True,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
device="auto",
lemmatize=False,
keep_punctuation=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
name=None,
family="word2vec",
algorithm="skipgram",
vector_size=100,
window=5,
min_count=5,
epochs=5,
negative=5,
seed=42,
reproducible=False,
concept_dicts=(),
weighting="tokens",
normalize_words=False,
probes="",
top_neighbors=20,
rounding=4
)
Train word vectors on the texts, save the model, and write its features.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as every other text analyzer: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_features_csv
|
str or Path
|
The features -- one row per text -- default
|
None
|
overwrite_existing
|
bool
|
If False and the output files exist, return them untouched. |
False
|
workers
|
int
|
Parallel processes for reading and tokenizing texts. 0 means automatic: three-quarters of the logical cores. |
0
|
text_cols
|
sequence of str
|
When gathering from a CSV, the column(s) holding the text. |
("text",)
|
id_cols
|
sequence of str
|
Columns that identify each row when gathering from a CSV. |
None
|
mode
|
('concat', 'separate')
|
With several text columns: join them into one text per row, or treat each as its own text. |
"concat"
|
group_by
|
sequence of str
|
Columns to combine rows by before analyzing (one text per group). |
None
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, if the stanza engine is used -- a runtime choice, deliberately not stored in the model. |
"auto"
|
out_model_json
|
str or Path
|
The model manifest; its matrix lands beside it as |
None
|
out_neighbors_csv
|
str or Path
|
The nearest-neighbors table and the training report, default
beside the model as |
None
|
out_report_md
|
str or Path
|
The nearest-neighbors table and the training report, default
beside the model as |
None
|
lemmatize
|
bool
|
How text becomes words, recorded in the model so new texts are read the same way. Text is always lower-cased (every tokenizer here does it): Death and death are one word to a study of meaning, and a vocabulary that kept both would spend its counts twice. |
False
|
keep_punctuation
|
bool
|
How text becomes words, recorded in the model so new texts are read the same way. Text is always lower-cased (every tokenizer here does it): Death and death are one word to a study of meaning, and a vocabulary that kept both would spend its counts twice. |
False
|
engine
|
bool
|
How text becomes words, recorded in the model so new texts are read the same way. Text is always lower-cased (every tokenizer here does it): Death and death are one word to a study of meaning, and a vocabulary that kept both would spend its counts twice. |
False
|
tokenizer
|
bool
|
How text becomes words, recorded in the model so new texts are read the same way. Text is always lower-cased (every tokenizer here does it): Death and death are one word to a study of meaning, and a vocabulary that kept both would spend its counts twice. |
False
|
stanza_lang
|
bool
|
How text becomes words, recorded in the model so new texts are read the same way. Text is always lower-cased (every tokenizer here does it): Death and death are one word to a study of meaning, and a vocabulary that kept both would spend its counts twice. |
False
|
name
|
str
|
The model's name in menus; default the manifest's file stem. |
None
|
family
|
('word2vec', 'fasttext')
|
word2vec learns a vector per word; fastText also learns from character n-grams during training, which helps with rare and misspelt words, though only whole-word vectors are kept here. |
"word2vec"
|
algorithm
|
('skipgram', 'cbow')
|
Skip-gram predicts context from a word and does better on small corpora and rare words; CBOW is faster and slightly better on very large ones. |
"skipgram"
|
vector_size
|
int
|
The usual: dimensions; context words either side; the fewest occurrences a word needs to get a vector; passes over the corpus; negative samples per positive. |
100
|
window
|
int
|
The usual: dimensions; context words either side; the fewest occurrences a word needs to get a vector; passes over the corpus; negative samples per positive. |
100
|
min_count
|
int
|
The usual: dimensions; context words either side; the fewest occurrences a word needs to get a vector; passes over the corpus; negative samples per positive. |
100
|
epochs
|
int
|
The usual: dimensions; context words either side; the fewest occurrences a word needs to get a vector; passes over the corpus; negative samples per positive. |
100
|
negative
|
int
|
The usual: dimensions; context words either side; the fewest occurrences a word needs to get a vector; passes over the corpus; negative samples per positive. |
100
|
seed
|
int
|
The random seed. Training runs in parallel threads and is then
reproducible only in distribution; |
42
|
reproducible
|
int
|
The random seed. Training runs in parallel threads and is then
reproducible only in distribution; |
42
|
concept_dicts
|
sequence of str or Path
|
LIWC-22 dictionaries ( |
()
|
weighting
|
('tokens', 'types', 'sif')
|
How words are averaged into a text's vector: every occurrence
( |
"tokens"
|
normalize_words
|
bool
|
Scale every word vector to unit length before averaging, so a frequent word with a long vector does not dominate. |
False
|
probes
|
str
|
Comma-separated words whose nearest neighbors the report shows;
every concept category's neighbors are shown as well. Empty:
:data: |
''
|
top_neighbors
|
int
|
Neighbors per probe in the table and the clouds. |
20
|
rounding
|
int
|
Decimal places written. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Notes
Nothing survives below min_count on a tiny corpus, and the refusal
says so before gensim is asked. The report beside the model has the
methods paragraph, the settings, the corpus coverage, the loss per
epoch, the neighbors, and the package versions -- what a paper needs.
Source code in src\taters\text\word_vectors.py
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 | |
taters.text.subtitle_parser ¶
SubtitleSegment
dataclass
¶
SubtitleSegment(number, start_ms, end_ms, text, name=None)
Normalized subtitle cue spanning a time interval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
number
|
int or None
|
SRT block index if present; |
required |
start_ms
|
int
|
Start time in milliseconds. |
required |
end_ms
|
int
|
End time in milliseconds. |
required |
text
|
str
|
Cue text content. May contain embedded newlines if the source had multiple lines. |
required |
name
|
str or None
|
Optional speaker/name field (not populated by the built-in parsers). |
None
|
Notes
Instances are immutable (frozen=True) so they can be safely shared and hashed.
convert_subtitles ¶
convert_subtitles(
*,
input,
to,
output=None,
encoding=None,
include_name=False,
overwrite_existing=False
)
Convert an SRT/VTT file to CSV/SRT/VTT.
Reads a subtitle file, parses into normalized segments, and renders to the
requested format. When output is omitted, a default path is created at
./features/subtitles/<input_stem>.<ext>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input
|
str or Path
|
Path to the input |
required |
to
|
('csv', 'srt', 'vtt')
|
Desired output format. |
'csv'
|
output
|
str or Path
|
Explicit output path. If |
None
|
encoding
|
str
|
Input encoding override; otherwise auto-detected (or UTF-8). |
None
|
include_name
|
bool
|
When |
False
|
overwrite_existing
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written output file. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the input file does not exist. |
ValueError
|
If the output format is unsupported or input content is malformed. |
Source code in src\taters\text\subtitle_parser.py
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 | |
main ¶
main()
Command-line entry point for subtitle parsing and conversion.
Parses arguments via :func:_build_arg_parser, calls
:func:convert_subtitles, and prints the resulting output path.
Examples:
$ python -m taters.text.subtitle_parser --input transcript.srt --to csv --output features/subtitles/transcript.csv
Source code in src\taters\text\subtitle_parser.py
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | |
parse_srt ¶
parse_srt(text)
Parse SRT content into normalized subtitle segments.
The parser tolerates extra whitespace and the optional numeric index line.
Each cue must include a timestamp line of the form
HH:MM:SS,mmm --> HH:MM:SS,mmm (a dot separator for milliseconds is also
accepted for robustness).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Entire SRT file content. |
required |
Returns:
| Type | Description |
|---|---|
list[SubtitleSegment]
|
Parsed cues with millisecond times and original (joined) text. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a well-formed timestamp line is missing where expected. |
Source code in src\taters\text\subtitle_parser.py
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | |
parse_subtitles ¶
parse_subtitles(input_path, *, encoding=None)
Auto-detect and parse a subtitle file by extension.
.vtt files are parsed as WebVTT; .srt and unknown extensions are
parsed as SRT. Input encoding is detected with chardet when available,
otherwise UTF-8 is assumed. Decoding errors are replaced.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_path
|
str or Path
|
Path to an SRT or VTT file. |
required |
encoding
|
str
|
Override input encoding. If omitted, try detect then fall back to UTF-8. |
None
|
Returns:
| Type | Description |
|---|---|
list[SubtitleSegment]
|
Normalized subtitle segments. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the path does not exist. |
Source code in src\taters\text\subtitle_parser.py
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | |
parse_vtt ¶
parse_vtt(text)
Parse WebVTT content into normalized subtitle segments.
Behavior:
- Skips the WEBVTT header and any header metadata.
- Skips NOTE and STYLE blocks.
- Ignores optional cue identifiers.
- Requires a timestamp line of the form
HH:MM:SS.mmm --> HH:MM:SS.mmm (comma also accepted).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Entire VTT file content. |
required |
Returns:
| Type | Description |
|---|---|
list[SubtitleSegment]
|
Parsed cues with millisecond times and original (joined) text. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a required timestamp line is malformed or missing. |
Source code in src\taters\text\subtitle_parser.py
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | |
render_to_csv ¶
render_to_csv(segs, out_path, *, include_name=False)
Write segments to a CSV file.
The CSV schema is:
start_time,end_time[,name],text
Times are written as integer milliseconds (stringified) to preserve exact alignment for downstream tools.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segs
|
Iterable[SubtitleSegment]
|
Segments to write. |
required |
out_path
|
str or Path
|
Output CSV path. |
required |
include_name
|
bool
|
Include a |
False
|
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written CSV file. |
Source code in src\taters\text\subtitle_parser.py
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | |
render_to_srt ¶
render_to_srt(segs, out_path)
Write segments to SRT format.
Blocks are 1-indexed and use HH:MM:SS,mmm timestamps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segs
|
Iterable[SubtitleSegment]
|
Segments to write. |
required |
out_path
|
str or Path
|
Output |
required |
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written SRT file. |
Source code in src\taters\text\subtitle_parser.py
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | |
render_to_vtt ¶
render_to_vtt(segs, out_path)
Write segments to WebVTT format.
Includes a standard WEBVTT header and uses HH:MM:SS.mmm timestamps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segs
|
Iterable[SubtitleSegment]
|
Segments to write. |
required |
out_path
|
str or Path
|
Output |
required |
Returns:
| Type | Description |
|---|---|
Path
|
Path to the written VTT file. |
Source code in src\taters\text\subtitle_parser.py
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | |
taters.text.analyze_ngram_frequencies ¶
Corpus-level n-gram frequency list, with collocation statistics.
One output row per retained n-gram (all orders 1..ngram_n), not one row
per text: this is a corpus tool. "Document" means one analysis-ready row,
so the same unit-of-analysis machinery the other text steps use decides what
NPMI's document counts mean here too.
The statistics follow Bouma (2009), "Normalized (Pointwise) Mutual Information in Collocation Extraction", with the chain split NPMI((w1..wn-1), wn) for orders above two. Three corrections over the C# plugin this replaces (BUTTER's Frequency List), all silent-result bugs rather than crashes:
- Every probability shares one sample space: the corpus token count -- p(g) = freq(g) / N for the n-gram and both of its parts, which is the formulation Bouma's bounds are proved for (and what e.g. gensim's phrase scorer computes). The plugin normalized each order by its own total (bigrams by the bigram count, words by the word count), mixing sample spaces -- under which NPMI is not bounded by 1 and "0.9" means nothing.
- Those counts are the full counts, taken before any minimum-frequency or document-share filter removes rows. Computing totals after filtering (as the plugin did) inflates every probability by exactly what was filtered, so NPMI values drifted with the user's filter settings.
- Nothing is pruned during counting, so every retained n-gram's subgram
counts exist and every retained n-gram gets a score. The plugin pruned
periodically for memory and then silently discarded any n-gram whose
subgram had been pruned out from under it. Memory is bounded a different
way: past a RAM budget (
max_ram_mb) the counting table spills sorted batches to disk and merges them once at the end (helpers.spill_counter) -- the counts stay exact and the output file stays identical.
Filters (minimum frequency, minimum document share, the stoplist, and the optional NPMI/logDice thresholds) apply only at write time, to output rows -- never to the counts the statistics are computed from.
analyze_ngram_frequencies ¶
analyze_ngram_frequencies(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
ngram_n=1,
lemmatize=False,
pos_tagged=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
device="auto",
stoplist_paths=None,
min_freq=5,
min_obs_pct=0.1,
min_token_count=10,
min_npmi=None,
min_logdice=None,
max_ram_mb=1024,
rounding=4
)
Build a corpus frequency list of 1..ngram_n-grams and write it as CSV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of
texts, a folder of |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of
texts, a folder of |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of
texts, a folder of |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of
texts, a folder of |
None
|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided:
|
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g. |
None
|
delimiter
|
str
|
Column separator of the input CSV. |
","
|
pattern
|
str
|
Which files to read when gathering from a folder of documents
(globs, |
every document type
|
ngram_n
|
int
|
Highest n-gram order. The output lists every order from 1 up to this, and the lower orders are needed internally for NPMI in any case. |
1
|
lemmatize
|
bool
|
Lemmatize tokens (WordNet, POS-guided) before counting. Always applied before the stoplist, so "be" catches "is/was/were". |
False
|
pos_tagged
|
bool
|
Treat word+tag as the unit: the verb "felt" and the noun "felt"
become separate rows, and a |
False
|
stoplist_paths
|
sequence of paths
|
Stop word/character lists (.txt, one entry per line; folders are expanded). None means a vanilla list. Applied to finished n-grams -- a row is dropped when any of its tokens is a stop entry -- never to the token stream, so counts and NPMI keep their true values. |
None
|
engine
|
('nltk', 'stanza')
|
Who tags: NLTK's perceptron tagger, or Stanza's neural pipeline. Stanza is slower, more accurate, multilingual, and GPU-optional. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Who splits text into tokens. The default (the Potts social-media tokenizer) keeps counts comparable across engines and keeps emoticons, hashtags and URLs whole; "stanza" (Stanza engine only) hands Stanza the whole job. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine; that language's model is downloaded once on first use (can be a few hundred MB). Only with engine="stanza". |
"en"
|
keep_punctuation
|
bool
|
Count punctuation (and emoticons) as terms. Off, only tokens with a letter or digit are counted, so "." and "," never become vocabulary. Must match across the frequency list, the matrix and the topic model. |
False
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs: "auto", "cuda", or "cpu". Only with engine="stanza"; the NLTK engine is CPU-only either way. |
"auto"
|
min_freq
|
int
|
Drop n-grams rarer than this from the output. |
5
|
min_obs_pct
|
float
|
Drop n-grams appearing in fewer than this percent of documents. |
0.10
|
min_token_count
|
int
|
Skip documents shorter than this many tokens entirely (they do not count toward document totals either). |
10
|
min_npmi
|
float
|
Optional collocation thresholds for orders above one. Default None: the metrics are reported and filtering stays an analysis decision. |
None
|
min_logdice
|
float
|
Optional collocation thresholds for orders above one. Default None: the metrics are reported and filtering stays an analysis decision. |
None
|
max_ram_mb
|
int
|
Approximate RAM budget (in MB) for the n-gram counting table. A corpus whose vocabulary outgrows it has sorted batches cached to a temporary folder and merged once at the end: the output file is identical, memory stays bounded, and the disk is touched in a few big sequential passes rather than per gram. A small corpus never reaches the budget and never notices; raise it on a big machine for speed on a huge corpus. |
1024
|
workers
|
int
|
Parallel processes, spent twice: reading documents during the gather,
then tokenizing-and-tallying during the count. |
0
|
rounding
|
int
|
Decimal places for the derived statistics. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If no document met |
Source code in src\taters\text\analyze_ngram_frequencies.py
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 | |
header_for ¶
header_for(pos_tagged)
The header this run will write; pos exists only when asked for.
Source code in src\taters\text\analyze_ngram_frequencies.py
75 76 77 78 79 | |
taters.text.build_doc_term_matrix ¶
Document-term matrix built over a vocabulary taken from a frequency list.
The companion step to :mod:analyze_ngram_frequencies, and the on-ramp for
the topic-modeling steps planned behind it: one row per document, one column
per retained term, cells weighted as counts, binary, relative frequency, or
TF-IDF. "Document" means one analysis-ready row, exactly as in the frequency
list, so the two steps agree about units by construction.
Two agreements with the frequency list are load-bearing:
- The token stream must match. The vocabulary was built from prepared
(possibly lemmatized) tokens; scanning unprepared text against it fails
silently -- near-zero matches, no error. Both steps therefore share
:mod:
taters.text.ngram_prep, and the pipeline recipes drive both steps'lemmatizefrom one shared variable. (The stoplist needs no such agreement: the vocabulary already encodes it.) - Longest match wins. Scanning prefers the highest-order n-gram at each position and consumes its tokens: "I study health behaviors" scores "health behaviors" once and "health" zero times, not both. A frequency list counts every window; a DTM must not double-count nested terms.
build_doc_term_matrix ¶
build_doc_term_matrix(
*,
freq_list_csv,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
lemmatize=False,
pos_tagged=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
device="auto",
weighting="count",
vocab_min_freq=0,
vocab_min_obs_pct=0,
vocab_rule="top_n",
vocab_top_n=500,
vocab_rank_by="frequency",
rounding=4
)
Score every document against a frequency-list vocabulary; write a wide CSV.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
freq_list_csv
|
PathLike
|
A frequency list written by
:func: |
required |
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided:
|
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g. |
None
|
delimiter
|
str
|
Column separator of the input CSV. |
","
|
pattern
|
str
|
Which files to read when gathering from a folder of documents
(globs, |
every document type
|
lemmatize
|
bool
|
Must match the frequency list's setting -- the pipeline drives both from one shared variable for exactly this reason. |
False
|
pos_tagged
|
bool
|
Must also match the frequency list (checked against its |
False
|
engine
|
('nltk', 'stanza')
|
Who tags: NLTK's perceptron tagger, or Stanza's neural pipeline. Stanza is slower, more accurate, multilingual, and GPU-optional. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Who splits text into tokens. The default (the Potts social-media tokenizer) keeps counts comparable across engines and keeps emoticons, hashtags and URLs whole; "stanza" (Stanza engine only) hands Stanza the whole job. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine; that language's model is downloaded once on first use (can be a few hundred MB). Only with engine="stanza". |
"en"
|
keep_punctuation
|
bool
|
Count punctuation (and emoticons) as terms. Off, only tokens with a letter or digit are counted, so "." and "," never become vocabulary. Must match across the frequency list, the matrix and the topic model. |
False
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs: "auto", "cuda", or "cpu". Only with engine="stanza"; the NLTK engine is CPU-only either way. |
"auto"
|
weighting
|
('count', 'binary', 'relfreq', 'tfidf')
|
Cell values: raw matches; 0/1; matches over the document's token count; or matches times the term's IDF from the frequency list. Counts are the neutral default and what topic models want. |
"count"
|
vocab_rule
|
('top_n', 'min_obs_pct', 'min_freq')
|
Which single rule decides the vocabulary. Exactly one applies, and the setting below that belongs to it is the only one read:
|
"top_n"
|
vocab_top_n
|
int
|
How many terms to keep, when |
500
|
vocab_min_obs_pct
|
float
|
The percentage of documents a term must appear in, when
|
0
|
vocab_min_freq
|
float
|
The total number of uses a term must have, when |
0
|
vocab_rank_by
|
('frequency', 'obs_pct')
|
What |
"frequency"
|
workers
|
int
|
Parallel processes for reading documents. |
0
|
rounding
|
int
|
Decimal places for the derived values. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\build_doc_term_matrix.py
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | |
column_names ¶
column_names(terms, pos_tagged)
Display names for the term columns, unique after the two reserved ones.
"text_id" is a perfectly plausible token, and left as-is it duplicated the header -- reading the file back by name then returned the term's count where the document id should be. Colliding terms get a trailing underscore (repeatedly, in case that name is somehow taken too). Tagged terms read "felt (VBD)", which also keeps the verb and the noun apart as columns.
Source code in src\taters\text\build_doc_term_matrix.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
scan_tokens ¶
scan_tokens(tokens, term_index, max_n)
Raw match counts for one document, longest match first.
Matched tokens are consumed, so a nested term is not also counted
("health behaviors" beats "health"). The C# plugin's window guard was off
by one and could never match an n-gram that ran to the end of the
document; i + n <= len can. Shared with the MEM topic model's apply
path, so a saved model scores new text with the same scan, not a copy.
Source code in src\taters\text\build_doc_term_matrix.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
weight_scores ¶
weight_scores(scores, weighting, n_tokens, idf, rounding)
Turn raw match counts into the requested cell values.
Source code in src\taters\text\build_doc_term_matrix.py
202 203 204 205 206 207 208 209 210 211 212 | |
taters.text.analyze_parts_of_speech ¶
Part-of-speech features, one row per document -- the content coder's shape.
Each document is tokenized (happierfuntokenizing, the same tokenizer as the
n-gram tools), POS-tagged with NLTK, and summarized as one column per tag:
relative frequencies by default, raw counts on request. Syntactic n-grams --
sequences of tags, "DT_NN", "PRP_VBD_JJ" -- are available the same way: all
orders from 1 up to sngram_n, each order normalized by its own number of
windows in the document, so within a document every order's columns sum to 1.
Two tag sets: Penn Treebank (tagset="penn", NLTK's native ~45 tags) and
the Universal tagset (12 coarse categories: NOUN, VERB, ADJ, ...), which is
often the better unit for psychological work.
The tagged text itself can also be written out (tagged_text_csv), one row
per document with tokens as word_TAG -- off by default, for the rare
downstream that wants the tags rather than the summary.
analyze_parts_of_speech ¶
analyze_parts_of_speech(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
tagset="penn",
engine="nltk",
tokenizer="potts",
stanza_lang="en",
device="auto",
relative_freq=True,
sngram_n=1,
tagged_text_csv=None,
rounding=4
)
Tag every document and write one row of POS features per document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided:
|
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g. |
None
|
delimiter
|
str
|
Column separator of the input CSV. |
","
|
pattern
|
str
|
Which files to read when gathering from a folder of documents
(globs, |
every document type
|
tagset
|
('penn', 'universal')
|
Penn Treebank's ~45 tags, or the Universal tagset's 12 coarse ones. |
"penn"
|
engine
|
('nltk', 'stanza')
|
Who tags: NLTK's perceptron tagger, or Stanza's neural pipeline. Stanza is slower, more accurate, multilingual, and GPU-optional. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Who splits text into tokens. The default (the Potts social-media tokenizer) keeps counts comparable across engines and keeps emoticons, hashtags and URLs whole; "stanza" (Stanza engine only) hands Stanza the whole job. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine; that language's model is downloaded once on first use (can be a few hundred MB). Only with engine="stanza". |
"en"
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs: "auto", "cuda", or "cpu". Only with engine="stanza"; the NLTK engine is CPU-only either way. |
"auto"
|
relative_freq
|
bool
|
Each order-n column is that sequence's count divided by the number of length-n windows in the document, so an order's columns sum to 1 per document. False writes raw counts. |
True
|
sngram_n
|
int
|
Highest syntactic-n-gram order. All orders from 1 up are written;
an order-2 column reads |
1
|
tagged_text_csv
|
path
|
Also write the tagged text itself -- |
None
|
workers
|
int
|
Parallel processes for reading documents. |
0
|
rounding
|
int
|
Decimal places for the derived values. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\analyze_parts_of_speech.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | |
taters.text.analyze_cohesion ¶
Text cohesion features (TAACO-style), one row per document.
The measure families of Crossley, Kyle & McNamara's TAACO (2016) and the Coh-Metrix lineage behind it (Graesser, McNamara, Louwerse & Cai 2004), reimplemented from their published definitions: lexical overlap between adjacent sentences and paragraphs across nine word classes, lemma-based type-token ratios and lexical density, and givenness. Column names follow TAACO 2.1.3 wherever the measure survives, so results can be read against the TAACO literature.
This is a REIMPLEMENTATION, not a port. The reference implementation
(reference/TAACO-main, CC BY-NC-SA -- nothing was copied) was audited
line by line and several of its defects are deliberately not reproduced.
Where behavior differs, the code comments say exactly what TAACO did and
what this does instead, and COHESION_MEASURES.md (shipped next to this
module) documents every column: formula, range, interpretation, lineage,
and differences. The headline corrections:
- Documents with too few segments emit empty cells (NA), not 0.0 -- TAACO scores a one-paragraph essay as "zero paragraph cohesion", which poisons any downstream average (5.6% of its own sample corpus).
- Real punctuation filtering: a token must contain a letter or digit.
TAACO's punctuation list mixed POS tags with literal characters that can
never match a tag, so
%(Penn-tagged NN) counted as a noun in every noun, content and argument index, and stray quotes/hyphens inflated the word counts underneath every ratio. - The two-segment windows are built by concatenation into fresh lists. TAACO appended the third segment into its shared sentence list, so computing one index corrupted the input of the next -- its published values depend on which checkboxes were ticked.
adjacent_semantic ¶
adjacent_semantic(embeddings)
Mean adjacent cosine over row-normalized segment embeddings: window one (segment i vs i+1) and window two (segment i vs the normalized mean of i+1 and i+2 -- a fresh vector, never TAACO's in-place concatenation of shared state, audit finding 4.2). Too few segments: None.
Source code in src\taters\text\analyze_cohesion.py
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 | |
analyze_cohesion ¶
analyze_cohesion(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
device="auto",
mattr_window=50,
connective_lists=None,
semantic_model=DEFAULT_SEMANTIC_MODEL,
rounding=4
)
Compute TAACO-style cohesion indices; one row per document.
See COHESION_MEASURES.md (shipped next to this module) for what every
column measures, how to interpret it, and where this implementation
deliberately differs from TAACO 2.1.3.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering. |
None
|
delimiter
|
str
|
Column separator of the input CSV. |
","
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
engine
|
('nltk', 'stanza')
|
Who tags and lemmatizes: NLTK (fast, English), or Stanza (neural, multilingual, GPU-optional). Sentence splitting is punkt under NLTK and the model's own under Stanza. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Who splits text into tokens, exactly as in the n-gram steps. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine; the model downloads once on first use. Only with engine="stanza". |
"en"
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs: "auto", "cuda", or "cpu". Only with engine="stanza". |
"auto"
|
mattr_window
|
int
|
Window (tokens) for the moving-average TTRs. TAACO hardcodes 50; this default matches, and shorter documents fall back to plain TTR. |
50
|
connective_lists
|
sequence of paths
|
Connectives category files (.txt, one word or phrase per line, |
None
|
semantic_model
|
str
|
The embedding model for the four semantic-similarity columns
(adjacent sentence/paragraph cosine). |
the MiniLM sentence-transformer
|
workers
|
int
|
Parallel processes for reading and measuring documents. |
0
|
rounding
|
int
|
Decimal places for every index. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\analyze_cohesion.py
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 | |
classify_sentence ¶
classify_sentence(triples)
One sentence's (word, lemma, tag) triples -> lemma lists per class.
Class rules (all deviations from TAACO documented):
- noun: NN/NNS/NNP/NNPS -- proper nouns included, as in TAACO.
- pronoun: PRP/PRP$ plus unattended demonstratives -- this/that/these/ those NOT followed by a noun or adjective ("I like that" yes, "that car" no). TAACO used the dependency parse for attendedness; this uses the next content-bearing token's tag, documented as a heuristic. "that" as a complementizer (tag IN) is neither.
- verb: content verbs only, exactly as TAACO's verb indices are -- a modal (MD) or a form of "be" is a function word. TAACO additionally demoted auxiliary "have"/"do" via spaCy's AUX tag; without a parse we keep have/do as content, a documented deviation.
- adv: deadjectival adverbs are content (see
_adverb_is_content). - cw: nouns + adjectives + content verbs + content adverbs. fw: every other counted token.
- argument: nouns + pronouns (TAACO's definition).
Source code in src\taters\text\analyze_cohesion.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
cohesion_row ¶
cohesion_row(
document,
*,
mattr_window,
connectives=(),
keep_texts=False
)
Every lexical index for one parsed document, in column order (see
header_columns, called with the same connectives; the semantic columns
are computed by the caller, which holds the embedding model). Returns
(token_count, values, sentence_texts, paragraph_texts) -- the texts
only when keep_texts (the semantic pass needs them; pickling them
back from workers is otherwise wasted weight).
Source code in src\taters\text\analyze_cohesion.py
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | |
count_connectives ¶
count_connectives(sentences, entries)
Occurrences of a category's entries over (word, tag) sentences.
Matching is per position WITHIN a sentence -- two of TAACO's counting
bugs are thereby structurally impossible: its str.count on the joined,
punctuation-stripped document both undercounted adjacent repeats
("so so" counted once) and manufactured phrase matches across sentence
boundaries ("...ends in. Fact is..." matched "in fact").
Source code in src\taters\text\analyze_cohesion.py
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | |
header_columns ¶
header_columns(connective_names=None, *, semantic=False)
The output columns, in order -- TAACO 2.1.3's names for every measure
that survives, so results read against the TAACO literature. Connective
columns are named by their list files; None means the shipped set.
The four semantic columns exist only when the embedding pass runs.
Source code in src\taters\text\analyze_cohesion.py
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | |
load_connective_lists ¶
load_connective_lists(paths=None)
Read connectives category files: one entry per line, # comments,
optional TAB + TAG or TAG|TAG Penn constraint. None means the
shipped categories; a directory means every .txt inside it. Each
file becomes one output column named by its stem, ordered canonically
(TAACO's order) with unknown stems appended alphabetically.
Source code in src\taters\text\analyze_cohesion.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
measures_guide ¶
measures_guide()
The full text of COHESION_MEASURES.md, shipped beside this module.
Source code in src\taters\text\analyze_cohesion.py
943 944 945 946 | |
overlap_indices ¶
overlap_indices(segments, window)
TAACO's adjacent-overlap triple over a list of per-segment lemma lists.
For each segment i, the types of segment i are looked up in the
next window segments (their concatenation -- built as a fresh list;
TAACO's version appended segment i+2 into its shared input list, so the
order its indices were computed in changed their values). Returns:
- proportion: total overlapping types / total types of the source
segments (TAACO's
adjacent_overlap_X: type-normalized, not word-normalized, despite the name -- kept, and documented). - per-pair mean: total overlapping types / number of comparisons
(
_div_seg; unbounded above). - binary: share of comparisons with at least one overlapping type.
A document with too few segments returns (None, None, None) -- NA,
where TAACO wrote 0.0 and made "one paragraph" look like "zero
cohesion".
Source code in src\taters\text\analyze_cohesion.py
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | |
synonym_overlap ¶
synonym_overlap(segments, pos)
Adjacent-segment synonym overlap; returns (count, proportion).
count is TAACO's definition, kept for comparability and documented
as what it is (audit finding 4.15): for each type of segment i, one
hit per word of segment i+1 whose synonym set contains it -- a multiply-
counting, unbounded count, averaged over segment pairs, NOT comparable
across texts with different sentence lengths. proportion is the
normalized companion this implementation adds: source types matched by
at least one next-segment word, over total source types -- the same
scale as the lexical overlap indices. Too few segments: (None, None).
Source code in src\taters\text\analyze_cohesion.py
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | |
taters.text.topic_model_mem ¶
Topic model: the Meaning Extraction Method (MEM).
Chung & Pennebaker's MEM (2008): take a document-term matrix over frequent content terms, run a PCA with varimax rotation, and read the rotated components as themes -- clusters of words that rise and fall together across documents. Each document gets a score per theme; each term gets a loading per theme.
Two commitments shape this module:
-
Exact and out-of-core, without heavy dependencies. A MEM matrix is long (documents) but narrow (hundreds to a few thousand terms), so the fit streams the rows once to accumulate the column means and the p-by-p cross-product matrix, then eigendecomposes the correlation matrix in memory. That is the exact full PCA -- no randomized SVD, no seed, no dask -- with memory bounded by the vocabulary width, never the corpus length. Should a vocabulary ever be too wide for p-by-p to fit, that is the moment to reach for an out-of-core SVD, behind this same interface.
-
A fitted model is a reusable instrument.
topic_model_memwrites a model file carrying everything needed to score new text on the same themes: the exact vocabulary (tagged keys included), the tokenizer/engine settings that produced it, the weighting, the training means and standard deviations, and the projection.apply_mem_modelrebuilds the same document-term matrix for new texts -- through the very same scan and weighting code the DTM step uses, imported rather than copied -- and projects it. Applying a model to its own training texts reproduces the training scores exactly.
Eigenvalues here are those of the correlation matrix (they average 1.0),
which is what the retention rules are stated over: n_components=0 picks
the count by parallel analysis (a theme is kept while its eigenvalue beats
what a random matrix of the same shape gives at that rank) or, on request, by
the Kaiser criterion (keep eigenvalue >= a cutoff).
Two files come out of this, because there are two quantities and only one of them is an eigenvalue. The eigenvalues file is the spectrum the rule read, every rank beside what chance reaches there. The theme variance file is each finished theme's sum of squared loadings -- which is the same quantity before rotation, and deliberately redistributed by it, so it comes out much flatter than the spectrum. Classic MEM tooling reports the second; this reports both, apart, because they are sorted lists of different things and a shared table made that look like a pairing.
apply_mem_model ¶
apply_mem_model(
*,
model_json,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
workers=0,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
device="auto",
rounding=4
)
Score new texts on the themes of a saved MEM model.
The model file (from :func:topic_model_mem) carries the vocabulary,
tokenizer settings, weighting, and projection of the original fit; this
function rebuilds the same document-term matrix for the new texts --
through the same scan and weighting code the matrix step uses -- then
standardizes with the training means and deviations and projects.
Applying a model to its own training texts reproduces the training
scores exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_json
|
PathLike
|
A |
required |
csv_path
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
txt_dir
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
analysis_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
gathered_csv
|
Optional[PathLike]
|
The same input contract as the other text analyzers: a spreadsheet of texts, a folder of documents, or a prebuilt analysis-ready CSV. |
None
|
out_features_csv
|
str or Path
|
Output file path. If |
None
|
overwrite_existing
|
bool
|
If |
False
|
workers
|
int
|
Parallel processes for reading documents during the gather. |
0
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering. |
None
|
delimiter
|
str
|
Column separator of the input CSV. |
","
|
pattern
|
str
|
Which files to read when gathering from a folder of documents. |
every document type
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, if the model was built with the stanza engine -- a runtime choice, deliberately not stored in the model. |
"auto"
|
rounding
|
int
|
Decimal places for the theme scores. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\topic_model_mem.py
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | |
topic_model_mem ¶
topic_model_mem(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
out_features_csv=None,
out_model_json=None,
out_loadings_csv=None,
out_eigenvalues_csv=None,
out_theme_variance_csv=None,
overwrite_existing=False,
on_progress=None,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
ngram_n=1,
stoplist_paths=None,
min_freq=5,
min_obs_pct=0.1,
min_token_count=10,
min_npmi=None,
lemmatize=False,
pos_tagged=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
weighting="count",
matrix_rounding=4,
vocab_min_freq=0,
vocab_min_obs_pct=0,
vocab_rule="top_n",
vocab_top_n=500,
vocab_rank_by="obs_pct",
n_components=0,
k_selection="parallel",
kaiser_cutoff=1.5,
k_values=_topics.DEFAULT_K_VALUES,
coherence_metric="npmi",
top_terms=15,
rotation=True,
rounding=4
)
Fit MEM themes to a corpus; write scores, the matrix, and a reusable model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of
documents, an already-gathered analysis-ready table, or a gathered
table to write and reuse. MEM builds its own frequency list and
document-term matrix from it, into a |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of
documents, an already-gathered analysis-ready table, or a gathered
table to write and reuse. MEM builds its own frequency list and
document-term matrix from it, into a |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of
documents, an already-gathered analysis-ready table, or a gathered
table to write and reuse. MEM builds its own frequency list and
document-term matrix from it, into a |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of
documents, an already-gathered analysis-ready table, or a gathered
table to write and reuse. MEM builds its own frequency list and
document-term matrix from it, into a |
None
|
workers
|
int
|
Worker processes for gathering, counting and scoring. 0 picks a sensible number for the machine and the size of the job. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, when |
"auto"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Gathering behavior when multiple text columns are provided:
|
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering (e.g. |
None
|
pattern
|
str
|
Which files to read when gathering from a folder of documents
(globs, |
every document type
|
ngram_n
|
int
|
Highest n-gram order to consider for the vocabulary. Themes are usually built from single words; raise it to let phrases compete. |
1
|
stoplist_paths
|
Sequence[str or Path] or None
|
Word lists to drop before counting. Function words carry grammar rather than topic, and leaving them in gives a first theme that is mostly "the". |
None
|
min_freq
|
int
|
The first of two cuts: a term used fewer times than this anywhere
in the corpus is never counted, so it cannot reach the frequency list.
The |
5
|
min_obs_pct
|
float
|
Also the first cut: drop terms found in fewer than this percent of documents. The setting that matters most for themes -- a term almost nobody uses cannot covary with anything. |
0.10
|
min_token_count
|
int
|
Skip whole documents shorter than this many tokens. A filter on texts, not on terms, despite sitting among the term filters. |
10
|
min_npmi
|
float
|
Optional collocation threshold for orders above one. Default None: the metric is reported and filtering stays an analysis decision. |
None
|
out_features_csv
|
str or Path
|
Per-document theme scores. Defaults to
|
None
|
out_model_json
|
optional
|
The reusable model (see :func: |
None
|
out_loadings_csv
|
optional
|
The reusable model (see :func: |
None
|
out_eigenvalues_csv
|
optional
|
The spectrum: one row per rank with the correlation matrix's eigenvalue, the level chance alone reaches at that rank (under parallel analysis), and whether it was kept. This is what the retention rule read and the curve to judge signal by. Every rank is listed, not just the kept ones, so you can see where the curve crosses. |
None
|
out_theme_variance_csv
|
optional
|
What each finished theme accounts for: its sum of squared loadings and that as a percent. Deliberately a separate file from the eigenvalues, because the two do not line up. Varimax rotates the kept axes within the space they span, so a rotated theme is a remix of all of them and theme 3 is not built from eigenvector 3. Both lists come out sorted descending, which is the only thing they share -- and putting them in one table made that coincidence look like a correspondence. |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
lemmatize
|
bool
|
Reduce words to a dictionary form before counting, so "running", "runs" and "ran" become one term instead of three. |
False
|
pos_tagged
|
bool
|
Keep each word's part of speech attached, so "book" the noun and "book" the verb count as different terms. |
False
|
engine
|
('nltk', 'stanza')
|
Which toolkit does the tagging and lemmatizing. NLTK is fast and English-only; Stanza handles many languages and is much slower, and downloads a model the first time you use it. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Which rules split the text into words. "potts" keeps emoticons and hashtags intact, which is usually what you want for social media; "stanza" uses Stanza's own splitter. |
"potts"
|
stanza_lang
|
str
|
The language code for Stanza, when the engine is Stanza. |
"en"
|
keep_punctuation
|
bool
|
Count punctuation marks as terms of their own. |
False
|
weighting
|
('count', 'binary', 'relfreq', 'tfidf')
|
The matrix's cell weighting, recorded so apply weights new text the same way. |
"count"
|
matrix_rounding
|
int
|
The |
4
|
vocab_rule
|
('top_n', 'min_obs_pct', 'min_freq')
|
Which of the surviving terms become columns of the matrix. This is the second of two cuts and the source of a long-standing
confusion.
|
"top_n"
|
vocab_top_n
|
int
|
How many terms the model gets under |
500
|
vocab_rank_by
|
('obs_pct', 'frequency')
|
What "strongest" means when taking the top N. |
"obs_pct"
|
vocab_min_freq
|
float
|
The threshold under |
0
|
vocab_min_obs_pct
|
float
|
The threshold under |
0
|
n_components
|
int
|
How many themes to keep. |
0
|
k_selection
|
('parallel', 'kaiser', 'coherence', 'coherence_exclusivity')
|
How
|
"parallel"
|
kaiser_cutoff
|
float
|
The eigenvalue a theme has to beat under |
1.5
|
k_values
|
str or sequence of int
|
The theme counts the two scoring rules try. |
DEFAULT_K_VALUES
|
coherence_metric
|
('npmi', 'umass')
|
Which coherence. NPMI is bounded, which is what lets it be balanced against exclusivity without rescaling. |
"npmi"
|
top_terms
|
int
|
How many of a theme's words the scoring rules look at. Only its positive pole counts: a theme is bipolar and its two ends anti-correlate by construction, so mixing them would make every theme score badly and the comparison across counts meaningless. |
15
|
rotation
|
bool
|
Varimax-rotate the components. MEM is defined over rotated loadings; turn off only to inspect the raw principal axes. |
True
|
rounding
|
int
|
Decimal places for theme scores, loadings, and eigenvalues. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\topic_model_mem.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | |
taters.text.topic_model_lda ¶
Latent Dirichlet Allocation: the topic model most papers mean by "topic model".
What it does
LDA tells a story about how a corpus got written. Every document is a mixture of topics -- eighty percent about food, twenty about work -- and every topic is a distribution over words. Fitting runs that story backwards: given the words that actually appeared, what mixtures and what topics would best explain them?
You get back, for each document, the proportion of it that belongs to each topic. Those proportions sum to one, which makes them read naturally as "this interview was mostly about X" and makes them awkward as ordinary predictors -- see the note on that below.
How it differs from MEM
Taters already has a topic model, :mod:taters.text.topic_model_mem, and they
answer different questions. MEM is PCA with a rotation: it finds the dimensions
along which word use co-varies, and a document gets a score on each, positive
or negative. LDA is generative and non-negative: it finds distributions over
words, and a document gets a share of each. MEM's themes are contrasts; LDA's
topics are ingredients. Neither is the better one, and a study that reports
both is not doing the same thing twice.
A note about MALLET
If you have used LDA through DLATK, you have used MALLET -- a Java program,
driven through a gensim wrapper that gensim deleted in version 4. Nothing
here shells out to Java. This is variational Bayes (Hoffman, Blei & Bach 2010),
which is the same family scikit-learn and gensim's own LdaModel use, and it
is not the same algorithm as MALLET's collapsed Gibbs sampling. Topics will be
comparable in character; the numbers will not match, and nothing here pretends
they do.
Counts, and only counts
LDA's story is about how many times a word was said. Handed a tf-idf matrix
it runs perfectly happily and returns numbers that mean nothing at all, so this
refuses any weighting but count, by name, before it does any work. NMF is
the one that wants tf-idf; see :mod:taters.text.topic_model_nmf.
topic_model_lda ¶
topic_model_lda(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
on_progress=None,
out_features_csv=None,
out_model_json=None,
out_loadings_csv=None,
out_top_terms_csv=None,
overwrite_existing=False,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
ngram_n=1,
stoplist_paths=None,
min_freq=5,
min_obs_pct=0.1,
min_token_count=10,
min_npmi=None,
lemmatize=False,
pos_tagged=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
weighting="count",
matrix_rounding=4,
vocab_min_freq=0,
vocab_min_obs_pct=0,
vocab_rule="top_n",
vocab_top_n=2000,
vocab_rank_by="obs_pct",
n_topics=20,
k_selection="coherence_exclusivity",
k_values=_topics.DEFAULT_K_VALUES,
coherence_metric="npmi",
alpha=0.1,
eta=0.01,
passes=10,
seed=42,
top_terms=15,
rounding=4
)
Fit LDA topics to a corpus; write per-document proportions and a model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
workers
|
int
|
Worker processes for gathering, counting and scoring. 0 picks a sensible number for the machine and the job. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, when |
"auto"
|
out_features_csv
|
str or Path
|
Per-document topic proportions. Defaults to
|
None
|
out_model_json
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
out_loadings_csv
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
out_top_terms_csv
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Whether multiple text columns are joined into one document or measured separately. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering -- one document per group instead of one per row. |
None
|
pattern
|
str
|
Which files to read when gathering from a folder. Only with |
every document type
|
ngram_n
|
int
|
Highest n-gram order to consider for the vocabulary. |
1
|
stoplist_paths
|
Sequence[str or Path] or None
|
Word lists to drop before counting. Worth using: function words are the most frequent words in any corpus and a topic made of them tells you nothing. |
None
|
min_freq
|
int
|
Drop terms rarer than this from the vocabulary. |
5
|
min_obs_pct
|
float
|
Drop terms appearing in fewer than this percent of documents. |
0.10
|
min_token_count
|
int
|
Skip documents shorter than this many tokens entirely. |
10
|
min_npmi
|
float
|
Optional collocation threshold for orders above one. |
None
|
lemmatize
|
bool
|
Lemmatize before counting, so "run" and "running" are one term. |
False
|
pos_tagged
|
bool
|
Keep part-of-speech tags on terms, so "book/NOUN" and "book/VERB" are different terms. |
False
|
engine
|
('nltk', 'stanza')
|
Which tokenizer and tagger to use. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Which tokenizer rules to apply. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine. |
"en"
|
keep_punctuation
|
bool
|
Count punctuation as terms. |
False
|
weighting
|
'count'
|
Counts only. LDA's generative story is about how many times a word
was said, so it is only defined over integers. Given a |
"count"
|
matrix_rounding
|
int
|
Decimal places in the written matrix. |
4
|
vocab_min_freq
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_min_obs_pct
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rule
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_top_n
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rank_by
|
('obs_pct', 'frequency')
|
What Spread, not volume, is what a topic model needs: a word one document repeats five hundred times outranks everything on frequency and cannot distinguish a thing, because it only ever describes that one document. A word used once each across half the corpus is what topics are made of. (The standalone document-term matrix still ranks by frequency, because a feature table usually does want the commonest terms.) |
"obs_pct"
|
n_topics
|
int
|
How many topics to fit. |
20
|
k_selection
|
('coherence', 'coherence_exclusivity')
|
How |
"coherence"
|
k_values
|
str or sequence of int
|
The counts to try. |
DEFAULT_K_VALUES
|
coherence_metric
|
('npmi', 'umass')
|
Which coherence. NPMI is bounded, which is what lets it be balanced against exclusivity without rescaling. |
"npmi"
|
alpha
|
float
|
Prior on the document-topic mixtures. Smaller makes each document commit to fewer topics. |
0.1
|
eta
|
float
|
Prior on the topic-word distributions. Smaller makes each topic commit to fewer words. |
0.01
|
passes
|
int
|
How many times to walk the corpus. |
10
|
seed
|
int
|
Fixes the initialization, and with it the whole fit. Recorded in the model, so a run can be repeated exactly. |
42
|
top_terms
|
int
|
How many words to list per topic in the readable summary. |
15
|
rounding
|
int
|
Decimal places in the written proportions and loadings. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Notes
Topic proportions sum to one for every document, so the last topic is one minus all the others and carries nothing the rest do not. Put every topic into a single regression and there is no unique answer -- many different sets of coefficients fit identically.
The field's answer to this is not a transform, it is the pipeline. Schwartz
et al. (2013), who put 2,000 LDA topics in front of personality outcomes,
ran a separate regression per feature with covariates for interpretation,
and reduced with PCA before the ridge for prediction. Taters' correlations
and group differences already work one measure at a time; for the ridge,
turn on the analysis step's pca setting. PCA also makes the sum-to-one
problem vanish on its own -- the redundant direction has zero variance, so
it is dropped without anybody having to think about it.
Neither MEM's themes nor NMF's factors are constrained this way.
Source code in src\taters\text\topic_model_lda.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | |
apply_lda_model ¶
apply_lda_model(
*,
model_json,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
rounding=4
)
Score a corpus with topics fitted somewhere else.
Takes no vocabulary, tokenizer or weighting settings at all: they come out of the model, because the model is the instrument. That is what makes two studies comparable -- measure the second corpus with the first one's topics, rather than fitting new topics and hoping Topic_3 means the same thing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_json
|
str or Path
|
A model written by :func: |
required |
csv_path
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
workers
|
int
|
Worker processes. 0 picks a sensible number. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, if the model was built with it. |
"auto"
|
out_features_csv
|
str or Path
|
Defaults to |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
id_cols
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
mode
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
group_by
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
pattern
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
rounding
|
int
|
Decimal places in the written proportions. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\topic_model_lda.py
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | |
taters.text.topic_model_nmf ¶
Non-negative matrix factorization: topics without the probability story.
What it does
NMF asks something simpler than LDA. Split the document-term matrix into two non-negative pieces -- documents by topics, topics by terms -- whose product is as close to the original as it can get. No generative story, no priors, nothing that has to sum to one. A document's topic weights are just weights: one can be large without forcing another to be small.
That simplicity is why it is worth having beside LDA rather than instead of it. On short texts -- tweets, open-ended survey answers, single utterances -- LDA often struggles because there is not enough of each document to infer a mixture from, and NMF's topics come out sharper and easier to name. On long documents the two tend to agree.
Which weighting, and why it differs from LDA
NMF defaults to tf-idf, and that is not an accident of taste: without it, the factorization spends its first topic on whatever words are simply common, because those are the cells with the most mass to explain. Down-weighting what is everywhere is how NMF gets topics rather than a frequency ranking.
LDA is the opposite -- it is only defined over integer counts, and refuses anything else. The two engines genuinely want different matrices, which is why each builds its own instead of sharing one.
What comes out
One column per topic, Factor_1..Factor_k. They are weights, not
proportions: bigger means more of that topic, zero means none, and they do not
add up to anything in particular. That makes them easier to use as ordinary
predictors than LDA's proportions, which are compositional.
topic_model_nmf ¶
topic_model_nmf(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
on_progress=None,
out_features_csv=None,
out_model_json=None,
out_loadings_csv=None,
out_top_terms_csv=None,
overwrite_existing=False,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
ngram_n=1,
stoplist_paths=None,
min_freq=5,
min_obs_pct=0.1,
min_token_count=10,
min_npmi=None,
lemmatize=False,
pos_tagged=False,
engine="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
weighting="tfidf",
matrix_rounding=4,
vocab_min_freq=0,
vocab_min_obs_pct=0,
vocab_rule="top_n",
vocab_top_n=2000,
vocab_rank_by="obs_pct",
n_topics=20,
k_selection="coherence_exclusivity",
k_values=_topics.DEFAULT_K_VALUES,
coherence_metric="npmi",
beta_loss="frobenius",
iters=200,
tol=0.0001,
top_terms=15,
rounding=4
)
Fit NMF factors to a corpus; write per-document weights and a model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus, given exactly one of these ways: a spreadsheet, a folder of documents, an already-gathered analysis-ready table, or a gathered table to write and reuse. |
None
|
workers
|
int
|
Worker processes for gathering, counting and scoring. 0 picks a sensible number for the machine and the job. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, when |
"auto"
|
out_features_csv
|
str or Path
|
Per-document topic proportions. Defaults to
|
None
|
out_model_json
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
out_loadings_csv
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
out_top_terms_csv
|
str or Path
|
Where the reusable model, the term-by-topic table the word clouds are drawn from, and the readable top-terms summary go. |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
When gathering from a CSV, name(s) of the column(s) containing text. |
("text",)
|
id_cols
|
Sequence[str] or None
|
Optional ID columns that identify each row when gathering from CSV. |
None
|
mode
|
('concat', 'separate')
|
Whether multiple text columns are joined into one document or measured separately. |
"concat"
|
group_by
|
Sequence[str] or None
|
Optional grouping keys used during CSV gathering -- one document per group instead of one per row. |
None
|
pattern
|
str
|
Which files to read when gathering from a folder. Only with |
every document type
|
ngram_n
|
int
|
Highest n-gram order to consider for the vocabulary. |
1
|
stoplist_paths
|
Sequence[str or Path] or None
|
Word lists to drop before counting. Worth using: function words are the most frequent words in any corpus and a topic made of them tells you nothing. |
None
|
min_freq
|
int
|
Drop terms rarer than this from the vocabulary. |
5
|
min_obs_pct
|
float
|
Drop terms appearing in fewer than this percent of documents. |
0.10
|
min_token_count
|
int
|
Skip documents shorter than this many tokens entirely. |
10
|
min_npmi
|
float
|
Optional collocation threshold for orders above one. |
None
|
lemmatize
|
bool
|
Lemmatize before counting, so "run" and "running" are one term. |
False
|
pos_tagged
|
bool
|
Keep part-of-speech tags on terms, so "book/NOUN" and "book/VERB" are different terms. |
False
|
engine
|
('nltk', 'stanza')
|
Which tokenizer and tagger to use. |
"nltk"
|
tokenizer
|
('potts', 'stanza')
|
Which tokenizer rules to apply. |
"potts"
|
stanza_lang
|
str
|
Language for the Stanza engine. |
"en"
|
keep_punctuation
|
bool
|
Count punctuation as terms. |
False
|
weighting
|
('tfidf', 'count')
|
tf-idf by default, and that is a real recommendation rather than a
shrug: without it the factorization spends its first factor on whatever
words are merely common, because those cells hold the most mass. Raw
counts are allowed for anyone who wants NMF and LDA over the same
matrix. |
"tfidf"
|
matrix_rounding
|
int
|
Decimal places in the written matrix. |
4
|
vocab_min_freq
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_min_obs_pct
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rule
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_top_n
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rank_by
|
('obs_pct', 'frequency')
|
What Spread, not volume, is what a topic model needs: a word one document repeats five hundred times outranks everything on frequency and cannot distinguish a thing, because it only ever describes that one document. A word used once each across half the corpus is what topics are made of. (The standalone document-term matrix still ranks by frequency, because a feature table usually does want the commonest terms.) |
"obs_pct"
|
n_topics
|
int
|
How many topics to fit. |
20
|
k_selection
|
('coherence', 'coherence_exclusivity')
|
How |
"coherence"
|
k_values
|
str or sequence of int
|
The counts to try. |
DEFAULT_K_VALUES
|
coherence_metric
|
('npmi', 'umass')
|
Which coherence. NPMI is bounded, which is what lets it be balanced against exclusivity without rescaling. |
"npmi"
|
beta_loss
|
('frobenius', 'kullback-leibler')
|
What "close to the original" means. Frobenius is least squares: faster, steadier, and the usual choice. Kullback-Leibler matches the way counts actually vary and tends to give sparser, more readable factors on short texts -- worth trying if the Frobenius factors all look alike. |
"frobenius"
|
iters
|
int
|
Most passes to make. It stops early when the error settles. |
200
|
tol
|
float
|
How small a relative improvement counts as settled. |
1e-4
|
top_terms
|
int
|
How many words to list per factor in the readable summary. |
15
|
rounding
|
int
|
Decimal places in the written weights and loadings. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Notes
There is no seed. NMF starts from NNDSVD, which is derived from the
matrix's own singular vectors and is therefore deterministic -- two runs on
one corpus give one answer, with nothing random to record.
The weights are not proportions. They do not sum to anything in particular, which makes them more straightforward as regression predictors than LDA's topic shares -- those are compositional, and using all of them at once is a known trap. Factor scale is arbitrary, though: doubling a factor in H and halving it in W is the same model, so compare a factor against itself across documents rather than against another factor.
Source code in src\taters\text\topic_model_nmf.py
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | |
apply_nmf_model ¶
apply_nmf_model(
*,
model_json,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
on_progress=None,
out_features_csv=None,
overwrite_existing=False,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
rounding=4
)
Score a corpus with topics fitted somewhere else.
Takes no vocabulary, tokenizer or weighting settings at all: they come out of the model, because the model is the instrument. That is what makes two studies comparable -- measure the second corpus with the first one's topics, rather than fitting new topics and hoping Topic_3 means the same thing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_json
|
str or Path
|
A model written by :func: |
required |
csv_path
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus to score, exactly one way. |
None
|
workers
|
int
|
Worker processes. 0 picks a sensible number. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, if the model was built with it. |
"auto"
|
out_features_csv
|
str or Path
|
Defaults to |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
id_cols
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
mode
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
group_by
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
pattern
|
Sequence[str]
|
Gathering options, as in :func: |
('text',)
|
rounding
|
int
|
Decimal places in the written proportions. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in src\taters\text\topic_model_nmf.py
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | |
taters.text.topic_count_sweep ¶
How many topics? Fit several, score them, and look at the curve.
The one setting a topic model cannot pick for you
Everything else about LDA and NMF has a defensible default. The number of topics does not. Ask for five and you get broad ones ("food", "work"); ask for fifty on the same corpus and you get narrow ones ("breakfast", "restaurant complaints"). Neither is wrong -- they are different questions -- and no statistic can tell you which question you meant to ask.
What this does is narrower and still useful: fit the model at each number you name, score how well each one's topics hang together, and write the curve out with the top words for every fit. Then you read them.
Topic coherence, and what it is not
Coherence asks whether the words that define a topic actually turn up in the same documents. A topic of bread, butter, cheese scores well because those co-occur; a topic of bread, deadline, quarterly scores badly because they do not. That correlates with topics a person would call meaningful, and it is not the same thing -- a model can score beautifully and still carve the corpus somewhere useless.
So this reports and recommends. It does not decide, and the report says so. Anyone who picks the peak of this curve without reading the words has replaced a judgment call with a number that was never meant to carry it.
Both metrics come out of the document-term matrix in a single pass -- see
_topics.coherence. C_v, the one most papers quote, is deliberately missing:
it needs sliding windows over the raw text, and wanting it is the usual reason
people end up adding gensim.
sweep_topic_count ¶
sweep_topic_count(
*,
csv_path=None,
txt_dir=None,
analysis_csv=None,
gathered_csv=None,
workers=0,
device="auto",
on_progress=None,
out_csv=None,
out_chart_png=None,
out_report_md=None,
overwrite_existing=False,
encoding="utf-8-sig",
text_cols=("text",),
id_cols=None,
mode="concat",
group_by=None,
delimiter=",",
joiner=" ",
num_buckets=512,
max_open_bucket_files=64,
tmp_root=None,
recursive=True,
pattern=DOCUMENT_PATTERN,
id_from="stem",
include_source_path=True,
ngram_n=1,
stoplist_paths=None,
min_freq=5,
min_obs_pct=0.1,
min_token_count=10,
min_npmi=None,
lemmatize=False,
pos_tagged=False,
engine_nlp="nltk",
tokenizer="potts",
stanza_lang="en",
keep_punctuation=False,
matrix_rounding=4,
vocab_min_freq=0,
vocab_min_obs_pct=0,
vocab_rule="top_n",
vocab_top_n=2000,
vocab_rank_by="obs_pct",
engine="lda",
k_values="5,10,20,40",
rule="coherence_exclusivity",
metric="npmi",
top_terms=10,
passes=10,
seed=42,
beta_loss="frobenius",
rounding=4
)
Fit a topic model at several topic counts and score each one's coherence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
Optional[PathLike]
|
The corpus, exactly one way. |
None
|
txt_dir
|
Optional[PathLike]
|
The corpus, exactly one way. |
None
|
analysis_csv
|
Optional[PathLike]
|
The corpus, exactly one way. |
None
|
gathered_csv
|
Optional[PathLike]
|
The corpus, exactly one way. |
None
|
workers
|
int
|
Worker processes for gathering and counting. 0 picks a sensible number. |
0
|
device
|
('auto', 'cuda', 'cpu')
|
Where Stanza runs, with |
"auto"
|
out_csv
|
str or Path
|
One row per topic count: the coherence, and the spread across topics.
Defaults to |
None
|
out_chart_png
|
str or Path
|
The curve, and a short write-up naming the best score and listing the
top words of every fit. Written beside |
None
|
out_report_md
|
str or Path
|
The curve, and a short write-up naming the best score and listing the
top words of every fit. Written beside |
None
|
overwrite_existing
|
bool
|
If |
False
|
encoding
|
str
|
Encoding for reading and writing CSV files. |
"utf-8-sig"
|
text_cols
|
Sequence[str]
|
Gathering options, as elsewhere. |
('text',)
|
id_cols
|
Sequence[str]
|
Gathering options, as elsewhere. |
('text',)
|
mode
|
Sequence[str]
|
Gathering options, as elsewhere. |
('text',)
|
group_by
|
Sequence[str]
|
Gathering options, as elsewhere. |
('text',)
|
pattern
|
Sequence[str]
|
Gathering options, as elsewhere. |
('text',)
|
ngram_n
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
stoplist_paths
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
min_freq
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
min_obs_pct
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
min_token_count
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
min_npmi
|
int
|
How the vocabulary is counted. Built once and shared by every fit: changing the vocabulary between fits would mean comparing coherence scores computed over different word lists, which compares nothing. |
1
|
lemmatize
|
bool
|
Tokenizer settings. |
False
|
pos_tagged
|
bool
|
Tokenizer settings. |
False
|
engine_nlp
|
bool
|
Tokenizer settings. |
False
|
tokenizer
|
bool
|
Tokenizer settings. |
False
|
stanza_lang
|
bool
|
Tokenizer settings. |
False
|
keep_punctuation
|
bool
|
Tokenizer settings. |
False
|
matrix_rounding
|
int
|
Decimal places in the shared matrix. |
4
|
vocab_min_freq
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_min_obs_pct
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rule
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_top_n
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
vocab_rank_by
|
float
|
How the vocabulary is cut from the frequency list. |
0
|
engine
|
('lda', 'nmf')
|
Which model to fit. The matrix follows: counts for LDA, tf-idf for NMF, exactly as when fitting one for real. |
"lda"
|
k_values
|
str or sequence of int
|
The topic counts to try, as a list or a comma-separated string. |
"5,10,20,40"
|
metric
|
('npmi', 'umass')
|
Which coherence to score with. |
"npmi"
|
top_terms
|
int
|
How many words of each topic the coherence is computed over, and how
many the report lists. Keep it well below |
10
|
passes
|
int
|
LDA's settings; ignored for NMF, which needs no seed. |
10
|
seed
|
int
|
LDA's settings; ignored for NMF, which needs no seed. |
10
|
beta_loss
|
('frobenius', 'kullback-leibler')
|
NMF's divergence; ignored for LDA. |
"frobenius"
|
rounding
|
int
|
Decimal places in the written scores. |
4
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Notes
Coherence is a guide, not a verdict, and this is not an optimizer. Fitting is repeated once per topic count, so a sweep over four counts costs about four fits -- the matrix, which is the slow part on a big corpus, is built once and reused.
Source code in src\taters\text\topic_count_sweep.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | |