# KR-WordRank: Korean keyword extraction without a morphological analyzer

> KR-WordRank extracts Korean words and keywords from raw text using a substring graph and a HITS-style ranking, with no part-of-speech tagger in the pipeline. It is a narrow tool with real limits, and the README is honest about how it works.

**lovit/KR-WordRank** — A library that automatically extracts words and keywords from Korean text using unsupervised learning methods.

- Repository: https://github.com/lovit/KR-WordRank
- Stars: 354 · Forks: 54
- Language: Python
- License: NOASSERTION
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/lovit-kr-wordrank

## The problem KR-WordRank solves for Korean text

Korean keyword extraction usually starts with a morphological analyzer. That means a dictionary, a tagger, and a decision about which parts of speech count as keywords. KR-WordRank removes that dependency. The README describes it as extracting words and keywords from Korean text without a separate morphological analyzer, and the package metadata lists only numpy, scipy and scikit-learn as runtime dependencies. There is no KoNLPy, no mecab, no jhannanum in the install graph.

The intended user is someone with a corpus and no tagging budget: product reviews, survey free-text, community posts. The README's own example is a set of La La Land movie reviews, and the CLI ships a test fixture at tests/integration/data/134963.txt. If your data is short, informal, and full of spacing errors, a tagger often fails on it anyway. KR-WordRank's approach is frequency and graph structure rather than grammar, which is exactly why it survives messy input.

## How the substring graph and HITS ranking actually work

The mechanism has three stages. First, each whitespace-delimited token (어절) in a sentence is decomposed into substrings. Second, those substrings become nodes in a graph, with edges derived from co-occurrence inside the same tokens. Third, a HITS-style algorithm, described in the README as similar to PageRank, assigns each substring an importance score using a decaying factor beta.

Two parameters control graph construction before ranking even starts: min_count, the minimum occurrence count for a substring to enter the graph, and max_length, the maximum substring length. After ranking, high-scoring substrings go through post-processing to be confirmed as words. That word confirmation step is the part the README does not detail, and it is where most of the surprising output comes from. In the published example, both 꿈을 and 여운이 appear as ranked entries, which tells you the confirmed units are not lemmas. They are frequent, graph-central strings.

Key-sentence extraction reuses the same ranking. The README states that a keyword vector is built from the rank values, and sentences are then selected in order by cosine similarity to that vector. Sentence-to-sentence similarity is deliberately not used. That is a meaningful design choice: two sentences can both be selected for covering the same keyword mass, and the diversity parameter exists to push back against exactly that.

## Installing KR-WordRank and running a first extraction

The README gives two installation paths. The project manages its environment with uv and targets Python 3.12; pip works as well. The package metadata requires Python >= 3.12, numpy >= 1.18.4, scipy >= 1.4.1 and scikit-learn >= 0.22.1.

```bash
pip install krwordrank
```

For a uv-managed checkout, the README shows creating the interpreter and syncing the lockfile:

```bash
uv venv --python 3.12
uv sync
```

Once installed, the console script is krwordrank, wired in pyproject.toml to krwordrank.cli:main. The CLI defaults to a text format where one line is one document, and if a line contains tabs only the first column is used, which makes TSV files work without conversion. To get the top 15 keywords from a file:

```bash
krwordrank keywords -i sentences.txt -n 15
```

The README's own run against tests/integration/data/134963.txt with -n 15 prints tab-separated word and rank pairs, starting with 영화 at 201.021643, then 너무 at 81.536356. If your documents live in JSON Lines instead, you must name the field holding the text:

```bash
krwordrank keywords -i reviews.jsonl --format jsonl --field text -n 50
```

For keywords plus representative sentences in one pass, use the keysents subcommand. The README shows the output as a keyword block, then a line containing three hyphens, then the selected sentences:

```bash
krwordrank keysents -i sentences.txt -n 20 -k 5
```

Both subcommands accept -s for stopwords and --json for machine-readable output. Run krwordrank keywords --help or krwordrank keysents --help for the full flag list, which the README points to rather than reproducing.

## Where the substring approach breaks down

The first limitation is visible in the README's own output. Because words are confirmed from ranked substrings rather than lemmatized, inflected forms surface as distinct keywords. 꿈을 and 여운이 both appear in the top 15 for the review corpus. If you need 꿈 and 여운 grouped, you are doing that merging yourself, after extraction.

The second limitation is parameter sensitivity. min_count and max_length are set at construction time, and the README's example values (5 and 10) are tuned for a corpus of thousands of short reviews. Feed it a few dozen long documents and min_count=5 may exclude most of the vocabulary; feed it thousands of one-line chat messages and max_length=10 may cut real compounds. The README does not offer a heuristic for choosing either value.

The third is the stopword burden. The CLI example passes -s "영화,너무,정말" and the Python example passes a stopwords set containing 영화, 너무, 정말, 진짜 and 보고. Those are the highest-ranked terms in the sample corpus. In other words, the top of the ranking is often dominated by domain-generic words, and filtering them is manual work you repeat per corpus. There is no built-in Korean stopword list documented in the README.

Finally, this is the wrong tool when you need syntax. No part-of-speech tags, no dependency structure, no entity types. If your downstream task is relation extraction or anything that needs to know a word is a verb, KR-WordRank gives you nothing to build on.

## KR-WordRank versus a morphological-analyzer pipeline

The obvious alternative is the conventional route: run a Korean morphological analyzer such as one of the taggers exposed through KoNLPy, filter to nouns, then count or rank. The difference in approach is fundamental. A tagger segments by grammar and dictionary lookup, so 꿈을 becomes 꿈 plus a particle, and you can group by lemma. KR-WordRank segments by substring frequency and graph centrality, so it never knows what a particle is.

That trade cuts both ways. The tagger route gives cleaner, groupable units and better handling of compounds it knows. It also requires a working analyzer installation, a tagset decision, and tolerance for the tagger's failure modes on informal text, which is precisely the text most review corpora contain. KR-WordRank installs with pip and three numeric dependencies, and the README states it works without a Korean tokenizer. It also handles new coinages and slang that a fixed dictionary may not carry, because nothing has to be in a dictionary to be a frequent substring.

If your corpus is formal and your output needs to be linguistically clean, the tagger route is the better fit. If your corpus is informal, your budget is small, and a ranked list of frequent strings is enough, KR-WordRank gets there with far less setup.

## Maintenance, licence and what an upgrade costs

The repository is not archived, and the last push was on 2026-06-04, which is roughly three and a half months before this writing. The version in pyproject.toml is 1.0.3. No releases were retrieved, so version history is not something this article can describe.

The Python requirement is >= 3.12, and the README states testing on Python 3.12.12. That is a modern floor. If your production environment is still on 3.10 or 3.11, this package will not install, and that is a real migration cost rather than a footnote. The dependency floors (numpy 1.18.4, scipy 1.4.1, scikit-learn 0.22.1) are old enough that they will not force upgrades on most stacks.

The licence is the awkward part. pyproject.toml declares license = { text = "LGPL" }, while the repository's licence identifier is reported as NOASSERTION. LGPL is a copyleft licence with a linking exception, and the exact version matters for how it interacts with your distribution model. The README does not state which LGPL version applies. Read the LICENSE file in the repository root and get your own advice before shipping this inside a closed product.

Upgrade cost is low in one sense: the public surface is a handful of functions and two subcommands. It is higher in another: extraction output is not stable across parameter changes, so any downstream system that consumes keyword lists will shift when you retune min_count or max_length.

## Conclusion

Adopt KR-WordRank if you have a pile of Korean text and want ranked keywords or representative sentences without building a tagger pipeline. Do not adopt it if you need lemmatized output, part-of-speech tags, or deterministic word boundaries: the README states that ranking substrings are confirmed as words through post-processing, and the output shown in the README keeps inflected forms such as 꿈을 and 여운이 as separate entries. Before committing, run krwordrank keywords -i yourfile.txt -n 50 on a sample of your own corpus and read the ranked list, since the quality of the result depends on min_count and max_length matching your document lengths.

## FAQ

### Does KR-WordRank need a Korean morphological analyzer to work?

No. The README states that KR-WordRank extracts words and keywords from Korean text without a separate morphological analyzer, and the package dependencies are only numpy, scipy and scikit-learn.

### How do I install KR-WordRank?

Run pip install krwordrank, or if you are working from a checkout managed with uv, create a Python 3.12 environment with uv venv --python 3.12 and then run uv sync. The package requires Python 3.12 or newer.

### What is the difference between the keywords and keysents commands in KR-WordRank?

The keywords subcommand outputs a ranked word list with scores. The keysents subcommand outputs that keyword block, then a line containing three hyphens, then the selected key sentences, which are chosen by cosine similarity to a vector built from the keyword ranks.

### Can KR-WordRank read JSON Lines input?

Yes. Pass --format jsonl together with --field to name the key that holds the document text, for example krwordrank keywords -i reviews.jsonl --format jsonl --field text -n 50. The README states that --field is required when the format is jsonl.

### What licence does KR-WordRank use?

pyproject.toml declares the licence as LGPL, while the repository's licence identifier is reported as NOASSERTION, so the specific LGPL version is not stated in the README. Check the LICENSE file in the repository root.

## Sources

- [Issues](https://github.com/lovit/KR-WordRank/issues)
- [lovit/KR-WordRank on GitHub](https://github.com/lovit/KR-WordRank)
- [README](https://github.com/lovit/KR-WordRank/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lovit-kr-wordrank
