Skip to main content

DadmaTools: A Python NLP Library for Persian

Documentation Status
Named Entity Recognition | Part of Speech Tagging | Dependency Parsing | Informal To Formal
Constituency Parsing | Chunking | Kasreh Ezafe Detection
Spellchecker | Normalizer | Tokenizer | Lemmatizer | Sentiment Analysis

DadmaTools

DadmaTools is a repository for Natural Language Processing resources for the Persian Language. The aim is to make it easier and more applicable to practitioners in the industry to use Persian NLP, and hence this project is licensed to allow commercial use. The project features code examples on how to use the models in popular NLP frameworks such as spaCy and Transformers, as well as Deep Learning frameworks such as PyTorch. Furthermore, DadmaTools support common Persian embedding and Persian datasets. for more details about how to use this tool read the instruction below.

Contents:

Installation

To get started using DadmaTools, install the project with pip, it supports python 10, 11 and 12 :

  • Full Version
    Includes all features, including transformers and trainable modules:

    pip install dadmatools[full]
    
  • Colab Version
    For installing in colab and kaggle environment:

    pip install dadmatools[colab]
    

NLP Models

Natural Language Processing is an active area of research, and it consists of many different tasks. The DadmaTools repository provides an overview of Persian models for some of the most basic NLP tasks (and is continuously evolving).

Here is the list of NLP tasks we currently cover in the repository. These NLP tasks are defined as pipelines. Therefore, a pipeline list must be created and passed through the model. This will allow the user to choose the only task needed without loading others. Each task has its abbreviation as follows:

  • Named Entity Recognition: ner
  • Part of speech tagging: pos
  • Dependency parsing: dep
  • Constituency parsing: cons
  • Kasreh Ezafe Detection: kasreh
  • Chunking: chunk
  • Lemmatizing: lem
  • Tokenizing: tok
  • Spellchecker: spellchecker
  • Normalizing
  • informal2formal: itf
  • Sentiment analysis: sent

Note that the normalizer can be used outside of the pipeline as there are several configs (the default config is in the pipeline with the name of def-norm). Note that if no pipeline is passed to the model, the tokenizer will be loaded as default.

Normalizer

cleaning text and unify characters.

Note: None means no action!

from dadmatools.normalizer import Normalizer

normalizer = Normalizer(
    full_cleaning=False,
    unify_chars=True,
    refine_punc_spacing=True,
    remove_extra_space=True,
    remove_puncs=False,
    remove_html=False,
    remove_stop_word=False,
    replace_email_with="<EMAIL>",
    replace_number_with=None,
    replace_url_with="",
    replace_mobile_number_with=None,
    replace_emoji_with=None,
    replace_home_number_with=None
)

text = """
<p>
دادماتولز اولین نسخش سال ۱۴۰۰ منتشر شده. 
امیدواریم که این تولز بتونه کار با متن رو براتون شیرین‌تر و راحت‌تر کنه
لطفا با ایمیل dadmatools@dadmatech.ir با ما در ارتباط باشید
آدرس گیت‌هاب هم که خب معرف حضور مبارک هست:
 https://github.com/Dadmatech/DadmaTools
</p>
"""
normalized_text = normalizer.normalize(text)
# <p> دادماتولز اولین نسخش سال 1400 منتشر شده. امیدواریم که این تولز بتونه کار با متن رو براتون شیرین‌تر و راحت‌تر کنه لطفا با ایمیل <EMAIL> با ما در ارتباط باشید آدرس گیت‌هاب هم که خب معرف حضور مبارک هست: </p>

# full cleaning
normalizer = Normalizer(full_cleaning=True)
normalized_text = normalizer.normalize(text)
# دادماتولز نسخش سال منتشر تولز بتونه کار متن براتون شیرین‌تر راحت‌تر کنه ایمیل ارتباط آدرس گیت‌هاب معرف حضور مبارک

Pipeline

Containing Tokenizer, Lemmatizer, POS Tagger, Dependancy Parser, Constituency Parser, Kasreh, Spellcheker, Infromal To Formal, Name Entity Recognation.

import dadmatools.pipeline.language as language

# here lemmatizer and pos tagger will be loaded
# as tokenizer is the default tool, it will be loaded as well even without calling
pips = 'tok,lem,pos,dep,chunk,cons,spellchecker,kasreh,itf,ner,sent'
nlp = language.Pipeline(pips)
# doc is now a proper spaCy Doc object
doc = nlp('کشور بزرگ ایران توانسته در طی سال‌ها اقشار مختلفی از قومیت‌های گوناگون را به خوبی در خودش جا بده')

The pipeline now returns a proper spaCy Doc object that conforms to spaCy's Doc type. This provides full compatibility with spaCy's ecosystem and allows you to use all spaCy features and utilities.

Key Features of the spaCy Doc Object:

  • Standard spaCy attributes: doc.text, doc.ents, doc.sents, etc.
  • Token-level annotations: token.pos_, token.lemma_, token.dep_, token.head, etc.
  • Custom extensions: token._.kasreh, doc._.lang, doc._.sentiment, doc._.spellchecker
  • Entity recognition: Named entities are properly converted to spaCy's entity format
  • Sentence boundaries: Sentence segmentation is preserved

Example Usage:

# Access token information
for token in doc:
    print(f"Token: {token.text}, POS: {token.pos_}, Lemma: {token.lemma_}, Dep: {token.dep_}")

# Access entities
for ent in doc.ents:
    print(f"Entity: {ent.text}, Type: {ent.label_}")

# Access custom attributes
print(f"Language: {doc._.lang}")
print(f"Sentiment: {doc._.sentiment}")

# Use spaCy utilities
from spacy import displacy
displacy.serve(doc, style="dep")  # Visualize dependency tree

Backward Compatibility:

You can still get the original dictionary format by setting return_spacy_doc=False:

# Get original dictionary format
doc_dict = nlp(text, return_spacy_doc=False)
print(type(doc_dict))  # <class 'dict'>

# Get spaCy Doc object (default)
doc = nlp(text)  # or nlp(text, return_spacy_doc=True)
print(type(doc))  # <class 'spacy.tokens.Doc'>
{'spellchecker': {'orginal': 'کشور بزرگ ایران توانسته در طی سال\u200cها اغشار مختلفی از قومیت\u200cهای گوناگون رو به خوبی تو خودش  جا بده', 'corrected': 'کشور بزرگ ایران توانسته در طی سال\u200cها اقشار مختلفی از قومیت\u200cهای گوناگون رو به خوبی تو خودش جا بده', 'checked_words': [('اغشار', 'اقشار')]}, 'itf': ' کشور بزرگ ایران توانسته در طی سال\u200cها اغشار مختلفی از قومیت های گوناگون را به خوبی در خودش جا بده', 'sentences': [{'id': 1, 'tokens': [{'id': 1, 'text': 'کشور', 'upos': 'NOUN', 'xpos': 'N_PL', 'feats': 'Number=Plur|Person=2|Polarity=Neg|Tense=Pres', 'head': 14, 'deprel': 'nsubj', 'lemma': 'کشور', 'ner': 'O', 'kasreh': 'S-kasreh'}, {'id': 2, 'text': 'بزرگ', 'upos': 'ADJ', 'xpos': 'ADJ', 'feats': 'Degree=Pos', 'head': 8, 'deprel': 'amod', 'lemma': 'بزرگ', 'ner': 'O', 'kasreh': 'S-kasreh'}, {'id': 3, 'text': 'ایران', 'upos': 'SCONJ', 'xpos': 'CON', 'feats': 'Number=Plur|Person=3|PronType=Prs', 'head': 2, 'deprel': 'nmod:poss', 'lemma': 'ایران', 'ner': 'S-loc', 'kasreh': 'O'}, {'id': 4, 'text': 'توانسته', 'upos': 'VERB', 'xpos': 'V_PP', 'feats': 'Number=Sing|Person=3|VerbForm=Part', 'head': 14, 'deprel': 'aux', 'lemma': 'توانست#توان', 'ner': 'O', 'kasreh': 'O'}, {'id': 5, 'text': 'در', 'upos': 'ADP', 'xpos': 'P', 'head': 14, 'deprel': 'case', 'lemma': 'در', 'ner': 'O', 'kasreh': 'O'}, {'id': 6, 'text': 'طی', 'upos': 'ADP', 'xpos': 'P', 'head': 5, 'deprel': 'fixed', 'lemma': 'طی', 'ner': 'O', 'kasreh': 'S-kasreh'}, {'id': 7, 'text': 'سال\u200cها', 'upos': 'AUX', 'xpos': 'V_PRS', 'feats': 'Number=Sing|Person=3|Tense=Pres', 'head': 14, 'deprel': 'fixed', 'lemma': 'سال', 'ner': 'O', 'kasreh': 'O'}, {'id': 8, 'text': 'اغشار', 'upos': 'NOUN', 'xpos': 'N_PL', 'feats': 'Number=Plur', 'head': 19, 'deprel': 'nsubj', 'lemma': 'اغشار', 'ner': 'O', 'kasreh': 'S-kasreh'}, {'id': 9, 'text': 'مختلفی', 'upos': 'ADJ', 'xpos': 'ADJ', 'feats': 'Degree=Pos', 'head': 8, 'deprel': 'amod', 'lemma': 'مختلفی', 'ner': 'O', 'kasreh': 'O'}, {'id': 10, 'text': 'از', 'upos': 'ADP', 'xpos': 'P', 'head': 15, 'deprel': 'case', 'lemma': 'از', 'ner': 'O', 'kasreh': 'O'}, {'id': 11, 'text': 'قومیت\u200cهای', 'upos': 'NOUN', 'xpos': 'N_PL', 'feats': 'Number=Plur', 'head': 8, 'deprel': 'nmod:poss', 'lemma': 'قومیت', 'ner': 'O', 'kasreh': 'S-kasreh'}, {'id': 12, 'text': 'گوناگون', 'upos': 'ADJ', 'xpos': 'ADJ', 'feats': 'Degree=Pos', 'head': 11, 'deprel': 'amod', 'lemma': 'گوناگون', 'ner': 'O', 'kasreh': 'O'}, {'id': 13, 'text': 'رو', 'upos': 'PART', 'xpos': 'CLITIC', 'head': 8, 'deprel': 'case', 'lemma': 'رو', 'ner': 'O', 'kasreh': 'O'}, {'id': 14, 'text': 'به', 'upos': 'ADP', 'xpos': 'P', 'head': 19, 'deprel': 'case', 'lemma': 'به', 'ner': 'O', 'kasreh': 'O'}, {'id': 15, 'text': 'خوبی', 'upos': 'ADJ', 'xpos': 'ADJ', 'feats': 'Degree=Pos', 'head': 14, 'deprel': 'advcl', 'lemma': 'خوب', 'ner': 'O', 'kasreh': 'O'}, {'id': 16, 'text': 'تو', 'upos': 'ADP', 'xpos': 'P', 'feats': 'Number=Sing|Person=2|PronType=Prs', 'head': 19, 'deprel': 'case', 'lemma': 'تو', 'ner': 'O', 'kasreh': 'O'}, {'id': 17, 'text': 'خودش', 'upos': 'PRON', 'xpos': 'PRO', 'feats': 'Number=Sing|Person=3|PronType=Prs|Reflex=Yes', 'head': 19, 'deprel': 'obl', 'lemma': 'خودش', 'ner': 'O', 'kasreh': 'O'}, {'id': 18, 'text': 'جا', 'upos': 'VERB', 'xpos': 'PREV', 'feats': 'Number=Sing|Person=3|Tense=Pres', 'head': 19, 'deprel': 'compound:lvc', 'lemma': 'جا', 'ner': 'O', 'kasreh': 'O'}, {'id': 19, 'text': 'بده', 'upos': 'VERB', 'xpos': 'V_SUB', 'feats': 'Mood=Sub', 'head': 0, 'deprel': 'root', 'lemma': 'داد#ده', 'ner': 'O', 'kasreh': 'O'}]}], 'lang': 'persian', 'sentiment': [{'label': 'positive', 'score': 0.7366364598274231}]}

Loading Persian NLP Datasets

We provide an easy-to-use way to load some popular Persian NLP datasets

Here is the list of supported datasets.

Dataset Task
PersianNER Named Entity Recognition
ARMAN Named Entity Recognition
Peyma Named Entity Recognition
FarsTail Textual Entailment
FaSpell Spell Checking
PersianNews Text Classification
PerUDT Universal Dependency
PnSummary Text Summarization
SnappfoodSentiment Sentiment Classification
TEP Text Translation(eng-fa)
WikipediaCorpus Corpus
PersianTweets Corpus

all datasets are iterator and can be used like below:

from dadmatools.datasets import FarsTail
from dadmatools.datasets import SnappfoodSentiment
from dadmatools.datasets import Peyma
from dadmatools.datasets import PerUDT
from dadmatools.datasets import PersianTweets
from dadmatools.datasets import PnSummary


farstail = FarsTail()
#len of dataset
print(len(farstail.train))

#like a generator
print(next(farstail.train))

#dataset details
pn_summary = PnSummary()
print('PnSummary dataset information: ', pn_summary.info)

#loop over dataset
snpfood_sa = SnappfoodSentiment()
for i, item in enumerate(snpfood_sa.test):
    print(item['comment'], item['label'])

#get first tokens' lemma of all dev items
perudt = PerUDT()
for token_list in perudt.dev:
    print(token_list[0]['lemma'])

#get NER tag of first Peyma's data
peyma = Peyma()
print(next(peyma.data)[0]['tag'])

#corpus 
tweets = PersianTweets()
print('tweets count : ', len(tweets.data))
print('sample tweet: ', next(tweets.data))

get dataset info:

from dadmatools.datasets import get_all_datasets_info

get_all_datasets_info().keys()
#dict_keys(['Persian-NEWS', 'fa-wiki', 'faspell', 'PnSummary', 'TEP', 'PerUDT', 'FarsTail', 'Peyma', 'snappfoodSentiment', 'Persian-NER', 'Arman', 'PerSent'])

#specify task
get_all_datasets_info(tasks=['NER', 'Sentiment-Analysis'])

the output will be:

{"ARMAN": {"description": "ARMAN dataset holds 7,682 sentences with 250,015 sentences tagged over six different classes.\n\nOrganization\nLocation\nFacility\nEvent\nProduct\nPerson",
  "filenames": ["train_fold1.txt",
   "train_fold2.txt",
   "train_fold3.txt",
   "test_fold1.txt",
   "test_fold2.txt",
   "test_fold3.txt"],
  "name": "ARMAN",
  "size": {"test": 7680, "train": 15361},
  "splits": ["train", "test"],
  "task": "NER",
  "version": "1.0.0"},
 "PersianNer": {"description": "source: https://github.com/Text-Mining/Persian-NER",
  "filenames": ["Persian-NER-part1.txt",
   "Persian-NER-part2.txt",
   "Persian-NER-part3.txt",
   "Persian-NER-part4.txt",
   "Persian-NER-part5.txt"],
  "name": "PersianNer",
  "size": 976599,
  "splits": [],
  "task": "NER",
  "version": "1.0.0"},
 "Peyma": {"description": "source: http://nsurl.org/2019-2/tasks/task-7-named-entity-recognition-ner-for-farsi/",
  "filenames": ["peyma/600K", "peyma/300K"],
  "name": "Peyma",
  "size": 10016,
  "splits": [],
  "task": "NER",
  "version": "1.0.0"},
 "snappfoodSentiment": {"description": "source: https://huggingface.co/HooshvareLab/bert-fa-base-uncased-sentiment-snappfood",
  "filenames": ["snappfood/train.csv",
   "snappfood/test.csv",
   "snappfood/dev.csv"],
  "name": "snappfoodSentiment",
  "size": {"dev": 6274, "test": 6972, "train": 56516},
  "splits": ["train", "test", "dev"],
  "task": "Sentiment-Analysis",
  "version": "1.0.0"}}

Loading Persian Word Embeddings

To start using embedding please install fasttext:

pip install fasttext

download, load and use some pre-trained Persian word embeddings.

dadmatools supports all glove, fasttext, and word2vec formats.

from dadmatools.embeddings import get_embedding, get_all_embeddings_info, get_embedding_info
from pprint import pprint

pprint(get_all_embeddings_info())

#get embedding information of specific embedding
embedding_info = get_embedding_info('glove-wiki')

#### load embedding ####
word_embedding = get_embedding('glove-wiki')

#get vector of the word
print(word_embedding['سلام'])

#vocab
vocab = word_embedding.get_vocab()

### some useful functions ###
print(word_embedding.top_nearest("زمستان", 10))
print(word_embedding.similarity('کتب', 'کتاب'))
print(word_embedding.embedding_text('امروز هوای خوبی بود'))

The following word embeddings are currently supported:

Name Embedding Algorithm Corpus
glove-wiki glove Wikipedia
fasttext-commoncrawl-bin fasttext CommonCrawl
fasttext-commoncrawl-vec fasttext CommonCrawl
word2vec-conll word2vec Persian CoNLL17 corpus

Evaluation

We have compared our pos tagging, dependancy parsing, and lemmatization models to stanza and hazm.

PerDT (F1 score)
Toolkit POS Tagger (UPOS) Dependancy Parser (UAS/LAS) Lemmatizer
DadmaTools 97.52% 95.36% / 92.54% 99.14%
stanza 97.35% 93.34% / 91.05% 98.97%
hazm - - 89.01%
Seraji (F1 score)
Toolkit POS Tagger (UPOS) Dependancy Parser (UAS/LAS) Lemmatizer
DadmaTools 97.83% 92.5% / 89.23% -
stanza 97.43% 87.20% / 83.89% -
hazm - - 86.93%
Tehran university tree bank (F1 score)
Toolkit Constituency Parser
DadmaTools (without preprocess)) 82.88%
Stanford (with some preprocess on POS tags) 80.28

How to use

You can see the codes and the output in colab.

Open In Colab

Cite

@inproceedings{jafari2025dadmatools,
  title={DadmaTools V2: an Adapter-Based Natural Language Processing Toolkit for the Persian Language},
  author={Jafari, Sadegh and Farsi, Farhan and Ebrahimi, Navid and Sajadi, Mohamad Bagher and Eetemadi, Sauleh},
  booktitle={Proceedings of the 1st Workshop on NLP for Languages Using Arabic Script},
  pages={37--43},
  year={2025}
}

Release files for dadmatools 2.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dadmatools 2.3.6
File Size Uploaded
dadmatools-2.3.6.tar.gz 710.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dadmatools 2.3.6
File Interpreter ABI Platform
dadmatools-2.3.6-py3-none-any.whl Python 3 none any Details

Total release size: 1.6 MB

Release files / dadmatools-2.3.6.tar.gz

Download URL dadmatools-2.3.6.tar.gz
Size 710.3 kB
Tags Source
SHA-256 checksum
How to use checksums
060323154b5ba4f1ae41d31beebfc75986e1ae3983c973b3c95ebe6a8df300a6
BLAKE2b-256 checksum
How to use checksums
79ac1e64c2deb73bcc4d6c6d7d04b1aab8921684be8db9aefbc3f13e5d96f5fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.23

Release files / dadmatools-2.3.6-py3-none-any.whl

Download URL dadmatools-2.3.6-py3-none-any.whl
Size 894.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7e44b32f4e8e77e3b0815a7aed362e198109150951e4327900d76c3471326927
BLAKE2b-256 checksum
How to use checksums
b2009bfba3824fa58b2591ac84fd694cb0c3359e68868a9d37457126d940f39f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.9.23
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page