Changelog¶
All notable changes to this project are documented here. The format is based on
Keep a Changelog, and this project
adheres to Semantic Versioning on the
package/API (the underlying lexicon data is fixed; its vintage is exposed as
kosac.__data_version__).
[0.5.0] — 2026-06-30¶
Sixth pre-release (beta). A performance release for polarity classification,
driven by the benchmark campaign in benchmarks/ (NSMC in-domain + NIKL
out-of-domain). The frozen 2016 KOSAC data is unchanged; the gains come from a
better scorer and an optional derived lexicon.
Added¶
Multi-scale scoring (
SentimentAnalyzer(..., scoring='multiscale'), now the default): sums every overlapping n-gram match instead of the greedy leftmost-longest non-overlapping matcher, so a long match no longer suppresses its unigrams. Strictly beats the old scorer on all four held-out metrics. Passscoring='greedy'for the legacy behavior, orngram_weights={1:…,2:…,3:…}to weight the scales.analyze()/polarity_score()use it;count()stays non-overlapping (correct for word tallies).polarity-blendlexicon (load_lexicon('polarity-blend')/SentimentAnalyzer('polarity-blend')): a derived POS/NEG lexicon — the frozen KOSAC seeds blended with an NSMC-learned lexicon — shipped gzipped (data/polarity-blend.csv.gz, ~0.9 MB). Far stronger than the frozen polarity lexicon out of domain (NIKL balanced-acc 0.53 → 0.73). It is loadable by name but is not one of the six canonicalFEATURES, soanalyzer('all')is unchanged. Regenerate (or build the larger “champion” variant) withpython -m benchmarks.build_shipped_blend [--full]. Derived data is CC BY-SA (seedata/polarity-blend.NOTICE).Convenience API:
SentimentAnalyzer.polarity_score(text)(continuousP(POS) − P(NEG)),predict_polarity(text, threshold=0.0), andpredict_polarity_batch(...).
Changed¶
Default analyzer scoring is now multi-scale (see above). For unigram-only use (
ngrams=[1]) this is identical to before; it differs only when bigrams/ trigrams are matched.
[0.4.1] — 2026-06-25¶
Fifth pre-release (beta). Fixes bundled-data loading on Python 3.9 and makes sentence scoring robust on out-of-vocabulary input.
Fixed¶
Loading bundled lexicons (
load_lexicon/Lexicon.load) raisedTypeErroron Python 3.9 (a supported version):kosac/dataships no__init__, soimportlib.resources.files('kosac.data')hit a namespace package whosespec.originisNone. The data is now resolved from thekosacpackage (files('kosac').joinpath('data', …)), which loads on 3.9–3.12+.SentimentLexicon.get_sent_probs()now returns a uniform distribution for a sentence with no lexicon matches, instead of raising on the empty match frame. An out-of-vocabulary sentence carries no evidence, so every label is equally likely.
[0.4.0] — 2026-06-25¶
Fourth pre-release (beta). Space-joined data CSVs; save() round-trips
punctuation morphemes.
Changed¶
Bundled data CSVs now join an N-gram’s morphemes with a space (matching the in-memory
entryindex) instead of;, so the loader andsave()no longer translate separators.
Fixed¶
save()now round-trips entries whose surface is itself;— e.g. the punctuation morpheme;/SPcommon in web text (and in corpus-built lexicons like the NSMC example). The old;-joined CSV format mangled them.
[0.3.0] — 2026-06-25¶
Third pre-release (beta). Faster corpus-built lexicons and an NSMC worked example.
Added¶
Tokenizer.tokenize_batch()/get_ngrams_batch()tokenize many sentences at once;KiwiTokenizeroverrides them to use Kiwi’s parallel batch API (results are identical to per-sentence tokenizing, just faster).
Changed¶
SentimentLexicon.update_from_corpus()now tallies counts with aCounterand assembles the lexicon in one vectorized pass instead of a per-token pandas update, and tokenizes the corpus in one batch. Building from a large corpus is dramatically faster (a 150k-review corpus that took ~20 min now builds in ~15 s); results are identical. It also fails loud (ValueError) when the corpus carries a label the lexicon doesn’t declare, and the rebuild now holds exactly the N-grams observed in the corpus (stale all-Nonerows from the previous index are no longer kept).
Documentation¶
New tutorial and runnable example (
examples/nsmc_lexicon.py) building a POS/NEG lexicon from the NSMC movie-review corpus end to end.
[0.2.0] — 2026-06-25¶
Second pre-release (beta).
Added¶
SentimentLexicon.save(filepath)writes a lexicon to the package’s CSV format (ngram+ one count column per label). The constructor reads it back — concrete subclasses use their declared labels, andGenericLexiconnow infers labels from the CSV columns, so custom lexicons round-trip via CSV.SentimentLexicon.update_from_corpus()gains optionalpos_tag,min_freq, andmax_value_thresholdarguments to filter a corpus-built lexicon.pos_tagkeeps only N-grams containing at least one token of an allowed POS (e.g. content words), dropping pure function-morpheme entries (이/MM,다/EF) while keeping mixed constructions likeㄹ/ETM 수/NNB 있/VV. Backward compatible (no arguments → unchanged).
Changed¶
Bundled data CSVs (
kosac/data/*.csv) now store only absolute integer counts (ngram+ one column per label) instead of relative frequencies;freq,max.value, andmax.propare no longer stored — the loader derives them. This preserves tied top labels and makes the data mergeable/extensible. The package API is unchanged — loaded values are identical (absolute count =round(relative × freq)from the original 2016 release).
Documentation¶
Sphinx documentation site (guides + auto-generated API reference) deployed to GitHub Pages, with five tutorials (getting started, counting, machine learning, negation/intensifier, custom lexicon).
[0.1.0] — 2026-06-24¶
First pre-release (beta) of the KOSAC morpheme-level Korean sentiment lexicon (data vintage 2016), for review by colleagues and beta users ahead of the 1.0 release. The API may still change.
Added¶
Installable package
kosac-lexiconwith the six sentiment-feature lexicons bundled as package data and loaded viakosac.load_lexicon(...)/<Lexicon>.load()— no file paths required.pandas/numpy-only core install; the Kiwi POS tokenizer (kiwipiepy, no Java required) and HuggingFace tokenizer are optional extras (kosac-lexicon[kiwi],[transformers]). N-gram generation is pure-Python, sonltkis no longer a dependency.SentimentAnalyzer: bundles lexicons + a tokenizer and scores text in one call, across one or all six features, returning the top label, full distribution, and matched entries with character spans;analyze_batch/analyze_frame(pandas) helpers.Opt-in negation and intensifier composition, and
align=to seed Kiwi’s user dictionary from the lexicon (KiwiTokenizer.from_lexicon).Frequency-count analysis (
SentimentAnalyzer.count/count_batch/count_frame, andkosac analyze --count): counts of matched words per label with proportions — the method common in social-science studies.kosaccommand-line interface (analyze/features/citation), a scikit-learnKosacVectorizer([sklearn]extra), andkosac.citation()/kosac.describe_feature()helpers.set_lexiconnow re-filters from the originally loaded lexicon, so a threshold can be loosened as well as tightened; legacylex.get/lex.matchaliases for the original notebook API.pytest test suite covering loading, filtering, matching, inference, the analyzer, CLI, and scikit-learn.
Fixed¶
The
'None'label (557 polarity / many intensity entries) was parsed as a missing value bypandas.read_csvand lost frommax.value; loading now useskeep_default_na=False.get_match_info/get_sent_probscalled a non-existentself.match(renamed tomatch_patterns).lexicon.pyusednumpyonly via a star-import leak; now imported explicitly.Regex pattern building now escapes entries, so wildcard morphemes such as
가*/JKSno longer raisere.error.HuggingFaceTokenizerwas constructed incorrectly (self = tokenizerno-op and bad multiple inheritance); now composes anAutoTokenizer.initialize_entrycould not insert into an empty lexicon (no columns); building a lexicon from scratch withadd_token/updatenow works.Heavy tagger dependencies are imported lazily, so
import kosacsucceeds without Java ortransformersinstalled.