def tokenMatchesNomAdvVinf( self, token, verb, vinf): ''' Teeb kindlaks, kas etteantud token v6iks olla verb'i alluv ning vinf'i ylemus (st paikneda nende vahel). Kui see nii on, tagastab j2rjendi vahele sobiva s6na morf analyysidega (meetodi _getMatchingAnalysisIDs abil), vastasel juhul tagastab tyhja j2rjendi; ''' if verb in self.verbRules: for (nounAdv, vinf1) in self.verbRules[verb]: if vinf == vinf1 and (self.nomAdvWordTemplates[nounAdv]).matches(token): return _getMatchingAnalysisIDs( token, self.nomAdvWordTemplates[nounAdv] ) return []
def tokenMatchesNomAdvVinf(self, token, verb, vinf): ''' Teeb kindlaks, kas etteantud token v6iks olla verb'i alluv ning vinf'i ylemus (st paikneda nende vahel). Kui see nii on, tagastab j2rjendi vahele sobiva s6na morf analyysidega (meetodi _getMatchingAnalysisIDs abil), vastasel juhul tagastab tyhja j2rjendi; ''' if verb in self.verbRules: for (nounAdv, vinf1) in self.verbRules[verb]: if vinf == vinf1 and ( self.nomAdvWordTemplates[nounAdv]).matches(token): return _getMatchingAnalysisIDs( token, self.nomAdvWordTemplates[nounAdv]) return []
def extendChainsInClause(self, clause, clauseID, foundChains): ''' Proovime etteantud osalauses leiduvaid verbiahelaid täiendada 'verb-nom/adv-vinf' rektsiooniseostega, nt: andma + võimalus + Vda : talle anti_0 võimalus_0 olukorda parandada_0 olema + vaja + Vda : nüüd on_0 küll vaja_0 asi lõpetada_0 Teeme seda kahel moel: 1) kui mingi olemasoleva verbiahela keskelt on puudu 'nom/adv' (nt 'andma', 'jätma' verbide vinf rektsiooniseoste leidmisel võib tekkida selliseid lünki), siis lisame ahela keskele 'nom/adv' sõna. 2) kui verbiahela lõpus on verb, mis on sageli ülemuseks 'nom/adv' sõnale, millest omakorda sõltub mingi Vinf verb (Vma, Vda), ning need on osalausekontekstis olemas, lisame need verbiahela lõppu; ''' expansionPerformed = False # J22dvustame s6nad, mis kuuluvad juba mingi tuvastatud verbifraasi koosseisu annotatedWords = [] for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX] and (len(verbObj[PATTERN])==1 and \ re.match('^(ei|ära|ega)$', verbObj[PATTERN][0])): # V2lja j22vad yksikuna esinevad ei/ära/ega, kuna need tõenäoliselt ei sega continue annotatedWords.extend(verbObj[PHRASE]) widToToken = {token[WORD_ID]: token for token in clause} verbDaMa = WordTemplate({POSTAG: 'V', FORM: '^(da|ma)$'}) verbOle = WordTemplate({ROOT: '^ole$', POSTAG: 'V'}) # # 1) Yritame leida, millised juba tuvastatud verbiahelatest on sellised, kust on # vahelt puudu nom/adv s6na, nt: # annab_0 kunstnik jälle põhjuse endast kirjutada_0 # see annab_0 võimaluse laua tagant tõusta_0 ja_0 minema jalutada_0 # Kui leiame ahelast puuduoleva s6na ja lisame selle ahelasse ... # for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX]: headVerb = '' headVerbWID = -1 dependentVerb = '' dependentVerbWIDs = [] firstDependentVerbID = -1 # Leiame ahela l6pust ylemus-verbi ja sellele alluva verbi if len(verbObj[PATTERN]) > 3 and verbObj[PATTERN][-2] == '&': headVerb = verbObj[ROOTS][-4] + " " + verbObj[POLARITY] dependentVerb = verbObj[MORPH][-3] headVerbWID = verbObj[PHRASE][-4] dependentVerbWIDs.append(verbObj[PHRASE][-3]) dependentVerbWIDs.append(verbObj[PHRASE][-1]) firstDependentVerbID = len(verbObj[PHRASE]) - 3 elif len(verbObj[PATTERN] ) > 1 and verbObj[PATTERN][-2] == 'verb': headVerb = verbObj[ROOTS][-2] + " " + verbObj[POLARITY] dependentVerb = verbObj[MORPH][-1] headVerbWID = verbObj[PHRASE][-2] dependentVerbWIDs.append(verbObj[PHRASE][-1]) firstDependentVerbID = len(verbObj[PHRASE]) - 1 # Kontrollime, kas ylemusverb ja sellele alluv verb v6iksid olla yhendatud # mingi nom/adv s6na kaudu if headVerb in self.verbRules and headVerb in self.verbToVinf and \ dependentVerb in self.verbToVinf[headVerb]: # Teeme kindlaks, kas s6nade vahele j22b puuduolev nom/adv minInd = min(min(dependentVerbWIDs), headVerbWID - 1) maxInd = max(max(dependentVerbWIDs) - 1, headVerbWID) if minInd < maxInd: for i in range(minInd, maxInd + 1): if i in widToToken and i not in annotatedWords: token = widToToken[i] matchingAnalyses = self.tokenMatchesNomAdvVinf( token, headVerb, dependentVerb) if matchingAnalyses and not expansionPerformed: # Kontrollime, kas vaheletorgatav sõna paikneb nii, et see on suure # tõenäosusega üksiksõna, mitte fraas. if self._isLikelyNotPhrase( headVerb, headVerbWID, token[WORD_ID], widToToken): # Torkame nimis6na/adverbi vahele verbObj[PHRASE].insert( firstDependentVerbID, token[WORD_ID]) verbObj[PATTERN].insert( firstDependentVerbID, 'nom/adv') verbObj[ANALYSIS_IDS].insert( firstDependentVerbID, matchingAnalyses) annotatedWords.append(token[WORD_ID]) expansionPerformed = True else: # Kui me ei saa olla kindlad, et vaheletorgatav sõna pole fraas, paneme # küsimärgi, näitamaks, et verbiahelast on suure tõenäosusega midagi # puudu ... verbObj[OTHER_VERBS] = True #_debugPrint( ' '+('+'.join(verbObj[PATTERN]))+' | '+_getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) # # 2) Yritame luua uusi ahelaid, laiendades verbe olemasolevate ahelate l6pus: # # millega antakse_0 võimalus_1 sõlmida_1 uus kokkulepe . # puudub_0 võimalus_1 spetsialiste täistööajaga rakendada_1 . # kui on_0 võimalus_1 rahulikult vooluga kaasa minna_1 # clauseMaxWID = max(list(widToToken.keys())) for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX] and verbObj[OTHER_VERBS]: if (len(verbObj[PATTERN])==1 or (len(verbObj[PATTERN])>1 and \ verbObj[PATTERN][-2] != '&')): headVerb = verbObj[ROOTS][-1] + " " + verbObj[POLARITY] headVerbWID = verbObj[PHRASE][-1] # # 2.1) Esimeses l2henduses vaatame tavalisi verbe (mitte-olema); # if headVerb in self.verbRules and not headVerb.startswith( 'ole '): minInd = headVerbWID - 1 if verbObj[PATTERN][ 0] != 'ega' else headVerbWID suitableNomAdvExpansions = [] expansionVerbs = [] for i in range(minInd, clauseMaxWID + 1): if i in widToToken and i not in annotatedWords: token = widToToken[i] if _isFollowedByComma(i, clause): # Katkestame, kui satume koma otsa (kuna ei saa kindel olla, # et teisel pool koma on olevad jupid kuuluvad ikka verbi # juurde) break if verbDaMa.matches(token): analysisIDs = _getMatchingAnalysisIDs( token, verbDaMa) form = token[ANALYSIS][ analysisIDs[0]][FORM] expansionVerbs.append( [i, token, "V_" + form]) else: for (nounAdv, vinf1) in self.verbRules[headVerb]: if (self.nomAdvWordTemplates[nounAdv] ).matches(token): suitableNomAdvExpansions.append( [i, token, vinf1, \ (self.nomAdvWordTemplates[nounAdv]), nounAdv ] ) # Teeme kindlaks, kas kontekst on laiendamiseks piisavalt yhene/selge ... suitableExpansionVerb = \ self._canBeExpanded( headVerb, headVerbWID, suitableNomAdvExpansions, \ expansionVerbs, widToToken ) if suitableExpansionVerb: phraseExt = [ suitableNomAdvExpansions[0][0], suitableExpansionVerb[0] ] expIsOle = verbOle.matches( suitableExpansionVerb[1]) patternExt = [ 'nom/adv', 'ole' if expIsOle else 'verb' ] analysisIDsExt = [ \ _getMatchingAnalysisIDs( suitableNomAdvExpansions[0][1], \ suitableNomAdvExpansions[0][3] ), \ _getMatchingAnalysisIDs( suitableExpansionVerb[1], verbDaMa ) ] # Lisame ahelale pikendused verbObj[PHRASE].extend(phraseExt) verbObj[PATTERN].extend(patternExt) verbObj[ANALYSIS_IDS].extend(analysisIDsExt) annotatedWords.extend(phraseExt) expansionPerformed = True #if headVerb.startswith('and '): # _debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) #_debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) elif headVerb in self.verbRules and headVerb.startswith( 'ole '): # # 2.2) Vaatame olema-verbi rektsiooniseoseid; # minInd = headVerbWID - 1 if verbObj[PATTERN][ 0] != 'ega' else headVerbWID suitableNomAdvExpansions = [] expansionVerbs = [] for i in range(minInd, clauseMaxWID + 1): if i in widToToken and i not in annotatedWords: token = widToToken[i] if verbDaMa.matches(token): analysisIDs = _getMatchingAnalysisIDs( token, verbDaMa) form = token[ANALYSIS][ analysisIDs[0]][FORM] expansionVerbs.append( [i, token, "V_" + form]) else: for (nounAdv, vinf1) in self.verbRules[headVerb]: if (self.nomAdvWordTemplates[nounAdv] ).matches(token): suitableNomAdvExpansions.append( [i, token, vinf1, \ (self.nomAdvWordTemplates[nounAdv]), nounAdv] ) if _isFollowedByComma(i, clause): # Katkestame, kui satume koma otsa (kuna ei saa kindel olla, # et teisel pool koma on olevad jupid kuuluvad ikka verbi # juurde) break # Teeme kindlaks, kas kontekst on laiendamiseks piisavalt yhene/selge ... suitableExpansionVerb = \ self._canBeExpanded( headVerb, headVerbWID, suitableNomAdvExpansions, \ expansionVerbs, widToToken ) if suitableExpansionVerb: phraseExt = [ suitableNomAdvExpansions[0][0], suitableExpansionVerb[0] ] expIsOle = verbOle.matches( suitableExpansionVerb[1]) patternExt = [ 'nom/adv', 'ole' if expIsOle else 'verb' ] analysisIDsExt = [ \ _getMatchingAnalysisIDs( suitableNomAdvExpansions[0][1], \ suitableNomAdvExpansions[0][3] ), \ _getMatchingAnalysisIDs( suitableExpansionVerb[1], verbDaMa ) ] # Lisame ahelale pikendused verbObj[PHRASE].extend(phraseExt) verbObj[PATTERN].extend(patternExt) verbObj[ANALYSIS_IDS].extend(analysisIDsExt) annotatedWords.extend(phraseExt) expansionPerformed = True #_debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) #if suitableNomAdvExpansions[0][4].startswith('aeg;'): # _debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+_getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) return expansionPerformed
def extendChainsInClause( self, clause, clauseID, foundChains ): ''' Proovime etteantud osalauses leiduvaid verbiahelaid täiendada 'verb-nom/adv-vinf' rektsiooniseostega, nt: andma + võimalus + Vda : talle anti_0 võimalus_0 olukorda parandada_0 olema + vaja + Vda : nüüd on_0 küll vaja_0 asi lõpetada_0 Teeme seda kahel moel: 1) kui mingi olemasoleva verbiahela keskelt on puudu 'nom/adv' (nt 'andma', 'jätma' verbide vinf rektsiooniseoste leidmisel võib tekkida selliseid lünki), siis lisame ahela keskele 'nom/adv' sõna. 2) kui verbiahela lõpus on verb, mis on sageli ülemuseks 'nom/adv' sõnale, millest omakorda sõltub mingi Vinf verb (Vma, Vda), ning need on osalausekontekstis olemas, lisame need verbiahela lõppu; ''' expansionPerformed = False # J22dvustame s6nad, mis kuuluvad juba mingi tuvastatud verbifraasi koosseisu annotatedWords = [] for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX] and (len(verbObj[PATTERN])==1 and \ re.match('^(ei|ära|ega)$', verbObj[PATTERN][0])): # V2lja j22vad yksikuna esinevad ei/ära/ega, kuna need tõenäoliselt ei sega continue annotatedWords.extend( verbObj[PHRASE] ) widToToken = { token[WORD_ID] : token for token in clause } verbDaMa = WordTemplate({POSTAG:'V', FORM:'^(da|ma)$'}) verbOle = WordTemplate({ROOT:'^ole$',POSTAG:'V'}) # # 1) Yritame leida, millised juba tuvastatud verbiahelatest on sellised, kust on # vahelt puudu nom/adv s6na, nt: # annab_0 kunstnik jälle põhjuse endast kirjutada_0 # see annab_0 võimaluse laua tagant tõusta_0 ja_0 minema jalutada_0 # Kui leiame ahelast puuduoleva s6na ja lisame selle ahelasse ... # for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX]: headVerb = '' headVerbWID = -1 dependentVerb = '' dependentVerbWIDs = [] firstDependentVerbID = -1 # Leiame ahela l6pust ylemus-verbi ja sellele alluva verbi if len(verbObj[PATTERN]) > 3 and verbObj[PATTERN][-2] == '&': headVerb = verbObj[ROOTS][-4]+" "+verbObj[POLARITY] dependentVerb = verbObj[MORPH][-3] headVerbWID = verbObj[PHRASE][-4] dependentVerbWIDs.append( verbObj[PHRASE][-3] ) dependentVerbWIDs.append( verbObj[PHRASE][-1] ) firstDependentVerbID = len(verbObj[PHRASE])-3 elif len(verbObj[PATTERN]) > 1 and verbObj[PATTERN][-2]=='verb': headVerb = verbObj[ROOTS][-2]+" "+verbObj[POLARITY] dependentVerb = verbObj[MORPH][-1] headVerbWID = verbObj[PHRASE][-2] dependentVerbWIDs.append(verbObj[PHRASE][-1]) firstDependentVerbID = len(verbObj[PHRASE])-1 # Kontrollime, kas ylemusverb ja sellele alluv verb v6iksid olla yhendatud # mingi nom/adv s6na kaudu if headVerb in self.verbRules and headVerb in self.verbToVinf and \ dependentVerb in self.verbToVinf[headVerb]: # Teeme kindlaks, kas s6nade vahele j22b puuduolev nom/adv minInd = min(min(dependentVerbWIDs), headVerbWID-1) maxInd = max(max(dependentVerbWIDs)-1, headVerbWID) if minInd < maxInd: for i in range(minInd, maxInd+1): if i in widToToken and i not in annotatedWords: token = widToToken[i] matchingAnalyses = self.tokenMatchesNomAdvVinf( token, headVerb, dependentVerb ) if matchingAnalyses and not expansionPerformed: # Kontrollime, kas vaheletorgatav sõna paikneb nii, et see on suure # tõenäosusega üksiksõna, mitte fraas. if self._isLikelyNotPhrase( headVerb, headVerbWID, token[WORD_ID], widToToken ): # Torkame nimis6na/adverbi vahele verbObj[PHRASE].insert( firstDependentVerbID, token[WORD_ID] ) verbObj[PATTERN].insert( firstDependentVerbID, 'nom/adv' ) verbObj[ANALYSIS_IDS].insert( firstDependentVerbID, matchingAnalyses ) annotatedWords.append( token[WORD_ID] ) expansionPerformed = True else: # Kui me ei saa olla kindlad, et vaheletorgatav sõna pole fraas, paneme # küsimärgi, näitamaks, et verbiahelast on suure tõenäosusega midagi # puudu ... verbObj[OTHER_VERBS] = True #_debugPrint( ' '+('+'.join(verbObj[PATTERN]))+' | '+_getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) # # 2) Yritame luua uusi ahelaid, laiendades verbe olemasolevate ahelate l6pus: # # millega antakse_0 võimalus_1 sõlmida_1 uus kokkulepe . # puudub_0 võimalus_1 spetsialiste täistööajaga rakendada_1 . # kui on_0 võimalus_1 rahulikult vooluga kaasa minna_1 # clauseMaxWID = max( list(widToToken.keys()) ) for verbObj in foundChains: if clauseID == verbObj[CLAUSE_IDX] and verbObj[OTHER_VERBS]: if (len(verbObj[PATTERN])==1 or (len(verbObj[PATTERN])>1 and \ verbObj[PATTERN][-2] != '&')): headVerb = verbObj[ROOTS][-1]+" "+verbObj[POLARITY] headVerbWID = verbObj[PHRASE][-1] # # 2.1) Esimeses l2henduses vaatame tavalisi verbe (mitte-olema); # if headVerb in self.verbRules and not headVerb.startswith('ole '): minInd = headVerbWID-1 if verbObj[PATTERN][0]!='ega' else headVerbWID suitableNomAdvExpansions = [] expansionVerbs = [] for i in range(minInd, clauseMaxWID+1): if i in widToToken and i not in annotatedWords: token = widToToken[i] if _isFollowedByComma( i, clause ): # Katkestame, kui satume koma otsa (kuna ei saa kindel olla, # et teisel pool koma on olevad jupid kuuluvad ikka verbi # juurde) break if verbDaMa.matches( token ): analysisIDs = _getMatchingAnalysisIDs( token, verbDaMa ) form = token[ANALYSIS][analysisIDs[0]][FORM] expansionVerbs.append( [i, token, "V_"+form ] ) else: for (nounAdv, vinf1) in self.verbRules[headVerb]: if (self.nomAdvWordTemplates[nounAdv]).matches(token): suitableNomAdvExpansions.append( [i, token, vinf1, \ (self.nomAdvWordTemplates[nounAdv]), nounAdv ] ) # Teeme kindlaks, kas kontekst on laiendamiseks piisavalt yhene/selge ... suitableExpansionVerb = \ self._canBeExpanded( headVerb, headVerbWID, suitableNomAdvExpansions, \ expansionVerbs, widToToken ) if suitableExpansionVerb: phraseExt = [suitableNomAdvExpansions[0][0], suitableExpansionVerb[0]] expIsOle = verbOle.matches(suitableExpansionVerb[1]) patternExt = ['nom/adv', 'ole' if expIsOle else 'verb'] analysisIDsExt = [ \ _getMatchingAnalysisIDs( suitableNomAdvExpansions[0][1], \ suitableNomAdvExpansions[0][3] ), \ _getMatchingAnalysisIDs( suitableExpansionVerb[1], verbDaMa ) ] # Lisame ahelale pikendused verbObj[PHRASE].extend( phraseExt ) verbObj[PATTERN].extend( patternExt ) verbObj[ANALYSIS_IDS].extend( analysisIDsExt ) annotatedWords.extend( phraseExt ) expansionPerformed = True #if headVerb.startswith('and '): # _debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) #_debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) elif headVerb in self.verbRules and headVerb.startswith('ole '): # # 2.2) Vaatame olema-verbi rektsiooniseoseid; # minInd = headVerbWID-1 if verbObj[PATTERN][0]!='ega' else headVerbWID suitableNomAdvExpansions = [] expansionVerbs = [] for i in range(minInd, clauseMaxWID+1): if i in widToToken and i not in annotatedWords: token = widToToken[i] if verbDaMa.matches( token ): analysisIDs = _getMatchingAnalysisIDs( token, verbDaMa ) form = token[ANALYSIS][analysisIDs[0]][FORM] expansionVerbs.append( [i, token, "V_"+form ] ) else: for (nounAdv, vinf1) in self.verbRules[headVerb]: if (self.nomAdvWordTemplates[nounAdv]).matches(token): suitableNomAdvExpansions.append( [i, token, vinf1, \ (self.nomAdvWordTemplates[nounAdv]), nounAdv] ) if _isFollowedByComma( i, clause ): # Katkestame, kui satume koma otsa (kuna ei saa kindel olla, # et teisel pool koma on olevad jupid kuuluvad ikka verbi # juurde) break # Teeme kindlaks, kas kontekst on laiendamiseks piisavalt yhene/selge ... suitableExpansionVerb = \ self._canBeExpanded( headVerb, headVerbWID, suitableNomAdvExpansions, \ expansionVerbs, widToToken ) if suitableExpansionVerb: phraseExt = [suitableNomAdvExpansions[0][0], suitableExpansionVerb[0]] expIsOle = verbOle.matches(suitableExpansionVerb[1]) patternExt = ['nom/adv', 'ole' if expIsOle else 'verb'] analysisIDsExt = [ \ _getMatchingAnalysisIDs( suitableNomAdvExpansions[0][1], \ suitableNomAdvExpansions[0][3] ), \ _getMatchingAnalysisIDs( suitableExpansionVerb[1], verbDaMa ) ] # Lisame ahelale pikendused verbObj[PHRASE].extend( phraseExt ) verbObj[PATTERN].extend( patternExt ) verbObj[ANALYSIS_IDS].extend( analysisIDsExt ) annotatedWords.extend( phraseExt ) expansionPerformed = True #_debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) #if suitableNomAdvExpansions[0][4].startswith('aeg;'): # _debugPrint( ('+'.join(verbObj[PATTERN]))+' | '+_getJsonAsTextString(clause, markTokens = [ verbObj[PHRASE] ] )) return expansionPerformed