├── .idea ├── vcs.xml ├── misc.xml ├── modules.xml ├── AzerbaijaniLanguageStemmer.iml └── workspace.xml ├── test1.txt ├── readme.md ├── test3.txt ├── suffix.txt ├── main.py ├── LICENSE.md ├── test4.txt ├── .gitignore ├── test2.txt ├── verb.txt └── stemmer.py /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test1.txt: -------------------------------------------------------------------------------- 1 | Salam hamıya! 2 | Bugün biz sizə bir şey təqdim edəcəyik. 3 | Təbii dilin emalı... Dənizin suyu. Qızın çöhrəsi. Onun kitabının. Prezident İlham Əliyev aprelin 3-də “Biləsuvar rayonunda xalça istehsalı emalatxanasının tikintisi, təchizatı və infrastrukturunun yaradılması ilə bağlı tədbirlər haqqında” sərəncam imzalayıb. -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Azerbaijani Language Stemmer 2 | 3 | Stemmer for Azerbaijani language written in Python. 4 | 5 | ## Installation 6 | 7 | The package will be published on PyPI after finishing the development of the first fully working version. 8 | 9 | Clone this repository instead of installation. 10 | 11 | 16 | 17 | ## License 18 | 19 | MIT © [Azerbaijani NLP](https://github.com/aznlp) -------------------------------------------------------------------------------- /.idea/AzerbaijaniLanguageStemmer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /test3.txt: -------------------------------------------------------------------------------- 1 | Mərkəzi ABŞ-da olan elektrikli avtomobil istehsalçısı Tesla tarixinin ən böyük geri çağırışını edib. Tesladan müştərilərinə göndərilən açıqlamada firmanın Model S avtomobilində görünən sükan problemi səbəbilə 2016-cı ilin aprelində istehsal olunan bütün Model S maşınlarının geri qaytarıldığı bildirilib. Soyuq hava şərtlərində duz atılmış yollardakı kalsium və maqneziumun Model S maşınlarının sükanındakı boltlarda aşınmaya səbəb olduğunu bildirən açıqlamaya bu aşınmanın aşağı sürətlərdə hərəkətdə olarkən və park edərkən sürücüyə çətin anlar yaşatdığı da əlavə olunub. Sözügedən problem səbəbilə heç bir Tesla maşınında bugünə qədər hər hansısa qəza hadisəsi baş verməyib. Lakin Tesla bu geri çağırışla maşınlarda lazımi hissələri yeniləriylə əvəz edəcək və bir növ özünü siğortalayacaq. -------------------------------------------------------------------------------- /suffix.txt: -------------------------------------------------------------------------------- 1 | kən 2 | dakı 3 | dəki 4 | lar 5 | lər 6 | la 7 | lə 8 | ıb 9 | ib 10 | ub 11 | üb 12 | ım 13 | im 14 | um 15 | üm 16 | ın 17 | in 18 | un 19 | ün 20 | ıl 21 | il 22 | ul 23 | ül 24 | sı 25 | si 26 | ı 27 | i 28 | u 29 | ü 30 | mız 31 | miz 32 | muz 33 | müz 34 | nız 35 | niz 36 | nuz 37 | nüz 38 | am 39 | əm 40 | ıq 41 | ik 42 | uq 43 | ük 44 | san 45 | sən 46 | sınız 47 | siniz 48 | sunuz 49 | sünüz 50 | dır 51 | dir 52 | dur 53 | dür 54 | da 55 | də 56 | dan 57 | dən 58 | ma 59 | mə 60 | aq 61 | ək 62 | ır 63 | ir 64 | ur 65 | ür 66 | acaq 67 | əcək 68 | ar 69 | ər 70 | a 71 | ə 72 | malı 73 | məli 74 | mış 75 | miş 76 | muş 77 | müş 78 | dı 79 | di 80 | du 81 | dü 82 | m 83 | s 84 | y 85 | n 86 | k 87 | - 88 | cı 89 | ci 90 | cu 91 | cü 92 | can 93 | cən 94 | dək 95 | ca 96 | cə 97 | sana 98 | sənə 99 | mı 100 | mi 101 | mu 102 | mü 103 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from stemmer import Stemmer 2 | from string import punctuation 3 | 4 | # Program starts here. 5 | if __name__ == '__main__': 6 | # Instantiate Stemmer object 7 | my_stemmer = Stemmer() 8 | # Generate your text 9 | with open("test1.txt", 'r', encoding="utf-8-sig") as text: 10 | my_text = text.read() 11 | # Preprocess your text: remove punctuation, lowercase the letters, trim the spaces and newlines, and split the text by space/s 12 | my_text=my_text.replace("İ", "I") 13 | my_text=my_text.replace("“", "") 14 | my_text=my_text.replace("”", "") 15 | my_text=my_text.replace("'", "") 16 | my_text=my_text.replace('"', "") 17 | my_text=my_text.split() 18 | my_words=[] 19 | for word in my_text: 20 | my_words.append(''.join(c for c in word if (c not in punctuation) or (c == '-'))) 21 | # Print words before stemming 22 | print(my_words) 23 | # Apply stemming to the list of words 24 | my_words = my_stemmer.stem_words(my_words) 25 | # Print words after stemming 26 | print(my_words) 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 AzerbaijaniNLP Research Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test4.txt: -------------------------------------------------------------------------------- 1 | Quba gedib-gəlmək Soyqırımı Memorial Kompleksinin ərazisində yerləşən kütləvi məzarlıqda abidənin inşası başa çatıb. APA-nın şimal bürosunun verdiyi məlumata görə, ərazidə 1918-dən ildə ermənilərin törətdiyi soyqırımı əks etdirən memorial lövhə və abidə ucaldılıb. Abidənin açılışı 31 Mart Azərbaycanlıların soyqrımı günü nəzərdə tutulub. Hazırda ərazidə son tamamla və 31 Mart tədbirlərinə hazırlıq işləri davam etdirilir. Qeyd edək ki, vaxtilə ermənilər tərəfindən vəhşicəsinə, qeyri-insani şəkildə öldürülənlərin toplu məzarlığında müəyyən yenidənqurma işləri aparılıb, azərbaycanlılarla yanaşı, digər millətlərə də aid olan insan sümükləri milli və dini adətlərə uyğun olaraq torpağa basdırılıb, qorunmaq üçün məzarlığın üstü örtülüb. Quba soyqırımı məzarlığı 2007-ci il aprelin 1-də ərazidə torpaq işləri görülərkən aşkarlanıb. Bundan sonra Azərbaycan Milli Elmlər Akademiyasının Arxeologiya və Etnoqrafiya İnstitutunun əməkdaşları tərəfindən kütləvi məzarlıqda geniş tədqiqat işləri aparılıb. Tədqiqatlar nəticəsində məzarlığın 1918-ci ildə ermənilərin yerli dinc əhaliyə qarşı törətdikləri soyqırımı ilə bağlı olduğu müəyyənləşib. Quba Soyqırımı Memorial Kompleksi Heydər Əliyev Fondunun dəstəyi ilə yaradılıb və 2013-cü il sentyabrın 18-də açılışı olub. Kompleksdə XX əsrin əvvəllərində Quba şəhərinin müxtəlif ərazilərinin görüntüləri, o zaman burada aparılan tikinti-quruculuq işləri, əhalinin həyat tərzi haqqında fotolar var. Burada quraşdırılmış sensor ekranlı monitordakı xüsusi xəritədə ermənilərin ötən əsrdə xalqımıza qarşı müxtəlif bölgələrdə həyata keçirdikləri soyqırımı aktları barədə bir neçə dildə məlumat əldə etmək mümkündür. Quba məşhurluğunu gedib gedən edən edib əmimgil əhaliyə -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /test2.txt: -------------------------------------------------------------------------------- 1 | Azərbaycanın paraqvayın ACM Bölməsi və ADA Universiteti ACM Tələbə Bölməsi aprelin 21-də “Ada mirası: informatikada qadınlar” mövzusunda 3-cü ACM tədbiri keçirəcək. Xeberler.az-ın məlumatına görə, İnformatikada qadınların iştirakını təbliğ edən beynəlxalq “womENcourage 2018” konfransı ərəfəsində ACM-W Avropanın dəstəyi ilə keçiriləcək bu tədbir saat 11.00-da, ADA Universitetinin böyük konfrans zalında başlayacaq. “Ada mirası: informatikada qadınlar” ilk kompüter proqramçısı hesab olunan ingilis riyaziyyatçısı Ada Lavleysin (Ada Lovelace, 1815-1852) adı ilə bağlıdır. ADA Universitetində artıq 3-cü il keçirilən tədbirin məqsədi texniki sahələrdə təhsil alan və ya işləyən xanımları bir araya gətirərək, beynəlxalq “womENcourage 2018” konfransında iştirak etməyə həvəsləndirməkdir. 2016-cı ildə keçirilən birinci tədbirdən sonra 5 azərbaycanlı tələbə səfər təqaüdü qazanaraq ilk dəfə bu beynəlxalq konfransda iştirak etdi, ötən il isə bu say artıq 7 nəfərə çatıb. Bu il qlobal ACM-W təşkilatının sədr müavini, Bilkent Universitetinin müəllimi Reyyan Ayfer “Ada mirası”nın qonağıdır. Tədbirdə Barselonadakı ötənilki “womENcourage” konfransında iştirak etmiş tələbələr öz təcrübələrini bölüşəcəklər. Həmçinin informatikanın tədrisi istiqamətində “Kod Saatı”, “Bebras” müsabiqəsi və “LEGO” robotlarının istifadəsi barədə çıxışlar olacaq. Daha sonra konfrans üçün təqdim olunan tədqiqat işləri qiymətləndiriləcək və təqdimatçılara məsləhətlər veriləcək. Tədbirin işçi dilləri Azərbaycan və ingilis dilidir. Beynəlxalq “womENcourage” konfransı bu il 3-5 oktyabr Serbiyanın paytaxtı Belqrad şəhərində keçiriləcək. Konfrans informatikada öz nailiyyətlərini təqdim etmək və təcrübələrini bölüşmək üçün xanımları bir araya gətirir. Daha geniş məlumatı https://womencourage.acm.org/ ünvanından almaq olar. Hər il “womENcourage” həm də bakalavr, magistr və dokotrantura tələbələrini (qadın və ya kişi olmasından asılı olmayaraq) informatikanın istənilən istiqaməti üzrə öz tədqiqatlarını poster şəklində təqdim etməyə dəvət edir. Posterləri qəbul olunmuş iştirakçılar konfransda qeydiyyat haqqını da əhatə edən səfər təqaüdü üçün müraciət edə biləcəklər. -------------------------------------------------------------------------------- /verb.txt: -------------------------------------------------------------------------------- 1 | görmüşdüm görmüşdün görmüşdü görmüşdük görmüşdünüz görmüşdülər 2 | görmüşmüşəm görmüşmüşsən görmüşmüş görmüşmüşük görmüşmüşsünüz görmüşmüşlər 3 | görmüşəmmiş görmüşsənmiş görmüşmüş görmüşükmüş görmüşsünüzmüş görmüşlərmiş 4 | görübmüşəm görübmüşsən görübmüş görübmüşük görübmüşsünüz görübmüşlər 5 | görübsənmiş görübmüş görübsünüzmüş görüblərmiş 6 | görmüşəmsə görmüşsənsə görmüşsə görmüşüksə görmüşsünüzsə görmüşlərsə 7 | görübsənsə görübsə görübsünüzsə görüblərsə 8 | görürdüm görürdün görürdü görürdük görürdünüz görürdülər 9 | görürmüşəm görürmüşsən görürmüş görürmüşük görürmüşsünüz görürmüşlər 10 | görürəmmiş görürsənmiş görürmüş görürükmüş görürsünüzmüş görürlərmiş 11 | görürəmsə görürsənsə görürsə görürüksə görürsünüzsə görürlərsə 12 | görəcəkdim görəcəkdin görəcəkdi görəcəkdik görəcəkdiniz görəcəkdilər 13 | görəcəkmişəm görəcəkmişsən görəcəkmiş görəcəkmişik görəcəkmişsiniz görəcəkmişlər 14 | görəcəyəmmiş görəcəksənmiş görəcəkmiş görəcəyikmiş görəcəksinizmiş görəcəklərmiş 15 | görəcəyəmsə görəcəksənsə görəcəksə görəcəyiksə görəcəksinizsə görəcəklərsə 16 | görərdim görərdin görərdi görərdik görərdiniz görərdilər 17 | görərmişəm görərmişsən görərmiş görərmişik görərmişsiniz görərmişlər 18 | görərəmmiş görərsənmiş görərmiş görərikmiş görərsinizmiş görərlərmiş 19 | görərəmsə görərsənsə görərsə görəriksə görərsinizsə görərlərsə 20 | görəydim görəydin görəydi görəydik görəydiniz görəydilər 21 | görəymişəm görəymişsən görəymiş görəymişik görəymişsiniz görəymişlər 22 | görəmmiş görəsənmiş görəmiş görəkmiş görəsinizmiş görələrmiş 23 | görməliydim görməliydin görməliydi görməliydik görməliydiniz görməliydilər 24 | görməliymişəm görməliymişsən görməliymiş görməliymişik görməliymişsiniz görməliymişlər 25 | görməliyəmmiş görməlisənmiş görməliymiş görməliyikmiş görməlisinizmiş görməlilərmiş 26 | görməliyəmsə görməlisənsə görməlidirsə görməliyiksə görməlisinizsə görməlidirləsə 27 | görəsiydim görəsiydin görəsiydi görəsiydik görəsiydiniz görəsiydilər 28 | görəsiymişəm görəsiymişsən görəsiymiş görəsiymişik görəsiymişsiniz görəsiymişlər 29 | görəsiyəmmiş görəsisənmiş görəsiymiş görəsiyikmiş görəsisinizmiş görəsiylərmiş 30 | görsəydim görsəydin görsəydi görsəydik görsəydiniz görsəydilər 31 | görsəymişəm görsəymişsən görsəymiş görsəymişik görsəymişsiniz görsəymişlər -------------------------------------------------------------------------------- /stemmer.py: -------------------------------------------------------------------------------- 1 | # Stemmer class definition 2 | class Stemmer: 3 | # Stores the words loaded from the words.txt file 4 | words = set() 5 | # Stores the suffixes loaded from the suffix.txt file 6 | suffixes = [] 7 | # Stores all possible stems of a word 8 | stems = [] 9 | 10 | # Constructor of the Stemmer class 11 | def __init__(self): 12 | # Loads words from the words.txt file 13 | self.__load_words() 14 | # Loads suffixes from the suffix.txt file 15 | self.__load_suffixes() 16 | 17 | # Destructor of the Stemmer class 18 | def __del__(self): 19 | # Clear both lists to free the memory space 20 | self.words.clear() 21 | self.suffixes.clear() 22 | 23 | # Loads the words from the word.txt file into memory 24 | def __load_words(self): 25 | # Open words.txt file in read mode with utf-8 encoding. 26 | with open("words.txt", "r", encoding="utf8") as words_file: 27 | # Iterate over each line in the words.txt file 28 | for word in words_file: 29 | # Trim the spaces and newline characters from the string before adding to the list 30 | self.words.add(word.strip()) 31 | 32 | # Loads the suffixes from the suffix.txt file into memory 33 | def __load_suffixes(self): 34 | # Open suffix.txt file in read mode with utf-8 encoding 35 | with open("suffix.txt", "r", encoding="utf8") as suffix_file: 36 | # Iterate over each line in the suffix.txt file 37 | for suffix in suffix_file: 38 | # Trim the spaces and newline characters from the string before adding to the list 39 | self.suffixes.append(suffix.strip()) 40 | 41 | # Removes one suffix at a time 42 | def suffix(self, word): 43 | for suffix in self.suffixes: 44 | # If the word ends with the particular suffix, create a new word by removing that suffix 45 | if word.endswith(suffix) and (word[:word.rfind(suffix)] in self.words): 46 | word = word[:word.rfind(suffix)] 47 | return word 48 | # Iterate over the suffixes 49 | for suffix in self.suffixes: 50 | # If the word ends with the particular suffix, create a new word by removing that suffix 51 | if word.endswith(suffix): 52 | word = word[:word.rfind(suffix)] 53 | return word 54 | return word 55 | 56 | # Converts changed suffixes and roots to their original forms 57 | def converter(self, word): 58 | if word.endswith('lığ') or word.endswith('luğ') or word.endswith('lağ') or word.endswith('cığ'): 59 | l=list(word); l[-1]='q'; return "".join(l) 60 | if word.endswith('liy') or word.endswith('lüy'): 61 | l=list(word); l[-1]='k'; return "".join(l) 62 | if word.endswith('cağ'): 63 | l=list(word); l[-1]='q'; return "".join(l) 64 | if word.endswith('cəy'): 65 | l=list(word); l[-1]='k'; return "".join(l) 66 | if word.endswith('ığ') or word.endswith('uğ') or word.endswith('ağ'): 67 | l=list(word); l[-1]='q'; return "".join(l) 68 | if word.endswith('iy') or word.endswith('üy') or word.endswith('əy'): 69 | l=list(word); l[-1]='k'; return "".join(l) 70 | if word == 'ed': 71 | l=list(word); l[1]='t'; return "".join(l) 72 | if word == 'ged': 73 | l=list(word); l[2]='t'; return "".join(l) 74 | return word 75 | 76 | # Returns the stemmed version of word 77 | def stem_word(self, word): 78 | # Change the word to lowercase. 79 | word = word.lower() 80 | # Convert if the word has changed root or suffix 81 | word = self.converter(word) 82 | # If word is already in the list, append it to stems list 83 | if word.isnumeric(): 84 | self.stems.append(word) 85 | else: 86 | if word in self.words: 87 | self.stems.append(word) 88 | # Iterate through suffixes 89 | for suffix in self.suffixes: 90 | # If word ends with current suffix, remove the suffix and stem again 91 | if word.endswith(suffix): 92 | self.stem_word(word[:word.rfind(suffix)]) 93 | 94 | # Returns the stemmed versions of the given words 95 | def stem_words(self, list_of_words): 96 | # Iterate over the range of word indexes 97 | list_of_stems = [] 98 | for word in list_of_words: 99 | # Empty the stems list for each word 100 | self.stems = [] 101 | # Apply stemming to each word in the list. 102 | self.stem_word(word) 103 | selected_stem = "" 104 | # Choose the stem with the maximum length 105 | for stem in self.stems: 106 | if len(stem) > len(selected_stem): selected_stem = stem 107 | # If there is no selected stem for word, append the word itself 108 | if selected_stem == "": 109 | selected_stem = word 110 | # Append the stem of the current word to the list of stems 111 | list_of_stems.append(selected_stem) 112 | 113 | # Return the updated list. 114 | return list_of_stems 115 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | clear 61 | 62 | 63 | 64 | 66 | 67 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |