Dictionary Management

Lindera Python provides functions for loading, building, and managing dictionaries used in morphological analysis.

Loading Dictionaries

System Dictionaries

Use load_dictionary(uri) to load a system dictionary. Download a pre-built dictionary from GitHub Releases and specify the path to the extracted directory:

from lindera import load_dictionary

dictionary = load_dictionary("/path/to/ipadic")

Embedded dictionaries (advanced) -- if you built with an embed-* feature flag, you can load an embedded dictionary:

dictionary = load_dictionary("embedded://ipadic")

A loaded Dictionary also exposes its own metadata:

print(dictionary.metadata_name())      # e.g. "ipadic"
print(dictionary.metadata_encoding())  # e.g. "UTF-8"
metadata = dictionary.metadata()       # the full Metadata object

A user dictionary must share the metadata (schema, encoding, etc.) of the system dictionary it augments. load_user_dictionary_for takes the system dictionary and uses its metadata, as in lindera-python/examples/tokenize_with_userdict.py (see User Dictionaries).

User Dictionaries

User dictionaries add custom vocabulary on top of a system dictionary. Load one for the system dictionary it is used with:

from lindera import load_dictionary, load_user_dictionary_for

dictionary = load_dictionary("/path/to/ipadic")
user_dict = load_user_dictionary_for("/path/to/user_dictionary.csv", dictionary)

A .csv is built with the system dictionary's metadata and checked against its connection matrix as it is built: a row whose context ID lies outside the matrix raises a ValueError that names its CSV line and surface (see Context IDs). A .bin is loaded as it was built.

load_user_dictionary(uri, metadata) loads a user dictionary with metadata only. A .csv loaded this way is checked when it is combined with the system dictionary, and the error names the entry by its word id:

from lindera import load_user_dictionary, Metadata

metadata = Metadata()
user_dict = load_user_dictionary("/path/to/user_dictionary.csv", metadata)

Pass the user dictionary when building a tokenizer:

from lindera import Mode, Tokenizer, load_dictionary, load_user_dictionary_for

dictionary = load_dictionary("/path/to/ipadic")
user_dict = load_user_dictionary_for("/path/to/user_dictionary.csv", dictionary)

tokenizer = Tokenizer(dictionary, mode=Mode.normal(), user_dictionary=user_dict)

Or via the builder:

from lindera import TokenizerBuilder

tokenizer = (
    TokenizerBuilder()
    .set_dictionary("/path/to/ipadic")
    .set_user_dictionary("/path/to/user_dictionary.csv")
    .build()
)

Building Dictionaries

System Dictionary

Build a system dictionary from source files:

from lindera import build_dictionary, Metadata

metadata = Metadata(name="custom", encoding="UTF-8")
build_dictionary("/path/to/input_dir", "/path/to/output_dir", metadata)

The input directory should contain the dictionary source files (CSV lexicon, matrix.def, etc.).

Metadata loaded with Metadata.from_json_file keeps the space_penalty rules of the metadata.json, and the built dictionary ships them. For ko-dic this means the left-space penalty stays on by default in the dictionary you build; turn it off per tokenizer with TokenizerBuilder.set_space_penalty(False) (see Tokenizer API). A Metadata created with the constructor carries no rules.

User Dictionary

Build a user dictionary from a CSV file for the system dictionary it is used with:

from lindera import build_user_dictionary_for, load_dictionary, load_user_dictionary_for

dictionary = load_dictionary("/path/to/ipadic")
build_user_dictionary_for("user_words.csv", "/path/to/user_dictionary.bin", dictionary)
user_dict = load_user_dictionary_for("/path/to/user_dictionary.bin", dictionary)

The CSV is built with the system dictionary's metadata and checked against its connection matrix: a row whose context ID lies outside the matrix raises a ValueError that names its CSV line and surface, and no file is written (see Context IDs). The second argument is the path of the file to write. End it with .bin, since the loaders choose how to load a user dictionary by its extension.

build_user_dictionary(kind, input_file, output_dir, metadata) builds a user dictionary with metadata only. Despite its name, output_dir is the path of the file to write too, not a directory. The rows are checked when the user dictionary is combined with the system dictionary, and the error names the entry by its word id:

from lindera import build_user_dictionary, Metadata

metadata = Metadata()
build_user_dictionary("ipadic", "user_words.csv", "/path/to/user_dictionary.bin", metadata)

The metadata parameter is optional. When omitted, default metadata values are used:

build_user_dictionary("ipadic", "user_words.csv", "/path/to/user_dictionary.bin")

Note: the first argument ("ipadic" above) is currently unused and reserved for future use -- it does not select or configure the build in any way. The build behavior is controlled entirely by metadata (in particular metadata.user_dictionary_schema). Any string may be passed today. To build for a system dictionary, use build_user_dictionary_for.

Metadata

The Metadata class configures dictionary parameters.

Creating Metadata

from lindera import Metadata

# Default metadata
metadata = Metadata()

# Custom metadata
metadata = Metadata(
    name="my_dictionary",
    encoding="UTF-8",
    default_word_cost=-10000,
)

Loading from JSON

metadata = Metadata.from_json_file("metadata.json")

Properties

PropertyTypeDefaultDescription
namestr"default"Dictionary name
encodingstr"UTF-8"Character encoding
default_word_costint-10000Default cost for unknown words
default_left_context_idint1288Default left context ID
default_right_context_idint1288Default right context ID
default_field_valuestr"*"Default value for missing fields
flexible_csvboolFalseAllow flexible CSV parsing
skip_invalid_cost_or_idboolFalseSkip entries with invalid cost or ID
dictionary_schemaSchemaIPADIC schemaSchema for the main dictionary
user_dictionary_schemaSchemaMinimal schemaSchema for user dictionaries

All properties support both getting and setting:

metadata = Metadata()
metadata.name = "custom_dict"
metadata.encoding = "EUC-JP"
print(metadata.name)  # "custom_dict"

to_dict()

Returns the metadata as a dict of strings: the properties above, with dictionary_schema_fields and user_dictionary_schema_fields (comma-separated field names) in place of the two schemas.

metadata = Metadata(name="test")
print(metadata.to_dict())

When the metadata carries left-space penalty rules, as ko-dic's does, the dict also has a space_penalty entry holding them as a JSON string; the key is absent otherwise:

metadata = Metadata.from_json_file("/path/to/ko-dic/metadata.json")
metadata.to_dict()["space_penalty"]
# '{"rules":[{"pos":["EC","EF",...],"cost":3000},{"pos":["JC","JKB",...],"cost":6000}]}'

Schema

Schema, FieldDefinition, and FieldType describe the field layout of a dictionary entry. A schema is used by Metadata.dictionary_schema and Metadata.user_dictionary_schema (see the table above).

FieldType

FieldType enumerates the category of a single field:

  • FieldType.Surface -- surface form (word text)
  • FieldType.LeftContextId -- left context ID
  • FieldType.RightContextId -- right context ID
  • FieldType.Cost -- word cost
  • FieldType.Custom -- any other, dictionary-specific field

FieldDefinition

FieldDefinition describes a single field within a schema.

FieldDefinition(index, name, field_type, description=None)

from lindera import FieldDefinition, FieldType

field = FieldDefinition(0, "surface", FieldType.Surface, "Surface form")

Properties (read-only):

PropertyTypeDescription
indexintZero-based position of the field within the schema
namestrField name
field_typeFieldTypeField type
descriptionstr or NoneOptional human-readable description

Creating a Schema

Schema holds an ordered list of field names and provides lookups between field name and index.

Schema(fields)

Creates a schema from a list of field names.

from lindera import Schema

schema = Schema([
    "surface",
    "left_context_id",
    "right_context_id",
    "cost",
    "major_pos",
    "reading",
])

Schema.create_default()

A static method that returns the built-in default schema: 13 fields matching the IPADIC-style layout (surface, left_context_id, right_context_id, cost, major_pos, pos_detail_1, pos_detail_2, pos_detail_3, conjugation_type, conjugation_form, base_form, reading, pronunciation).

schema = Schema.create_default()

Schema Methods and Properties

MemberReturnsDescription
fields (property)list[str]All field names, in order
field_count()intTotal number of fields
get_field_index(name)int or NoneIndex of the field named name
get_field_name(index)str or NoneField name at index
get_custom_fields()list[str]Field names after the four fixed fields (surface, left_context_id, right_context_id, cost)
get_field_by_name(name)FieldDefinition or NoneFull field definition for name
validate_record(record)NoneRaises ValueError if record does not match the schema
__len__()intSame as field_count()
schema = Schema.create_default()

schema.field_count()               # 13
schema.get_field_index("cost")     # 3
schema.get_field_name(0)           # "surface"
schema.get_custom_fields()         # ["major_pos", "pos_detail_1", ..., "pronunciation"]
len(schema)                        # 13

field = schema.get_field_by_name("surface")
print(field.index, field.name, field.field_type)  # 0 surface FieldType.Surface

schema.validate_record([
    "東京", "1288", "1288", "100",
    "名詞", "固有名詞", "地域", "一般", "*", "*",
    "東京", "トウキョウ", "トーキョー",
])