Package miopia :: Package analyzer :: Module SentimentAnalyzer
[hide private]
[frames] | no frames]

Source Code for Module miopia.analyzer.SentimentAnalyzer

  1  #-*- coding: utf-8 -*-  
  2  ''' 
  3  @author: David Vilares 
  4  ''' 
  5  import codecs 
  6  import os 
  7  from miopia.analyzer.Analyzer import Analyzer 
  8  from miopia.util.exceptions.UnknownSOException import UnknownSOException 
  9  from miopia.analyzer.SentimentInfo import SentimentInfo 
 10  from miopia.analyzer.NegationInfo import NegationInfo 
 11  from miopia.analyzer.NegationRules import NegationRules 
 12  from miopia.analyzer.SemanticCategory import SemanticCategory 
 13  from miopia.parser.SentimentDependencyGraph import SentimentDependencyGraphNodeKeys 
 14  from miopia.preparator.LexicalSentimentInfo import LexicalValenceShifter 
 15   
16 -class SentimentAnalyzer(Analyzer):
17 """ 18 Instances of L{SentimentAnalyzer} evaluates 19 L{SentimentDependencyGraph}'s of a text, returning a summary in a L{SentimentInfo} object 20 """ 21 #TODO: Proof number_intensifiers, length_text and number_words 22
23 - def __init__(self, parser, dictionaries,conf, 24 preprocessor, lexical_processor):
25 """ 26 @param parser: An instance of L{Parser} 27 @param dictionaries: An instance of L{Dictionary} 28 @param conf: An instance of L{AnalyzerConfiguration} 29 @param preprocessor: An instance of L{PreProcessor} 30 @param lexical_processor: An instance of L{LexicalProcessor} 31 """ 32 self._conf = conf 33 self._sentence_lsi = None #Auxiliar structure for lexical sentiment info 34 35 self._visit_functions_dict = {SemanticCategory.NOUN: self._visit_noun, 36 SemanticCategory.ADJECTIVE: self._visit_adjective, 37 SemanticCategory.ADVERB: self._visit_adverb, 38 SemanticCategory.VERB: self._visit_verb, 39 SemanticCategory.INTENSIFIER: self._visit_intensifier, 40 SemanticCategory.SUBORDINATE_ADVERSATIVE: self._visit_adversative, 41 SemanticCategory.NEGATION: self._visit_negation, 42 SemanticCategory.NEGATION_WITHOUT: self._visit_without_negation, 43 SemanticCategory.EMOTICON: self._visit_emoticon, 44 SemanticCategory.OTHER: self._visit_other 45 } 46 super(SentimentAnalyzer,self).__init__(parser,dictionaries,preprocessor,lexical_processor)
47 48
49 - def add_sentiment_info_to_node(self,node,sentiment_info):
50 """ 51 @param node: A node of a L{SentimentDependencyGraph} 52 @param sentiment_info: An instance of L{SentimentInfo} containing 53 the new information for the node. 54 """ 55 node[SentimentDependencyGraphNodeKeys.SEMANTIC_ORIENTATION] = sentiment_info.get_so() 56 node[SentimentDependencyGraphNodeKeys.INTENSIFICATION] = sentiment_info.get_int() 57 node[SentimentDependencyGraphNodeKeys.SUBJECTIVITY] = sentiment_info.get_subjectivity() 58 node[SentimentDependencyGraphNodeKeys.POSITIVE_WORDS] = sentiment_info.get_pos_words() 59 node[SentimentDependencyGraphNodeKeys.NEGATIVE_WORDS] = sentiment_info.get_neg_words() 60 node[SentimentDependencyGraphNodeKeys.NUMBER_OF_INTENSIFIERS] = sentiment_info.get_number_intensifiers() 61 node[SentimentDependencyGraphNodeKeys.LENGTH_TEXT] = sentiment_info.get_length_text() 62 node[SentimentDependencyGraphNodeKeys.NUMBER_OF_WORDS] = sentiment_info.get_number_words()
63 64 65
66 - def _analyze_graphs(self, dependency_graphs, lsi):
67 """ 68 @param dependency_graphs: A list of L{SentimentDependencyGraph} 69 @return: A tuple ([L{SentimentDependencyGraph}],L{SentimentInfo}) 70 The first element is a list of the dependency graphs of the texts. Each 71 node contains their own L{src.miope.anayzer.SentimentInfo}. The second element is 72 SentimentInfo object containing the general information for the whole text 73 """ 74 semantic_orientation, positive_words, negative_words = 0,0,0 75 number_intensifiers, length_text, number_words = 0,0,0 76 subjectivity = False 77 i = 0 78 for dg in dependency_graphs: 79 80 if lsi is None : self._sentence_lsi = None 81 try: 82 self._sentence_lsi = lsi.get_sentence_info(i+1) 83 except AttributeError: 84 self._sentence_lsi = None 85 root_node = dg.get_by_address(0) 86 self.evaluate(dg,root_node) 87 subjectivity = subjectivity or root_node[SentimentDependencyGraphNodeKeys.SUBJECTIVITY] 88 positive_words+= root_node[SentimentDependencyGraphNodeKeys.POSITIVE_WORDS] 89 negative_words+= root_node[SentimentDependencyGraphNodeKeys.NEGATIVE_WORDS] 90 number_intensifiers+= root_node[SentimentDependencyGraphNodeKeys.NUMBER_OF_INTENSIFIERS] 91 length_text+= root_node[SentimentDependencyGraphNodeKeys.LENGTH_TEXT] 92 number_words+= root_node[SentimentDependencyGraphNodeKeys.NUMBER_OF_WORDS] 93 if i >= len(dependency_graphs)-3: 94 semantic_orientation+= self._conf.get_final_sentences_weight()*root_node[SentimentDependencyGraphNodeKeys.SEMANTIC_ORIENTATION] 95 else: 96 semantic_orientation+= root_node[SentimentDependencyGraphNodeKeys.SEMANTIC_ORIENTATION] 97 i = i +1 98 99 return dependency_graphs,SentimentInfo (0.,semantic_orientation, 100 subjectivity,positive_words, 101 negative_words, number_intensifiers, 102 length_text, number_words)
103 104
105 - def analyze_from_conll(self,file_path,**kwargs):
106 """ 107 It analyzes a file in ConLL-X format 108 @todo: param lsi not integrated yet. 109 @param file_path: The path to the ConLL file 110 @param **kwargs: Optional key 'lsi' (L{LexicalSentimentInfo}) 111 @return A tuple ([L{SentimentDependencyGraph}],L{SentimentInfo}) 112 """ 113 try: 114 lsi=kwargs['lsi'] 115 except KeyError: 116 lsi = None 117 dependency_graphs = self._parser.parse_from_conll(file_path) 118 return self._analyze_graphs(dependency_graphs, lsi)
119 120
121 - def analyze_dir(self,dir_path,input_encoding='utf-8'):
122 """ 123 It analyzes a directory of plain texts 124 @param dir_path: Path to the directory of plain files to be analysed 125 @param input_encoding: Encoding of the texts inside the directory 126 @return A list of tuples (text_id,([L{SentimentDependencyGraph}],L{SentimentInfo})) 127 """ 128 list_of_tagged_sentences = [] 129 list_id_and_linguistic_info = [] 130 dict_lsi = {} 131 files = os.listdir(dir_path) 132 for file_id in files: 133 text = codecs.open(dir_path+os.sep+file_id,encoding=input_encoding).read() 134 tagged_sentences, lsi = self._preanalyze(text) 135 list_of_tagged_sentences.append((file_id,tagged_sentences)) 136 dict_lsi[file_id] = lsi 137 138 files_parsed = self._parser.parse_dir(list_of_tagged_sentences) 139 for file_id,dependency_graphs in files_parsed: 140 list_id_and_linguistic_info.append((file_id, 141 self._analyze_graphs(dependency_graphs, 142 dict_lsi[file_id])[1])) 143 return list_id_and_linguistic_info
144 145
146 - def analyze_from_conll_list(self,list_conll_files):
147 raise NotImplementedError
148
149 - def analyze(self, text):
150 """ 151 @param text: The string to be analysed.. Use unicode 152 @return A tuple ([L{SentimentDependencyGraph}],L{SentimentInfo}) 153 """ 154 tagged_sentences, lsi = self._preanalyze(text) 155 dependency_graphs = self._parser.parse(tagged_sentences) 156 return self._analyze_graphs(dependency_graphs, lsi)
157 158
159 - def analyze_from_plain_file(self, file_path,input_encoding='utf-8',**kwargs):
160 """ 161 It analyzes a plain file 162 @param file_path: The path to the input plain file to be analysed 163 @param input_encoding: The encoding of the file 164 @return A tuple ([L{SentimentDependencyGraph}],L{SentimentInfo}) 165 """ 166 f = codecs.open(file_path,encoding=input_encoding) 167 text = f.read() 168 tagged_sentences, lsi = self._preanalyze(text) 169 dependency_graphs = self._parser.parse(tagged_sentences) 170 f.close() 171 return self._analyze_graphs(dependency_graphs, lsi)
172 173 174 175
176 - def evaluate(self,graph,node):
177 """ 178 This method analyzes recursively an instance of a L{SentimentDependencyGraph} 179 @param graph: A L{SentimentDependencyGraph} object 180 @param node: A node of a L{SentimentDependencyGraph} 181 @return: A L{SentimentInfo} 182 """ 183 if graph.is_leaf(node): 184 sentiment_info = self.get_sentiment_info(graph,node) 185 self.add_sentiment_info_to_node(node, sentiment_info) 186 return sentiment_info 187 else: 188 semantic_category = self.get_semantic_category(graph,node) 189 f = self.visit_function(semantic_category) 190 return f(graph,node)
191 192 193
194 - def _get_lexical_ponderation(self,list_of_lvs):
195 """ 196 @param list_of_lvs: A list of L{LexicalValenceShifters} 197 @return: A float with the lexical weighting 198 """ 199 ponderation = 0. 200 switch = {LexicalValenceShifter.CAPS: self._conf.get_caps_int(), 201 LexicalValenceShifter.REPLICATION: self._conf.get_replication_int()} 202 for lvs in list_of_lvs: 203 ponderation+=switch[lvs] 204 return ponderation
205 206 207 # def _nesting_word_ponderation(self,graph,node): 208 # """ 209 # @param graph: 210 # @param node: A node of the graph 211 # @return A float. It represents a weighting value with respect 212 # to the level of node in the graph. 213 # """ 214 # if not graph.is_root_node(node): 215 # return (1. / graph.level(graph, graph.get_address(node))) 216 # return 0 217
218 - def get_sentiment_info(self,graph,node):
219 """ 220 @param graph: A L{SentimentDependencyGraph} object 221 @param node: A node of the graph 222 @return: A instance of the L{SentimentInfo} with the sentiment info 223 of that node, considered in an isolated way. 224 """ 225 lexical_category = graph.get_lexical_category(node) 226 semantic_category = self.get_semantic_category(graph, node) 227 word = graph.get_word(node) 228 len_word = (lambda node: len(word) if not graph.is_root_node(node) else 0)(node) 229 number_words = (lambda node: 1 if not graph.is_root_node(node) else 0)(node) 230 lemma = self._dictionaries.get_lemma(lexical_category,word) 231 # word_ponderation = self._nesting_word_ponderation(graph,node) 232 pond = 1 233 semantic_orientation = 0 234 235 #Obtain the lexical sentiment info of the token 236 if self._sentence_lsi is not None: 237 try: 238 pond+=self._get_lexical_ponderation(self._sentence_lsi[graph.get_address(node)]) 239 except KeyError: 240 pass 241 242 if graph.is_intensifier(node,self._dictionaries): 243 try: 244 #XXX: With SFU was used True as subjectivity, but actually is False because we its significance has changed 245 return SentimentInfo(pond*self._dictionaries.get_semantic_orientation(lemma,semantic_category), 246 semantic_orientation, False,0,0,1,len_word,number_words) 247 except UnknownSOException: 248 return SentimentInfo(0,semantic_orientation, False,0,0,0,len_word,number_words) 249 else: 250 try: 251 pond+= 0.2 #Optimized to the SFU Corpus 252 semantic_orientation = self._dictionaries.get_semantic_orientation(lemma,semantic_category)*pond 253 return SentimentInfo(0, semantic_orientation ,True, 254 int(semantic_orientation>0),#*(1+word_ponderation), 255 int(semantic_orientation<0),#*(1+word_ponderation), 256 0,len_word,number_words) 257 except UnknownSOException: 258 return SentimentInfo(0,semantic_orientation,False,0,0,0,len_word,number_words)
259
260 - def visit_function(self, semantic_category):
261 """ 262 @param semantic_category: A value of L{SemanticCategory} 263 @return: An specific visit function according the semantic category of the word 264 """ 265 #TODO: Implement visit interjections, visit negation "ni" and visit irrealis 266 return self._visit_functions_dict[semantic_category]
267 268
269 - def _compute_children(self,graph,children):
270 """ 271 It aggregates the L{SentimentInfo} of a list of children nodes. 272 @param graph: An instance of a L{SentimentDependencyGraph} 273 @param children: A list of id's of the node children. Id's are integers. 274 @return: A L{SentimentInfo} object with the aggregated 275 sentiment info for the children nodes 276 """ 277 semantic_orientation_children, intensification = 0,0 278 positive_words, negative_words = 0,0 279 number_intensifiers, length_text, number_words =0,0,0 280 subjectivity = False 281 282 for child in children: 283 child_node = graph.get_by_address(child) 284 self.evaluate(graph,child_node) 285 positive_words+= child_node[SentimentDependencyGraphNodeKeys.POSITIVE_WORDS] 286 negative_words+= child_node[SentimentDependencyGraphNodeKeys.NEGATIVE_WORDS] 287 number_intensifiers+= child_node[SentimentDependencyGraphNodeKeys.NUMBER_OF_INTENSIFIERS] 288 length_text+= child_node[SentimentDependencyGraphNodeKeys.LENGTH_TEXT] 289 subjectivity = subjectivity or child_node[SentimentDependencyGraphNodeKeys.SUBJECTIVITY] 290 number_words+= child_node[SentimentDependencyGraphNodeKeys.NUMBER_OF_WORDS] 291 intensification+= child_node[SentimentDependencyGraphNodeKeys.INTENSIFICATION] 292 semantic_orientation_children+= child_node[SentimentDependencyGraphNodeKeys.SEMANTIC_ORIENTATION] 293 294 return SentimentInfo(intensification, semantic_orientation_children, 295 subjectivity, positive_words,negative_words, 296 number_intensifiers,length_text,number_words)
297 298 299
300 - def _default_join_semantic_orientation(self,parent, children):
301 """ 302 The default function for joining the semantic orientation of two 303 L{src.miope.analyzer.SentimentInfo} objects. 304 @param parent: The L{SentimentInfo} object which contains the semantic info information of a parent node. 305 @param children: The L{SentimentInfo} object which contains the semantic info information of their children. 306 """ 307 308 return (1+parent.get_int() + children.get_int())*parent.get_so() + children.get_so()
309
310 - def _default_join_intensification(self,parent,children):
311 """ 312 The default function for joining the semantic orientation of two 313 L{src.miope.analyzer.SentimentInfo} objects. 314 @param parent: The L{SentimentInfo} object which contains the semantic info information of a parent node. 315 @param children: The L{SentimentInfo} object which contains the semantic info information of their children. 316 """ 317 return parent.get_int() + children.get_int()
318
319 - def _swap_pos_neg_words(self,sentiment_info):
320 """ 321 It swaps the positive and negative words of a 322 L{SentimentInfo} object. 323 @param sentiment_info: The L{SentimentInfo} object 324 @return: The swapped L{SentimentInfo} object 325 """ 326 aux_pos = sentiment_info.get_pos_words() 327 aux_neg = sentiment_info.get_neg_words() 328 sentiment_info.set_neg_words(aux_pos) 329 sentiment_info.set_pos_words(aux_neg) 330 return sentiment_info
331 332
333 - def _join_sentiment_info(self,parent, children, 334 f_join_semantic_orientation, 335 f_join_intensification = None):
336 """ 337 @param parent: The parent L{SentimentInfo} object 338 @param children: The joined L{SentimentInfo} object of the children 339 @param f_join_semantic_orientation: A function detailing how to join 340 the semantic orientation of the parent and their children 341 """ 342 #intensification = parent.get_int() + children.get_int() 343 if f_join_intensification is None: 344 intensification = 0. 345 else: 346 intensification = f_join_intensification(parent,children) 347 semantic_orientation = f_join_semantic_orientation(parent, children) 348 subjectivity = parent.get_subjectivity() or children.get_subjectivity() 349 positive_words = parent.get_pos_words() + children.get_pos_words() 350 negative_words = parent.get_neg_words() + children.get_neg_words() 351 number_intensifiers = (parent.get_number_intensifiers() 352 + children.get_number_intensifiers()) 353 length_text = parent.get_length_text() + children.get_length_text() 354 number_words = parent.get_number_words() + children.get_number_words() 355 return SentimentInfo(intensification, semantic_orientation, 356 subjectivity, positive_words, negative_words, 357 number_intensifiers, length_text, number_words)
358 359
360 - def _visit_noun(self,graph,node):
361 """ 362 This function processes a noun node and realizes 363 the recursive calls to their children. 364 @param graph: L{SentimentDependencyGraph} 365 @param node: A noun node of the graph 366 @return A L{SentimentInfo}. It contains 367 the joined sentiment info of the noun node and their children. 368 """ 369 node_sentiment_info = self.get_sentiment_info(graph,node) 370 children = graph.get_deps(node) 371 children_sentiment_info = self._compute_children(graph, children) 372 sentiment_info = self._join_sentiment_info(node_sentiment_info, 373 children_sentiment_info, 374 self._default_join_semantic_orientation) 375 self.add_sentiment_info_to_node(node, sentiment_info) 376 return sentiment_info
377 378
379 - def _visit_adjective(self,graph,node):
380 """ 381 This function processes an adjective node and realizes 382 the recursive calls to their children. 383 @param graph: L{SentimentDependencyGraph} 384 @param node: A adjective node of the graph 385 @return A L{SentimentInfo}. It contains 386 the joined sentiment info of the adjective node and their children. 387 """ 388 node_sentiment_info = self.get_sentiment_info(graph,node) 389 children = graph.get_deps(node) 390 children_sentiment_info = self._compute_children(graph, children) 391 sentiment_info = self._join_sentiment_info(node_sentiment_info, 392 children_sentiment_info, 393 self._default_join_semantic_orientation) 394 self.add_sentiment_info_to_node(node, sentiment_info) 395 return sentiment_info
396 397
398 - def _visit_verb(self,graph,node):
399 """ 400 This function processes a verb node and realizes 401 the recursive calls to their children. 402 @param graph: L{SentimentDependencyGraph} 403 @param node: A verb node of the graph 404 @return A L{SentimentInfo}. It contains 405 the joined sentiment info of the verb node and their children. 406 """ 407 node_sentiment_info = self.get_sentiment_info(graph,node) 408 children = graph.get_deps(node) 409 children_sentiment_info = self._compute_children(graph, children) 410 sentiment_info = self._join_sentiment_info(node_sentiment_info, 411 children_sentiment_info, 412 self._default_join_semantic_orientation) 413 self.add_sentiment_info_to_node(node, sentiment_info) 414 return sentiment_info
415
416 - def _visit_adverb(self,graph,node):
417 """ 418 This function processes an adverb node and realizes 419 the recursive calls to their children. 420 @param graph: L{SentimentDependencyGraph} 421 @param node: An adverb node of the graph 422 @return A L{SentimentInfo}. It contains 423 the joined sentiment info of the adverb node and their children. 424 """ 425 node_sentiment_info = self.get_sentiment_info(graph,node) 426 children = graph.get_deps(node) 427 children_sentiment_info = self._compute_children(graph, children) 428 sentiment_info = self._join_sentiment_info(node_sentiment_info, 429 children_sentiment_info, 430 self._default_join_semantic_orientation) 431 432 self.add_sentiment_info_to_node(node, sentiment_info) 433 return sentiment_info
434 435
436 - def _visit_intensifier(self,graph,node):
437 """ 438 This function processes an intensfier node and realizes 439 the recursive calls to their children. 440 @param graph: L{SentimentDependencyGraph} 441 @param node: An intensifier node of the graph 442 @return A L{SentimentInfo}. It contains 443 the joined sentiment info of the intensifier node and their children. 444 """ 445 node_sentiment_info = self.get_sentiment_info(graph,node) 446 children = graph.get_deps(node) 447 children_sentiment_info = self._compute_children(graph, children) 448 sentiment_info = self._join_sentiment_info(node_sentiment_info, 449 children_sentiment_info, 450 self._default_join_semantic_orientation, 451 self._default_join_intensification) 452 self.add_sentiment_info_to_node(node, sentiment_info) 453 return sentiment_info
454 455 456
457 - def _visit_adversative(self,graph,node):
458 """ 459 This function processes an artificial adversative node and realizes 460 the recursive calls to their children. 461 @param graph: L{SentimentDependencyGraph} 462 @param node: An artificial adversative node of the graph 463 @return A L{SentimentInfo}. It contains 464 the joined sentiment info of the adversative node and their children. 465 """ 466 467 nodes_before_adversative = [] 468 nodes_after_adversative = [] 469 470 471 472 def is_restrict_adversative(tag): 473 """ 474 It determines if the artificial adversative node is 475 restrictive 476 """ 477 return tag.split(":")[1].split("@")[0] == 'restrict'
478 479 480 children = graph.get_deps(node) 481 tag = graph.get_tag(node) 482 adversative_id = graph._get_adversative_id(node) 483 for c in children: 484 if c < adversative_id: nodes_before_adversative.append(c) 485 else: nodes_after_adversative.append(c) 486 nodes_before_sentiment_info = self._compute_children(graph, 487 nodes_before_adversative) 488 nodes_after_sentiment_info = self._compute_children(graph, nodes_after_adversative) 489 490 if is_restrict_adversative(tag): 491 adv_pond = self._conf.get_restrictive_adv_weight() 492 main_pond = self._conf.get_restrictive_main_weight() 493 else: 494 adv_pond = 0. 495 main_pond = 1. 496 497 join_function = lambda b,a : adv_pond*b.get_so()+main_pond*a.get_so() 498 sentiment_info = self._join_sentiment_info(nodes_before_sentiment_info, 499 nodes_after_sentiment_info, 500 join_function, 501 self._default_join_intensification) 502 self.add_sentiment_info_to_node(node, sentiment_info) 503 return sentiment_info 504 505 506
507 - def _shift_negation_without(self,semantic_orientation):
508 """ 509 A function to shift the semantic orientation of a 'without' semantic dependent words 510 @param semantic_orientation: An integer 511 @return The shifted semantic orientation 512 """ 513 return (semantic_orientation + self._conf.get_without_shift() 514 * (-1 if semantic_orientation >= 0 else 1))
515
516 - def _shift_negation(self,semantic_orientation):
517 """ 518 A function to shift the semantic orientation of a negation semantic dependent words 519 @param semantic_orientation: An integer 520 @return The shifted semantic orientation 521 """ 522 return (semantic_orientation + self._conf.get_neg_shift() 523 * (-1 if semantic_orientation >= 0 else 1))
524
525 - def _is_adjunct_node(self,graph,node):
526 """ 527 It determines if the node is an adjunct branch 528 @param node: A node of L{SentimentDependencyGraph} 529 @return: A boolean. 530 """ 531 list_adjunct = ['cc','creg'] 532 return graph.get_rel(node) in list_adjunct
533
534 - def is_subjective_node(self,graph,node):
535 """ 536 It determines if a node is subjective 537 @param graph: An instance of L{SentimentDependencyGraph} 538 @param node: A node of the graph 539 @return: A boolean 540 """ 541 return self.get_sentiment_info(graph,node).get_subjectivity()
542
543 - def _negated_and_other_branches(self,graph,children_nodes,inclusion_function, 544 include_all_branches):
545 """ 546 @param graph: An instance of L{SentimentDependencyGraph} 547 @param children_nodes: A list of the children_nodes 548 @param inclusion_function: A function to determine if a node should 549 be negated or not. 550 @param include_all_branches: A boolean. True: It negates all the nodes 551 that match the inclusion function. False: It only includes the 552 first node that match the inclusion function 553 @return A tuple ([negated_nodes],[non_negated_nodes]) 554 """ 555 negated_branches = [] 556 other_branches = [] 557 include_branch = True 558 for child in children_nodes: 559 if inclusion_function(graph,child) and include_branch: 560 negated_branches.append(graph.get_address(child)) 561 include_branch = include_all_branches 562 else: 563 other_branches.append(graph.get_address(child)) 564 return negated_branches, other_branches
565 566 567
568 - def _visit_without_negation(self,graph,node):
569 """ 570 This function processes a 'without' node and realizes 571 the recursive calls to their children. 572 @param graph: An instance of L{SentimentDependencyGraph} 573 @param node: An artificial without node of the graph 574 @return A L{SentimentInfo}. It contains 575 the joined sentiment info of the without node and their children. 576 """ 577 578 node_sentiment_info = self.get_sentiment_info(graph,node) #SentimentInfo object 579 children = graph.get_deps(node) 580 children_sentiment_info = self._compute_children(graph, children) 581 children_sentiment_info = self._swap_pos_neg_words(children_sentiment_info) 582 join_function_semantic_orientation = lambda p,c: self._shift_negation_without(c.get_so())*(p.get_subjectivity()* 583 c.get_subjectivity()) 584 sentiment_info = self._join_sentiment_info(node_sentiment_info, 585 children_sentiment_info, 586 join_function_semantic_orientation, 587 self._default_join_intensification) 588 self.add_sentiment_info_to_node(node, sentiment_info) 589 return sentiment_info
590 591
592 - def _scope_negation_detection(self,graph,node):
593 """ 594 This method analyzes the children of a node and determines which branches must be negated and 595 which not. 596 @param graph: An instance of L{SentimentDependencyGraph} 597 @param node: A node of the graph 598 @return: A L{NegationInfo} object 599 """ 600 children_id = graph.get_deps(node) 601 602 if self.is_subjective_node(graph,node): 603 return NegationInfo([],children_id, NegationRules.verb) 604 children_nodes = map(graph.get_by_address,children_id) 605 #Sentimental noun rule and sentimental adjetive rule 606 neg_branch, other_branch = self._negated_and_other_branches(graph,children_nodes, 607 lambda graph,child: graph.get_rel(child) in ['cd','atr','cpred'], 608 True) 609 if neg_branch != []: 610 return NegationInfo(neg_branch,other_branch,NegationRules.branch) 611 #Sentimental cc and creg rule 612 neg_branch, other_branch = self._negated_and_other_branches(graph,children_nodes, 613 self._is_adjunct_node, False) 614 if neg_branch != []: 615 return NegationInfo(neg_branch,other_branch,NegationRules.branch) 616 #Default rule 617 return NegationInfo(children_id,[],NegationRules.default)
618 619
620 - def _negation_sentimental_word_rule(self,graph,node,**kwargs):
621 """ 622 This function negates the parent of a negation 623 @param graph: An instance of L{SentimentDependencyGraph} 624 @param node: A node of the graph 625 @param **kwargs: Not used right now 626 """ 627 node_sentiment_info = self.get_sentiment_info(graph,node) 628 children = graph.get_deps(node) 629 children_sentiment_info = self._compute_children(graph, children) 630 join_function = lambda p,c: self._shift_negation((1+ p.get_int()+c.get_int())*p.get_so())*p.get_subjectivity()+ c.get_so() 631 sentiment_info = self._join_sentiment_info(node_sentiment_info, 632 children_sentiment_info, 633 join_function, 634 self._default_join_intensification 635 ) 636 self.add_sentiment_info_to_node(node, sentiment_info) 637 return sentiment_info
638
639 - def _negation_default_rule(self,graph,node,**kwargs):
640 """ 641 This function negates the sibling branches of a negation 642 @param graph: An instance of L{SentimentDependencyGraph} 643 @param node: A node of the graph 644 @param **kwargs: Not used right now 645 """ 646 node_sentiment_info = self.get_sentiment_info(graph,node) 647 children = graph.get_deps(node) 648 children_sentiment_info = self._compute_children(graph, children) 649 children_sentiment_info = self._swap_pos_neg_words(children_sentiment_info) 650 join_function = lambda p,c: self._shift_negation(p.get_so()+c.get_so())*(p.get_subjectivity() or c.get_subjectivity()) 651 sentiment_info = self._join_sentiment_info(node_sentiment_info, 652 children_sentiment_info, 653 join_function, 654 self._default_join_intensification 655 ) 656 self.add_sentiment_info_to_node(node, sentiment_info) 657 return sentiment_info
658
659 - def _negation_sentimental_branch_rule(self,graph,node,**kwargs):
660 """ 661 This function negates the negated branches specified in kwargs['negated_branches']. 662 The branches inside the parameter kwargs['other_branches'] are not negated 663 @param graph: An instance of L{SentimentDependencyGraph} 664 @param node: A node of the graph 665 @param **kwargs: Keys ={'negated_branches','other_branches'}. 666 The parameter 'negated_branches' represent a list of nodes 667 at the same level are negated. The parameter 'other_branches' 668 represent a list of node at the same level that are not negated. 669 """ 670 node_sentiment_info = self.get_sentiment_info(graph,node) 671 negated_sentiment_info = self._compute_children(graph, 672 kwargs['negated_branches']) 673 negated_sentiment_info = self._swap_pos_neg_words(negated_sentiment_info) 674 other_sentiment_info = self._compute_children(graph, 675 kwargs['other_branches']) 676 join_function = lambda n,o: self._shift_negation(n.get_so())+o.get_so() 677 joined_sentiment_info = self._join_sentiment_info(negated_sentiment_info, 678 other_sentiment_info, 679 join_function, 680 self._default_join_intensification 681 ) 682 final_sentiment_info = self._join_sentiment_info(node_sentiment_info, 683 joined_sentiment_info, 684 self._default_join_semantic_orientation, 685 self._default_join_intensification) 686 self.add_sentiment_info_to_node(node, final_sentiment_info) 687 return final_sentiment_info 688 689 690
691 - def _negation_function(self,delimiter):
692 """ 693 @param delimiter: L{NegationRules} 694 @return: The negation function to apply given a delimiter 695 """ 696 rules = {NegationRules.verb: self._negation_sentimental_word_rule, 697 NegationRules.branch: self._negation_sentimental_branch_rule, 698 NegationRules.default: self._negation_default_rule} 699 return rules[delimiter]
700 701 702
703 - def _visit_negation(self,graph,node):
704 """ 705 This function processes a negation node and realizes 706 the recursive calls to their children. 707 @param graph: L{SentimentDependencyGraph} 708 @param node: A negation node of the graph 709 @return A L{SentimentInfo}. It has computed 710 the joined sentiment info of the negation node and their children. 711 """ 712 scope_of_negation = self._scope_negation_detection(graph, node) 713 neg_branches = scope_of_negation.get_neg_branches() 714 non_negated_branches = scope_of_negation.get_other_branches() 715 type_delimiter = scope_of_negation.get_delimiter() 716 return self._negation_function(type_delimiter)(graph,node,negated_branches=neg_branches, 717 other_branches=non_negated_branches)
718
719 - def _visit_emoticon(self,graph,node):
720 """ 721 This function processes a emoticon node and realizes 722 the recursive calls to their children. 723 @param graph: L{SentimentDependencyGraph} 724 @param node: A emoticon node of the graph 725 @return A L{SentimentInfo}. It has computed 726 the joined sentiment info of the emoticon node and their children. 727 """ 728 node_sentiment_info = self.get_sentiment_info(graph,node) 729 children = graph.get_deps(node) 730 children_sentiment_info = self._compute_children(graph, children) 731 sentiment_info = self._join_sentiment_info(node_sentiment_info, 732 children_sentiment_info, 733 self._default_join_semantic_orientation, 734 self._default_join_intensification) 735 self.add_sentiment_info_to_node(node, sentiment_info) 736 return sentiment_info
737 738
739 - def _visit_other(self,graph,node):
740 """ 741 This function processes an other node and realizes 742 the recursive calls to their children. 743 @param graph: L{SentimentDependencyGraph} 744 @param node: An other node of the graph 745 @return A L{SentimentInfo}. It has computed 746 the joined sentiment info of the 'other' node and their children. 747 """ 748 node_sentiment_info = self.get_sentiment_info(graph,node) 749 children = graph.get_deps(node) 750 children_sentiment_info = self._compute_children(graph, children) 751 # if self.is_root_node(node): 752 # join_function = lambda p,c: (1+p.get_int()+c.get_int())*(p.get_so()+c.get_so()) 753 # else: 754 join_function = self._default_join_semantic_orientation 755 sentiment_info = self._join_sentiment_info(node_sentiment_info, 756 children_sentiment_info, 757 join_function, 758 self._default_join_intensification) 759 self.add_sentiment_info_to_node(node, sentiment_info) 760 return sentiment_info
761