Skip to main content

LiNT-II is a readability assessment tool for Dutch.

Project description

LiNT-II: readability assessment for Dutch

License: EUPL v1.2 Python 3.11 Binder

Table of contents

  1. Introduction
  2. Changelog
  3. Installation
  4. Usage
  5. What is LiNT-II?
  6. References and Credits

Introduction

LiNT-II is a readability assessment tool for Dutch. The library:

LiNT-II is a new implementation of the original LiNT tool. The main differences between LiNT and LiNT-II are:

  • The NLP tools used to extract linguistic features from the text. LiNT has T-Scan under the hood, while LiNT-II uses spaCy.
  • The coefficients (weights) used in the formula. Since the features are calculated differently, a new linear regression model was fitted on the original reading comprehension data from LiNT. This resulted in new coefficients. The performance of the LiNT-II model is the same as the original LiNT: adjusted R2 = 0.74, meaning that the model explains 74% of the variance in the comprehension data.

For detailed information, please refer to What is LiNT-II? and LiNT-II documentation.

Changelog

New in 0.1.1

  • Added 4 new linguistic features (not used of the formula); see here.
  • Refactored return types: return WordFeatures instead of flattened text everywhere.

New in 0.1.2

  • Added 5 new linguistic features (not used of the formula); see here.
  • Updated the NOUN_DATA list: the semantic type of words related to kabinet/regering (cabinet/government) was changed to human.

Installation

pip install lint_ii
python -m spacy download nl_core_news_lg

Usage

Create ReadabilityAnalysis from text

>>> from lint_ii import ReadabilityAnalysis

>>> text = """De Oudegracht is het sfeervolle hart van de stad.
In de middeleeuwen was het hier een drukte van belang met de aan- en afvoer van goederen. 
Nu is het een prachtige plek om te winkelen en te lunchen of te dineren in de oude stadskastelen."""

>>> analysis = ReadabilityAnalysis.from_text(text)
Loading Dutch language model from spaCy...  nl_core_news_lg

Note:

LiNT-II can process plain text or markdown. Other formats (e.g. html) or very "unclean" text might produce inaccurate results due to sentence segmentation issues.

Get LiNT-II score and difficulty level

You can see the score and difficulty level for the whole document and/or per sentence:

>>> analysis.lint.score
48.20593518603563

>>> analysis.lint.level
3

>>> analysis.lint_scores_per_sentence
[18.511612982419507, 54.27056340066443, 63.24402181810589]

Get linguistic features

All the linguistic features can be accessed on a text-level, a sentence-level, and a word-level (if applicable). This includes the 4 features used in the formula (e.g. word frequency), as well as additional features related to readability (e.g. passive constructions). For example:

Getting the mean word frequency for the whole text:

>>> analysis.mean_log_word_frequency
4.208347333820788

Getting the list of content words in each sentence:

>>> for sent in analysis.sentences:
      print([feat.text for feat in sent.content_words])
['oudegracht', 'sfeervolle', 'hart', 'stad']
['middeleeuwen', 'drukte', 'belang', 'afvoer', 'goederen']
['prachtige', 'plek', 'winkelen', 'lunchen', 'dineren', 'oude', 'stadskastelen']

Getting the word frequencies for each content word in the text:

>>> frequencies = {
    feat.text:freq
    for feat in analysis.word_features
    if (freq := feat.word_frequency) is not None
}
print(frequencies)
{'hart': 5.293120582960477, 'stad': 5.435577664689725, 'middeleeuwen': 3.423686536423184, 'drukte': 3.96775458077346, 'belang': 4.509063243912051, 'afvoer': 3.3845344124610364, 'goederen': 3.7985612410265284, 'prachtige': 4.7805033384066125, 'plek': 5.249034299064351, 'winkelen': 4.177454440810221, 'dineren': 3.893254653252401, 'oude': 5.436741798693928, 'stadskastelen': 1.359228547196266}

For a list of available properties, refer to the documentation in readability_analysis.py, sentence_analysis.py, and word_features.py.

Get dictionary with detailed analysis

For a human-readable dictionary with a detailed analysis, use the get_detailed_analysis() method. Note that running this method calculates all 13 features (core + additional), which might be a bit slow. If you need only some of the features, accessing them directly is faster.

>>> detailed_analysis = analysis.get_detailed_analysis()
>>> detailed_analysis.keys()
dict_keys(['document_stats', 'sentence_stats', 'contextually_new_words'])
>>> detailed_analysis['document_stats']
{'sentence_count': 3,
 'document_lint_score': 48.20593518603563,
 'document_difficulty_level': 3,
 'min_lint_score': 18.511612982419507,
 'max_lint_score': 63.24402181810589}
>>> detailed_analysis['sentence_stats'][0]
{'text': 'De Oudegracht is het sfeervolle hart van de stad.',
'score': 18.511612982419507,
'level': 1,
'mean_log_word_frequency': 5.364349123825101,
'top_n_least_freq_words': [('hart', 5.293120582960477),
 ('stad', 5.435577664689725)],
'proportion_concrete_nouns': 0.5,
'concrete_nouns': ['stad'],
'abstract_nouns': [],
'undefined_nouns': ['hart'],
'unknown_nouns': ['oudegracht'],
'max_sdl': 3,
'sdls': [{'token': 'de', 'dep_length': 0, 'heads': ['Oudegracht']},
 {'token': 'oudegracht', 'dep_length': 3, 'heads': ['hart']},
 {'token': 'is', 'dep_length': 2, 'heads': ['hart']},
 {'token': 'het', 'dep_length': 1, 'heads': ['hart']},
 {'token': 'sfeervolle', 'dep_length': 0, 'heads': ['hart']},
 {'token': 'hart', 'dep_length': 0, 'heads': ['hart']},
 {'token': 'van', 'dep_length': 1, 'heads': ['stad']},
 {'token': 'de', 'dep_length': 0, 'heads': ['stad']},
 {'token': 'stad', 'dep_length': 2, 'heads': ['hart']}],
'content_words_per_clause': 4.0,
'content_words': ['oudegracht', 'sfeervolle', 'hart', 'stad'],
'finite_verbs': ['is'],
'sent_length': 9,
'mean_clause_length': 9.0,
'passives': [],
'n_subordinate_clauses': 0,
'subordinate_clauses': [],
'adjectival_modifiers_per_clause': 2.0,
'adjectival_modifiers': ['sfeervolle', 'van de stad'],
'coordinated_constituents_per_clause': 0.0,
'coordinated_constituents': [],
'pronouns_first_person': [],
'pronouns_second_person': [],
'pronouns_third_person': [],
'humans': []}

Visualization in Jupyter Notebook (Binder)

To visualize your readability analysis, you can use this notebook.

Note: The Binder notebook takes a while to build (~2 minutes). Alternatively, you can download the repo and set up a conda environment to run the notebook locally.

What is LiNT-II?

LiNT-II is a Python implementation of LiNT (Leesbaar­heids­instrument voor Nederlandse Teksten), a readability assessment tool that analyzes Dutch texts and estimates their difficulty.

Features used in the formula

LiNT-II readability score is based on 4 features:

Feature Description
word frequency Mean word frequency of all the content words in the text (excluding proper nouns).
➡ Less frequent words make a text more difficult.
syntactic dependency length Syntactic dependency length (SDL) is the number of words between a syntactic head and its dependent (e.g., verb-subject). We take the biggest SDL in each sentence, and calculate their mean value for the whole text.
➡ Bigger SDL's make a text more difficult.
content words per clause Mean number of content words per clause.
➡ Larger number of content words indicates dense information and makes a text more difficult.
proportion concrete nouns Mean proportion of concrete nouns out of all the nouns in the text.
➡ Smaller proportion of concrete nouns (i.e. many abstract nouns) makes a text more difficult.

Definitions

  • Content words are words that possess semantic content and contribute to the meaning of the sentence. We consider a word as a content word if it belongs to one of the following part-of-speech (POS): nouns (NOUN), proper nouns (PROPN), lexical verbs (VERB), adjectives (ADJ), or if it's a manner adverb (based on a custom list).
  • Clause: A clause is a group of words that contains a subject and a verb, functioning as a part of a sentence. In this library, the number of clauses is determined by the number of finite verbs (= verbs that show tense) in the sentence.

LiNT-II score

The readability score is calculated based on the following formula:

LiNT-II score = 

  100 - (
      - 4.21
      + (17.28 * word frequency)
      - (1.62  * syntactic dependency length)
      - (2.54  * content words per clause)
      + (16.00 * proportion concrete nouns)
  )

The formula's coefficients were estimated using a linear regression model fitted on empirical reading comprehension data from high school students.

For more information, please refer to the LiNT-II documentation.

Difficulty levels

LiNT-II scores are mapped to 4 difficulty levels. For each level, it is estimated which percentage of adult Dutch readers have difficulty understanding texts on this level.

Score Difficulty level Proportion of adults who have difficulty understanding this level
[0-34) 1 14%
[34-46) 2 29%
[46-58) 3 53%
[58-100] 4 78%

For more information, please refer to the LiNT-II documentation.

Additional features

LiNT-II outputs additional linguistic features, which are not part of the formula, but can be relevant for readability.

Feature Description Release
sentence length The number of words in a sentence, excluding punctuation. v0.1.1
pronouns All the pronouns in the text, categorized into 1st / 2nd / 3rd person. v0.1.1
passives Passive voice constructions.
Example: Het boek is gepubliceerd nadat de auteur was overleden. "The book was published after the author's death"
v0.1.1
subordinate clauses A subordinate clause cannot stand alone as a complete sentence and relies on a main clause for its meaning. It adds additional information to the main clause.
Example: Mijn broer, die in Leuven woont, is morgen jarig. "My brother, who lives in Leuven, has his birthday tomorrow."
v0.1.1
mean clause length The number of words in a sentence, divided by the number of clauses in the sentence. v0.1.2
adjectival modifiers per clause The number of adjectival modifiers in a sentence, divided by the number of clauses in the sentence. Adjectival modifiers are words or phrases that describe nouns, providing additional information about them.
Example: De man [met hoed] [die daar loopt] is mijn vriend. "The man [with the hat] [walking there] is my friend." (Two adjectival modifiers; one clause).
v0.1.2
coordinated constituents per clause Coordination is a syntactic structure that links two or more elements using words like "and", "or", "but". We count the number of coordinated constituents in a sentence (excluding coordinated verbs or clauses), divided by the number of clauses in the sentence.
Example: Daar horen Schotland, [Wales] en [Noord-Ierland] ook bij. "Scotland, Wales, and Northern Ireland are also part of it." (Two coordinated constituents; one clause. Note that the first conjunct - the head of the coordination, Schotland - is not counted.)
v0.1.2
humans All the words that refer to people (including pronouns). v0.1.2
contextually new words Entities and situations that are new in the text, i.e. that were not mentioned in the preceding 50 words. v0.1.2

The feature contextually new words is available on the ReadabilityAnalysis level. The other 8 additional features are available on a SentenceAnalysis level.

References and Credits

LiNT-II

LiNT-II was developed by Jenia Kim (Hogeschool Utrecht, VU Amsterdam), in collaboration with Henk Pander Maat (Utrecht University).

If you use this library, please cite as follows:

@software{lint_ii,
  author = {Kim, Jenia and Pander Maat, Henk},
  title = {{LiNT-II: readability assessment for Dutch}},
  year = {2025},
  url = {https://github.com/vanboefer/lint_ii},
  version = {0.1.1},
  note = {Python package}
}
  • Special thanks to Antal van den Bosch (Utrecht University) for setting up and facilitating the collaboration.
  • Special thanks to Lawrence Vriend for his work on the LiNT-II Visualizer and other help with the code.
  • The code for LiNT-II was inspired by a spaCy implementation of LiNT by the City of Amsterdam: alletaal-lint.

Original LiNT

The first version of LiNT was developed in the NWO project Toward a validated reading level tool for Dutch (2012-2017). Later versions were developed in the Digital Humanities Lab of Utrecht University.

More details about the original LiNT can be found on:

The readability research on which LiNT is based is described in the PhD thesis of Suzanne Kleijn (English) and in Pander Maat et al. 2023 (Dutch). Please cite as follows:

@article{pander2023lint,
  title={{LiNT}: een leesbaarheidsformule en een leesbaarheidsinstrument},
  author={Pander Maat, Henk and Kleijn, Suzanne and Frissen, Servaas},
  journal={Tijdschrift voor Taalbeheersing},
  volume={45},
  number={1},
  pages={2--39},
  year={2023},
  publisher={Amsterdam University Press Amsterdam}
}
@phdthesis{kleijn2018clozing,
  title={Clozing in on readability: How linguistic features affect and predict text comprehension and on-line processing},
  author={Kleijn, Suzanne},
  year={2018},
  school={Utrecht University}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lint_ii-0.1.2.tar.gz (8.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lint_ii-0.1.2-py3-none-any.whl (8.4 MB view details)

Uploaded Python 3

File details

Details for the file lint_ii-0.1.2.tar.gz.

File metadata

  • Download URL: lint_ii-0.1.2.tar.gz
  • Upload date:
  • Size: 8.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for lint_ii-0.1.2.tar.gz
Algorithm Hash digest
SHA256 94f954e721594fec0d9b5ed05c7a9e676e2918fcb7da54de63891f1e48895d29
MD5 a7625896df8df5a4ccdaba04f7b14a91
BLAKE2b-256 07d098fc1cc2f60f8ad854308694aa11b890f0714e2c5d08dd5cdd946d097132

See more details on using hashes here.

File details

Details for the file lint_ii-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: lint_ii-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 8.4 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for lint_ii-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 812e4325f6c40d4f7dc34a8f9e3990dab50f96951a012c1ecdfe776859b1fb80
MD5 a4230df4b0805ee695a88d24213de615
BLAKE2b-256 680c3da730506815c306a300ef3ebdc8a074371c2a13d9b3278fe62e5baea2fd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page