From bf29ca9e1ca47a124f1524abdf1dd3cc04f54d92 Mon Sep 17 00:00:00 2001 From: stephantul Date: Fri, 20 Oct 2017 11:06:58 +0100 Subject: [PATCH 1/5] fix return of garbage words because of inclusion of None in mapping to synsets --- les.py | 130 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/les.py b/les.py index 351cd8d..1fc6310 100644 --- a/les.py +++ b/les.py @@ -6,21 +6,21 @@ from lxml import etree except ImportError: import xml.etree.ElementTree as etree - + class Les(): ''' ''' def __init__(self): - pass - + pass + def les_get_generator(self,mw=False): ''' create generator of LexicalEntry elements (based on xml path in self.path_to_lus_els) - + @type mw: bool @param mw: default is False, multi-words will be ignored. - if set to True, multi-words will be returned. + if set to True, multi-words will be returned. @rtype: generator @return: generator of LexicalEntry XML elements @@ -37,11 +37,11 @@ def les_get_generator(self,mw=False): def les_find_le(self,le_identifier): ''' find lexical entry based on identifier - + @type le_identifier: str - @param le_identifier: lexical entry identifier + @param le_identifier: lexical entry identifier (for example havermout-n-1) - + @rtype: instance @return: if found, instance of class Le, else None ''' @@ -50,18 +50,18 @@ def les_find_le(self,le_identifier): return le_el else: return None - + def les_all_les_of_one_synset(self,synset_identifier): ''' given a synset identifier, return list of class instances of all les that belong to that synset (for example 'eng-30-00324560-v') - + @rtype: list @return: list of class instances of Class Le ''' return [le_el for le_el in self.les_get_generator() if le_el.get_synset_id() == synset_identifier] - + def les_add_le(self,lemma, long_pos, short_pos, @@ -73,44 +73,44 @@ def les_add_le(self,lemma, annotator=None): ''' add lexical entry - + WARNING: if lemma,pos already occurs in synset, le will not be added but the provenance will be changed - + WARNING: if sense_id already exists for this lemma, le will not be added - + if sense_id and le_sense_id are not provided, new ones will be created - + @type lemma: str @param lemma: lemma to be added (for example "leuningstoel") - + @type long_pos: str @param long_pos: noun | verb (perhaps "adjective" in the future") - + @type short_pos: str @param short_pos: n | v (perhaps "a" in the future") - + @type synset_identifier: str @param synset_identifier: (for example 'eng-30-00324560-v') - + @type provenances: list @param provenances: list of resources that were used to add this synonym (for example ['wikipedia','wiktionary']) - + @type definition: str @param definition: definition (default empty string) - + @type sense_id: str @param sense_id: [optional]: sense_id of le. will be created if not given. for example "1" - + @type le_sense_id: str @param le_sense_id: sense id of le. will be created if not provided - + @type annotator: str @param annotation: if there was any: name of annotator. (will be added to attr 'annotator' in child element 'Sense') - + @@ -124,18 +124,18 @@ def les_add_le(self,lemma, - + @rtype: tuple @return: (succes,message) ''' all_les_of_one_synset = [le_obj for le_obj in self.les_all_les_of_one_synset(synset_identifier)] - + #WARNING: if lemma,pos already occurs in synset, le will not be added #but provenance will be changed for le_obj in all_les_of_one_synset: - l,p = le_obj.get_lemma(),le_obj.get_pos() - + l,p = le_obj.get_lemma(),le_obj.get_pos() + #change provenance if (lemma,long_pos) == (l,p): provenance_tag = le_obj.get_provenance() @@ -143,24 +143,24 @@ def les_add_le(self,lemma, if provenance not in provenance_tag: provenance_tag += "+"+provenance le_obj.sense_el.attrib['provenance'] = provenance_tag - + #change annotator tag if needed if annotator is not None: annotator_tag = le_obj.get_annotator() if annotator not in annotator_tag: annotator_tag += '+'+annotator - le_obj.sense_el.attrib['annotator'] = annotator_tag - - + le_obj.sense_el.attrib['annotator'] = annotator_tag + + #WARNING:if sense_id already exists for this lemma,le will not be added sense_ids = [le_obj.get_sense_id() for le_obj in all_les_of_one_synset ] - + if all([sense_id is not None, sense_id in sense_ids]): return (False, "sense_id %s already in sense ids of synset" % sense_id) - + #create orbn_id if sense_id is None: sense_id = self.les_new_le_sense_id(short_pos) @@ -168,79 +168,79 @@ def les_add_le(self,lemma, #create lexical entry if sense_number is None: sense_number = self.lemma_highest_sense_number(lemma,pos=long_pos)+1 - + # le_att={'id': "{lemma}-{short_pos}-{sense_number}".format(**locals()), 'partOfSpeech': long_pos} - + # lemma_att = {'writtenForm': lemma} - - # sense_att = {'id': sense_id, 'senseId': str(sense_number), 'definition': definition, 'synset': synset_identifier, 'provenance': "+".join(provenances)} - + #add manual annotator info if annotator is not None: sense_att['annotator'] = annotator - + new_le_el = etree.Element("LexicalEntry",attrib=le_att) children = ['Lemma','WordForms','Morphology','MorphoSyntax','Sense'] sense_children = ["SenseRelations","Semantics-%s" % long_pos, "Pragmatics"] - + for element_name in children: attrib = {} - + if element_name == "Lemma": attrib = lemma_att elif element_name == "Sense": attrib = sense_att - + new_sub_el = etree.Element(element_name,attrib) if element_name == "Sense": for sense_child in sense_children: new_sub_sub_el = etree.Element(sense_child) new_sub_el.append(new_sub_sub_el) - - + + new_le_el.append(new_sub_el) - + self.lexicon_el.insert(0,new_le_el) self.orbn_ids[sense_id] = "" return (True,"") - + def les_remove_le(self,le_identifier): ''' method tries to remove a LexicalEntry. for example - + - + [questionable due to orbn]: renumber le's - + @type le_identifier: str - @param le_identifier: lexicalentry identifier, which is the value of + @param le_identifier: lexicalentry identifier, which is the value of the 'id' attribute of the LexicalEntry element ''' le_obj = self.les_find_le(le_identifier) - + if le_obj is not None: le_obj.remove_me() #sy_id = le_obj.get_synset_id() #all_les_of_sy_id = self.les_all_les_of_one_synset(sy_id) - + #TODO: decide if you want to remove empty synset #if len(all_les_of_sy_id) == 1: # self.synsets_remove_synset(sy_id,remove_les=False) - + def les_remove_a_resource(self,resource): ''' this method loop sover all LexicalEntry elements and checks @@ -251,36 +251,36 @@ def les_remove_a_resource(self,resource): resource -> resource is removed from tag ''' for le_obj in self.les_get_generator(): - + provenance_tag = le_obj.get_provenance() resources = provenance_tag.split("+") - + if resource in resources: #(2) if the tag is only the resource -> le is removed if len(resources) == 1: le_obj.remove_me() - + #(3) if the resource is in the resource, but not the only else: resources.remove(resource) provenance_tag = "+".join(resources) le_obj.sense_el.attrib['provenance'] = provenance_tag - + def les_new_le_sense_id(self,short_pos): ''' new le sense id. - + @type short_pos: str @param short_pos: n | v (perhaps "a" in the future") - + @rtype: str @return: new identifier (for example "o_n-106739250"). ''' while True: number = "".join([str(randint(0,9)) for x in range(9)]) candidate = "o_n-%s" % number - + if candidate not in self.orbn_ids: self.orbn_ids[candidate] = "" return candidate @@ -301,10 +301,14 @@ def les_load_synonyms_dicts(self): lemma = le_obj.get_lemma() synset_id = le_obj.get_synset_id() - if lemma is not None: + if lemma is not None and synset_id is not None: self.synset2lemmas[synset_id].add(lemma) self.lemma2synsets[lemma].add(synset_id) + # We don't want to keep them as defaultdicts + # we might create garbage synsets by accident. + self.synset2lemmas = dict(self.synset2lemmas) + self.lemma2synsets = dict(self.lemma2synsets) def les_lemma_synonyms(self, lemma): ''' @@ -323,4 +327,4 @@ def les_lemma_synonyms(self, lemma): for synset_id in self.lemma2synsets[lemma]: synonyms.update(self.synset2lemmas[synset_id]) - return synonyms \ No newline at end of file + return synonyms From cd1a543cca1b7d320bff20f4647babf1d38be8dd Mon Sep 17 00:00:00 2001 From: stephantul Date: Tue, 24 Oct 2017 13:58:58 +0100 Subject: [PATCH 2/5] Complete restructuring --- README.md | 8 +- annotate.sh | 1 - .../lib/opendutchwordnet/__init__.py | 12 +- .../lib/opendutchwordnet/base_concepts.py | 0 .../lib/opendutchwordnet/clean.py | 24 +- .../lib/opendutchwordnet/configuration.py | 0 .../lib/opendutchwordnet/ili_conversion.py | 4 +- le.py => build/lib/opendutchwordnet/le.py | 102 +- .../lib/opendutchwordnet/lemma.py | 20 +- les.py => build/lib/opendutchwordnet/les.py | 40 +- orbn.py => build/lib/opendutchwordnet/orbn.py | 12 +- .../lib/opendutchwordnet/orbn_inspection.py | 0 own.py => build/lib/opendutchwordnet/own.py | 0 .../lib/opendutchwordnet/relation.py | 20 +- .../lib/opendutchwordnet/stats.py | 319 +- .../lib/opendutchwordnet/synset.py | 166 +- .../lib/opendutchwordnet/synsets.py | 162 +- .../lib/opendutchwordnet/user_input.py | 20 +- .../lib/opendutchwordnet/user_input_test.py | 0 .../lib/opendutchwordnet/wn_grid_parser.py | 186 +- create_version_1_1.sh | 3 - create_version_1_2.sh | 4 - create_virtual_env.sh | 73 - dist/opendutchwordnet-1.3-py3.6.egg | Bin 0 -> 66819 bytes citation.bib => documentation/citation.bib | 0 .../gwc2016_odwn13.pdf | Bin {html => documentation/html}/api-objects.txt | 0 {html => documentation/html}/class-tree.html | 0 {html => documentation/html}/crarr.png | Bin {html => documentation/html}/epydoc.css | 0 {html => documentation/html}/epydoc.js | 0 {html => documentation/html}/frames.html | 0 {html => documentation/html}/help.html | 0 .../html}/identifier-index.html | 0 {html => documentation/html}/index.html | 0 {html => documentation/html}/module-tree.html | 0 .../html}/odwn.clean-module.html | 0 .../html}/odwn.clean-pysrc.html | 24 +- .../html}/odwn.clean.Clean-class.html | 0 .../html}/odwn.configuration-module.html | 0 .../html}/odwn.configuration-pysrc.html | 0 .../html}/odwn.le-module.html | 0 .../html}/odwn.le-pysrc.html | 44 +- .../html}/odwn.le.Le-class.html | 0 .../html}/odwn.lemma-module.html | 0 .../html}/odwn.lemma-pysrc.html | 20 +- .../html}/odwn.lemma.Lemma-class.html | 0 .../html}/odwn.les-module.html | 0 .../html}/odwn.les-pysrc.html | 32 +- .../html}/odwn.les.Les-class.html | 0 .../html}/odwn.orbn-module.html | 0 .../html}/odwn.orbn-pysrc.html | 12 +- .../html}/odwn.orbn.Orbn-class.html | 0 .../html}/odwn.relation-module.html | 0 .../html}/odwn.relation-pysrc.html | 20 +- .../html}/odwn.relation.Relation-class.html | 0 .../html}/odwn.stats-module.html | 0 .../html}/odwn.stats-pysrc.html | 72 +- .../html}/odwn.stats.Stats-class.html | 0 .../html}/odwn.synset-module.html | 0 .../html}/odwn.synset-pysrc.html | 38 +- .../html}/odwn.synset.Synset-class.html | 0 .../html}/odwn.synsets-module.html | 0 .../html}/odwn.synsets-pysrc.html | 32 +- .../html}/odwn.synsets.Synsets-class.html | 0 .../html}/odwn.user_input-module.html | 0 .../html}/odwn.user_input-pysrc.html | 20 +- .../html}/odwn.user_input.User-class.html | 0 .../html}/odwn.wn_grid_parser-module.html | 0 .../html}/odwn.wn_grid_parser-pysrc.html | 28 +- ...n.wn_grid_parser.Wn_grid_parser-class.html | 8 +- {html => documentation/html}/redirect.html | 0 .../html}/toc-everything.html | 0 .../html}/toc-odwn.clean-module.html | 0 .../html}/toc-odwn.configuration-module.html | 0 .../html}/toc-odwn.le-module.html | 0 .../html}/toc-odwn.lemma-module.html | 0 .../html}/toc-odwn.les-module.html | 0 .../html}/toc-odwn.orbn-module.html | 0 .../html}/toc-odwn.relation-module.html | 0 .../html}/toc-odwn.stats-module.html | 0 .../html}/toc-odwn.synset-module.html | 0 .../html}/toc-odwn.synsets-module.html | 0 .../html}/toc-odwn.user_input-module.html | 0 .../html}/toc-odwn.wn_grid_parser-module.html | 0 {html => documentation/html}/toc.html | 0 .../odwn_documentation.pdf | Bin .../slides_gwc2016_odwn13.pdf | Bin errors.txt | 1 - .../Ili conversion.ipynb | 4 +- experiment.ipynb => examples/experiment.ipynb | 0 install.sh | 3 - opendutchwordnet.egg-info/PKG-INFO | 14 + opendutchwordnet.egg-info/SOURCES.txt | 24 + .../dependency_links.txt | 1 + opendutchwordnet.egg-info/top_level.txt | 1 + opendutchwordnet.egg-info/zip-safe | 1 + opendutchwordnet/__init__.py | 32 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 885 bytes .../__pycache__/clean.cpython-36.pyc | Bin 0 -> 2365 bytes .../__pycache__/configuration.cpython-36.pyc | Bin 0 -> 306 bytes .../__pycache__/le.cpython-36.pyc | Bin 0 -> 4033 bytes .../__pycache__/lemma.cpython-36.pyc | Bin 0 -> 2718 bytes .../__pycache__/les.cpython-36.pyc | Bin 0 -> 9194 bytes .../__pycache__/lexicalentry.cpython-36.pyc | Bin 0 -> 3975 bytes .../__pycache__/orbn.cpython-36.pyc | Bin 0 -> 3261 bytes .../__pycache__/relation.cpython-36.pyc | Bin 0 -> 1518 bytes .../__pycache__/stats.cpython-36.pyc | Bin 0 -> 12931 bytes .../__pycache__/synset.cpython-36.pyc | Bin 0 -> 4693 bytes .../__pycache__/synsets.cpython-36.pyc | Bin 0 -> 6318 bytes .../__pycache__/user_input.cpython-36.pyc | Bin 0 -> 7477 bytes .../__pycache__/wn_grid_parser.cpython-36.pyc | Bin 0 -> 11134 bytes .../__pycache__/wordnetparser.cpython-36.pyc | Bin 0 -> 14485 bytes opendutchwordnet/base_concepts.py | 77 + opendutchwordnet/configuration.py | 3 + opendutchwordnet/export.py | 157 + opendutchwordnet/ili_conversion.py | 339 + opendutchwordnet/lexicalentry.py | 117 + opendutchwordnet/relation.py | 43 + .../resources}/5000_bc.xml | 0 .../resources}/5000_bc.zip | Bin .../adjectives/adjectives_monosemous.csv | 0 .../adjectives/adjectives_monosemous_sp.csv | 0 .../resources}/base_concepts_30.bin | Bin .../cili/Add new cili synsets.ipynb | 0 .../resources}/cili/Relations.ipynb | 0 .../resources}/cili/WN-LMF-1.0.dtd | 458 +- .../resources}/cili/new_synsets.csv | 0 .../resources}/cili/new_synsets.xlsx | Bin .../resources}/cili/new_synsets_v1.csv | 18 +- .../resources}/cili/new_synsets_v2.csv | 18 +- .../resources}/cili/odwn_cili.xml | 0 .../resources}/cili/reltypes_mapping.txt | 0 .../resources}/cili/starting_point.xml | 0 .../resources}/cili/the_starting_point.xml | 0 .../resources}/cili/the_starting_point2.xml | 0 .../resources}/cili/wrong_ones.txt | 0 .../resources}/cili/xml_formatting.ipynb | 0 .../resources}/empty_base_synsets.bin | Bin .../resources}/empty_base_synsets1.bin | Bin .../resources}/empty_base_synsets2.bin | Bin .../resources/empty_pwn_synsets.bin | Bin 0 -> 1790393 bytes .../resources}/ili-map-odwn13.ttl | 0 .../resources}/ili.nt.gz | Bin .../resources}/large_synsets.bin | Bin .../resources}/large_synsets1.bin | Bin .../resources}/large_synsets2.bin | Bin .../resources/leave_pwn_synsets.bin | Bin 0 -> 2023774 bytes .../resources}/odwn/odwn-orbn-lmf.dtd | 0 .../resources}/odwn/odwn-orbn-lmf.xsd | 0 .../resources}/odwn/odwn_orbn_gwg-LMF.xml.gz | Bin .../odwn/odwn_orbn_gwg-LMF_1.1.xml.gz | Bin .../odwn/odwn_orbn_gwg-LMF_1.2.xml.gz | Bin .../odwn/odwn_orbn_gwg-LMF_1.2.xml.log | 0 .../odwn/odwn_orbn_gwg-LMF_1.3.xml.gz | Bin .../resources}/odwn/orbn_1.0.xml.gz | Bin .../resources}/orbn.bin | Bin .../resources}/rbn.bin | Bin .../resources}/synonym_dict.bin | Bin opendutchwordnet/resources/synsets_5_10.bin | Bin 0 -> 41530 bytes .../resources}/tops.bin | Bin .../resources}/wn-google-nld.txt | 0 .../resources}/wn-omegawiki-nld.txt | 0 .../resources}/wn-wiktionary-nld.txt | 0 opendutchwordnet/stats.py | 476 + opendutchwordnet/synset.py | 141 + opendutchwordnet/wordnetparser.py | 513 + orbn.sh | 4 - resources/empty_pwn_synsets.bin | Bin 1790393 -> 0 bytes resources/leave_pwn_synsets.bin | Bin 2023774 -> 0 bytes resources/synsets_5_10.bin | Bin 41530 -> 0 bytes setup.py | 22 + synonyms.tsv | 28728 ---------------- user_input/Anneleen/synsets_5_10.bin | Bin 294332 -> 0 bytes user_input/anneleen/_sub_.bin | Bin 309922 -> 0 bytes ...2_Manual_monosemous_False_instances_50.bin | Bin 15491 -> 0 bytes ...b2.2_None_monosemous_True_instances_50.bin | Bin 5920 -> 0 bytes user_input/anneleen/empty_base_synsets1.bin | Bin 18775 -> 0 bytes ...megawiki_monosemous_False_instances_50.bin | Bin 9086 -> 0 bytes ...wikipedia_monosemous_True_instances_50.bin | Bin 8424 -> 0 bytes ...b2.2_Auto_monosemous_True_instances_50.bin | Bin 10065 -> 0 bytes ...2.2_None_monosemous_False_instances_50.bin | Bin 5740 -> 0 bytes user_input/marten/large_synsets1.bin | Bin 29167 -> 0 bytes ...omegawiki_monosemous_True_instances_50.bin | Bin 8691 -> 0 bytes ...ikipedia_monosemous_False_instances_50.bin | Bin 8984 -> 0 bytes ...iktionary_monosemous_True_instances_50.bin | Bin 9600 -> 0 bytes ...2.2_Auto_monosemous_False_instances_50.bin | Bin 10590 -> 0 bytes ....2_Manual_monosemous_True_instances_50.bin | Bin 11970 -> 0 bytes user_input/roxane/empty_base_synsets2.bin | Bin 18868 -> 0 bytes .../google_monosemous_False_instances_50.bin | Bin 7608 -> 0 bytes .../google_monosemous_True_instances_50.bin | Bin 8278 -> 0 bytes user_input/roxane/large_synsets.bin | Bin 11444 -> 0 bytes user_input/roxane/large_synsets2.bin | Bin 28248 -> 0 bytes ...ktionary_monosemous_False_instances_50.bin | Bin 9107 -> 0 bytes version1_1.py | 80 - version1_2.py | 371 - version1_3.py | 71 - 197 files changed, 2924 insertions(+), 30319 deletions(-) delete mode 100644 annotate.sh rename __init__.py => build/lib/opendutchwordnet/__init__.py (84%) rename base_concepts.py => build/lib/opendutchwordnet/base_concepts.py (100%) rename clean.py => build/lib/opendutchwordnet/clean.py (92%) rename configuration.py => build/lib/opendutchwordnet/configuration.py (100%) rename ili_conversion.py => build/lib/opendutchwordnet/ili_conversion.py (99%) rename le.py => build/lib/opendutchwordnet/le.py (79%) rename lemma.py => build/lib/opendutchwordnet/lemma.py (95%) rename les.py => build/lib/opendutchwordnet/les.py (97%) rename orbn.py => build/lib/opendutchwordnet/orbn.py (98%) rename orbn_inspection.py => build/lib/opendutchwordnet/orbn_inspection.py (100%) rename own.py => build/lib/opendutchwordnet/own.py (100%) rename relation.py => build/lib/opendutchwordnet/relation.py (88%) rename stats.py => build/lib/opendutchwordnet/stats.py (85%) rename synset.py => build/lib/opendutchwordnet/synset.py (56%) rename synsets.py => build/lib/opendutchwordnet/synsets.py (75%) rename user_input.py => build/lib/opendutchwordnet/user_input.py (98%) rename user_input_test.py => build/lib/opendutchwordnet/user_input_test.py (100%) rename wn_grid_parser.py => build/lib/opendutchwordnet/wn_grid_parser.py (90%) delete mode 100644 create_version_1_1.sh delete mode 100644 create_version_1_2.sh delete mode 100644 create_virtual_env.sh create mode 100644 dist/opendutchwordnet-1.3-py3.6.egg rename citation.bib => documentation/citation.bib (100%) rename gwc2016_odwn13.pdf => documentation/gwc2016_odwn13.pdf (100%) rename {html => documentation/html}/api-objects.txt (100%) rename {html => documentation/html}/class-tree.html (100%) rename {html => documentation/html}/crarr.png (100%) rename {html => documentation/html}/epydoc.css (100%) rename {html => documentation/html}/epydoc.js (100%) rename {html => documentation/html}/frames.html (100%) rename {html => documentation/html}/help.html (100%) rename {html => documentation/html}/identifier-index.html (100%) rename {html => documentation/html}/index.html (100%) rename {html => documentation/html}/module-tree.html (100%) rename {html => documentation/html}/odwn.clean-module.html (100%) rename {html => documentation/html}/odwn.clean-pysrc.html (97%) rename {html => documentation/html}/odwn.clean.Clean-class.html (100%) rename {html => documentation/html}/odwn.configuration-module.html (100%) rename {html => documentation/html}/odwn.configuration-pysrc.html (100%) rename {html => documentation/html}/odwn.le-module.html (100%) rename {html => documentation/html}/odwn.le-pysrc.html (95%) rename {html => documentation/html}/odwn.le.Le-class.html (100%) rename {html => documentation/html}/odwn.lemma-module.html (100%) rename {html => documentation/html}/odwn.lemma-pysrc.html (98%) rename {html => documentation/html}/odwn.lemma.Lemma-class.html (100%) rename {html => documentation/html}/odwn.les-module.html (100%) rename {html => documentation/html}/odwn.les-pysrc.html (98%) rename {html => documentation/html}/odwn.les.Les-class.html (100%) rename {html => documentation/html}/odwn.orbn-module.html (100%) rename {html => documentation/html}/odwn.orbn-pysrc.html (99%) rename {html => documentation/html}/odwn.orbn.Orbn-class.html (100%) rename {html => documentation/html}/odwn.relation-module.html (100%) rename {html => documentation/html}/odwn.relation-pysrc.html (95%) rename {html => documentation/html}/odwn.relation.Relation-class.html (100%) rename {html => documentation/html}/odwn.stats-module.html (100%) rename {html => documentation/html}/odwn.stats-pysrc.html (98%) rename {html => documentation/html}/odwn.stats.Stats-class.html (100%) rename {html => documentation/html}/odwn.synset-module.html (100%) rename {html => documentation/html}/odwn.synset-pysrc.html (96%) rename {html => documentation/html}/odwn.synset.Synset-class.html (100%) rename {html => documentation/html}/odwn.synsets-module.html (100%) rename {html => documentation/html}/odwn.synsets-pysrc.html (98%) rename {html => documentation/html}/odwn.synsets.Synsets-class.html (100%) rename {html => documentation/html}/odwn.user_input-module.html (100%) rename {html => documentation/html}/odwn.user_input-pysrc.html (99%) rename {html => documentation/html}/odwn.user_input.User-class.html (100%) rename {html => documentation/html}/odwn.wn_grid_parser-module.html (100%) rename {html => documentation/html}/odwn.wn_grid_parser-pysrc.html (98%) rename {html => documentation/html}/odwn.wn_grid_parser.Wn_grid_parser-class.html (99%) rename {html => documentation/html}/redirect.html (100%) rename {html => documentation/html}/toc-everything.html (100%) rename {html => documentation/html}/toc-odwn.clean-module.html (100%) rename {html => documentation/html}/toc-odwn.configuration-module.html (100%) rename {html => documentation/html}/toc-odwn.le-module.html (100%) rename {html => documentation/html}/toc-odwn.lemma-module.html (100%) rename {html => documentation/html}/toc-odwn.les-module.html (100%) rename {html => documentation/html}/toc-odwn.orbn-module.html (100%) rename {html => documentation/html}/toc-odwn.relation-module.html (100%) rename {html => documentation/html}/toc-odwn.stats-module.html (100%) rename {html => documentation/html}/toc-odwn.synset-module.html (100%) rename {html => documentation/html}/toc-odwn.synsets-module.html (100%) rename {html => documentation/html}/toc-odwn.user_input-module.html (100%) rename {html => documentation/html}/toc-odwn.wn_grid_parser-module.html (100%) rename {html => documentation/html}/toc.html (100%) rename odwn_documentation.pdf => documentation/odwn_documentation.pdf (100%) rename slides_gwc2016_odwn13.pdf => documentation/slides_gwc2016_odwn13.pdf (100%) delete mode 100644 errors.txt rename Ili conversion.ipynb => examples/Ili conversion.ipynb (99%) rename experiment.ipynb => examples/experiment.ipynb (100%) delete mode 100644 install.sh create mode 100644 opendutchwordnet.egg-info/PKG-INFO create mode 100644 opendutchwordnet.egg-info/SOURCES.txt create mode 100644 opendutchwordnet.egg-info/dependency_links.txt create mode 100644 opendutchwordnet.egg-info/top_level.txt create mode 100644 opendutchwordnet.egg-info/zip-safe create mode 100644 opendutchwordnet/__init__.py create mode 100644 opendutchwordnet/__pycache__/__init__.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/clean.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/configuration.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/le.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/lemma.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/les.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/lexicalentry.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/orbn.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/relation.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/stats.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/synset.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/synsets.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/user_input.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/wn_grid_parser.cpython-36.pyc create mode 100644 opendutchwordnet/__pycache__/wordnetparser.cpython-36.pyc create mode 100644 opendutchwordnet/base_concepts.py create mode 100644 opendutchwordnet/configuration.py create mode 100644 opendutchwordnet/export.py create mode 100644 opendutchwordnet/ili_conversion.py create mode 100644 opendutchwordnet/lexicalentry.py create mode 100644 opendutchwordnet/relation.py rename {resources => opendutchwordnet/resources}/5000_bc.xml (100%) rename {resources => opendutchwordnet/resources}/5000_bc.zip (100%) rename {resources => opendutchwordnet/resources}/adjectives/adjectives_monosemous.csv (100%) rename {resources => opendutchwordnet/resources}/adjectives/adjectives_monosemous_sp.csv (100%) rename {resources => opendutchwordnet/resources}/base_concepts_30.bin (100%) rename {resources => opendutchwordnet/resources}/cili/Add new cili synsets.ipynb (100%) rename {resources => opendutchwordnet/resources}/cili/Relations.ipynb (100%) rename {resources => opendutchwordnet/resources}/cili/WN-LMF-1.0.dtd (97%) rename {resources => opendutchwordnet/resources}/cili/new_synsets.csv (100%) rename {resources => opendutchwordnet/resources}/cili/new_synsets.xlsx (100%) rename {resources => opendutchwordnet/resources}/cili/new_synsets_v1.csv (99%) rename {resources => opendutchwordnet/resources}/cili/new_synsets_v2.csv (99%) rename {resources => opendutchwordnet/resources}/cili/odwn_cili.xml (100%) rename {resources => opendutchwordnet/resources}/cili/reltypes_mapping.txt (100%) rename {resources => opendutchwordnet/resources}/cili/starting_point.xml (100%) rename {resources => opendutchwordnet/resources}/cili/the_starting_point.xml (100%) rename {resources => opendutchwordnet/resources}/cili/the_starting_point2.xml (100%) rename {resources => opendutchwordnet/resources}/cili/wrong_ones.txt (100%) rename {resources => opendutchwordnet/resources}/cili/xml_formatting.ipynb (100%) rename {resources => opendutchwordnet/resources}/empty_base_synsets.bin (100%) rename {resources => opendutchwordnet/resources}/empty_base_synsets1.bin (100%) rename {resources => opendutchwordnet/resources}/empty_base_synsets2.bin (100%) create mode 100644 opendutchwordnet/resources/empty_pwn_synsets.bin rename {resources => opendutchwordnet/resources}/ili-map-odwn13.ttl (100%) rename {resources => opendutchwordnet/resources}/ili.nt.gz (100%) rename {resources => opendutchwordnet/resources}/large_synsets.bin (100%) rename {resources => opendutchwordnet/resources}/large_synsets1.bin (100%) rename {resources => opendutchwordnet/resources}/large_synsets2.bin (100%) create mode 100644 opendutchwordnet/resources/leave_pwn_synsets.bin rename {resources => opendutchwordnet/resources}/odwn/odwn-orbn-lmf.dtd (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn-orbn-lmf.xsd (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn_orbn_gwg-LMF.xml.gz (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn_orbn_gwg-LMF_1.1.xml.gz (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn_orbn_gwg-LMF_1.2.xml.gz (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn_orbn_gwg-LMF_1.2.xml.log (100%) rename {resources => opendutchwordnet/resources}/odwn/odwn_orbn_gwg-LMF_1.3.xml.gz (100%) rename {resources => opendutchwordnet/resources}/odwn/orbn_1.0.xml.gz (100%) rename {resources => opendutchwordnet/resources}/orbn.bin (100%) rename {resources => opendutchwordnet/resources}/rbn.bin (100%) rename {resources => opendutchwordnet/resources}/synonym_dict.bin (100%) create mode 100644 opendutchwordnet/resources/synsets_5_10.bin rename {resources => opendutchwordnet/resources}/tops.bin (100%) rename {resources => opendutchwordnet/resources}/wn-google-nld.txt (100%) rename {resources => opendutchwordnet/resources}/wn-omegawiki-nld.txt (100%) rename {resources => opendutchwordnet/resources}/wn-wiktionary-nld.txt (100%) create mode 100644 opendutchwordnet/stats.py create mode 100644 opendutchwordnet/synset.py create mode 100644 opendutchwordnet/wordnetparser.py delete mode 100644 orbn.sh delete mode 100644 resources/empty_pwn_synsets.bin delete mode 100644 resources/leave_pwn_synsets.bin delete mode 100644 resources/synsets_5_10.bin create mode 100644 setup.py delete mode 100644 synonyms.tsv delete mode 100644 user_input/Anneleen/synsets_5_10.bin delete mode 100644 user_input/anneleen/_sub_.bin delete mode 100644 user_input/anneleen/cdb2.2_Manual_monosemous_False_instances_50.bin delete mode 100644 user_input/anneleen/cdb2.2_None_monosemous_True_instances_50.bin delete mode 100644 user_input/anneleen/empty_base_synsets1.bin delete mode 100644 user_input/anneleen/omegawiki_monosemous_False_instances_50.bin delete mode 100644 user_input/anneleen/wikipedia_monosemous_True_instances_50.bin delete mode 100644 user_input/marten/cdb2.2_Auto_monosemous_True_instances_50.bin delete mode 100644 user_input/marten/cdb2.2_None_monosemous_False_instances_50.bin delete mode 100644 user_input/marten/large_synsets1.bin delete mode 100644 user_input/marten/omegawiki_monosemous_True_instances_50.bin delete mode 100644 user_input/marten/wikipedia_monosemous_False_instances_50.bin delete mode 100644 user_input/marten/wiktionary_monosemous_True_instances_50.bin delete mode 100644 user_input/roxane/cdb2.2_Auto_monosemous_False_instances_50.bin delete mode 100644 user_input/roxane/cdb2.2_Manual_monosemous_True_instances_50.bin delete mode 100644 user_input/roxane/empty_base_synsets2.bin delete mode 100644 user_input/roxane/google_monosemous_False_instances_50.bin delete mode 100644 user_input/roxane/google_monosemous_True_instances_50.bin delete mode 100644 user_input/roxane/large_synsets.bin delete mode 100644 user_input/roxane/large_synsets2.bin delete mode 100644 user_input/roxane/wiktionary_monosemous_False_instances_50.bin delete mode 100644 version1_1.py delete mode 100644 version1_2.py delete mode 100644 version1_3.py diff --git a/README.md b/README.md index 7246a09..c9b207c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#Global WordNet Grid LMF parser +# Global WordNet Grid LMF parser This repo provides a python module to work with Open Dutch WordNet. Please first check the [Issues](https://github.com/MartenPostma/OpenDutchWordnet/issues) to see if your question has already @@ -20,11 +20,11 @@ If you make use of the resource and/or this repository, please cite the followin } ## Demo -A demo for word similarity using Open Dutch WordNet can be found [here](http://130.37.53.15:5000/). +A demo for word similarity using Open Dutch WordNet can be found [here](http://130.37.53.15:5000/). In the background, this uses the [WordNetTools](https://github.com/cltl/WordnetTools/). We encourage to use the module locally when you need to run for many word pairs. -##USAGE AND INSTALL +## USAGE AND INSTALL git clone this repository. The python module 'lxml' is needed. Hopefully, 'pip install lxml' @@ -80,7 +80,7 @@ python 'eng-30-00322847-v' ``` -##Contact +## Contact * Marten Postma * m.c.postma@vu.nl * http://martenpostma.com/ diff --git a/annotate.sh b/annotate.sh deleted file mode 100644 index 6447281..0000000 --- a/annotate.sh +++ /dev/null @@ -1 +0,0 @@ -python user_input_test.py diff --git a/__init__.py b/build/lib/opendutchwordnet/__init__.py similarity index 84% rename from __init__.py rename to build/lib/opendutchwordnet/__init__.py index 131daf3..c796f4a 100644 --- a/__init__.py +++ b/build/lib/opendutchwordnet/__init__.py @@ -1,4 +1,4 @@ -import os +import os import sys import subprocess @@ -12,9 +12,9 @@ #try: # cmd_output = subprocess.check_output(source_command,shell=True) #except subprocess.CalledProcessError: -# print ('''please first run 'bash install.sh' from the command line \ -#from inside the module and try again''') - +# print ("""please first run 'bash install.sh' from the command line \ +#from inside the module and try again""") + from wn_grid_parser import Wn_grid_parser #documentation attributes @@ -34,8 +34,8 @@ 'resources', 'odwn', 'odwn-orbn-lmf.dtd') -Wn_grid_parser.README = open(os.path.join(cwd,"README.md")).read() -Wn_grid_parser.LICENSE = open(os.path.join(cwd,"LICENSE.md")).read() +Wn_grid_parser.README = open(os.path.join(cwd,"../README.md")).read() +Wn_grid_parser.LICENSE = open(os.path.join(cwd,"../LICENSE.md")).read() Wn_grid_parser.__author__ = "Marten Postma" Wn_grid_parser.__license__ = "CC-BY-SA 4.0" Wn_grid_parser.__version__ = "1.3" diff --git a/base_concepts.py b/build/lib/opendutchwordnet/base_concepts.py similarity index 100% rename from base_concepts.py rename to build/lib/opendutchwordnet/base_concepts.py diff --git a/clean.py b/build/lib/opendutchwordnet/clean.py similarity index 92% rename from clean.py rename to build/lib/opendutchwordnet/clean.py index 73c5b75..700e460 100644 --- a/clean.py +++ b/build/lib/opendutchwordnet/clean.py @@ -2,17 +2,17 @@ class Clean(): - ''' + """ method to clean resource - ''' + """ def __init__(self): pass def clean_impossible_relations(self): - ''' + """ all relations in self.stats['impossible_rels'] are removed - ''' + """ self.get_stats() for rel_el in self.stats['impossible_rels']: rel_el.remove_me() @@ -21,10 +21,10 @@ def clean_impossible_relations(self): print(len(self.stats['impossible_rels'])) def clean_bidirectional_relations(self): - ''' + """ all proposed relations in self.stats['bidirectional_relations'] are added - ''' + """ self.get_stats() for source,target,reltype in self.stats['bidirectional_relations']: sy_obj = self.synsets_find_synset(source) @@ -38,10 +38,10 @@ def clean_bidirectional_relations(self): def clean_provenance_to_all_les(self): - ''' + """ some LexicalEntry elements do not have a provenance tag. this method adds the "cdb2.2_Auto" tag as provenance - ''' + """ default = "cdb2.2_Auto" added = 0 for le_obj in self.les_get_generator(): @@ -56,12 +56,12 @@ def clean_provenance_to_all_les(self): print(added) def clean_remove_synsets_without_relations(self,list_of_synsets): - ''' - ''' + """ + """ pass def clean_synsets_without_synonyms(self): - ''' - ''' + """ + """ pass \ No newline at end of file diff --git a/configuration.py b/build/lib/opendutchwordnet/configuration.py similarity index 100% rename from configuration.py rename to build/lib/opendutchwordnet/configuration.py diff --git a/ili_conversion.py b/build/lib/opendutchwordnet/ili_conversion.py similarity index 99% rename from ili_conversion.py rename to build/lib/opendutchwordnet/ili_conversion.py index c725a5b..daaa298 100644 --- a/ili_conversion.py +++ b/build/lib/opendutchwordnet/ili_conversion.py @@ -121,14 +121,14 @@ odwn_ids[odwn_id] = ilidef def validate(dtd_path, loaded_xml): - ''' + """ validate against dtd :param str dtd_path: full path to dtd :rtype: tuple :return: (succes,message) - ''' + """ f = open(dtd_path) dtd = etree.DTD(f) message = "" diff --git a/le.py b/build/lib/opendutchwordnet/le.py similarity index 79% rename from le.py rename to build/lib/opendutchwordnet/le.py index 402b5fd..5ee4c33 100644 --- a/le.py +++ b/build/lib/opendutchwordnet/le.py @@ -1,121 +1,121 @@ class Le(): - ''' + """ class from XML element LexicalEntry - - .. highlight:: + + .. highlight:: - @@ -228,7 +228,7 @@ def les_remove_le(self,le_identifier): @type le_identifier: str @param le_identifier: lexicalentry identifier, which is the value of the 'id' attribute of the LexicalEntry element - ''' + """ le_obj = self.les_find_le(le_identifier) if le_obj is not None: @@ -242,14 +242,14 @@ def les_remove_le(self,le_identifier): # self.synsets_remove_synset(sy_id,remove_les=False) def les_remove_a_resource(self,resource): - ''' + """ this method loop sover all LexicalEntry elements and checks the provenance tag of each LexicalEntry and: (1) if the tag does not contain the resource -> nothing happens (2) if the tag is only the resource -> le is removed (3) if the resource is in the resource, but not the only resource -> resource is removed from tag - ''' + """ for le_obj in self.les_get_generator(): provenance_tag = le_obj.get_provenance() @@ -268,7 +268,7 @@ def les_remove_a_resource(self,resource): le_obj.sense_el.attrib['provenance'] = provenance_tag def les_new_le_sense_id(self,short_pos): - ''' + """ new le sense id. @type short_pos: str @@ -276,7 +276,7 @@ def les_new_le_sense_id(self,short_pos): @rtype: str @return: new identifier (for example "o_n-106739250"). - ''' + """ while True: number = "".join([str(randint(0,9)) for x in range(9)]) candidate = "o_n-%s" % number @@ -287,12 +287,12 @@ def les_new_le_sense_id(self,short_pos): def les_load_synonyms_dicts(self): - ''' + """ load dicts to obtain synonyms of lemma :rtype: dict :return: mapping from lemma to set of synonyms - ''' + """ self.synset2lemmas = defaultdict(set) self.lemma2synsets = defaultdict(set) @@ -311,14 +311,14 @@ def les_load_synonyms_dicts(self): self.lemma2synsets = dict(self.lemma2synsets) def les_lemma_synonyms(self, lemma): - ''' + """ return the synonyms of a lemma :param str lemma: a lemma (for example 'paard') :rtype: set :return: set of synonyms of the lemma according to odwn - ''' + """ if not all([hasattr(self, 'synset2lemmas'), hasattr(self, 'lemma2synsets')]): self.les_load_synonyms_dicts() diff --git a/orbn.py b/build/lib/opendutchwordnet/orbn.py similarity index 98% rename from orbn.py rename to build/lib/opendutchwordnet/orbn.py index 42524b8..11b52e4 100644 --- a/orbn.py +++ b/build/lib/opendutchwordnet/orbn.py @@ -3,15 +3,15 @@ import os class Orbn(): - ''' - ''' + """ + """ def __init__(self): pass def orbn_definition_dict(self,path_orbn,set_of_orbn_ids=[]): - ''' + """ given a path to the orbn xml file this method will return a mapping from c_lu_id -> 'definition' -> definition @@ -28,7 +28,7 @@ def orbn_definition_dict(self,path_orbn,set_of_orbn_ids=[]): 'definition' -> definition 'examples' -> list of examples 'lemma' -> lemma - ''' + """ data = {} doc = etree.parse(path_orbn) @@ -66,11 +66,11 @@ def orbn_definition_dict(self,path_orbn,set_of_orbn_ids=[]): return data def orbn_search(self,load=True): - ''' + """ this is an interactive method that allows to enter a lemma and return the orbn ids that are not yet in open source dutch wordnet. - ''' + """ path_rbn_bin = os.path.join(self.cwd,'resources','rbn.bin') if load: le_ids_in_odwn,rbn_data = pickle.load(open(path_rbn_bin,'rb')) diff --git a/orbn_inspection.py b/build/lib/opendutchwordnet/orbn_inspection.py similarity index 100% rename from orbn_inspection.py rename to build/lib/opendutchwordnet/orbn_inspection.py diff --git a/own.py b/build/lib/opendutchwordnet/own.py similarity index 100% rename from own.py rename to build/lib/opendutchwordnet/own.py diff --git a/relation.py b/build/lib/opendutchwordnet/relation.py similarity index 88% rename from relation.py rename to build/lib/opendutchwordnet/relation.py index 1ced1f0..7c4e386 100644 --- a/relation.py +++ b/build/lib/opendutchwordnet/relation.py @@ -1,43 +1,43 @@ class Relation(): - ''' + """ class for XML element Synset - ''' + """ def __init__(self,relation_el): self.relation_el = relation_el def get_provenance(self): - ''' + """ return value provenance attribute @rtype: str @return: provenance tag - ''' + """ return self.relation_el.get("provenance") def get_reltype(self): - ''' + """ return value relType attribute @rtype: str @return: relType tag - ''' + """ return self.relation_el.get("relType") def get_target(self): - ''' + """ return value target attribute @rtype: str @return: synset target of relation - ''' + """ return self.relation_el.get("target") def remove_me(self): - ''' + """ remove relation element - ''' + """ self.relation_el.getparent().remove(self.relation_el) diff --git a/stats.py b/build/lib/opendutchwordnet/stats.py similarity index 85% rename from stats.py rename to build/lib/opendutchwordnet/stats.py index 4b37fff..1382fb5 100644 --- a/stats.py +++ b/build/lib/opendutchwordnet/stats.py @@ -1,124 +1,124 @@ -from collections import defaultdict +from collections import defaultdict from lxml import etree -import random +import random import os import pickle class Stats(): - ''' + """ generate stats about resources - ''' - def __init__(self): - pass - + """ + def __init__(self): + pass + def stats_num_synsets(self): - ''' + """ return number of synsets (length of self.syn_ids) - + @rtype: int @return: number of synsets - ''' + """ return len(self.syn_ids) - + def stats_num_les(self): - ''' + """ return number of lexical entries - + @rtype: int @return: number of lexical entries - ''' - num_les = 0 + """ + num_les = 0 for le_el in self.les_get_generator(): num_les += 1 - + return num_les - + def stats_rels(self): - ''' + """ return number of synset relations - + @rtype: int @return: number of synset relations - ''' - num_rels = 0 - impossible = [] - + """ + num_rels = 0 + impossible = [] + for sy_obj in self.synsets_get_generator(): existing = [] - + for rel_obj in sy_obj.get_all_relations(): - num_rels +=1 - - source = sy_obj.get_id() - reltype = rel_obj.get_reltype() - target = rel_obj.get_target() - succes,message = self.validate_relation(source,reltype,target) - if succes: - if (source,reltype,target) in existing: + num_rels += 1 + + source = sy_obj.get_id() + reltype = rel_obj.get_reltype() + target = rel_obj.get_target() + try: + self.validate_relation(source, reltype, target) + if (source, reltype, target) in existing: impossible.append(rel_obj) else: - existing.append((source,reltype,target)) - else: + existing.append((source, reltype, target)) + except ValueError: impossible.append(rel_obj) - - return num_rels,impossible - + + return num_rels, impossible + def empty_lemmas(self): - ''' + """ return number of synoynyms that contain an empty lemma - + @rtype: int @return: number of synonym with an empty lemma - ''' + """ empty_lemmas = 0 - + for le_obj in self.les_get_generator(): lemma = le_obj.get_lemma() if not lemma: empty_lemmas += 1 - + return empty_lemmas - + def tops(self): - ''' + """ return number of tops (synsets without hypernym relation) - + @rtype: int @return: number of synsets without hypernyms - ''' + """ tops = [] for sy_obj in self.synsets_get_generator(): hyperonyms = sy_obj.get_relations('has_hyperonym') if not hyperonyms: tops.append(sy_obj.get_id()) - + return tops def stats_empty_synsets(self): - ''' + """ method to obtains the following stats: (1) number of empty synsets (no synonyms) (2) number of empty odwn synsets (+ of which leave nodes) (3) number of empty pwn synsets (+ of which leave nodes) - + @rtype: tuple @return: (num_empty_synsets, num_empty_odwn_synsets, num_empty_pwn_synsets, empty_odwn_leave_synsets) - ''' + """ non_empty_synsets = set() for le_obj in self.les_get_generator(): target = le_obj.get_synset_id() non_empty_synsets.update([target]) - + empty_synsets = set(self.syn_ids.keys()) - non_empty_synsets - + empty_odwn_synsets = [sy_id for sy_id in empty_synsets if sy_id.startswith("odwn")] empty_pwn_synsets = [sy_id for sy_id in empty_synsets if sy_id.startswith("eng")] - - leave_empty_odwn_synsets = set() + + leave_empty_odwn_synsets = set() leave_pwn_synsets = set() for sy_obj in self.synsets_get_generator(): @@ -143,101 +143,101 @@ def stats_empty_synsets(self): 'leave_empty_odwn_synsets' : leave_empty_odwn_synsets} def no_gloss(self): - ''' - return number of + """ + return number of synsets without gloss + number of empty glosses + number of one word glosses - + @rtype: tuple @return: sy_no_gloss,empty_glosses,one_word - ''' + """ sy_no_gloss = 0 empty_glosses = 0 one_word = 0 - + for sy_obj in self.synsets_get_generator(): - + glosses = sy_obj.get_glosses() - + if not glosses: sy_no_gloss += 1 - + for gloss in glosses: if not gloss: empty_glosses += 1 - + split = gloss.split() if len(split) == 1: one_word += 1 - - + + return sy_no_gloss,empty_glosses,one_word - - + + def count_pos(self): - ''' + """ return number of nouns and verbs - + @rtype: tuple @return: list of tuples (pos,count) - ''' + """ pos = defaultdict(int) - + for sy_obj in self.synsets_get_generator(): - + part_of_speech = sy_obj.get_pos() pos[part_of_speech] += 1 - + return [(key,value) for key,value in sorted(pos.items())] - - + + def resources_check(self): - ''' + """ count provenances - + @rtype: dict @return: list of tuples (resource,count) - ''' + """ resources_dict = defaultdict(int) - + for le_el in self.les_get_generator(): - + resource_tag = le_el.get_provenance() - + if resource_tag is None: resources_dict['None'] += 1 - + else: resources = resource_tag.split("+") for resource in resources: resources_dict[resource] += 1 - + return [(key,value) for key,value in sorted(resources_dict.items())] - + def missing_bidirectional_relations(self,rel1,rel2): - ''' + """ given two relations that imply each other, for example has_hyponym and has_hyperonym, this method returns the relations to add - + @type rel1: str @param rel1: relation, for example 'has_hyponym' - + @type rel2: str @param rel2: relation, for example 'has_hyperonym' - + @rtype: list @return: list of 3-tuples (source synset, target synset, relation) - ''' + """ rels = {} - + for sy_obj in self.synsets_get_generator(): source_synset = sy_obj.get_id() - + for rel_el in sy_obj.get_all_relations(): reltype = rel_el.get_reltype() - + if reltype in [rel1,rel2]: target_synset = rel_el.get_target() rel = (source_synset,target_synset,reltype) @@ -246,34 +246,34 @@ def missing_bidirectional_relations(self,rel1,rel2): rels[key] = defaultdict(list) rels[key]['reltypes'].append(reltype) rels[key]['rels'].append(rel) - + rels_to_add = [] goal = set([rel1,rel2]) for value in rels.values(): if set(value['reltypes']) != goal: - + source,target,reltype = value['rels'][0] miss_reltypes = goal.difference(set(value['reltypes'])) - for rel_to_add in miss_reltypes: + for rel_to_add in miss_reltypes: rels_to_add.append((target,source,rel_to_add)) - - + + return rels_to_add - - + + def polysemy_dict(self): - ''' + """ return polysemy dict mapping polysemy to list of lemmas - + @rtype: dict @return: mapping polysemy to list of lemmas - ''' + """ polysemy_dict = defaultdict(list) - + for lemma,polysemy in self.lemmas_generator().items(): polysemy_dict[polysemy].append(lemma) - + total = 0.0 instance = 0.0 for polysemy,list_lemmas in polysemy_dict.items(): @@ -282,60 +282,60 @@ def polysemy_dict(self): average_polysemy = round(total/instance,1) return average_polysemy,polysemy_dict - - - - + + + + def sy_no_rels(self): - ''' + """ find synsets without relations - + @rtype: set @return: set of synset identifiers - ''' + """ no_rels = set() - + for sy_obj in self.synsets_get_generator(): - + if not any([sy_obj.get_relations("has_hyponym"), sy_obj.get_relations("has_hyperonym")]): no_rels.update([sy_obj.get_id()]) - + return no_rels - + def contradicting_rels(self): - ''' + """ method to identify different reltypes linking the same source and target - + @rtype: dict @return: mapping (source,target) -> list of rel_obj - ''' + """ contradicting = defaultdict(list) - + for sy_obj in self.synsets_get_generator(): source = sy_obj.get_id() - + for rel_obj in sy_obj.get_all_relations(): - + reltype = rel_obj.get_reltype() target = rel_obj.get_target() contradicting[(source,target)].append(reltype) - - keys_to_remove = [key + + keys_to_remove = [key for key,value in contradicting.items() if len(value) <= 1] - + for key in keys_to_remove: del contradicting[key] - + return contradicting - + def stats_large_synsets(self): - ''' + """ creates a dict mapping synset identifier to find large synsets - ''' + """ freq = {} for le_obj in self.les_get_generator(): @@ -358,17 +358,17 @@ def stats_large_synsets(self): not value['annotated'], key not in large_synsets]): large_synsets.append(key) - + #print(len(large_synsets)) - + with open( os.path.join(self.cwd,'resources','synsets_%s_%s.bin' % (minimum,maximum)),'wb') as outfile: pickle.dump(large_synsets,outfile) def stats_evaluate_resources(self): - ''' + """ - ''' + """ rootDir = os.path.join(self.cwd,'user_input') for dirName, subdirList, fileList in os.walk(rootDir): for fname in fileList: @@ -376,17 +376,17 @@ def stats_evaluate_resources(self): user_bin = os.path.join(dirName,fname) evaluation,d = pickle.load(open(user_bin,'rb')) print(fname,evaluation) - - + + def random_le_and_sy(self): - ''' - ''' + """ + """ from nltk.corpus import wordnet as wn - + start_at = random.choice( range( len(self.orbn_ids))) - + for counter,le_obj in enumerate(self.les_get_generator()): - + if counter >= start_at: print() print(etree.tostring(le_obj.le_el, @@ -405,10 +405,10 @@ def random_le_and_sy(self): input('continue?') def stats_plot_depth(self,output_path): - ''' - ''' + """ + """ import matplotlib.pyplot as plt - + depth = {} keep_going = True cur_depth = 1 @@ -417,42 +417,42 @@ def stats_plot_depth(self,output_path): if not top.endswith('-a')} non_empty_synsets = {le_obj.get_synset_id() for le_obj in self.les_get_generator()} - + current_layer = {sy_obj for sy_obj in self.synsets_get_generator() if sy_obj.get_id() in tops} - + while keep_going: - + next_layer_ids = set() total += len(current_layer) print(cur_depth,len(current_layer),total) for sy_obj in current_layer: sy_id = sy_obj.get_id() - + depth[sy_id] = cur_depth - + for relation_el in sy_obj.get_relations('has_hyponym'): rel_target = relation_el.get_target() if rel_target not in depth: next_layer_ids.add(rel_target) - + current_layer = {sy_obj for sy_obj in self.synsets_get_generator() if sy_obj.get_id() in next_layer_ids} - + cur_depth +=1 - + if not current_layer: keep_going = False - + y = range(1,cur_depth) x1 = [] x2 = [] depths = [depth_value for depth_value in set(depth.values())] - + for depth_value in sorted(depths): - + empty = [] full = [] for key,value in depth.items(): @@ -462,19 +462,19 @@ def stats_plot_depth(self,output_path): else: empty.append(key) total = len(empty) + len(full) - + perc_empty = 0.0 if empty: perc_empty = 100 * (len(empty)/total) perc_full = 0.0 if full: perc_full = 100 * (len(full)/total) - + x1.append(perc_empty) x2.append(perc_full) - + states = [str(depth_value) for depth_value in depths] - + fig, axes = plt.subplots(ncols=2, sharey=True) axes[0].barh(y, x1, align='center', color='red') axes[0].set(title='% of synsets without synonyms') @@ -484,10 +484,9 @@ def stats_plot_depth(self,output_path): axes[0].set(yticks=y, yticklabels=states) axes[0].yaxis.tick_right() - + plt.xlim(0,100) - + fig.tight_layout() fig.subplots_adjust(wspace=0.09) plt.savefig(output_path) - diff --git a/synset.py b/build/lib/opendutchwordnet/synset.py similarity index 56% rename from synset.py rename to build/lib/opendutchwordnet/synset.py index ecc8bf1..86c0b0e 100644 --- a/synset.py +++ b/build/lib/opendutchwordnet/synset.py @@ -5,11 +5,12 @@ from lxml import etree except ImportError: import xml.etree.ElementTree as etree - + + class Synset(): - ''' + """ class for XML element Synset - + example of Synset element: @@ -18,134 +19,127 @@ class for XML element Synset - ''' - def __init__(self,synset_el,reltypes,syn_ids): - - self.synset_el = synset_el - self.reltypes = reltypes - self.syn_ids = syn_ids - - self.defs_els = self.synset_el.find("Definitions") - self.refs_el = self.synset_el.find("SynsetRelations") - - def get_id(self): - ''' + + """ + + def __init__(self, synset_el, reltypes, syn_ids): + + self.synset_el = synset_el + self.reltypes = reltypes + self.syn_ids = syn_ids + + self.defs_els = self.synset_el.find("Definitions") + self.refs_el = self.synset_el.find("SynsetRelations") + + def id(self): + """ return synset identifier by returning attribute "id" - + @rtype: str @return: synset identifier - ''' + """ return self.synset_el.get("id") - - def get_ili(self): - ''' + + def ili(self): + """ return gwg ili by returning value of attribute "ili" - + @rtype: str @return: global wordnet grid ili - ''' + """ return self.synset_el.get("ili") - - def get_glosses(self,languages=['en','nl']): - ''' + + def glosses(self, languages=('en','nl')): + """ get gloss of synset by returning value of attribute "gloss" of child "Definition" - + @rtype: generator @return: generator of synset definitions - ''' + """ if self.defs_els is not None: - return [def_el.get('gloss') + return (def_el.get('gloss') for def_el in self.defs_els.iterfind("Definition") - if def_el.get('language') in languages] + if def_el.get('language') in languages) else: - return [] - - def get_all_relations(self): - ''' + return () + + def all_relations(self): + """ return list of instances of class Relation - + @rtype: generator @return: generator of instances of class Relation - ''' - path_to_rels="SynsetRelations/SynsetRelation" + """ + path_to_rels = "SynsetRelations/SynsetRelation" for relation_el in self.synset_el.iterfind(path_to_rels): yield Relation(relation_el) - - def get_pos(self): - ''' - return pos (last charachter of ili) - + + def pos(self): + """ + return pos (last charachter of id) + @rtype: str @return: pos - ''' - return self.get_id()[-1] - - def get_relations(self,reltype): - ''' + """ + return self.id()[-1] + + def relations(self, reltype): + """ return list of instance of class Relations that match the relation type - + @type reltype: str @param: reltype: relation type (most typical are has_hyperonym and has_hyponym - + @rtype: list @return: list of instances of class Relation - ''' - xml_query='''SynsetRelations/SynsetRelation[@relType="%s"]''' % reltype + """ + xml_query="""SynsetRelations/SynsetRelation[@relType="{}"]""".format(reltype) return [Relation(relation_el) for relation_el in self.synset_el.iterfind(xml_query)] - - def remove_me(self): - ''' + + def remove(self): + """ remove synset element - ''' + """ self.synset_el.getparent().remove(self.synset_el) - + def add_relation(self,reltype,target): - ''' + """ add a SynsetRelation - + @type reltype: str @param reltype: type of relation - + @type target: str @param target: target synset - + @rtype: tuple @return: (succes, message) - - ''' - source = self.get_id() - succes,message = self.validate_relation(source,reltype,target) - - if not succes: - return (succes,message) - - existing_rels = [(rel_el.get_reltype(),rel_el.get_target()) - for rel_el in self.get_all_relations()] - - if (reltype,target) in existing_rels: - return (False,"relation already exists") - - - #add SynsetRelations element if it does not exists + + """ + source = self.id() + self.validate_relation(source, reltype, target) + + existing_rels = [(rel_el.get_reltype(), rel_el.get_target()) + for rel_el in self.all_relations()] + + if (reltype, target) in existing_rels: + raise ValueError("relation already exists") + + # add SynsetRelations element if it does not exists if self.refs_el is None: new_refs_el = etree.SubElement(self.synset_el, "SynsetRelations") self.synset_el.append(new_refs_el) - - #add SynsetRelations element - new_rel_el = etree.SubElement(self.synset_el, + + # add SynsetRelations element + new_rel_el = etree.SubElement(self.synset_el, "SynsetRelation", - {'provenance': 'odwn', - 'relType' : reltype, - 'target' : target}) + {'provenance': 'odwn', + 'relType': reltype, + 'target': target}) self.refs_el.append(new_rel_el) - - return (True,"") - - - \ No newline at end of file diff --git a/synsets.py b/build/lib/opendutchwordnet/synsets.py similarity index 75% rename from synsets.py rename to build/lib/opendutchwordnet/synsets.py index dd23138..d341d78 100644 --- a/synsets.py +++ b/build/lib/opendutchwordnet/synsets.py @@ -1,49 +1,49 @@ from synset import Synset from collections import defaultdict -import os -from lxml import etree +import os +from lxml import etree import gzip class Synsets(): - ''' + """ class to inspect and modify synsets objects - ''' + """ def __init__(self): pass - + def synsets_get_generator(self): - ''' + """ create generator of Synset elements (based on xml path in self.path_to_synset_els) - + @rtype: generator @return: generator of Synset XML elements - ''' + """ for synset_el in self.doc.iterfind(self.path_to_synset_els): yield Synset(synset_el, self.reltypes, - self.syn_ids) - - + self.syn_ids) + + def synsets_find_synset(self,synset_identifier): - ''' + """ find synset based on identifier - + @type synset_identifier: str - @param synset_identifier: synset identifier + @param synset_identifier: synset identifier (for example eng-30-00325085-v) - + @rtype: instance @return: if found, instance of class Synset, else None - ''' + """ for synset_el in self.synsets_get_generator(): if synset_el.get_id() == synset_identifier: return synset_el else: return None - + def synsets_get_definition_dict(self): - ''' + """ this method loops over all Synset elements and creates a dict mapping from sy_id -> 'definition' -> definition @@ -51,7 +51,7 @@ def synsets_get_definition_dict(self): @rtype: dict @return: mapping from sy_id -> 'definition' -> definition - ''' + """ synset_info = defaultdict(dict) for sy_obj in self.synsets_get_generator(): sy_id = sy_obj.get_id() @@ -60,141 +60,134 @@ def synsets_get_definition_dict(self): - def synsets_add_synset(self, sy_id, synset_provenance, definition, rels): - ''' - synset is added if it has a hypernym relation to an existing + """ + synset is added if it has a hypernym relation to an existing synset. - + WARNING not added if: (1) sy_id already exists (2) no succesful hypernym relation added (except for adjectives) - + @type sy_id: str @param sy_id: synset identifier - + @type synset_provenance: str @param synset_provenance: origin english synset: 'pwn', else 'odwn' - + @type definition: str @param definition: definition - + @type rels: list @param rels: list of tuples (reltype,target) - + @return: tuple @returun: (succes,message) - ''' + """ if not hasattr(self, 'ili_dict'): - ili_nt_path = os.path.join(self.cwd,'resources','ili.nt.gz') + ili_nt_path = os.path.join(self.cwd, 'resources', 'ili.nt.gz') infile = gzip.GzipFile(ili_nt_path) self.set_ili_dict(infile) - - #get ili + + # get ili if sy_id not in self.ili_dict: - return (False,'no ili identifier found for %s' % sy_id) - - ili = self.ili_dict[sy_id] - - #check if sy_id already exists + raise ValueError('no ili identifier found for {}'.format(sy_id)) + + ili = self.ili_dict[sy_id] + + # check if sy_id already exists if sy_id in self.syn_ids: - return (False,'synset exists already: %s' % sy_id) - + raise ValueError('synset exists already: {}'.format(sy_id)) + added_hypernym_rel = False - - base = ''' + + base = """ -'''.format(**locals()) +""".format(**locals()) synset_el = etree.fromstring(base) - - sy_obj = Synset(synset_el,self.reltypes,self.syn_ids) - - for reltype,target in rels: - succes,message = sy_obj.add_relation(reltype,target) - if all([reltype == 'has_hyperonym', - succes]): + + sy_obj = Synset(synset_el, self.reltypes, self.syn_ids) + + for reltype, target in rels: + sy_obj.add_relation(reltype, target) + if reltype == 'has_hyperonym': added_hypernym_rel = True - - if any([added_hypernym_rel, - sy_id.endswith('a')]): + + if added_hypernym_rel or sy_id.endswith('a'): self.lexicon_el.append(sy_obj.synset_el) - return (True,'succes') - else: - return (False,'no hypernym rel added') - - + def synsets_remove_synset(self,sy_identifier,remove_les=True,synset_el=None): - ''' + """ (1) if removes_les: all lexical entries are removed from this synset (2) if no hyponyms: remove synset (3) if no hyponyms: remove all relations pointing to this synset - + @type sy_identifier: str @param sy_identifier: synset identifier (i.e. eng-30-89405202-n) - + @type remove_les: bool @param remove_les: remove les in synset (default is True) @type synset_el: None | etree.Element @param synset_el: default is None, odwn will be searched for element. else, an Synset instance has to be provided - ''' - #find synset + """ + # find synset if synset_el is None: synset_el = self.synsets_find_synset(sy_identifier) - + if synset_el is not None: - - #if wanted, remove les + + # if wanted, remove les if remove_les: - [le_obj.remove_me() + [le_obj.remove_me() for le_obj in self.les_all_les_of_one_synset(sy_identifier)] - - #remove synset if a leaf node + + # remove synset if a leaf node hyponyms = synset_el.get_relations("has_hyponym") if not hyponyms: synset_el.remove_me() del self.syn_ids[sy_identifier] - - #remove all relations to this synset + + # remove all relations to this synset for sy_obj in self.synsets_get_generator(): for rel_obj in sy_obj.get_all_relations(): target = rel_obj.get_target() - + if target == sy_identifier: rel_obj.remove_me() - + def validate_relation(self,source,reltype,target): - ''' + """ this method check if a relation is valid or not. invalid if: (1) target does not exist (2) source does not exist - (3) cross pos for has_hyponym has_hyperonym + (3) cross pos for has_hyponym has_hyperonym (4) source == target (5) reltype not in existing list of reltypes (6) relation exists already - + @type source: str @param source: source synset identifier - + @type reltype: str @param reltype: relation type - + @type target: str @param target: target synset identifier - + @rtype: tuple @return: (succes,message) - ''' + """ #(1) target does not exist if target not in self.syn_ids: return (False,"target: %s not in existing synsets" % target) @@ -203,24 +196,21 @@ def validate_relation(self,source,reltype,target): elif source not in self.syn_ids: return (False,"source: %s not in existing synsets" % source) - #(3) cross pos for has_hyponym has_hyperonym + #(3) cross pos for has_hyponym has_hyperonym elif all([source[-1] != target[-1], reltype in ['has_hyperonym','has_hyponym'] ]): return (False, "reltype: %s can not have cross pos relations" % reltype) - + #(4) source == target elif source == target: return (False,"source is same as target") - + #(5) reltype not in existing list of reltypes elif reltype not in self.reltypes: return (False, 'reltype: %s not in list of reltypes' % reltype) - + else: return (True,"") - - - diff --git a/user_input.py b/build/lib/opendutchwordnet/user_input.py similarity index 98% rename from user_input.py rename to build/lib/opendutchwordnet/user_input.py index e5c4a20..1e0b6a3 100644 --- a/user_input.py +++ b/build/lib/opendutchwordnet/user_input.py @@ -5,10 +5,10 @@ from lxml import etree class User(): - ''' + """ - ''' + """ def __init__(self): pass @@ -40,10 +40,10 @@ def annotate(self): self.stats_synset_inspection() def evaluate_resource(self): - ''' + """ randomly pick x examples and use user input to evaluate if they are correct - ''' + """ resource = input('which resource do you want to evaluate?: ') num_instances = input('how many instances do you want to evaluate?: ') answer = input('monosemous (m) or polysemous (p)?: ') @@ -110,10 +110,10 @@ def evaluate_resource(self): print('saved at %s' % output_path) def resolve_sub_les(self): - ''' + """ there are LexicalEntry elements in the resource with _sub_ in it. this method provides a way to choose which one is correct - ''' + """ annotation = {} synsets = set() set_of_orbn_ids = set() @@ -184,16 +184,16 @@ def resolve_sub_les(self): def lemma_inspection(self,min_polysemy=1): - ''' - ''' + """ + """ pass def stats_synset_inspection(self): - ''' + """ @type task: str @ivar task: empty_base_synsets | tops | large_synsets - ''' + """ output_path = os.path.join(self.out_folder,self.task+'.bin') synsets = pickle.load( open( os.path.join(self.cwd,'resources',self.task+'.bin'),'rb') ) if None in synsets: diff --git a/user_input_test.py b/build/lib/opendutchwordnet/user_input_test.py similarity index 100% rename from user_input_test.py rename to build/lib/opendutchwordnet/user_input_test.py diff --git a/wn_grid_parser.py b/build/lib/opendutchwordnet/wn_grid_parser.py similarity index 90% rename from wn_grid_parser.py rename to build/lib/opendutchwordnet/wn_grid_parser.py index e83cfe8..6a16ff1 100644 --- a/wn_grid_parser.py +++ b/build/lib/opendutchwordnet/wn_grid_parser.py @@ -1,8 +1,8 @@ #import built-in modules -import os +import os import pickle -import gzip -import subprocess +import gzip +import subprocess from collections import defaultdict #import xml parser (lxml is preferred, else built-in module xml is used) @@ -28,21 +28,21 @@ class Wn_grid_parser(Synsets, Clean, User, Orbn): - ''' + """ Parser for Global WordNet Grid LMF (inspection, stats, editing) - + @type path_wn_grid_lmf: str @param path_wn_grid_lmf: path to wn grid lmf file @ivar path_wn_grid_lmf: str @param path_wn_grid_lmf: path to wn grid lmf file - + @ivar doc: lxml.etree._ElementTree @param doc: param path_wn_grid_lmf parsed with etree.parse - + >>> path="resources/odwn/odwn_orbn_gwg-LMF_1.2.xml.gz" >>> instance = Wn_grid_parser(path_wn_grid_lmf=path) - + >>> le_el = instance.les_find_le("havenplaats-n-1") >>> le_el.get_id() 'havenplaats-n-1' @@ -56,7 +56,7 @@ class Wn_grid_parser(Synsets, 'cdb2.2_Auto' >>> le_el.get_synset_id() 'eng-30-08633957-n' - + >>> synset_el = instance.synsets_find_synset('eng-30-00324560-v') >>> synset_el.get_id() 'eng-30-00324560-v' @@ -69,85 +69,85 @@ class Wn_grid_parser(Synsets, 'has_hyperonym' >>> relation_el.get_target() 'eng-30-00322847-v' - + >>> instance.lemma_num_senses("huis",pos="noun") 6 - ''' + """ def __init__(self,path_wn_grid_lmf=None): self.path_wn_grid_lmf = path_wn_grid_lmf - + #read xml file and set general variables self.initialize() - + def initialize(self): - ''' + """ (1) parse ivar path_wn_grid_lmf into ivar doc (2) set general class attributes - ''' + """ infile = gzip.GzipFile(self.path_wn_grid_lmf) self.doc = etree.parse(infile,etree.XMLParser(remove_blank_text=True)) self.lexicon_el = self.doc.find("Lexicon") self.reltypes = {} self.syn_ids = {} self.cwd = os.path.dirname(os.path.realpath(__file__)) - + #make xml paths class attributes [setattr(self, key, value) for key,value in xml_paths.items()] - + #set of synset identifiers self.syn_ids = {sy_el.get_id():0 for sy_el in self.synsets_get_generator()} - + #relations self.reltypes = {rel_obj.get_reltype(): "" for sy_obj in self.synsets_get_generator() for rel_obj in sy_obj.get_all_relations()} - + self.orbn_ids = {le_obj.get_sense_id(): "" for le_obj in self.les_get_generator()} - + def validate(self,dtd_path): - ''' + """ validate against dtd - + @type dtd_path: str @param dtd_path: full path to dtd - + @rtype: tuple @return: (succes,message) - ''' + """ f = open(dtd_path) dtd = etree.DTD(f) message = "" - + succes = dtd.validate(self.doc) if not succes: message = dtd.error_log.filter_from_errors()[0] - + return (succes,message) - + def export(self,output_path,format='lmf'): - ''' + """ export resource to file. self.doc is first validated against dtd. if this fails, export will not be done - + @type output_path: str @param output_path: output path - + @type format: str - @param format: default is 'lmf', - + @param format: default is 'lmf', + others include: 'omw', which is the Open Multilingual Wordnet format (http://compling.hss.ntu.edu.sg/omw/). 'ili': mapping between pwn and odwn in rdf The output will be stored in the 'resources' folder - ''' + """ self.clean() - + #validate it validation,message = self.validate(self.dtd) - + if validation: if format == 'lmf': with open(output_path,"wb") as outfile: @@ -157,34 +157,34 @@ def export(self,output_path,format='lmf'): encoding='utf-8') elif format == 'omw': self.omw_export() - + elif format == 'ili': self.ili_map_export() - + else: print("dtd validation was not succesful.") print(message) - - + + def ili_map_export(self): - ''' + """ creates export file in resources/ili-map-odwnVERSION.ttl based on the original English one at: https://raw.githubusercontent.com/globalwordnet/ili/master/ili-map.ttl - ''' + """ version = self.__version__.replace('.','') output_path = os.path.join(self.cwd, 'resources', 'ili-map-odwn%s.ttl' % version) - + synonyms_dict = defaultdict(set) for le_obj in self.les_get_generator(): synset_id = le_obj.get_synset_id() lemma = le_obj.get_lemma() synonyms_dict[synset_id].add(lemma) - + with open(output_path,'w') as outfile: - + outfile.write('\n') outfile.write('@prefix\towl:\t .\n') outfile.write('\n') @@ -205,34 +205,34 @@ def ili_map_export(self): synonyms = ', '.join(synonyms_dict[synset_id]) outline = 'ili:{ili}\towl:sameAs\todwn13:{offset_pos} . # {synonyms}\n'.format(**locals()) outfile.write(outline) - + def omw_export(self): - ''' + """ this method performs the following steps: (1) creates new folder in resources: resources/nld (2) copies LICENSE in it (3) copies reference in it (4) creates wn-data-nld.tab - ''' + """ cwd = self.cwd out = os.path.join(self.cwd,'resources','nld') - + #(1) creates new folder in resources: resources/nld command = 'rm -rf {out} && mkdir {out}'.format(**locals()) subprocess.call(command,shell=True) - + #(2) copies LICENSE in it command = 'cp {cwd}/LICENSE.md {out}/LICENSE'.format(**locals()) subprocess.call(command,shell=True) - + #(3) copies reference in it command = 'cp {cwd}/citation.bib {out}/'.format(**locals()) subprocess.call(command,shell=True) - + #(4) creates wn-data-nld.tab output_path = os.path.join(out,'wn-data-nld.tab') with open(output_path,'w') as outfile: - + #write header header = '\t'.join([ '# Open Dutch WordNet', @@ -241,36 +241,36 @@ def omw_export(self): 'CC BY SA 4.0']) outfile.write(header+'\n') for le_obj in self.les_get_generator(): - + synset_id = le_obj.get_synset_id() lemma = le_obj.get_lemma() if not synset_id: continue prov,version,offset,pos = synset_id.split('-') - + if all([prov == 'eng', lemma]): output = '{offset}-{pos}\tnld:lemma\t{lemma}\n'.format(**locals()) outfile.write(output) - - - - + + + + def get_stats(self,verbose=False): - ''' + """ return most important stats into dict - + @type verbose: bool @param verbose: [optional]. if set to True, general stats are send to stdout. - ''' + """ Stats.__init__(self) - + num_rels,none_targets = self.stats_rels() tops = self.tops() with open( os.path.join(self.cwd,'resources','tops.bin'),'wb') as outfile: pickle.dump(tops,outfile) - empty_synsets = self.stats_empty_synsets() + empty_synsets = self.stats_empty_synsets() average_polysemy, polysemy_dict = self.polysemy_dict() self.stats_large_synsets() @@ -293,7 +293,7 @@ def get_stats(self,verbose=False): 'contradicting' : self.contradicting_rels() } - + if verbose: print('general stats for input file:') print(os.path.basename(self.path_wn_grid_lmf)) @@ -301,29 +301,29 @@ def get_stats(self,verbose=False): if key in ["bidirectional_relations","polysemy_dict",'empty_leave_odwn_synsets', "impossible_rels","tops","no_rels","contradicting"]: - print(key,len(value)) - else: + print(key,len(value)) + else: print(key,value) - + def set_ili_dict(self,file_object): - ''' + """ given the path to mapping from ili to eng-30 synset identifiers this method returns the mapping itself - - - - + + + + . - + @type file_object: str @param file_object: file object or ili.nt.gz file containing mapping from ili to eng-30 synset identifiers - + @rtype: dict @return: mapping eng-30 synset identifier -> ili - ''' + """ self.ili_dict = {} - + for line in file_object: if 'http://www.w3.org/2002/07/owl#sameAs>' in line: s,r,o,e = line.strip().split() @@ -333,49 +333,49 @@ def set_ili_dict(self,file_object): self.ili_dict[eng] = ili def clean(self): - ''' + """ clean resource - ''' + """ self.clean_provenance_to_all_les() self.clean_impossible_relations() self.clean_bidirectional_relations() def load_synonyms_dicts(self): - ''' + """ load dicts to obtain synonyms of lemma - + :rtype: dict :return: mapping from lemma to set of synonyms - ''' + """ self.synset2lemmas = defaultdict(set) self.lemma2synsets = defaultdict(set) - + for le_obj in self.les_get_generator(): - + lemma = le_obj.get_lemma() synset_id = le_obj.get_synset_id() - + if lemma is not None: self.synset2lemmas[synset_id].add(lemma) self.lemma2synsets[lemma].add(synset_id) - + def lemma_synonyms(self,lemma): - ''' - return the synonyms of a lemma - + """ + return the synonyms of a lemma + :param str lemma: a lemma (for example 'paard') - + :rtype: set :return: set of synonyms of the lemma according to odwn - ''' + """ if not all([hasattr(self,'synset2lemmas'), hasattr(self,'lemma2synsets')]): self.load_synonyms_dicts() - - + + synonyms = set() for synset_id in self.lemma2synsets[lemma]: synonyms.update(self.synset2lemmas[synset_id]) - + return synonyms diff --git a/create_version_1_1.sh b/create_version_1_1.sh deleted file mode 100644 index 09618d0..0000000 --- a/create_version_1_1.sh +++ /dev/null @@ -1,3 +0,0 @@ -cp version1_1.py ../ -cd .. -python version1_1.py \ No newline at end of file diff --git a/create_version_1_2.sh b/create_version_1_2.sh deleted file mode 100644 index 5998e70..0000000 --- a/create_version_1_2.sh +++ /dev/null @@ -1,4 +0,0 @@ -cp version1_2.py ../ -cd .. -python version1_2.py - diff --git a/create_virtual_env.sh b/create_virtual_env.sh deleted file mode 100644 index 979c267..0000000 --- a/create_virtual_env.sh +++ /dev/null @@ -1,73 +0,0 @@ -#check if enough arguments are passed, else print usage information -if [ $# -eq 0 ]; -then - echo - echo "This script is meant to help check if the correct versions of python and virtualenv are installed." - echo "It will perform checks for this and will try to install external python modules with pip." - echo "If there is any error in one of these steps, the script will exit." - echo - echo "Usage : $0 python_version wanted_virtual_env_version vir_env_dir ext_modules" - echo - echo "python_version : python version (major.minor for example 3.4)" - echo "vir_env_dir : full path (not just name of folder) to virtual environment directory (will be created)" - echo "ext_modules : path to file in which each line contains a module_name (pip install module_name is run)" - exit -1 -fi - -#rename user input to logical variable names -cwd=${PWD#*} -log=$cwd/log -rm -rf $log && mkdir $log - -python_version=$1 -vir_env_dir=$2 -ext_modules=$3 - -function command_check () { - -RETVAL=$? -[ $RETVAL -eq 0 ] && echo $succes -[ $RETVAL -ne 0 ] && echo $failure && echo 'exiting...' && exit -1 - -} -#check if python version is installed -echo -echo "Checking python version" -export succes="Succes: python$python_version is installed" -export failure="Fail: please install python version $python_version" - -python$python_version -c "exit()" -command_check - -#create virtualenv and echo source command to stdout -echo -echo "Creating virtual environment" -virtualenv --python=python$python_version --system-site-packages $vir_env_dir -echo -echo "to activate: source $vir_env_dir/bin/activate" -echo -echo "activating virtualenv" -source $vir_env_dir/bin/activate - -#install external python modules -echo -echo "Installing external python modules" - -while read p -do - export succes="Succes: succesfully installed module $p" - export failure="Failure: error in installing module $p, please inspect $log/$p.log or $log/$p.log for the error log" - pip install $p > $log/$p.log 2> $log/$p.err - command_check - -done < $ext_modules -echo -echo "#############################################################" -echo "it seems that the virtual environment was succesfully created" -echo -echo "virtual environment directory can be found here:" -echo "$vir_env_dir" -echo -echo "to activate run:" -echo "source $vir_env_dir/bin/activate" -echo "to not have to do this everything, add the above command to files like ~/.bash_profile (files that are run on login)" diff --git a/dist/opendutchwordnet-1.3-py3.6.egg b/dist/opendutchwordnet-1.3-py3.6.egg new file mode 100644 index 0000000000000000000000000000000000000000..4165f86d7d2e78e6115e8dc1da5dfd843692f179 GIT binary patch literal 66819 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 rZktq8hXQOr7SZlG{=b9p-jwR{us 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 literal 0 HcmV?d00001 diff --git a/citation.bib b/documentation/citation.bib similarity index 100% rename from citation.bib rename to documentation/citation.bib diff --git a/gwc2016_odwn13.pdf b/documentation/gwc2016_odwn13.pdf similarity index 100% rename from gwc2016_odwn13.pdf rename to documentation/gwc2016_odwn13.pdf diff --git a/html/api-objects.txt b/documentation/html/api-objects.txt similarity index 100% rename from html/api-objects.txt rename to documentation/html/api-objects.txt diff --git a/html/class-tree.html b/documentation/html/class-tree.html similarity index 100% rename from html/class-tree.html rename to documentation/html/class-tree.html diff --git a/html/crarr.png b/documentation/html/crarr.png similarity index 100% rename from html/crarr.png rename to documentation/html/crarr.png diff --git a/html/epydoc.css b/documentation/html/epydoc.css similarity index 100% rename from html/epydoc.css rename to documentation/html/epydoc.css diff --git a/html/epydoc.js b/documentation/html/epydoc.js similarity index 100% rename from html/epydoc.js rename to documentation/html/epydoc.js diff --git a/html/frames.html b/documentation/html/frames.html similarity index 100% rename from html/frames.html rename to documentation/html/frames.html diff --git a/html/help.html b/documentation/html/help.html similarity index 100% rename from html/help.html rename to documentation/html/help.html diff --git a/html/identifier-index.html b/documentation/html/identifier-index.html similarity index 100% rename from html/identifier-index.html rename to documentation/html/identifier-index.html diff --git a/html/index.html b/documentation/html/index.html similarity index 100% rename from html/index.html rename to documentation/html/index.html diff --git a/html/module-tree.html b/documentation/html/module-tree.html similarity index 100% rename from html/module-tree.html rename to documentation/html/module-tree.html diff --git a/html/odwn.clean-module.html b/documentation/html/odwn.clean-module.html similarity index 100% rename from html/odwn.clean-module.html rename to documentation/html/odwn.clean-module.html diff --git a/html/odwn.clean-pysrc.html b/documentation/html/odwn.clean-pysrc.html similarity index 97% rename from html/odwn.clean-pysrc.html rename to documentation/html/odwn.clean-pysrc.html index 92fcc80..893e236 100644 --- a/html/odwn.clean-pysrc.html +++ b/documentation/html/odwn.clean-pysrc.html @@ -57,17 +57,17 @@

Source Code for Module odwn. 2 3
4 -class Clean(): -
5 ''' +
5 """ 6 method to clean resource - 7 ''' + 7 """
8 - def __init__(self):
9 pass
10
12 ''' +
12 """ 13 all relations in 14 self.stats['impossible_rels'] are removed -15 ''' +15 """ 16 self.get_stats() 17 for rel_el in self.stats['impossible_rels']: 18 rel_el.Source Code for Module odwn. 21 print(len(self.stats['impossible_rels']))
22
24 ''' +
24 """ 25 all proposed relations in self.stats['bidirectional_relations'] 26 are added -27 ''' +27 """ 28 self.get_stats() 29 for source,target,reltype in self.stats['bidirectional_relations']: 30 sy_obj = self.synsets_find_synset(source) @@ -95,10 +95,10 @@

Source Code for Module odwn.

38 39
41 ''' +
41 """ 42 some LexicalEntry elements do not have a provenance tag. 43 this method adds the "cdb2.2_Auto" tag as provenance -44 ''' +44 """ 45 default = "cdb2.2_Auto" 46 added = 0 47 for le_obj in self.les_get_generator(): @@ -114,13 +114,13 @@

Source Code for Module odwn. 56 print(added)

57
58 - def clean_remove_synsets_without_relations(self,list_of_synsets): -
59 ''' -60 ''' +
59 """ +60 """ 61 pass
62
64 ''' -65 ''' +
64 """ +65 """ 66 pass
67