nltk 3.2__zip → 3.3__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
  2. {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
  3. {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
  4. {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
  5. nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
  6. nltk-3.3/nltk/VERSION +1 -0
  7. {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
  8. {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
  9. {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
  10. {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
  11. {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
  12. {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
  13. {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
  14. {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
  15. {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
  16. {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
  17. {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
  18. {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
  19. {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
  20. {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
  21. {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
  22. {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
  23. {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
  24. nltk-3.3/nltk/ccg/logic.py +46 -0
  25. {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
  26. {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
  27. {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
  28. {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
  29. {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
  30. {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
  31. {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
  32. {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
  33. {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
  34. {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
  35. {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
  36. {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
  37. {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
  38. {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
  39. {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
  40. {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
  41. {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
  42. {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
  43. {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
  44. {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
  45. {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
  46. {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
  47. {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
  48. {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
  49. {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
  50. {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
  51. {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
  52. {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
  53. {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
  54. {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
  55. {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
  56. {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
  57. {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
  58. nltk-3.3/nltk/collections.py +640 -0
  59. {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
  60. nltk-3.3/nltk/compat.py +365 -0
  61. {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
  62. {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
  63. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
  64. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
  65. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
  66. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
  67. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
  68. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
  69. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
  70. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
  71. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
  72. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
  73. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
  74. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
  75. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
  76. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
  77. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
  78. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
  79. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
  80. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
  81. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
  82. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
  83. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
  84. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
  85. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
  86. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
  87. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
  88. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
  89. nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
  90. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
  91. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
  92. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
  93. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
  94. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
  95. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
  96. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
  97. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
  98. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
  99. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
  100. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
  101. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
  102. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
  103. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
  104. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
  105. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
  106. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
  107. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
  108. nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
  109. nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
  110. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
  111. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
  112. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
  113. {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
  114. {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
  115. {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
  116. {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
  117. {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
  118. {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
  119. {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
  120. {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
  121. {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
  122. {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
  123. {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
  124. {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
  125. {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
  126. {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
  127. {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
  128. {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
  129. {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
  130. {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
  131. {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
  132. {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
  133. {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
  134. {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
  135. {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
  136. {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
  137. {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
  138. {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
  139. nltk-3.3/nltk/metrics/aline.py +607 -0
  140. {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
  141. {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
  142. {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
  143. {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
  144. {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
  145. {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
  146. {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
  147. {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
  148. {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
  149. {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
  150. {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
  151. {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
  152. {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
  153. {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
  154. {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
  155. {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
  156. {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
  157. nltk-3.3/nltk/parse/corenlp.py +780 -0
  158. {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
  159. {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
  160. {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
  161. {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
  162. {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
  163. {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
  164. {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
  165. {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
  166. {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
  167. {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
  168. {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
  169. {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
  170. {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
  171. {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
  172. {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
  173. {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
  174. {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
  175. {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
  176. {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
  177. {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
  178. {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
  179. {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
  180. {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
  181. {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
  182. {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
  183. {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
  184. {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
  185. {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
  186. {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
  187. {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
  188. {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
  189. {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
  190. {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
  191. {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
  192. {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
  193. {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
  194. {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
  195. nltk-3.3/nltk/stem/arlstem.py +369 -0
  196. {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
  197. {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
  198. nltk-3.3/nltk/stem/porter.py +710 -0
  199. {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
  200. {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
  201. {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
  202. {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
  203. {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
  204. {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
  205. {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
  206. {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
  207. {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
  208. {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
  209. {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
  210. {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
  211. {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
  212. {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
  213. {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
  214. {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
  215. {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
  216. {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
  217. {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
  218. {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
  219. {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
  220. {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
  221. {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
  222. {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
  223. {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
  224. {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
  225. {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
  226. {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
  227. {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
  228. {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
  229. {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
  230. nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
  231. {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
  232. {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
  233. {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
  234. {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
  235. {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
  236. {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
  237. nltk-3.3/nltk/test/collections.doctest +20 -0
  238. {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
  239. {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
  240. {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
  241. nltk-3.3/nltk/test/concordance.doctest +55 -0
  242. {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
  243. {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
  244. {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
  245. {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
  246. {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
  247. {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
  248. {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
  249. {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
  250. {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
  251. {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
  252. {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
  253. {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
  254. {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
  255. {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
  256. {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
  257. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
  258. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
  259. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
  260. {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
  261. {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
  262. {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
  263. {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
  264. {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
  265. {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
  266. {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
  267. {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
  268. {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
  269. {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
  270. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
  271. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
  272. {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
  273. {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
  274. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
  275. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
  276. {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
  277. {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
  278. {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
  279. {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
  280. {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
  281. {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
  282. {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
  283. {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
  284. {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
  285. {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
  286. {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
  287. {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
  288. {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
  289. {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
  290. {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
  291. {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
  292. {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
  293. {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
  294. {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
  295. {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
  296. {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
  297. nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
  298. nltk-3.3/nltk/test/unit/test_aline.py +52 -0
  299. nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
  300. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
  301. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
  302. nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
  303. nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
  304. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
  305. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
  306. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
  307. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
  308. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
  309. nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
  310. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
  311. nltk-3.3/nltk/test/unit/test_senna.py +74 -0
  312. nltk-3.3/nltk/test/unit/test_stem.py +121 -0
  313. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
  314. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
  315. nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
  316. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
  317. nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
  318. nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
  319. nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
  320. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
  321. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
  322. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
  323. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
  324. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
  325. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
  326. nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
  327. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
  328. {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
  329. {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
  330. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
  331. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
  332. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
  333. {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
  334. {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
  335. {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
  336. {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
  337. {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
  338. {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
  339. {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
  340. nltk-3.3/nltk/tokenize/nist.py +173 -0
  341. {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
  342. {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
  343. nltk-3.3/nltk/tokenize/repp.py +151 -0
  344. {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
  345. {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
  346. {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
  347. nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
  348. {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
  349. nltk-3.3/nltk/tokenize/toktok.py +155 -0
  350. nltk-3.3/nltk/tokenize/treebank.py +363 -0
  351. nltk-3.3/nltk/tokenize/util.py +261 -0
  352. {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
  353. {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
  354. {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
  355. {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
  356. nltk-3.3/nltk/translate/chrf_score.py +137 -0
  357. {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
  358. {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
  359. nltk-3.3/nltk/translate/gleu_score.py +193 -0
  360. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
  361. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
  362. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
  363. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
  364. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
  365. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
  366. {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
  367. nltk-3.3/nltk/translate/nist_score.py +178 -0
  368. {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
  369. {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
  370. {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
  371. {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
  372. {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
  373. {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
  374. {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
  375. {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
  376. {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
  377. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
  378. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
  379. {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
  380. nltk-3.3/nltk/util.py +751 -0
  381. {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
  382. {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
  383. {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
  384. nltk-3.3/nltk.egg-info/requires.txt +31 -0
  385. {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
  386. {nltk-3.2 → nltk-3.3}/setup.py +102 -75
  387. nltk-3.2/nltk/VERSION +0 -1
  388. nltk-3.2/nltk/compat.py +0 -680
  389. nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
  390. nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
  391. nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
  392. nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
  393. nltk-3.2/nltk/six.py +0 -868
  394. nltk-3.2/nltk/stem/porter.py +0 -692
  395. nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
  396. nltk-3.2/nltk/test/unit/test_stem.py +0 -36
  397. nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
  398. nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
  399. nltk-3.2/nltk/tokenize/treebank.py +0 -124
  400. nltk-3.2/nltk/tokenize/util.py +0 -90
  401. nltk-3.2/nltk/util.py +0 -1364
  402. {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
  403. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
  404. {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
  405. {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
  406. {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
@@ -1,2036 +1,2108 @@
1
- # -*- coding: utf-8 -*-
2
- # Natural Language Toolkit: WordNet
3
- #
4
- # Copyright (C) 2001-2015 NLTK Project
5
- # Author: Steven Bethard <Steven.Bethard@colorado.edu>
6
- # Steven Bird <stevenbird1@gmail.com>
7
- # Edward Loper <edloper@gmail.com>
8
- # Nitin Madnani <nmadnani@ets.org>
9
- # Nasruddin A’aidil Shari
10
- # Sim Wei Ying Geraldine
11
- # Soe Lynn
12
- # Francis Bond <bond@ieee.org>
13
- # URL: <http://nltk.org/>
14
- # For license information, see LICENSE.TXT
15
-
16
- """
17
- An NLTK interface for WordNet
18
-
19
- WordNet is a lexical database of English.
20
- Using synsets, helps find conceptual relationships between words
21
- such as hypernyms, hyponyms, synonyms, antonyms etc.
22
-
23
- For details about WordNet see:
24
- http://wordnet.princeton.edu/
25
-
26
- This module also allows you to find lemmas in languages
27
- other than English from the Open Multilingual Wordnet
28
- http://compling.hss.ntu.edu.sg/omw/
29
-
30
- """
31
-
32
- from __future__ import print_function, unicode_literals
33
-
34
- import math
35
- import re
36
- from itertools import islice, chain
37
- from operator import itemgetter, attrgetter
38
- from collections import defaultdict, deque
39
-
40
- from nltk.corpus.reader import CorpusReader
41
- from nltk.util import binary_search_file as _binary_search_file
42
- from nltk.probability import FreqDist
43
- from nltk.compat import (iteritems, python_2_unicode_compatible,
44
- total_ordering, xrange)
45
-
46
- ######################################################################
47
- ## Table of Contents
48
- ######################################################################
49
- ## - Constants
50
- ## - Data Classes
51
- ## - WordNetError
52
- ## - Lemma
53
- ## - Synset
54
- ## - WordNet Corpus Reader
55
- ## - WordNet Information Content Corpus Reader
56
- ## - Similarity Metrics
57
- ## - Demo
58
-
59
- ######################################################################
60
- ## Constants
61
- ######################################################################
62
-
63
- #: Positive infinity (for similarity functions)
64
- _INF = 1e300
65
-
66
- #{ Part-of-speech constants
67
- ADJ, ADJ_SAT, ADV, NOUN, VERB = 'a', 's', 'r', 'n', 'v'
68
- #}
69
-
70
- POS_LIST = [NOUN, VERB, ADJ, ADV]
71
-
72
- #: A table of strings that are used to express verb frames.
73
- VERB_FRAME_STRINGS = (
74
- None,
75
- "Something %s",
76
- "Somebody %s",
77
- "It is %sing",
78
- "Something is %sing PP",
79
- "Something %s something Adjective/Noun",
80
- "Something %s Adjective/Noun",
81
- "Somebody %s Adjective",
82
- "Somebody %s something",
83
- "Somebody %s somebody",
84
- "Something %s somebody",
85
- "Something %s something",
86
- "Something %s to somebody",
87
- "Somebody %s on something",
88
- "Somebody %s somebody something",
89
- "Somebody %s something to somebody",
90
- "Somebody %s something from somebody",
91
- "Somebody %s somebody with something",
92
- "Somebody %s somebody of something",
93
- "Somebody %s something on somebody",
94
- "Somebody %s somebody PP",
95
- "Somebody %s something PP",
96
- "Somebody %s PP",
97
- "Somebody's (body part) %s",
98
- "Somebody %s somebody to INFINITIVE",
99
- "Somebody %s somebody INFINITIVE",
100
- "Somebody %s that CLAUSE",
101
- "Somebody %s to somebody",
102
- "Somebody %s to INFINITIVE",
103
- "Somebody %s whether INFINITIVE",
104
- "Somebody %s somebody into V-ing something",
105
- "Somebody %s something with something",
106
- "Somebody %s INFINITIVE",
107
- "Somebody %s VERB-ing",
108
- "It %s that CLAUSE",
109
- "Something %s INFINITIVE")
110
-
111
- SENSENUM_RE = re.compile(r'\.\d\d\.')
112
-
113
- ######################################################################
114
- ## Data Classes
115
- ######################################################################
116
-
117
- class WordNetError(Exception):
118
- """An exception class for wordnet-related errors."""
119
-
120
-
121
- @total_ordering
122
- class _WordNetObject(object):
123
- """A common base class for lemmas and synsets."""
124
-
125
- def hypernyms(self):
126
- return self._related('@')
127
-
128
- def _hypernyms(self):
129
- return self._related('@', sort=False)
130
-
131
- def instance_hypernyms(self):
132
- return self._related('@i')
133
-
134
- def _instance_hypernyms(self):
135
- return self._related('@i', sort=False)
136
-
137
- def hyponyms(self):
138
- return self._related('~')
139
-
140
- def instance_hyponyms(self):
141
- return self._related('~i')
142
-
143
- def member_holonyms(self):
144
- return self._related('#m')
145
-
146
- def substance_holonyms(self):
147
- return self._related('#s')
148
-
149
- def part_holonyms(self):
150
- return self._related('#p')
151
-
152
- def member_meronyms(self):
153
- return self._related('%m')
154
-
155
- def substance_meronyms(self):
156
- return self._related('%s')
157
-
158
- def part_meronyms(self):
159
- return self._related('%p')
160
-
161
- def topic_domains(self):
162
- return self._related(';c')
163
-
164
- def region_domains(self):
165
- return self._related(';r')
166
-
167
- def usage_domains(self):
168
- return self._related(';u')
169
-
170
- def attributes(self):
171
- return self._related('=')
172
-
173
- def entailments(self):
174
- return self._related('*')
175
-
176
- def causes(self):
177
- return self._related('>')
178
-
179
- def also_sees(self):
180
- return self._related('^')
181
-
182
- def verb_groups(self):
183
- return self._related('$')
184
-
185
- def similar_tos(self):
186
- return self._related('&')
187
-
188
- def __hash__(self):
189
- return hash(self._name)
190
-
191
- def __eq__(self, other):
192
- return self._name == other._name
193
-
194
- def __ne__(self, other):
195
- return self._name != other._name
196
-
197
- def __lt__(self, other):
198
- return self._name < other._name
199
-
200
-
201
- @python_2_unicode_compatible
202
- class Lemma(_WordNetObject):
203
- """
204
- The lexical entry for a single morphological form of a
205
- sense-disambiguated word.
206
-
207
- Create a Lemma from a "<word>.<pos>.<number>.<lemma>" string where:
208
- <word> is the morphological stem identifying the synset
209
- <pos> is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB
210
- <number> is the sense number, counting from 0.
211
- <lemma> is the morphological form of interest
212
-
213
- Note that <word> and <lemma> can be different, e.g. the Synset
214
- 'salt.n.03' has the Lemmas 'salt.n.03.salt', 'salt.n.03.saltiness' and
215
- 'salt.n.03.salinity'.
216
-
217
- Lemma attributes, accessible via methods with the same name::
218
-
219
- - name: The canonical name of this lemma.
220
- - synset: The synset that this lemma belongs to.
221
- - syntactic_marker: For adjectives, the WordNet string identifying the
222
- syntactic position relative modified noun. See:
223
- http://wordnet.princeton.edu/man/wninput.5WN.html#sect10
224
- For all other parts of speech, this attribute is None.
225
- - count: The frequency of this lemma in wordnet.
226
-
227
- Lemma methods:
228
-
229
- Lemmas have the following methods for retrieving related Lemmas. They
230
- correspond to the names for the pointer symbols defined here:
231
- http://wordnet.princeton.edu/man/wninput.5WN.html#sect3
232
- These methods all return lists of Lemmas:
233
-
234
- - antonyms
235
- - hypernyms, instance_hypernyms
236
- - hyponyms, instance_hyponyms
237
- - member_holonyms, substance_holonyms, part_holonyms
238
- - member_meronyms, substance_meronyms, part_meronyms
239
- - topic_domains, region_domains, usage_domains
240
- - attributes
241
- - derivationally_related_forms
242
- - entailments
243
- - causes
244
- - also_sees
245
- - verb_groups
246
- - similar_tos
247
- - pertainyms
248
- """
249
-
250
- __slots__ = ['_wordnet_corpus_reader', '_name', '_syntactic_marker',
251
- '_synset', '_frame_strings', '_frame_ids',
252
- '_lexname_index', '_lex_id', '_lang', '_key']
253
-
254
- def __init__(self, wordnet_corpus_reader, synset, name,
255
- lexname_index, lex_id, syntactic_marker):
256
- self._wordnet_corpus_reader = wordnet_corpus_reader
257
- self._name = name
258
- self._syntactic_marker = syntactic_marker
259
- self._synset = synset
260
- self._frame_strings = []
261
- self._frame_ids = []
262
- self._lexname_index = lexname_index
263
- self._lex_id = lex_id
264
- self._lang = 'eng'
265
-
266
- self._key = None # gets set later.
267
-
268
- def name(self):
269
- return self._name
270
-
271
- def syntactic_marker(self):
272
- return self._syntactic_marker
273
-
274
- def synset(self):
275
- return self._synset
276
-
277
- def frame_strings(self):
278
- return self._frame_strings
279
-
280
- def frame_ids(self):
281
- return self._frame_ids
282
-
283
- def lang(self):
284
- return self._lang
285
-
286
- def key(self):
287
- return self._key
288
-
289
- def __repr__(self):
290
- tup = type(self).__name__, self._synset._name, self._name
291
- return "%s('%s.%s')" % tup
292
-
293
- def _related(self, relation_symbol):
294
- get_synset = self._wordnet_corpus_reader._synset_from_pos_and_offset
295
- return sorted([get_synset(pos, offset)._lemmas[lemma_index]
296
- for pos, offset, lemma_index
297
- in self._synset._lemma_pointers[self._name, relation_symbol]])
298
-
299
- def count(self):
300
- """Return the frequency count for this Lemma"""
301
- return self._wordnet_corpus_reader.lemma_count(self)
302
-
303
- def antonyms(self):
304
- return self._related('!')
305
-
306
- def derivationally_related_forms(self):
307
- return self._related('+')
308
-
309
- def pertainyms(self):
310
- return self._related('\\')
311
-
312
-
313
- @python_2_unicode_compatible
314
- class Synset(_WordNetObject):
315
- """Create a Synset from a "<lemma>.<pos>.<number>" string where:
316
- <lemma> is the word's morphological stem
317
- <pos> is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB
318
- <number> is the sense number, counting from 0.
319
-
320
- Synset attributes, accessible via methods with the same name:
321
-
322
- - name: The canonical name of this synset, formed using the first lemma
323
- of this synset. Note that this may be different from the name
324
- passed to the constructor if that string used a different lemma to
325
- identify the synset.
326
- - pos: The synset's part of speech, matching one of the module level
327
- attributes ADJ, ADJ_SAT, ADV, NOUN or VERB.
328
- - lemmas: A list of the Lemma objects for this synset.
329
- - definition: The definition for this synset.
330
- - examples: A list of example strings for this synset.
331
- - offset: The offset in the WordNet dict file of this synset.
332
- - lexname: The name of the lexicographer file containing this synset.
333
-
334
- Synset methods:
335
-
336
- Synsets have the following methods for retrieving related Synsets.
337
- They correspond to the names for the pointer symbols defined here:
338
- http://wordnet.princeton.edu/man/wninput.5WN.html#sect3
339
- These methods all return lists of Synsets.
340
-
341
- - hypernyms, instance_hypernyms
342
- - hyponyms, instance_hyponyms
343
- - member_holonyms, substance_holonyms, part_holonyms
344
- - member_meronyms, substance_meronyms, part_meronyms
345
- - attributes
346
- - entailments
347
- - causes
348
- - also_sees
349
- - verb_groups
350
- - similar_tos
351
-
352
- Additionally, Synsets support the following methods specific to the
353
- hypernym relation:
354
-
355
- - root_hypernyms
356
- - common_hypernyms
357
- - lowest_common_hypernyms
358
-
359
- Note that Synsets do not support the following relations because
360
- these are defined by WordNet as lexical relations:
361
-
362
- - antonyms
363
- - derivationally_related_forms
364
- - pertainyms
365
- """
366
-
367
- __slots__ = ['_pos', '_offset', '_name', '_frame_ids',
368
- '_lemmas', '_lemma_names',
369
- '_definition', '_examples', '_lexname',
370
- '_pointers', '_lemma_pointers', '_max_depth',
371
- '_min_depth']
372
-
373
- def __init__(self, wordnet_corpus_reader):
374
- self._wordnet_corpus_reader = wordnet_corpus_reader
375
- # All of these attributes get initialized by
376
- # WordNetCorpusReader._synset_from_pos_and_line()
377
-
378
- self._pos = None
379
- self._offset = None
380
- self._name = None
381
- self._frame_ids = []
382
- self._lemmas = []
383
- self._lemma_names = []
384
- self._definition = None
385
- self._examples = []
386
- self._lexname = None # lexicographer name
387
- self._all_hypernyms = None
388
-
389
- self._pointers = defaultdict(set)
390
- self._lemma_pointers = defaultdict(set)
391
-
392
- def pos(self):
393
- return self._pos
394
-
395
- def offset(self):
396
- return self._offset
397
-
398
- def name(self):
399
- return self._name
400
-
401
- def frame_ids(self):
402
- return self._frame_ids
403
-
404
- def definition(self):
405
- return self._definition
406
-
407
- def examples(self):
408
- return self._examples
409
-
410
- def lexname(self):
411
- return self._lexname
412
-
413
- def _needs_root(self):
414
- if self._pos == NOUN:
415
- if self._wordnet_corpus_reader.get_version() == '1.6':
416
- return True
417
- else:
418
- return False
419
- elif self._pos == VERB:
420
- return True
421
-
422
- def lemma_names(self, lang='eng'):
423
- '''Return all the lemma_names associated with the synset'''
424
- if lang=='eng':
425
- return self._lemma_names
426
- else:
427
- self._wordnet_corpus_reader._load_lang_data(lang)
428
-
429
- i = self._wordnet_corpus_reader.ss2of(self)
430
- if i in self._wordnet_corpus_reader._lang_data[lang][0]:
431
- return self._wordnet_corpus_reader._lang_data[lang][0][i]
432
- else:
433
- return []
434
-
435
- def lemmas(self, lang='eng'):
436
- '''Return all the lemma objects associated with the synset'''
437
- if lang=='eng':
438
- return self._lemmas
439
- else:
440
- self._wordnet_corpus_reader._load_lang_data(lang)
441
- lemmark = []
442
- lemmy = self.lemma_names(lang)
443
- for lem in lemmy:
444
- temp= Lemma(self._wordnet_corpus_reader, self, lem, self._wordnet_corpus_reader._lexnames.index(self.lexname()), 0, None)
445
- temp._lang=lang
446
- lemmark.append(temp)
447
- return lemmark
448
-
449
- def root_hypernyms(self):
450
- """Get the topmost hypernyms of this synset in WordNet."""
451
-
452
- result = []
453
- seen = set()
454
- todo = [self]
455
- while todo:
456
- next_synset = todo.pop()
457
- if next_synset not in seen:
458
- seen.add(next_synset)
459
- next_hypernyms = next_synset.hypernyms() + \
460
- next_synset.instance_hypernyms()
461
- if not next_hypernyms:
462
- result.append(next_synset)
463
- else:
464
- todo.extend(next_hypernyms)
465
- return result
466
-
467
- # Simpler implementation which makes incorrect assumption that
468
- # hypernym hierarchy is acyclic:
469
- #
470
- # if not self.hypernyms():
471
- # return [self]
472
- # else:
473
- # return list(set(root for h in self.hypernyms()
474
- # for root in h.root_hypernyms()))
475
- def max_depth(self):
476
- """
477
- :return: The length of the longest hypernym path from this
478
- synset to the root.
479
- """
480
-
481
- if "_max_depth" not in self.__dict__:
482
- hypernyms = self.hypernyms() + self.instance_hypernyms()
483
- if not hypernyms:
484
- self._max_depth = 0
485
- else:
486
- self._max_depth = 1 + max(h.max_depth() for h in hypernyms)
487
- return self._max_depth
488
-
489
- def min_depth(self):
490
- """
491
- :return: The length of the shortest hypernym path from this
492
- synset to the root.
493
- """
494
-
495
- if "_min_depth" not in self.__dict__:
496
- hypernyms = self.hypernyms() + self.instance_hypernyms()
497
- if not hypernyms:
498
- self._min_depth = 0
499
- else:
500
- self._min_depth = 1 + min(h.min_depth() for h in hypernyms)
501
- return self._min_depth
502
-
503
- def closure(self, rel, depth=-1):
504
- """Return the transitive closure of source under the rel
505
- relationship, breadth-first
506
-
507
- >>> from nltk.corpus import wordnet as wn
508
- >>> dog = wn.synset('dog.n.01')
509
- >>> hyp = lambda s:s.hypernyms()
510
- >>> list(dog.closure(hyp))
511
- [Synset('canine.n.02'), Synset('domestic_animal.n.01'),
512
- Synset('carnivore.n.01'), Synset('animal.n.01'),
513
- Synset('placental.n.01'), Synset('organism.n.01'),
514
- Synset('mammal.n.01'), Synset('living_thing.n.01'),
515
- Synset('vertebrate.n.01'), Synset('whole.n.02'),
516
- Synset('chordate.n.01'), Synset('object.n.01'),
517
- Synset('physical_entity.n.01'), Synset('entity.n.01')]
518
-
519
- """
520
- from nltk.util import breadth_first
521
- synset_offsets = []
522
- for synset in breadth_first(self, rel, depth):
523
- if synset._offset != self._offset:
524
- if synset._offset not in synset_offsets:
525
- synset_offsets.append(synset._offset)
526
- yield synset
527
-
528
- def hypernym_paths(self):
529
- """
530
- Get the path(s) from this synset to the root, where each path is a
531
- list of the synset nodes traversed on the way to the root.
532
-
533
- :return: A list of lists, where each list gives the node sequence
534
- connecting the initial ``Synset`` node and a root node.
535
- """
536
- paths = []
537
-
538
- hypernyms = self.hypernyms() + self.instance_hypernyms()
539
- if len(hypernyms) == 0:
540
- paths = [[self]]
541
-
542
- for hypernym in hypernyms:
543
- for ancestor_list in hypernym.hypernym_paths():
544
- ancestor_list.append(self)
545
- paths.append(ancestor_list)
546
- return paths
547
-
548
- def common_hypernyms(self, other):
549
- """
550
- Find all synsets that are hypernyms of this synset and the
551
- other synset.
552
-
553
- :type other: Synset
554
- :param other: other input synset.
555
- :return: The synsets that are hypernyms of both synsets.
556
- """
557
- if not self._all_hypernyms:
558
- self._all_hypernyms = set(self_synset
559
- for self_synsets in self._iter_hypernym_lists()
560
- for self_synset in self_synsets)
561
- if not other._all_hypernyms:
562
- other._all_hypernyms = set(other_synset
563
- for other_synsets in other._iter_hypernym_lists()
564
- for other_synset in other_synsets)
565
- return list(self._all_hypernyms.intersection(other._all_hypernyms))
566
-
567
- def lowest_common_hypernyms(self, other, simulate_root=False, use_min_depth=False):
568
- """
569
- Get a list of lowest synset(s) that both synsets have as a hypernym.
570
- When `use_min_depth == False` this means that the synset which appears as a
571
- hypernym of both `self` and `other` with the lowest maximum depth is returned
572
- or if there are multiple such synsets at the same depth they are all returned
573
-
574
- However, if `use_min_depth == True` then the synset(s) which has/have the lowest
575
- minimum depth and appear(s) in both paths is/are returned.
576
-
577
- By setting the use_min_depth flag to True, the behavior of NLTK2 can be preserved.
578
- This was changed in NLTK3 to give more accurate results in a small set of cases,
579
- generally with synsets concerning people. (eg: 'chef.n.01', 'fireman.n.01', etc.)
580
-
581
- This method is an implementation of Ted Pedersen's "Lowest Common Subsumer" method
582
- from the Perl Wordnet module. It can return either "self" or "other" if they are a
583
- hypernym of the other.
584
-
585
- :type other: Synset
586
- :param other: other input synset
587
- :type simulate_root: bool
588
- :param simulate_root: The various verb taxonomies do not
589
- share a single root which disallows this metric from working for
590
- synsets that are not connected. This flag (False by default)
591
- creates a fake root that connects all the taxonomies. Set it
592
- to True to enable this behavior. For the noun taxonomy,
593
- there is usually a default root except for WordNet version 1.6.
594
- If you are using wordnet 1.6, a fake root will need to be added
595
- for nouns as well.
596
- :type use_min_depth: bool
597
- :param use_min_depth: This setting mimics older (v2) behavior of NLTK wordnet
598
- If True, will use the min_depth function to calculate the lowest common
599
- hypernyms. This is known to give strange results for some synset pairs
600
- (eg: 'chef.n.01', 'fireman.n.01') but is retained for backwards compatibility
601
- :return: The synsets that are the lowest common hypernyms of both synsets
602
- """
603
- synsets = self.common_hypernyms(other)
604
- if simulate_root:
605
- fake_synset = Synset(None)
606
- fake_synset._name = '*ROOT*'
607
- fake_synset.hypernyms = lambda: []
608
- fake_synset.instance_hypernyms = lambda: []
609
- synsets.append(fake_synset)
610
-
611
- try:
612
- if use_min_depth:
613
- max_depth = max(s.min_depth() for s in synsets)
614
- unsorted_lch = [s for s in synsets if s.min_depth() == max_depth]
615
- else:
616
- max_depth = max(s.max_depth() for s in synsets)
617
- unsorted_lch = [s for s in synsets if s.max_depth() == max_depth]
618
- return sorted(unsorted_lch)
619
- except ValueError:
620
- return []
621
-
622
- def hypernym_distances(self, distance=0, simulate_root=False):
623
- """
624
- Get the path(s) from this synset to the root, counting the distance
625
- of each node from the initial node on the way. A set of
626
- (synset, distance) tuples is returned.
627
-
628
- :type distance: int
629
- :param distance: the distance (number of edges) from this hypernym to
630
- the original hypernym ``Synset`` on which this method was called.
631
- :return: A set of ``(Synset, int)`` tuples where each ``Synset`` is
632
- a hypernym of the first ``Synset``.
633
- """
634
- distances = set([(self, distance)])
635
- for hypernym in self._hypernyms() + self._instance_hypernyms():
636
- distances |= hypernym.hypernym_distances(distance+1, simulate_root=False)
637
- if simulate_root:
638
- fake_synset = Synset(None)
639
- fake_synset._name = '*ROOT*'
640
- fake_synset_distance = max(distances, key=itemgetter(1))[1]
641
- distances.add((fake_synset, fake_synset_distance+1))
642
- return distances
643
-
644
- def _shortest_hypernym_paths(self, simulate_root):
645
- if self._name == '*ROOT*':
646
- return {self: 0}
647
-
648
- queue = deque([(self, 0)])
649
- path = {}
650
-
651
- while queue:
652
- s, depth = queue.popleft()
653
- if s in path:
654
- continue
655
- path[s] = depth
656
-
657
- depth += 1
658
- queue.extend((hyp, depth) for hyp in s._hypernyms())
659
- queue.extend((hyp, depth) for hyp in s._instance_hypernyms())
660
-
661
- if simulate_root:
662
- fake_synset = Synset(None)
663
- fake_synset._name = '*ROOT*'
664
- path[fake_synset] = max(path.values()) + 1
665
-
666
- return path
667
-
668
- def shortest_path_distance(self, other, simulate_root=False):
669
- """
670
- Returns the distance of the shortest path linking the two synsets (if
671
- one exists). For each synset, all the ancestor nodes and their
672
- distances are recorded and compared. The ancestor node common to both
673
- synsets that can be reached with the minimum number of traversals is
674
- used. If no ancestor nodes are common, None is returned. If a node is
675
- compared with itself 0 is returned.
676
-
677
- :type other: Synset
678
- :param other: The Synset to which the shortest path will be found.
679
- :return: The number of edges in the shortest path connecting the two
680
- nodes, or None if no path exists.
681
- """
682
-
683
- if self == other:
684
- return 0
685
-
686
- dist_dict1 = self._shortest_hypernym_paths(simulate_root)
687
- dist_dict2 = other._shortest_hypernym_paths(simulate_root)
688
-
689
- # For each ancestor synset common to both subject synsets, find the
690
- # connecting path length. Return the shortest of these.
691
-
692
- inf = float('inf')
693
- path_distance = inf
694
- for synset, d1 in iteritems(dist_dict1):
695
- d2 = dist_dict2.get(synset, inf)
696
- path_distance = min(path_distance, d1 + d2)
697
-
698
- return None if math.isinf(path_distance) else path_distance
699
-
700
- def tree(self, rel, depth=-1, cut_mark=None):
701
- """
702
- >>> from nltk.corpus import wordnet as wn
703
- >>> dog = wn.synset('dog.n.01')
704
- >>> hyp = lambda s:s.hypernyms()
705
- >>> from pprint import pprint
706
- >>> pprint(dog.tree(hyp))
707
- [Synset('dog.n.01'),
708
- [Synset('canine.n.02'),
709
- [Synset('carnivore.n.01'),
710
- [Synset('placental.n.01'),
711
- [Synset('mammal.n.01'),
712
- [Synset('vertebrate.n.01'),
713
- [Synset('chordate.n.01'),
714
- [Synset('animal.n.01'),
715
- [Synset('organism.n.01'),
716
- [Synset('living_thing.n.01'),
717
- [Synset('whole.n.02'),
718
- [Synset('object.n.01'),
719
- [Synset('physical_entity.n.01'),
720
- [Synset('entity.n.01')]]]]]]]]]]]]],
721
- [Synset('domestic_animal.n.01'),
722
- [Synset('animal.n.01'),
723
- [Synset('organism.n.01'),
724
- [Synset('living_thing.n.01'),
725
- [Synset('whole.n.02'),
726
- [Synset('object.n.01'),
727
- [Synset('physical_entity.n.01'), [Synset('entity.n.01')]]]]]]]]]
728
- """
729
-
730
- tree = [self]
731
- if depth != 0:
732
- tree += [x.tree(rel, depth-1, cut_mark) for x in rel(self)]
733
- elif cut_mark:
734
- tree += [cut_mark]
735
- return tree
736
-
737
- # interface to similarity methods
738
- def path_similarity(self, other, verbose=False, simulate_root=True):
739
- """
740
- Path Distance Similarity:
741
- Return a score denoting how similar two word senses are, based on the
742
- shortest path that connects the senses in the is-a (hypernym/hypnoym)
743
- taxonomy. The score is in the range 0 to 1, except in those cases where
744
- a path cannot be found (will only be true for verbs as there are many
745
- distinct verb taxonomies), in which case None is returned. A score of
746
- 1 represents identity i.e. comparing a sense with itself will return 1.
747
-
748
- :type other: Synset
749
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
750
- :type simulate_root: bool
751
- :param simulate_root: The various verb taxonomies do not
752
- share a single root which disallows this metric from working for
753
- synsets that are not connected. This flag (True by default)
754
- creates a fake root that connects all the taxonomies. Set it
755
- to false to disable this behavior. For the noun taxonomy,
756
- there is usually a default root except for WordNet version 1.6.
757
- If you are using wordnet 1.6, a fake root will be added for nouns
758
- as well.
759
- :return: A score denoting the similarity of the two ``Synset`` objects,
760
- normally between 0 and 1. None is returned if no connecting path
761
- could be found. 1 is returned if a ``Synset`` is compared with
762
- itself.
763
- """
764
-
765
- distance = self.shortest_path_distance(other, simulate_root=simulate_root and self._needs_root())
766
- if distance is None or distance < 0:
767
- return None
768
- return 1.0 / (distance + 1)
769
-
770
- def lch_similarity(self, other, verbose=False, simulate_root=True):
771
- """
772
- Leacock Chodorow Similarity:
773
- Return a score denoting how similar two word senses are, based on the
774
- shortest path that connects the senses (as above) and the maximum depth
775
- of the taxonomy in which the senses occur. The relationship is given as
776
- -log(p/2d) where p is the shortest path length and d is the taxonomy
777
- depth.
778
-
779
- :type other: Synset
780
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
781
- :type simulate_root: bool
782
- :param simulate_root: The various verb taxonomies do not
783
- share a single root which disallows this metric from working for
784
- synsets that are not connected. This flag (True by default)
785
- creates a fake root that connects all the taxonomies. Set it
786
- to false to disable this behavior. For the noun taxonomy,
787
- there is usually a default root except for WordNet version 1.6.
788
- If you are using wordnet 1.6, a fake root will be added for nouns
789
- as well.
790
- :return: A score denoting the similarity of the two ``Synset`` objects,
791
- normally greater than 0. None is returned if no connecting path
792
- could be found. If a ``Synset`` is compared with itself, the
793
- maximum score is returned, which varies depending on the taxonomy
794
- depth.
795
- """
796
-
797
- if self._pos != other._pos:
798
- raise WordNetError('Computing the lch similarity requires ' + \
799
- '%s and %s to have the same part of speech.' % \
800
- (self, other))
801
-
802
- need_root = self._needs_root()
803
-
804
- if self._pos not in self._wordnet_corpus_reader._max_depth:
805
- self._wordnet_corpus_reader._compute_max_depth(self._pos, need_root)
806
-
807
- depth = self._wordnet_corpus_reader._max_depth[self._pos]
808
-
809
- distance = self.shortest_path_distance(other, simulate_root=simulate_root and need_root)
810
-
811
- if distance is None or distance < 0 or depth == 0:
812
- return None
813
- return -math.log((distance + 1) / (2.0 * depth))
814
-
815
- def wup_similarity(self, other, verbose=False, simulate_root=True):
816
- """
817
- Wu-Palmer Similarity:
818
- Return a score denoting how similar two word senses are, based on the
819
- depth of the two senses in the taxonomy and that of their Least Common
820
- Subsumer (most specific ancestor node). Previously, the scores computed
821
- by this implementation did _not_ always agree with those given by
822
- Pedersen's Perl implementation of WordNet Similarity. However, with
823
- the addition of the simulate_root flag (see below), the score for
824
- verbs now almost always agree but not always for nouns.
825
-
826
- The LCS does not necessarily feature in the shortest path connecting
827
- the two senses, as it is by definition the common ancestor deepest in
828
- the taxonomy, not closest to the two senses. Typically, however, it
829
- will so feature. Where multiple candidates for the LCS exist, that
830
- whose shortest path to the root node is the longest will be selected.
831
- Where the LCS has multiple paths to the root, the longer path is used
832
- for the purposes of the calculation.
833
-
834
- :type other: Synset
835
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
836
- :type simulate_root: bool
837
- :param simulate_root: The various verb taxonomies do not
838
- share a single root which disallows this metric from working for
839
- synsets that are not connected. This flag (True by default)
840
- creates a fake root that connects all the taxonomies. Set it
841
- to false to disable this behavior. For the noun taxonomy,
842
- there is usually a default root except for WordNet version 1.6.
843
- If you are using wordnet 1.6, a fake root will be added for nouns
844
- as well.
845
- :return: A float score denoting the similarity of the two ``Synset`` objects,
846
- normally greater than zero. If no connecting path between the two
847
- senses can be found, None is returned.
848
-
849
- """
850
-
851
- need_root = self._needs_root()
852
- # Note that to preserve behavior from NLTK2 we set use_min_depth=True
853
- # It is possible that more accurate results could be obtained by
854
- # removing this setting and it should be tested later on
855
- subsumers = self.lowest_common_hypernyms(other, simulate_root=simulate_root and need_root, use_min_depth=True)
856
-
857
- # If no LCS was found return None
858
- if len(subsumers) == 0:
859
- return None
860
-
861
- subsumer = subsumers[0]
862
-
863
- # Get the longest path from the LCS to the root,
864
- # including a correction:
865
- # - add one because the calculations include both the start and end
866
- # nodes
867
- depth = subsumer.max_depth() + 1
868
-
869
- # Note: No need for an additional add-one correction for non-nouns
870
- # to account for an imaginary root node because that is now automatically
871
- # handled by simulate_root
872
- # if subsumer._pos != NOUN:
873
- # depth += 1
874
-
875
- # Get the shortest path from the LCS to each of the synsets it is
876
- # subsuming. Add this to the LCS path length to get the path
877
- # length from each synset to the root.
878
- len1 = self.shortest_path_distance(subsumer, simulate_root=simulate_root and need_root)
879
- len2 = other.shortest_path_distance(subsumer, simulate_root=simulate_root and need_root)
880
- if len1 is None or len2 is None:
881
- return None
882
- len1 += depth
883
- len2 += depth
884
- return (2.0 * depth) / (len1 + len2)
885
-
886
- def res_similarity(self, other, ic, verbose=False):
887
- """
888
- Resnik Similarity:
889
- Return a score denoting how similar two word senses are, based on the
890
- Information Content (IC) of the Least Common Subsumer (most specific
891
- ancestor node).
892
-
893
- :type other: Synset
894
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
895
- :type ic: dict
896
- :param ic: an information content object (as returned by ``nltk.corpus.wordnet_ic.ic()``).
897
- :return: A float score denoting the similarity of the two ``Synset`` objects.
898
- Synsets whose LCS is the root node of the taxonomy will have a
899
- score of 0 (e.g. N['dog'][0] and N['table'][0]).
900
- """
901
-
902
- ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
903
- return lcs_ic
904
-
905
- def jcn_similarity(self, other, ic, verbose=False):
906
- """
907
- Jiang-Conrath Similarity:
908
- Return a score denoting how similar two word senses are, based on the
909
- Information Content (IC) of the Least Common Subsumer (most specific
910
- ancestor node) and that of the two input Synsets. The relationship is
911
- given by the equation 1 / (IC(s1) + IC(s2) - 2 * IC(lcs)).
912
-
913
- :type other: Synset
914
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
915
- :type ic: dict
916
- :param ic: an information content object (as returned by ``nltk.corpus.wordnet_ic.ic()``).
917
- :return: A float score denoting the similarity of the two ``Synset`` objects.
918
- """
919
-
920
- if self == other:
921
- return _INF
922
-
923
- ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
924
-
925
- # If either of the input synsets are the root synset, or have a
926
- # frequency of 0 (sparse data problem), return 0.
927
- if ic1 == 0 or ic2 == 0:
928
- return 0
929
-
930
- ic_difference = ic1 + ic2 - 2 * lcs_ic
931
-
932
- if ic_difference == 0:
933
- return _INF
934
-
935
- return 1 / ic_difference
936
-
937
- def lin_similarity(self, other, ic, verbose=False):
938
- """
939
- Lin Similarity:
940
- Return a score denoting how similar two word senses are, based on the
941
- Information Content (IC) of the Least Common Subsumer (most specific
942
- ancestor node) and that of the two input Synsets. The relationship is
943
- given by the equation 2 * IC(lcs) / (IC(s1) + IC(s2)).
944
-
945
- :type other: Synset
946
- :param other: The ``Synset`` that this ``Synset`` is being compared to.
947
- :type ic: dict
948
- :param ic: an information content object (as returned by ``nltk.corpus.wordnet_ic.ic()``).
949
- :return: A float score denoting the similarity of the two ``Synset`` objects,
950
- in the range 0 to 1.
951
- """
952
-
953
- ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
954
- return (2.0 * lcs_ic) / (ic1 + ic2)
955
-
956
- def _iter_hypernym_lists(self):
957
- """
958
- :return: An iterator over ``Synset`` objects that are either proper
959
- hypernyms or instance of hypernyms of the synset.
960
- """
961
- todo = [self]
962
- seen = set()
963
- while todo:
964
- for synset in todo:
965
- seen.add(synset)
966
- yield todo
967
- todo = [hypernym
968
- for synset in todo
969
- for hypernym in (synset.hypernyms() +
970
- synset.instance_hypernyms())
971
- if hypernym not in seen]
972
-
973
- def __repr__(self):
974
- return "%s('%s')" % (type(self).__name__, self._name)
975
-
976
- def _related(self, relation_symbol, sort=True):
977
- get_synset = self._wordnet_corpus_reader._synset_from_pos_and_offset
978
- pointer_tuples = self._pointers[relation_symbol]
979
- r = [get_synset(pos, offset) for pos, offset in pointer_tuples]
980
- if sort:
981
- r.sort()
982
- return r
983
-
984
-
985
- ######################################################################
986
- ## WordNet Corpus Reader
987
- ######################################################################
988
-
989
- class WordNetCorpusReader(CorpusReader):
990
- """
991
- A corpus reader used to access wordnet or its variants.
992
- """
993
-
994
- _ENCODING = 'utf8'
995
-
996
- #{ Part-of-speech constants
997
- ADJ, ADJ_SAT, ADV, NOUN, VERB = 'a', 's', 'r', 'n', 'v'
998
- #}
999
-
1000
- #{ Filename constants
1001
- _FILEMAP = {ADJ: 'adj', ADV: 'adv', NOUN: 'noun', VERB: 'verb'}
1002
- #}
1003
-
1004
- #{ Part of speech constants
1005
- _pos_numbers = {NOUN: 1, VERB: 2, ADJ: 3, ADV: 4, ADJ_SAT: 5}
1006
- _pos_names = dict(tup[::-1] for tup in _pos_numbers.items())
1007
- #}
1008
-
1009
- #: A list of file identifiers for all the fileids used by this
1010
- #: corpus reader.
1011
- _FILES = ('cntlist.rev', 'lexnames', 'index.sense',
1012
- 'index.adj', 'index.adv', 'index.noun', 'index.verb',
1013
- 'data.adj', 'data.adv', 'data.noun', 'data.verb',
1014
- 'adj.exc', 'adv.exc', 'noun.exc', 'verb.exc', )
1015
-
1016
- def __init__(self, root, omw_reader):
1017
- """
1018
- Construct a new wordnet corpus reader, with the given root
1019
- directory.
1020
- """
1021
- super(WordNetCorpusReader, self).__init__(root, self._FILES,
1022
- encoding=self._ENCODING)
1023
-
1024
- # A index that provides the file offset
1025
- # Map from lemma -> pos -> synset_index -> offset
1026
- self._lemma_pos_offset_map = defaultdict(dict)
1027
-
1028
- # A cache so we don't have to reconstuct synsets
1029
- # Map from pos -> offset -> synset
1030
- self._synset_offset_cache = defaultdict(dict)
1031
-
1032
- # A lookup for the maximum depth of each part of speech. Useful for
1033
- # the lch similarity metric.
1034
- self._max_depth = defaultdict(dict)
1035
-
1036
- # Corpus reader containing omw data.
1037
- self._omw_reader = omw_reader
1038
-
1039
- # A cache to store the wordnet data of multiple languages
1040
- self._lang_data = defaultdict(list)
1041
-
1042
- self._data_file_map = {}
1043
- self._exception_map = {}
1044
- self._lexnames = []
1045
- self._key_count_file = None
1046
- self._key_synset_file = None
1047
-
1048
- # Load the lexnames
1049
- for i, line in enumerate(self.open('lexnames')):
1050
- index, lexname, _ = line.split()
1051
- assert int(index) == i
1052
- self._lexnames.append(lexname)
1053
-
1054
- # Load the indices for lemmas and synset offsets
1055
- self._load_lemma_pos_offset_map()
1056
-
1057
- # load the exception file data into memory
1058
- self._load_exception_map()
1059
-
1060
- # Open Multilingual WordNet functions, contributed by
1061
- # Nasruddin A’aidil Shari, Sim Wei Ying Geraldine, and Soe Lynn
1062
-
1063
- def of2ss(self, of):
1064
- ''' take an id and return the synsets '''
1065
- return self._synset_from_pos_and_offset(of[-1], int(of[:8]))
1066
-
1067
- def ss2of(self, ss):
1068
- ''' return the ID of the synset '''
1069
- return ("{:08d}-{}".format(ss.offset(), ss.pos()))
1070
-
1071
- def _load_lang_data(self, lang):
1072
- ''' load the wordnet data of the requested language from the file to the cache, _lang_data '''
1073
-
1074
- if lang not in self.langs():
1075
- raise WordNetError("Language is not supported.")
1076
-
1077
- if lang in self._lang_data.keys():
1078
- return
1079
-
1080
- f = self._omw_reader.open('{0:}/wn-data-{0:}.tab'.format(lang))
1081
-
1082
- self._lang_data[lang].append(defaultdict(list))
1083
- self._lang_data[lang].append(defaultdict(list))
1084
-
1085
- for l in f.readlines():
1086
- l = l.replace('\n', '')
1087
- l = l.replace(' ', '_')
1088
- if l[0] != '#':
1089
- word = l.split('\t')
1090
- self._lang_data[lang][0][word[0]].append(word[2])
1091
- self._lang_data[lang][1][word[2]].append(word[0])
1092
- f.close()
1093
-
1094
- def langs(self):
1095
- ''' return a list of languages supported by Multilingual Wordnet '''
1096
- import os
1097
- langs = [ 'eng' ]
1098
- fileids = self._omw_reader.fileids()
1099
- for fileid in fileids:
1100
- file_name, file_extension = os.path.splitext(fileid)
1101
- if file_extension == '.tab':
1102
- langs.append(file_name.split('-')[-1])
1103
-
1104
- return langs
1105
-
1106
-
1107
- def _load_lemma_pos_offset_map(self):
1108
- for suffix in self._FILEMAP.values():
1109
-
1110
- # parse each line of the file (ignoring comment lines)
1111
- for i, line in enumerate(self.open('index.%s' % suffix)):
1112
- if line.startswith(' '):
1113
- continue
1114
-
1115
- _iter = iter(line.split())
1116
- _next_token = lambda: next(_iter)
1117
- try:
1118
-
1119
- # get the lemma and part-of-speech
1120
- lemma = _next_token()
1121
- pos = _next_token()
1122
-
1123
- # get the number of synsets for this lemma
1124
- n_synsets = int(_next_token())
1125
- assert n_synsets > 0
1126
-
1127
- # get the pointer symbols for all synsets of this lemma
1128
- n_pointers = int(_next_token())
1129
- _ = [_next_token() for _ in xrange(n_pointers)]
1130
-
1131
- # same as number of synsets
1132
- n_senses = int(_next_token())
1133
- assert n_synsets == n_senses
1134
-
1135
- # get number of senses ranked according to frequency
1136
- _ = int(_next_token())
1137
-
1138
- # get synset offsets
1139
- synset_offsets = [int(_next_token()) for _ in xrange(n_synsets)]
1140
-
1141
- # raise more informative error with file name and line number
1142
- except (AssertionError, ValueError) as e:
1143
- tup = ('index.%s' % suffix), (i + 1), e
1144
- raise WordNetError('file %s, line %i: %s' % tup)
1145
-
1146
- # map lemmas and parts of speech to synsets
1147
- self._lemma_pos_offset_map[lemma][pos] = synset_offsets
1148
- if pos == ADJ:
1149
- self._lemma_pos_offset_map[lemma][ADJ_SAT] = synset_offsets
1150
-
1151
- def _load_exception_map(self):
1152
- # load the exception file data into memory
1153
- for pos, suffix in self._FILEMAP.items():
1154
- self._exception_map[pos] = {}
1155
- for line in self.open('%s.exc' % suffix):
1156
- terms = line.split()
1157
- self._exception_map[pos][terms[0]] = terms[1:]
1158
- self._exception_map[ADJ_SAT] = self._exception_map[ADJ]
1159
-
1160
- def _compute_max_depth(self, pos, simulate_root):
1161
- """
1162
- Compute the max depth for the given part of speech. This is
1163
- used by the lch similarity metric.
1164
- """
1165
- depth = 0
1166
- for ii in self.all_synsets(pos):
1167
- try:
1168
- depth = max(depth, ii.max_depth())
1169
- except RuntimeError:
1170
- print(ii)
1171
- if simulate_root:
1172
- depth += 1
1173
- self._max_depth[pos] = depth
1174
-
1175
- def get_version(self):
1176
- fh = self._data_file(ADJ)
1177
- for line in fh:
1178
- match = re.search(r'WordNet (\d+\.\d+) Copyright', line)
1179
- if match is not None:
1180
- version = match.group(1)
1181
- fh.seek(0)
1182
- return version
1183
-
1184
- #////////////////////////////////////////////////////////////
1185
- # Loading Lemmas
1186
- #////////////////////////////////////////////////////////////
1187
-
1188
- def lemma(self, name, lang='eng'):
1189
- '''Return lemma object that matches the name'''
1190
- # cannot simply split on first '.', e.g.: '.45_caliber.a.01..45_caliber'
1191
- separator = SENSENUM_RE.search(name).start()
1192
- synset_name, lemma_name = name[:separator+3], name[separator+4:]
1193
- synset = self.synset(synset_name)
1194
- for lemma in synset.lemmas(lang):
1195
- if lemma._name == lemma_name:
1196
- return lemma
1197
- raise WordNetError('no lemma %r in %r' % (lemma_name, synset_name))
1198
-
1199
- def lemma_from_key(self, key):
1200
- # Keys are case sensitive and always lower-case
1201
- key = key.lower()
1202
-
1203
- lemma_name, lex_sense = key.split('%')
1204
- pos_number, lexname_index, lex_id, _, _ = lex_sense.split(':')
1205
- pos = self._pos_names[int(pos_number)]
1206
-
1207
- # open the key -> synset file if necessary
1208
- if self._key_synset_file is None:
1209
- self._key_synset_file = self.open('index.sense')
1210
-
1211
- # Find the synset for the lemma.
1212
- synset_line = _binary_search_file(self._key_synset_file, key)
1213
- if not synset_line:
1214
- raise WordNetError("No synset found for key %r" % key)
1215
- offset = int(synset_line.split()[1])
1216
- synset = self._synset_from_pos_and_offset(pos, offset)
1217
-
1218
- # return the corresponding lemma
1219
- for lemma in synset._lemmas:
1220
- if lemma._key == key:
1221
- return lemma
1222
- raise WordNetError("No lemma found for for key %r" % key)
1223
-
1224
- #////////////////////////////////////////////////////////////
1225
- # Loading Synsets
1226
- #////////////////////////////////////////////////////////////
1227
- def synset(self, name):
1228
- # split name into lemma, part of speech and synset number
1229
- lemma, pos, synset_index_str = name.lower().rsplit('.', 2)
1230
- synset_index = int(synset_index_str) - 1
1231
-
1232
- # get the offset for this synset
1233
- try:
1234
- offset = self._lemma_pos_offset_map[lemma][pos][synset_index]
1235
- except KeyError:
1236
- message = 'no lemma %r with part of speech %r'
1237
- raise WordNetError(message % (lemma, pos))
1238
- except IndexError:
1239
- n_senses = len(self._lemma_pos_offset_map[lemma][pos])
1240
- message = "lemma %r with part of speech %r has only %i %s"
1241
- if n_senses == 1:
1242
- tup = lemma, pos, n_senses, "sense"
1243
- else:
1244
- tup = lemma, pos, n_senses, "senses"
1245
- raise WordNetError(message % tup)
1246
-
1247
- # load synset information from the appropriate file
1248
- synset = self._synset_from_pos_and_offset(pos, offset)
1249
-
1250
- # some basic sanity checks on loaded attributes
1251
- if pos == 's' and synset._pos == 'a':
1252
- message = ('adjective satellite requested but only plain '
1253
- 'adjective found for lemma %r')
1254
- raise WordNetError(message % lemma)
1255
- assert synset._pos == pos or (pos == 'a' and synset._pos == 's')
1256
-
1257
- # Return the synset object.
1258
- return synset
1259
-
1260
- def _data_file(self, pos):
1261
- """
1262
- Return an open file pointer for the data file for the given
1263
- part of speech.
1264
- """
1265
- if pos == ADJ_SAT:
1266
- pos = ADJ
1267
- if self._data_file_map.get(pos) is None:
1268
- fileid = 'data.%s' % self._FILEMAP[pos]
1269
- self._data_file_map[pos] = self.open(fileid)
1270
- return self._data_file_map[pos]
1271
-
1272
- def _synset_from_pos_and_offset(self, pos, offset):
1273
- # Check to see if the synset is in the cache
1274
- if offset in self._synset_offset_cache[pos]:
1275
- return self._synset_offset_cache[pos][offset]
1276
-
1277
- data_file = self._data_file(pos)
1278
- data_file.seek(offset)
1279
- data_file_line = data_file.readline()
1280
- synset = self._synset_from_pos_and_line(pos, data_file_line)
1281
- assert synset._offset == offset
1282
- self._synset_offset_cache[pos][offset] = synset
1283
- return synset
1284
-
1285
- def _synset_from_pos_and_line(self, pos, data_file_line):
1286
- # Construct a new (empty) synset.
1287
- synset = Synset(self)
1288
-
1289
- # parse the entry for this synset
1290
- try:
1291
-
1292
- # parse out the definitions and examples from the gloss
1293
- columns_str, gloss = data_file_line.split('|')
1294
- gloss = gloss.strip()
1295
- definitions = []
1296
- for gloss_part in gloss.split(';'):
1297
- gloss_part = gloss_part.strip()
1298
- if gloss_part.startswith('"'):
1299
- synset._examples.append(gloss_part.strip('"'))
1300
- else:
1301
- definitions.append(gloss_part)
1302
- synset._definition = '; '.join(definitions)
1303
-
1304
- # split the other info into fields
1305
- _iter = iter(columns_str.split())
1306
- _next_token = lambda: next(_iter)
1307
-
1308
- # get the offset
1309
- synset._offset = int(_next_token())
1310
-
1311
- # determine the lexicographer file name
1312
- lexname_index = int(_next_token())
1313
- synset._lexname = self._lexnames[lexname_index]
1314
-
1315
- # get the part of speech
1316
- synset._pos = _next_token()
1317
-
1318
- # create Lemma objects for each lemma
1319
- n_lemmas = int(_next_token(), 16)
1320
- for _ in xrange(n_lemmas):
1321
- # get the lemma name
1322
- lemma_name = _next_token()
1323
- # get the lex_id (used for sense_keys)
1324
- lex_id = int(_next_token(), 16)
1325
- # If the lemma has a syntactic marker, extract it.
1326
- m = re.match(r'(.*?)(\(.*\))?$', lemma_name)
1327
- lemma_name, syn_mark = m.groups()
1328
- # create the lemma object
1329
- lemma = Lemma(self, synset, lemma_name, lexname_index,
1330
- lex_id, syn_mark)
1331
- synset._lemmas.append(lemma)
1332
- synset._lemma_names.append(lemma._name)
1333
-
1334
- # collect the pointer tuples
1335
- n_pointers = int(_next_token())
1336
- for _ in xrange(n_pointers):
1337
- symbol = _next_token()
1338
- offset = int(_next_token())
1339
- pos = _next_token()
1340
- lemma_ids_str = _next_token()
1341
- if lemma_ids_str == '0000':
1342
- synset._pointers[symbol].add((pos, offset))
1343
- else:
1344
- source_index = int(lemma_ids_str[:2], 16) - 1
1345
- target_index = int(lemma_ids_str[2:], 16) - 1
1346
- source_lemma_name = synset._lemmas[source_index]._name
1347
- lemma_pointers = synset._lemma_pointers
1348
- tups = lemma_pointers[source_lemma_name, symbol]
1349
- tups.add((pos, offset, target_index))
1350
-
1351
- # read the verb frames
1352
- try:
1353
- frame_count = int(_next_token())
1354
- except StopIteration:
1355
- pass
1356
- else:
1357
- for _ in xrange(frame_count):
1358
- # read the plus sign
1359
- plus = _next_token()
1360
- assert plus == '+'
1361
- # read the frame and lemma number
1362
- frame_number = int(_next_token())
1363
- frame_string_fmt = VERB_FRAME_STRINGS[frame_number]
1364
- lemma_number = int(_next_token(), 16)
1365
- # lemma number of 00 means all words in the synset
1366
- if lemma_number == 0:
1367
- synset._frame_ids.append(frame_number)
1368
- for lemma in synset._lemmas:
1369
- lemma._frame_ids.append(frame_number)
1370
- lemma._frame_strings.append(frame_string_fmt %
1371
- lemma._name)
1372
- # only a specific word in the synset
1373
- else:
1374
- lemma = synset._lemmas[lemma_number - 1]
1375
- lemma._frame_ids.append(frame_number)
1376
- lemma._frame_strings.append(frame_string_fmt %
1377
- lemma._name)
1378
-
1379
- # raise a more informative error with line text
1380
- except ValueError as e:
1381
- raise WordNetError('line %r: %s' % (data_file_line, e))
1382
-
1383
- # set sense keys for Lemma objects - note that this has to be
1384
- # done afterwards so that the relations are available
1385
- for lemma in synset._lemmas:
1386
- if synset._pos == ADJ_SAT:
1387
- head_lemma = synset.similar_tos()[0]._lemmas[0]
1388
- head_name = head_lemma._name
1389
- head_id = '%02d' % head_lemma._lex_id
1390
- else:
1391
- head_name = head_id = ''
1392
- tup = (lemma._name, WordNetCorpusReader._pos_numbers[synset._pos],
1393
- lemma._lexname_index, lemma._lex_id, head_name, head_id)
1394
- lemma._key = ('%s%%%d:%02d:%02d:%s:%s' % tup).lower()
1395
-
1396
- # the canonical name is based on the first lemma
1397
- lemma_name = synset._lemmas[0]._name.lower()
1398
- offsets = self._lemma_pos_offset_map[lemma_name][synset._pos]
1399
- sense_index = offsets.index(synset._offset)
1400
- tup = lemma_name, synset._pos, sense_index + 1
1401
- synset._name = '%s.%s.%02i' % tup
1402
-
1403
- return synset
1404
-
1405
- #////////////////////////////////////////////////////////////
1406
- # Retrieve synsets and lemmas.
1407
- #////////////////////////////////////////////////////////////
1408
-
1409
- def synsets(self, lemma, pos=None, lang='eng'):
1410
- """Load all synsets with a given lemma and part of speech tag.
1411
- If no pos is specified, all synsets for all parts of speech
1412
- will be loaded.
1413
- If lang is specified, all the synsets associated with the lemma name
1414
- of that language will be returned.
1415
- """
1416
- lemma = lemma.lower()
1417
-
1418
- if lang == 'eng':
1419
- get_synset = self._synset_from_pos_and_offset
1420
- index = self._lemma_pos_offset_map
1421
- if pos is None:
1422
- pos = POS_LIST
1423
- return [get_synset(p, offset)
1424
- for p in pos
1425
- for form in self._morphy(lemma, p)
1426
- for offset in index[form].get(p, [])]
1427
-
1428
- else:
1429
- self._load_lang_data(lang)
1430
- synset_list = []
1431
- for l in self._lang_data[lang][1][lemma]:
1432
- if pos is not None and l[-1] != pos:
1433
- continue
1434
- synset_list.append(self.of2ss(l))
1435
- return synset_list
1436
-
1437
- def lemmas(self, lemma, pos=None, lang='eng'):
1438
- """Return all Lemma objects with a name matching the specified lemma
1439
- name and part of speech tag. Matches any part of speech tag if none is
1440
- specified."""
1441
-
1442
- if lang == 'eng':
1443
- lemma = lemma.lower()
1444
- return [lemma_obj
1445
- for synset in self.synsets(lemma, pos)
1446
- for lemma_obj in synset.lemmas()
1447
- if lemma_obj.name().lower() == lemma]
1448
-
1449
- else:
1450
- self._load_lang_data(lang)
1451
- lemmas = []
1452
- syn = self.synsets(lemma, lang=lang)
1453
- for s in syn:
1454
- if pos is not None and s.pos() != pos:
1455
- continue
1456
- a = Lemma(self, s, lemma, self._lexnames.index(s.lexname()), 0, None)
1457
- a._lang = lang
1458
- lemmas.append(a)
1459
- return lemmas
1460
-
1461
- def all_lemma_names(self, pos=None, lang='eng'):
1462
- """Return all lemma names for all synsets for the given
1463
- part of speech tag and language or languages. If pos is not specified, all synsets
1464
- for all parts of speech will be used."""
1465
-
1466
- if lang == 'eng':
1467
- if pos is None:
1468
- return iter(self._lemma_pos_offset_map)
1469
- else:
1470
- return (lemma
1471
- for lemma in self._lemma_pos_offset_map
1472
- if pos in self._lemma_pos_offset_map[lemma])
1473
- else:
1474
- self._load_lang_data(lang)
1475
- lemma = []
1476
- for i in self._lang_data[lang][0]:
1477
- if pos is not None and i[-1] != pos:
1478
- continue
1479
- lemma.extend(self._lang_data[lang][0][i])
1480
-
1481
- lemma = list(set(lemma))
1482
- return lemma
1483
-
1484
- def all_synsets(self, pos=None):
1485
- """Iterate over all synsets with a given part of speech tag.
1486
- If no pos is specified, all synsets for all parts of speech
1487
- will be loaded.
1488
- """
1489
- if pos is None:
1490
- pos_tags = self._FILEMAP.keys()
1491
- else:
1492
- pos_tags = [pos]
1493
-
1494
- cache = self._synset_offset_cache
1495
- from_pos_and_line = self._synset_from_pos_and_line
1496
-
1497
- # generate all synsets for each part of speech
1498
- for pos_tag in pos_tags:
1499
- # Open the file for reading. Note that we can not re-use
1500
- # the file poitners from self._data_file_map here, because
1501
- # we're defining an iterator, and those file pointers might
1502
- # be moved while we're not looking.
1503
- if pos_tag == ADJ_SAT:
1504
- pos_tag = ADJ
1505
- fileid = 'data.%s' % self._FILEMAP[pos_tag]
1506
- data_file = self.open(fileid)
1507
-
1508
- try:
1509
- # generate synsets for each line in the POS file
1510
- offset = data_file.tell()
1511
- line = data_file.readline()
1512
- while line:
1513
- if not line[0].isspace():
1514
- if offset in cache[pos_tag]:
1515
- # See if the synset is cached
1516
- synset = cache[pos_tag][offset]
1517
- else:
1518
- # Otherwise, parse the line
1519
- synset = from_pos_and_line(pos_tag, line)
1520
- cache[pos_tag][offset] = synset
1521
-
1522
- # adjective satellites are in the same file as
1523
- # adjectives so only yield the synset if it's actually
1524
- # a satellite
1525
- if synset._pos == ADJ_SAT:
1526
- yield synset
1527
-
1528
- # for all other POS tags, yield all synsets (this means
1529
- # that adjectives also include adjective satellites)
1530
- else:
1531
- yield synset
1532
- offset = data_file.tell()
1533
- line = data_file.readline()
1534
-
1535
- # close the extra file handle we opened
1536
- except:
1537
- data_file.close()
1538
- raise
1539
- else:
1540
- data_file.close()
1541
-
1542
- def words(self, lang='eng'):
1543
- """return lemmas of the given language as list of words"""
1544
- return self.all_lemma_names(lang=lang)
1545
-
1546
- def license(self, lang='eng'):
1547
- """Return the contents of LICENSE (for omw)
1548
- use lang=lang to get the license for an individual language"""
1549
- if lang == 'eng':
1550
- return self.open("LICENSE").read()
1551
- elif lang in self.langs():
1552
- return self._omw_reader.open("{}/LICENSE".format(lang)).read()
1553
- elif lang == 'omw':
1554
- ### under the not unreasonable assumption you don't mean Omwunra-Toqura
1555
- return self._omw_reader.open("LICENSE").read()
1556
- else:
1557
- raise WordNetError("Language is not supported.")
1558
-
1559
- def readme(self, lang='omw'):
1560
- """Return the contents of README (for omw)
1561
- use lang=lang to get the readme for an individual language"""
1562
- if lang == 'eng':
1563
- return self.open("README").read()
1564
- elif lang in self.langs():
1565
- return self._omw_reader.open("{}/README".format(lang)).read()
1566
- elif lang == 'omw':
1567
- ### under the not unreasonable assumption you don't mean Omwunra-Toqura
1568
- return self._omw_reader.open("README").read()
1569
- else:
1570
- raise WordNetError("Language is not supported.")
1571
-
1572
- def citation(self, lang='omw'):
1573
- """Return the contents of citation.bib file (for omw)
1574
- use lang=lang to get the citation for an individual language"""
1575
- if lang == 'eng':
1576
- return self.open("citation.bib").read()
1577
- elif lang in self.langs():
1578
- return self._omw_reader.open("{}/citation.bib".format(lang)).read()
1579
- elif lang == 'omw':
1580
- ### under the not unreasonable assumption you don't mean Omwunra-Toqura
1581
- return self._omw_reader.open("citation.bib").read()
1582
- else:
1583
- raise WordNetError("Language is not supported.")
1584
-
1585
-
1586
-
1587
- #////////////////////////////////////////////////////////////
1588
- # Misc
1589
- #////////////////////////////////////////////////////////////
1590
- def lemma_count(self, lemma):
1591
- """Return the frequency count for this Lemma"""
1592
- # Currently, count is only work for English
1593
- if lemma._lang != 'eng':
1594
- return 0
1595
- # open the count file if we haven't already
1596
- if self._key_count_file is None:
1597
- self._key_count_file = self.open('cntlist.rev')
1598
- # find the key in the counts file and return the count
1599
- line = _binary_search_file(self._key_count_file, lemma._key)
1600
- if line:
1601
- return int(line.rsplit(' ', 1)[-1])
1602
- else:
1603
- return 0
1604
-
1605
- def path_similarity(self, synset1, synset2, verbose=False, simulate_root=True):
1606
- return synset1.path_similarity(synset2, verbose, simulate_root)
1607
- path_similarity.__doc__ = Synset.path_similarity.__doc__
1608
-
1609
- def lch_similarity(self, synset1, synset2, verbose=False, simulate_root=True):
1610
- return synset1.lch_similarity(synset2, verbose, simulate_root)
1611
- lch_similarity.__doc__ = Synset.lch_similarity.__doc__
1612
-
1613
- def wup_similarity(self, synset1, synset2, verbose=False, simulate_root=True):
1614
- return synset1.wup_similarity(synset2, verbose, simulate_root)
1615
- wup_similarity.__doc__ = Synset.wup_similarity.__doc__
1616
-
1617
- def res_similarity(self, synset1, synset2, ic, verbose=False):
1618
- return synset1.res_similarity(synset2, ic, verbose)
1619
- res_similarity.__doc__ = Synset.res_similarity.__doc__
1620
-
1621
- def jcn_similarity(self, synset1, synset2, ic, verbose=False):
1622
- return synset1.jcn_similarity(synset2, ic, verbose)
1623
- jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__
1624
-
1625
- def lin_similarity(self, synset1, synset2, ic, verbose=False):
1626
- return synset1.lin_similarity(synset2, ic, verbose)
1627
- lin_similarity.__doc__ = Synset.lin_similarity.__doc__
1628
-
1629
- #////////////////////////////////////////////////////////////
1630
- # Morphy
1631
- #////////////////////////////////////////////////////////////
1632
- # Morphy, adapted from Oliver Steele's pywordnet
1633
- def morphy(self, form, pos=None):
1634
- """
1635
- Find a possible base form for the given form, with the given
1636
- part of speech, by checking WordNet's list of exceptional
1637
- forms, and by recursively stripping affixes for this part of
1638
- speech until a form in WordNet is found.
1639
-
1640
- >>> from nltk.corpus import wordnet as wn
1641
- >>> print(wn.morphy('dogs'))
1642
- dog
1643
- >>> print(wn.morphy('churches'))
1644
- church
1645
- >>> print(wn.morphy('aardwolves'))
1646
- aardwolf
1647
- >>> print(wn.morphy('abaci'))
1648
- abacus
1649
- >>> wn.morphy('hardrock', wn.ADV)
1650
- >>> print(wn.morphy('book', wn.NOUN))
1651
- book
1652
- >>> wn.morphy('book', wn.ADJ)
1653
- """
1654
-
1655
- if pos is None:
1656
- morphy = self._morphy
1657
- analyses = chain(a for p in POS_LIST for a in morphy(form, p))
1658
- else:
1659
- analyses = self._morphy(form, pos)
1660
-
1661
- # get the first one we find
1662
- first = list(islice(analyses, 1))
1663
- if len(first) == 1:
1664
- return first[0]
1665
- else:
1666
- return None
1667
-
1668
- MORPHOLOGICAL_SUBSTITUTIONS = {
1669
- NOUN: [('s', ''), ('ses', 's'), ('ves', 'f'), ('xes', 'x'),
1670
- ('zes', 'z'), ('ches', 'ch'), ('shes', 'sh'),
1671
- ('men', 'man'), ('ies', 'y')],
1672
- VERB: [('s', ''), ('ies', 'y'), ('es', 'e'), ('es', ''),
1673
- ('ed', 'e'), ('ed', ''), ('ing', 'e'), ('ing', '')],
1674
- ADJ: [('er', ''), ('est', ''), ('er', 'e'), ('est', 'e')],
1675
- ADV: []}
1676
-
1677
- MORPHOLOGICAL_SUBSTITUTIONS[ADJ_SAT] = MORPHOLOGICAL_SUBSTITUTIONS[ADJ]
1678
-
1679
- def _morphy(self, form, pos):
1680
- # from jordanbg:
1681
- # Given an original string x
1682
- # 1. Apply rules once to the input to get y1, y2, y3, etc.
1683
- # 2. Return all that are in the database
1684
- # 3. If there are no matches, keep applying rules until you either
1685
- # find a match or you can't go any further
1686
-
1687
- exceptions = self._exception_map[pos]
1688
- substitutions = self.MORPHOLOGICAL_SUBSTITUTIONS[pos]
1689
-
1690
- def apply_rules(forms):
1691
- return [form[:-len(old)] + new
1692
- for form in forms
1693
- for old, new in substitutions
1694
- if form.endswith(old)]
1695
-
1696
- def filter_forms(forms):
1697
- result = []
1698
- seen = set()
1699
- for form in forms:
1700
- if form in self._lemma_pos_offset_map:
1701
- if pos in self._lemma_pos_offset_map[form]:
1702
- if form not in seen:
1703
- result.append(form)
1704
- seen.add(form)
1705
- return result
1706
-
1707
- # 0. Check the exception lists
1708
- if form in exceptions:
1709
- return filter_forms([form] + exceptions[form])
1710
-
1711
- # 1. Apply rules once to the input to get y1, y2, y3, etc.
1712
- forms = apply_rules([form])
1713
-
1714
- # 2. Return all that are in the database (and check the original too)
1715
- results = filter_forms([form] + forms)
1716
- if results:
1717
- return results
1718
-
1719
- # 3. If there are no matches, keep applying rules until we find a match
1720
- while forms:
1721
- forms = apply_rules(forms)
1722
- results = filter_forms(forms)
1723
- if results:
1724
- return results
1725
-
1726
- # Return an empty list if we can't find anything
1727
- return []
1728
-
1729
- #////////////////////////////////////////////////////////////
1730
- # Create information content from corpus
1731
- #////////////////////////////////////////////////////////////
1732
- def ic(self, corpus, weight_senses_equally = False, smoothing = 1.0):
1733
- """
1734
- Creates an information content lookup dictionary from a corpus.
1735
-
1736
- :type corpus: CorpusReader
1737
- :param corpus: The corpus from which we create an information
1738
- content dictionary.
1739
- :type weight_senses_equally: bool
1740
- :param weight_senses_equally: If this is True, gives all
1741
- possible senses equal weight rather than dividing by the
1742
- number of possible senses. (If a word has 3 synses, each
1743
- sense gets 0.3333 per appearance when this is False, 1.0 when
1744
- it is true.)
1745
- :param smoothing: How much do we smooth synset counts (default is 1.0)
1746
- :type smoothing: float
1747
- :return: An information content dictionary
1748
- """
1749
- counts = FreqDist()
1750
- for ww in corpus.words():
1751
- counts[ww] += 1
1752
-
1753
- ic = {}
1754
- for pp in POS_LIST:
1755
- ic[pp] = defaultdict(float)
1756
-
1757
- # Initialize the counts with the smoothing value
1758
- if smoothing > 0.0:
1759
- for ss in self.all_synsets():
1760
- pos = ss._pos
1761
- if pos == ADJ_SAT:
1762
- pos = ADJ
1763
- ic[pos][ss._offset] = smoothing
1764
-
1765
- for ww in counts:
1766
- possible_synsets = self.synsets(ww)
1767
- if len(possible_synsets) == 0:
1768
- continue
1769
-
1770
- # Distribute weight among possible synsets
1771
- weight = float(counts[ww])
1772
- if not weight_senses_equally:
1773
- weight /= float(len(possible_synsets))
1774
-
1775
- for ss in possible_synsets:
1776
- pos = ss._pos
1777
- if pos == ADJ_SAT:
1778
- pos = ADJ
1779
- for level in ss._iter_hypernym_lists():
1780
- for hh in level:
1781
- ic[pos][hh._offset] += weight
1782
- # Add the weight to the root
1783
- ic[pos][0] += weight
1784
- return ic
1785
-
1786
-
1787
- ######################################################################
1788
- ## WordNet Information Content Corpus Reader
1789
- ######################################################################
1790
-
1791
- class WordNetICCorpusReader(CorpusReader):
1792
- """
1793
- A corpus reader for the WordNet information content corpus.
1794
- """
1795
-
1796
- def __init__(self, root, fileids):
1797
- CorpusReader.__init__(self, root, fileids, encoding='utf8')
1798
-
1799
- # this load function would be more efficient if the data was pickled
1800
- # Note that we can't use NLTK's frequency distributions because
1801
- # synsets are overlapping (each instance of a synset also counts
1802
- # as an instance of its hypernyms)
1803
- def ic(self, icfile):
1804
- """
1805
- Load an information content file from the wordnet_ic corpus
1806
- and return a dictionary. This dictionary has just two keys,
1807
- NOUN and VERB, whose values are dictionaries that map from
1808
- synsets to information content values.
1809
-
1810
- :type icfile: str
1811
- :param icfile: The name of the wordnet_ic file (e.g. "ic-brown.dat")
1812
- :return: An information content dictionary
1813
- """
1814
- ic = {}
1815
- ic[NOUN] = defaultdict(float)
1816
- ic[VERB] = defaultdict(float)
1817
- for num, line in enumerate(self.open(icfile)):
1818
- if num == 0: # skip the header
1819
- continue
1820
- fields = line.split()
1821
- offset = int(fields[0][:-1])
1822
- value = float(fields[1])
1823
- pos = _get_pos(fields[0])
1824
- if len(fields) == 3 and fields[2] == "ROOT":
1825
- # Store root count.
1826
- ic[pos][0] += value
1827
- if value != 0:
1828
- ic[pos][offset] = value
1829
- return ic
1830
-
1831
-
1832
- ######################################################################
1833
- # Similarity metrics
1834
- ######################################################################
1835
-
1836
- # TODO: Add in the option to manually add a new root node; this will be
1837
- # useful for verb similarity as there exist multiple verb taxonomies.
1838
-
1839
- # More information about the metrics is available at
1840
- # http://marimba.d.umn.edu/similarity/measures.html
1841
-
1842
- def path_similarity(synset1, synset2, verbose=False, simulate_root=True):
1843
- return synset1.path_similarity(synset2, verbose, simulate_root)
1844
- path_similarity.__doc__ = Synset.path_similarity.__doc__
1845
-
1846
-
1847
- def lch_similarity(synset1, synset2, verbose=False, simulate_root=True):
1848
- return synset1.lch_similarity(synset2, verbose, simulate_root)
1849
- lch_similarity.__doc__ = Synset.lch_similarity.__doc__
1850
-
1851
-
1852
- def wup_similarity(synset1, synset2, verbose=False, simulate_root=True):
1853
- return synset1.wup_similarity(synset2, verbose, simulate_root)
1854
- wup_similarity.__doc__ = Synset.wup_similarity.__doc__
1855
-
1856
-
1857
- def res_similarity(synset1, synset2, ic, verbose=False):
1858
- return synset1.res_similarity(synset2, verbose)
1859
- res_similarity.__doc__ = Synset.res_similarity.__doc__
1860
-
1861
-
1862
- def jcn_similarity(synset1, synset2, ic, verbose=False):
1863
- return synset1.jcn_similarity(synset2, verbose)
1864
- jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__
1865
-
1866
-
1867
- def lin_similarity(synset1, synset2, ic, verbose=False):
1868
- return synset1.lin_similarity(synset2, verbose)
1869
- lin_similarity.__doc__ = Synset.lin_similarity.__doc__
1870
-
1871
-
1872
- def _lcs_ic(synset1, synset2, ic, verbose=False):
1873
- """
1874
- Get the information content of the least common subsumer that has
1875
- the highest information content value. If two nodes have no
1876
- explicit common subsumer, assume that they share an artificial
1877
- root node that is the hypernym of all explicit roots.
1878
-
1879
- :type synset1: Synset
1880
- :param synset1: First input synset.
1881
- :type synset2: Synset
1882
- :param synset2: Second input synset. Must be the same part of
1883
- speech as the first synset.
1884
- :type ic: dict
1885
- :param ic: an information content object (as returned by ``load_ic()``).
1886
- :return: The information content of the two synsets and their most
1887
- informative subsumer
1888
- """
1889
- if synset1._pos != synset2._pos:
1890
- raise WordNetError('Computing the least common subsumer requires ' + \
1891
- '%s and %s to have the same part of speech.' % \
1892
- (synset1, synset2))
1893
-
1894
- ic1 = information_content(synset1, ic)
1895
- ic2 = information_content(synset2, ic)
1896
- subsumers = synset1.common_hypernyms(synset2)
1897
- if len(subsumers) == 0:
1898
- subsumer_ic = 0
1899
- else:
1900
- subsumer_ic = max(information_content(s, ic) for s in subsumers)
1901
-
1902
- if verbose:
1903
- print("> LCS Subsumer by content:", subsumer_ic)
1904
-
1905
- return ic1, ic2, subsumer_ic
1906
-
1907
-
1908
- # Utility functions
1909
-
1910
- def information_content(synset, ic):
1911
- try:
1912
- icpos = ic[synset._pos]
1913
- except KeyError:
1914
- msg = 'Information content file has no entries for part-of-speech: %s'
1915
- raise WordNetError(msg % synset._pos)
1916
-
1917
- counts = icpos[synset._offset]
1918
- if counts == 0:
1919
- return _INF
1920
- else:
1921
- return -math.log(counts / icpos[0])
1922
-
1923
-
1924
- # get the part of speech (NOUN or VERB) from the information content record
1925
- # (each identifier has a 'n' or 'v' suffix)
1926
-
1927
- def _get_pos(field):
1928
- if field[-1] == 'n':
1929
- return NOUN
1930
- elif field[-1] == 'v':
1931
- return VERB
1932
- else:
1933
- msg = "Unidentified part of speech in WordNet Information Content file for field %s" % field
1934
- raise ValueError(msg)
1935
-
1936
-
1937
- # unload corpus after tests
1938
- def teardown_module(module=None):
1939
- from nltk.corpus import wordnet
1940
- wordnet._unload()
1941
-
1942
-
1943
- ######################################################################
1944
- # Demo
1945
- ######################################################################
1946
-
1947
- def demo():
1948
- import nltk
1949
- print('loading wordnet')
1950
- wn = WordNetCorpusReader(nltk.data.find('corpora/wordnet'), None)
1951
- print('done loading')
1952
- S = wn.synset
1953
- L = wn.lemma
1954
-
1955
- print('getting a synset for go')
1956
- move_synset = S('go.v.21')
1957
- print(move_synset.name(), move_synset.pos(), move_synset.lexname())
1958
- print(move_synset.lemma_names())
1959
- print(move_synset.definition())
1960
- print(move_synset.examples())
1961
-
1962
- zap_n = ['zap.n.01']
1963
- zap_v = ['zap.v.01', 'zap.v.02', 'nuke.v.01', 'microwave.v.01']
1964
-
1965
- def _get_synsets(synset_strings):
1966
- return [S(synset) for synset in synset_strings]
1967
-
1968
- zap_n_synsets = _get_synsets(zap_n)
1969
- zap_v_synsets = _get_synsets(zap_v)
1970
-
1971
- print(zap_n_synsets)
1972
- print(zap_v_synsets)
1973
-
1974
- print("Navigations:")
1975
- print(S('travel.v.01').hypernyms())
1976
- print(S('travel.v.02').hypernyms())
1977
- print(S('travel.v.03').hypernyms())
1978
-
1979
- print(L('zap.v.03.nuke').derivationally_related_forms())
1980
- print(L('zap.v.03.atomize').derivationally_related_forms())
1981
- print(L('zap.v.03.atomise').derivationally_related_forms())
1982
- print(L('zap.v.03.zap').derivationally_related_forms())
1983
-
1984
- print(S('dog.n.01').member_holonyms())
1985
- print(S('dog.n.01').part_meronyms())
1986
-
1987
- print(S('breakfast.n.1').hypernyms())
1988
- print(S('meal.n.1').hyponyms())
1989
- print(S('Austen.n.1').instance_hypernyms())
1990
- print(S('composer.n.1').instance_hyponyms())
1991
-
1992
- print(S('faculty.n.2').member_meronyms())
1993
- print(S('copilot.n.1').member_holonyms())
1994
-
1995
- print(S('table.n.2').part_meronyms())
1996
- print(S('course.n.7').part_holonyms())
1997
-
1998
- print(S('water.n.1').substance_meronyms())
1999
- print(S('gin.n.1').substance_holonyms())
2000
-
2001
- print(L('leader.n.1.leader').antonyms())
2002
- print(L('increase.v.1.increase').antonyms())
2003
-
2004
- print(S('snore.v.1').entailments())
2005
- print(S('heavy.a.1').similar_tos())
2006
- print(S('light.a.1').attributes())
2007
- print(S('heavy.a.1').attributes())
2008
-
2009
- print(L('English.a.1.English').pertainyms())
2010
-
2011
- print(S('person.n.01').root_hypernyms())
2012
- print(S('sail.v.01').root_hypernyms())
2013
- print(S('fall.v.12').root_hypernyms())
2014
-
2015
- print(S('person.n.01').lowest_common_hypernyms(S('dog.n.01')))
2016
- print(S('woman.n.01').lowest_common_hypernyms(S('girlfriend.n.02')))
2017
-
2018
- print(S('dog.n.01').path_similarity(S('cat.n.01')))
2019
- print(S('dog.n.01').lch_similarity(S('cat.n.01')))
2020
- print(S('dog.n.01').wup_similarity(S('cat.n.01')))
2021
-
2022
- wnic = WordNetICCorpusReader(nltk.data.find('corpora/wordnet_ic'),
2023
- '.*\.dat')
2024
- ic = wnic.ic('ic-brown.dat')
2025
- print(S('dog.n.01').jcn_similarity(S('cat.n.01'), ic))
2026
-
2027
- ic = wnic.ic('ic-semcor.dat')
2028
- print(S('dog.n.01').lin_similarity(S('cat.n.01'), ic))
2029
-
2030
- print(S('code.n.03').topic_domains())
2031
- print(S('pukka.a.01').region_domains())
2032
- print(S('freaky.a.01').usage_domains())
2033
-
2034
-
2035
- if __name__ == '__main__':
2036
- demo()
1
+ # -*- coding: utf-8 -*-
2
+ # Natural Language Toolkit: WordNet
3
+ #
4
+ # Copyright (C) 2001-2018 NLTK Project
5
+ # Author: Steven Bethard <Steven.Bethard@colorado.edu>
6
+ # Steven Bird <stevenbird1@gmail.com>
7
+ # Edward Loper <edloper@gmail.com>
8
+ # Nitin Madnani <nmadnani@ets.org>
9
+ # Nasruddin A’aidil Shari
10
+ # Sim Wei Ying Geraldine
11
+ # Soe Lynn
12
+ # Francis Bond <bond@ieee.org>
13
+ # URL: <http://nltk.org/>
14
+ # For license information, see LICENSE.TXT
15
+
16
+ """
17
+ An NLTK interface for WordNet
18
+
19
+ WordNet is a lexical database of English.
20
+ Using synsets, helps find conceptual relationships between words
21
+ such as hypernyms, hyponyms, synonyms, antonyms etc.
22
+
23
+ For details about WordNet see:
24
+ http://wordnet.princeton.edu/
25
+
26
+ This module also allows you to find lemmas in languages
27
+ other than English from the Open Multilingual Wordnet
28
+ http://compling.hss.ntu.edu.sg/omw/
29
+
30
+ """
31
+
32
+ from __future__ import print_function, unicode_literals
33
+
34
+ import math
35
+ import re
36
+ from itertools import islice, chain
37
+ from functools import total_ordering
38
+ from operator import itemgetter
39
+ from collections import defaultdict, deque
40
+
41
+ from six import iteritems
42
+ from six.moves import range
43
+
44
+ from nltk.corpus.reader import CorpusReader
45
+ from nltk.util import binary_search_file as _binary_search_file
46
+ from nltk.probability import FreqDist
47
+ from nltk.compat import python_2_unicode_compatible
48
+ from nltk.internals import deprecated
49
+
50
+ ######################################################################
51
+ # Table of Contents
52
+ ######################################################################
53
+ # - Constants
54
+ # - Data Classes
55
+ # - WordNetError
56
+ # - Lemma
57
+ # - Synset
58
+ # - WordNet Corpus Reader
59
+ # - WordNet Information Content Corpus Reader
60
+ # - Similarity Metrics
61
+ # - Demo
62
+
63
+ ######################################################################
64
+ # Constants
65
+ ######################################################################
66
+
67
+ #: Positive infinity (for similarity functions)
68
+ _INF = 1e300
69
+
70
+ # { Part-of-speech constants
71
+ ADJ, ADJ_SAT, ADV, NOUN, VERB = 'a', 's', 'r', 'n', 'v'
72
+ # }
73
+
74
+ POS_LIST = [NOUN, VERB, ADJ, ADV]
75
+
76
+ # A table of strings that are used to express verb frames.
77
+ VERB_FRAME_STRINGS = (
78
+ None,
79
+ "Something %s",
80
+ "Somebody %s",
81
+ "It is %sing",
82
+ "Something is %sing PP",
83
+ "Something %s something Adjective/Noun",
84
+ "Something %s Adjective/Noun",
85
+ "Somebody %s Adjective",
86
+ "Somebody %s something",
87
+ "Somebody %s somebody",
88
+ "Something %s somebody",
89
+ "Something %s something",
90
+ "Something %s to somebody",
91
+ "Somebody %s on something",
92
+ "Somebody %s somebody something",
93
+ "Somebody %s something to somebody",
94
+ "Somebody %s something from somebody",
95
+ "Somebody %s somebody with something",
96
+ "Somebody %s somebody of something",
97
+ "Somebody %s something on somebody",
98
+ "Somebody %s somebody PP",
99
+ "Somebody %s something PP",
100
+ "Somebody %s PP",
101
+ "Somebody's (body part) %s",
102
+ "Somebody %s somebody to INFINITIVE",
103
+ "Somebody %s somebody INFINITIVE",
104
+ "Somebody %s that CLAUSE",
105
+ "Somebody %s to somebody",
106
+ "Somebody %s to INFINITIVE",
107
+ "Somebody %s whether INFINITIVE",
108
+ "Somebody %s somebody into V-ing something",
109
+ "Somebody %s something with something",
110
+ "Somebody %s INFINITIVE",
111
+ "Somebody %s VERB-ing",
112
+ "It %s that CLAUSE",
113
+ "Something %s INFINITIVE")
114
+
115
+ SENSENUM_RE = re.compile(r'\.[\d]+\.')
116
+
117
+
118
+ ######################################################################
119
+ # Data Classes
120
+ ######################################################################
121
+
122
+
123
+ class WordNetError(Exception):
124
+ """An exception class for wordnet-related errors."""
125
+
126
+
127
+ @total_ordering
128
+ class _WordNetObject(object):
129
+ """A common base class for lemmas and synsets."""
130
+
131
+ def hypernyms(self):
132
+ return self._related('@')
133
+
134
+ def _hypernyms(self):
135
+ return self._related('@')
136
+
137
+ def instance_hypernyms(self):
138
+ return self._related('@i')
139
+
140
+ def _instance_hypernyms(self):
141
+ return self._related('@i')
142
+
143
+ def hyponyms(self):
144
+ return self._related('~')
145
+
146
+ def instance_hyponyms(self):
147
+ return self._related('~i')
148
+
149
+ def member_holonyms(self):
150
+ return self._related('#m')
151
+
152
+ def substance_holonyms(self):
153
+ return self._related('#s')
154
+
155
+ def part_holonyms(self):
156
+ return self._related('#p')
157
+
158
+ def member_meronyms(self):
159
+ return self._related('%m')
160
+
161
+ def substance_meronyms(self):
162
+ return self._related('%s')
163
+
164
+ def part_meronyms(self):
165
+ return self._related('%p')
166
+
167
+ def topic_domains(self):
168
+ return self._related(';c')
169
+
170
+ def region_domains(self):
171
+ return self._related(';r')
172
+
173
+ def usage_domains(self):
174
+ return self._related(';u')
175
+
176
+ def attributes(self):
177
+ return self._related('=')
178
+
179
+ def entailments(self):
180
+ return self._related('*')
181
+
182
+ def causes(self):
183
+ return self._related('>')
184
+
185
+ def also_sees(self):
186
+ return self._related('^')
187
+
188
+ def verb_groups(self):
189
+ return self._related('$')
190
+
191
+ def similar_tos(self):
192
+ return self._related('&')
193
+
194
+ def __hash__(self):
195
+ return hash(self._name)
196
+
197
+ def __eq__(self, other):
198
+ return self._name == other._name
199
+
200
+ def __ne__(self, other):
201
+ return self._name != other._name
202
+
203
+ def __lt__(self, other):
204
+ return self._name < other._name
205
+
206
+
207
+ @python_2_unicode_compatible
208
+ class Lemma(_WordNetObject):
209
+ """
210
+ The lexical entry for a single morphological form of a
211
+ sense-disambiguated word.
212
+
213
+ Create a Lemma from a "<word>.<pos>.<number>.<lemma>" string where:
214
+ <word> is the morphological stem identifying the synset
215
+ <pos> is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB
216
+ <number> is the sense number, counting from 0.
217
+ <lemma> is the morphological form of interest
218
+
219
+ Note that <word> and <lemma> can be different, e.g. the Synset
220
+ 'salt.n.03' has the Lemmas 'salt.n.03.salt', 'salt.n.03.saltiness' and
221
+ 'salt.n.03.salinity'.
222
+
223
+ Lemma attributes, accessible via methods with the same name::
224
+
225
+ - name: The canonical name of this lemma.
226
+ - synset: The synset that this lemma belongs to.
227
+ - syntactic_marker: For adjectives, the WordNet string identifying the
228
+ syntactic position relative modified noun. See:
229
+ http://wordnet.princeton.edu/man/wninput.5WN.html#sect10
230
+ For all other parts of speech, this attribute is None.
231
+ - count: The frequency of this lemma in wordnet.
232
+
233
+ Lemma methods:
234
+
235
+ Lemmas have the following methods for retrieving related Lemmas. They
236
+ correspond to the names for the pointer symbols defined here:
237
+ http://wordnet.princeton.edu/man/wninput.5WN.html#sect3
238
+ These methods all return lists of Lemmas:
239
+
240
+ - antonyms
241
+ - hypernyms, instance_hypernyms
242
+ - hyponyms, instance_hyponyms
243
+ - member_holonyms, substance_holonyms, part_holonyms
244
+ - member_meronyms, substance_meronyms, part_meronyms
245
+ - topic_domains, region_domains, usage_domains
246
+ - attributes
247
+ - derivationally_related_forms
248
+ - entailments
249
+ - causes
250
+ - also_sees
251
+ - verb_groups
252
+ - similar_tos
253
+ - pertainyms
254
+ """
255
+
256
+ __slots__ = ['_wordnet_corpus_reader', '_name', '_syntactic_marker',
257
+ '_synset', '_frame_strings', '_frame_ids',
258
+ '_lexname_index', '_lex_id', '_lang', '_key']
259
+
260
+ def __init__(self, wordnet_corpus_reader, synset, name,
261
+ lexname_index, lex_id, syntactic_marker):
262
+ self._wordnet_corpus_reader = wordnet_corpus_reader
263
+ self._name = name
264
+ self._syntactic_marker = syntactic_marker
265
+ self._synset = synset
266
+ self._frame_strings = []
267
+ self._frame_ids = []
268
+ self._lexname_index = lexname_index
269
+ self._lex_id = lex_id
270
+ self._lang = 'eng'
271
+
272
+ self._key = None # gets set later.
273
+
274
+ def name(self):
275
+ return self._name
276
+
277
+ def syntactic_marker(self):
278
+ return self._syntactic_marker
279
+
280
+ def synset(self):
281
+ return self._synset
282
+
283
+ def frame_strings(self):
284
+ return self._frame_strings
285
+
286
+ def frame_ids(self):
287
+ return self._frame_ids
288
+
289
+ def lang(self):
290
+ return self._lang
291
+
292
+ def key(self):
293
+ return self._key
294
+
295
+ def __repr__(self):
296
+ tup = type(self).__name__, self._synset._name, self._name
297
+ return "%s('%s.%s')" % tup
298
+
299
+ def _related(self, relation_symbol):
300
+ get_synset = self._wordnet_corpus_reader.synset_from_pos_and_offset
301
+ return [
302
+ get_synset(pos, offset)._lemmas[lemma_index]
303
+ for pos, offset, lemma_index
304
+ in self._synset._lemma_pointers[self._name, relation_symbol]
305
+ ]
306
+
307
+ def count(self):
308
+ """Return the frequency count for this Lemma"""
309
+ return self._wordnet_corpus_reader.lemma_count(self)
310
+
311
+ def antonyms(self):
312
+ return self._related('!')
313
+
314
+ def derivationally_related_forms(self):
315
+ return self._related('+')
316
+
317
+ def pertainyms(self):
318
+ return self._related('\\')
319
+
320
+
321
+ @python_2_unicode_compatible
322
+ class Synset(_WordNetObject):
323
+ """Create a Synset from a "<lemma>.<pos>.<number>" string where:
324
+ <lemma> is the word's morphological stem
325
+ <pos> is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB
326
+ <number> is the sense number, counting from 0.
327
+
328
+ Synset attributes, accessible via methods with the same name:
329
+
330
+ - name: The canonical name of this synset, formed using the first lemma
331
+ of this synset. Note that this may be different from the name
332
+ passed to the constructor if that string used a different lemma to
333
+ identify the synset.
334
+ - pos: The synset's part of speech, matching one of the module level
335
+ attributes ADJ, ADJ_SAT, ADV, NOUN or VERB.
336
+ - lemmas: A list of the Lemma objects for this synset.
337
+ - definition: The definition for this synset.
338
+ - examples: A list of example strings for this synset.
339
+ - offset: The offset in the WordNet dict file of this synset.
340
+ - lexname: The name of the lexicographer file containing this synset.
341
+
342
+ Synset methods:
343
+
344
+ Synsets have the following methods for retrieving related Synsets.
345
+ They correspond to the names for the pointer symbols defined here:
346
+ http://wordnet.princeton.edu/man/wninput.5WN.html#sect3
347
+ These methods all return lists of Synsets.
348
+
349
+ - hypernyms, instance_hypernyms
350
+ - hyponyms, instance_hyponyms
351
+ - member_holonyms, substance_holonyms, part_holonyms
352
+ - member_meronyms, substance_meronyms, part_meronyms
353
+ - attributes
354
+ - entailments
355
+ - causes
356
+ - also_sees
357
+ - verb_groups
358
+ - similar_tos
359
+
360
+ Additionally, Synsets support the following methods specific to the
361
+ hypernym relation:
362
+
363
+ - root_hypernyms
364
+ - common_hypernyms
365
+ - lowest_common_hypernyms
366
+
367
+ Note that Synsets do not support the following relations because
368
+ these are defined by WordNet as lexical relations:
369
+
370
+ - antonyms
371
+ - derivationally_related_forms
372
+ - pertainyms
373
+ """
374
+
375
+ __slots__ = ['_pos', '_offset', '_name', '_frame_ids',
376
+ '_lemmas', '_lemma_names',
377
+ '_definition', '_examples', '_lexname',
378
+ '_pointers', '_lemma_pointers', '_max_depth',
379
+ '_min_depth']
380
+
381
+ def __init__(self, wordnet_corpus_reader):
382
+ self._wordnet_corpus_reader = wordnet_corpus_reader
383
+ # All of these attributes get initialized by
384
+ # WordNetCorpusReader._synset_from_pos_and_line()
385
+
386
+ self._pos = None
387
+ self._offset = None
388
+ self._name = None
389
+ self._frame_ids = []
390
+ self._lemmas = []
391
+ self._lemma_names = []
392
+ self._definition = None
393
+ self._examples = []
394
+ self._lexname = None # lexicographer name
395
+ self._all_hypernyms = None
396
+
397
+ self._pointers = defaultdict(set)
398
+ self._lemma_pointers = defaultdict(list)
399
+
400
+ def pos(self):
401
+ return self._pos
402
+
403
+ def offset(self):
404
+ return self._offset
405
+
406
+ def name(self):
407
+ return self._name
408
+
409
+ def frame_ids(self):
410
+ return self._frame_ids
411
+
412
+ def definition(self):
413
+ return self._definition
414
+
415
+ def examples(self):
416
+ return self._examples
417
+
418
+ def lexname(self):
419
+ return self._lexname
420
+
421
+ def _needs_root(self):
422
+ if self._pos == NOUN:
423
+ if self._wordnet_corpus_reader.get_version() == '1.6':
424
+ return True
425
+ else:
426
+ return False
427
+ elif self._pos == VERB:
428
+ return True
429
+
430
+ def lemma_names(self, lang='eng'):
431
+ '''Return all the lemma_names associated with the synset'''
432
+ if lang == 'eng':
433
+ return self._lemma_names
434
+ else:
435
+ self._wordnet_corpus_reader._load_lang_data(lang)
436
+
437
+ i = self._wordnet_corpus_reader.ss2of(self)
438
+ if i in self._wordnet_corpus_reader._lang_data[lang][0]:
439
+ return self._wordnet_corpus_reader._lang_data[lang][0][i]
440
+ else:
441
+ return []
442
+
443
+ def lemmas(self, lang='eng'):
444
+ '''Return all the lemma objects associated with the synset'''
445
+ if lang == 'eng':
446
+ return self._lemmas
447
+ else:
448
+ self._wordnet_corpus_reader._load_lang_data(lang)
449
+ lemmark = []
450
+ lemmy = self.lemma_names(lang)
451
+ for lem in lemmy:
452
+ temp = Lemma(
453
+ self._wordnet_corpus_reader,
454
+ self,
455
+ lem,
456
+ self._wordnet_corpus_reader._lexnames.index(
457
+ self.lexname()
458
+ ),
459
+ 0,
460
+ None
461
+ )
462
+ temp._lang = lang
463
+ lemmark.append(temp)
464
+ return lemmark
465
+
466
+ def root_hypernyms(self):
467
+ """Get the topmost hypernyms of this synset in WordNet."""
468
+
469
+ result = []
470
+ seen = set()
471
+ todo = [self]
472
+ while todo:
473
+ next_synset = todo.pop()
474
+ if next_synset not in seen:
475
+ seen.add(next_synset)
476
+ next_hypernyms = next_synset.hypernyms() + \
477
+ next_synset.instance_hypernyms()
478
+ if not next_hypernyms:
479
+ result.append(next_synset)
480
+ else:
481
+ todo.extend(next_hypernyms)
482
+ return result
483
+
484
+ # Simpler implementation which makes incorrect assumption that
485
+ # hypernym hierarchy is acyclic:
486
+ #
487
+ # if not self.hypernyms():
488
+ # return [self]
489
+ # else:
490
+ # return list(set(root for h in self.hypernyms()
491
+ # for root in h.root_hypernyms()))
492
+ def max_depth(self):
493
+ """
494
+ :return: The length of the longest hypernym path from this
495
+ synset to the root.
496
+ """
497
+
498
+ if "_max_depth" not in self.__dict__:
499
+ hypernyms = self.hypernyms() + self.instance_hypernyms()
500
+ if not hypernyms:
501
+ self._max_depth = 0
502
+ else:
503
+ self._max_depth = 1 + max(h.max_depth() for h in hypernyms)
504
+ return self._max_depth
505
+
506
+ def min_depth(self):
507
+ """
508
+ :return: The length of the shortest hypernym path from this
509
+ synset to the root.
510
+ """
511
+
512
+ if "_min_depth" not in self.__dict__:
513
+ hypernyms = self.hypernyms() + self.instance_hypernyms()
514
+ if not hypernyms:
515
+ self._min_depth = 0
516
+ else:
517
+ self._min_depth = 1 + min(h.min_depth() for h in hypernyms)
518
+ return self._min_depth
519
+
520
+ def closure(self, rel, depth=-1):
521
+ """Return the transitive closure of source under the rel
522
+ relationship, breadth-first
523
+
524
+ >>> from nltk.corpus import wordnet as wn
525
+ >>> dog = wn.synset('dog.n.01')
526
+ >>> hyp = lambda s:s.hypernyms()
527
+ >>> list(dog.closure(hyp))
528
+ [Synset('canine.n.02'), Synset('domestic_animal.n.01'),
529
+ Synset('carnivore.n.01'), Synset('animal.n.01'),
530
+ Synset('placental.n.01'), Synset('organism.n.01'),
531
+ Synset('mammal.n.01'), Synset('living_thing.n.01'),
532
+ Synset('vertebrate.n.01'), Synset('whole.n.02'),
533
+ Synset('chordate.n.01'), Synset('object.n.01'),
534
+ Synset('physical_entity.n.01'), Synset('entity.n.01')]
535
+
536
+ """
537
+ from nltk.util import breadth_first
538
+ synset_offsets = []
539
+ for synset in breadth_first(self, rel, depth):
540
+ if synset._offset != self._offset:
541
+ if synset._offset not in synset_offsets:
542
+ synset_offsets.append(synset._offset)
543
+ yield synset
544
+
545
+ def hypernym_paths(self):
546
+ """
547
+ Get the path(s) from this synset to the root, where each path is a
548
+ list of the synset nodes traversed on the way to the root.
549
+
550
+ :return: A list of lists, where each list gives the node sequence
551
+ connecting the initial ``Synset`` node and a root node.
552
+ """
553
+ paths = []
554
+
555
+ hypernyms = self.hypernyms() + self.instance_hypernyms()
556
+ if len(hypernyms) == 0:
557
+ paths = [[self]]
558
+
559
+ for hypernym in hypernyms:
560
+ for ancestor_list in hypernym.hypernym_paths():
561
+ ancestor_list.append(self)
562
+ paths.append(ancestor_list)
563
+ return paths
564
+
565
+ def common_hypernyms(self, other):
566
+ """
567
+ Find all synsets that are hypernyms of this synset and the
568
+ other synset.
569
+
570
+ :type other: Synset
571
+ :param other: other input synset.
572
+ :return: The synsets that are hypernyms of both synsets.
573
+ """
574
+ if not self._all_hypernyms:
575
+ self._all_hypernyms = set(
576
+ self_synset
577
+ for self_synsets in self._iter_hypernym_lists()
578
+ for self_synset in self_synsets
579
+ )
580
+ if not other._all_hypernyms:
581
+ other._all_hypernyms = set(
582
+ other_synset
583
+ for other_synsets in other._iter_hypernym_lists()
584
+ for other_synset in other_synsets
585
+ )
586
+ return list(self._all_hypernyms.intersection(other._all_hypernyms))
587
+
588
+ def lowest_common_hypernyms(
589
+ self, other, simulate_root=False, use_min_depth=False
590
+ ):
591
+ """
592
+ Get a list of lowest synset(s) that both synsets have as a hypernym.
593
+ When `use_min_depth == False` this means that the synset which appears
594
+ as a hypernym of both `self` and `other` with the lowest maximum depth
595
+ is returned or if there are multiple such synsets at the same depth
596
+ they are all returned
597
+
598
+ However, if `use_min_depth == True` then the synset(s) which has/have
599
+ the lowest minimum depth and appear(s) in both paths is/are returned.
600
+
601
+ By setting the use_min_depth flag to True, the behavior of NLTK2 can be
602
+ preserved. This was changed in NLTK3 to give more accurate results in a
603
+ small set of cases, generally with synsets concerning people. (eg:
604
+ 'chef.n.01', 'fireman.n.01', etc.)
605
+
606
+ This method is an implementation of Ted Pedersen's "Lowest Common
607
+ Subsumer" method from the Perl Wordnet module. It can return either
608
+ "self" or "other" if they are a hypernym of the other.
609
+
610
+ :type other: Synset
611
+ :param other: other input synset
612
+ :type simulate_root: bool
613
+ :param simulate_root: The various verb taxonomies do not
614
+ share a single root which disallows this metric from working for
615
+ synsets that are not connected. This flag (False by default)
616
+ creates a fake root that connects all the taxonomies. Set it
617
+ to True to enable this behavior. For the noun taxonomy,
618
+ there is usually a default root except for WordNet version 1.6.
619
+ If you are using wordnet 1.6, a fake root will need to be added
620
+ for nouns as well.
621
+ :type use_min_depth: bool
622
+ :param use_min_depth: This setting mimics older (v2) behavior of NLTK
623
+ wordnet If True, will use the min_depth function to calculate the
624
+ lowest common hypernyms. This is known to give strange results for
625
+ some synset pairs (eg: 'chef.n.01', 'fireman.n.01') but is retained
626
+ for backwards compatibility
627
+ :return: The synsets that are the lowest common hypernyms of both
628
+ synsets
629
+ """
630
+ synsets = self.common_hypernyms(other)
631
+ if simulate_root:
632
+ fake_synset = Synset(None)
633
+ fake_synset._name = '*ROOT*'
634
+ fake_synset.hypernyms = lambda: []
635
+ fake_synset.instance_hypernyms = lambda: []
636
+ synsets.append(fake_synset)
637
+
638
+ try:
639
+ if use_min_depth:
640
+ max_depth = max(s.min_depth() for s in synsets)
641
+ unsorted_lch = [
642
+ s for s in synsets if s.min_depth() == max_depth
643
+ ]
644
+ else:
645
+ max_depth = max(s.max_depth() for s in synsets)
646
+ unsorted_lch = [
647
+ s for s in synsets if s.max_depth() == max_depth
648
+ ]
649
+ return sorted(unsorted_lch)
650
+ except ValueError:
651
+ return []
652
+
653
+ def hypernym_distances(self, distance=0, simulate_root=False):
654
+ """
655
+ Get the path(s) from this synset to the root, counting the distance
656
+ of each node from the initial node on the way. A set of
657
+ (synset, distance) tuples is returned.
658
+
659
+ :type distance: int
660
+ :param distance: the distance (number of edges) from this hypernym to
661
+ the original hypernym ``Synset`` on which this method was called.
662
+ :return: A set of ``(Synset, int)`` tuples where each ``Synset`` is
663
+ a hypernym of the first ``Synset``.
664
+ """
665
+ distances = set([(self, distance)])
666
+ for hypernym in self._hypernyms() + self._instance_hypernyms():
667
+ distances |= hypernym.hypernym_distances(
668
+ distance+1,
669
+ simulate_root=False
670
+ )
671
+ if simulate_root:
672
+ fake_synset = Synset(None)
673
+ fake_synset._name = '*ROOT*'
674
+ fake_synset_distance = max(distances, key=itemgetter(1))[1]
675
+ distances.add((fake_synset, fake_synset_distance+1))
676
+ return distances
677
+
678
+ def _shortest_hypernym_paths(self, simulate_root):
679
+ if self._name == '*ROOT*':
680
+ return {self: 0}
681
+
682
+ queue = deque([(self, 0)])
683
+ path = {}
684
+
685
+ while queue:
686
+ s, depth = queue.popleft()
687
+ if s in path:
688
+ continue
689
+ path[s] = depth
690
+
691
+ depth += 1
692
+ queue.extend((hyp, depth) for hyp in s._hypernyms())
693
+ queue.extend((hyp, depth) for hyp in s._instance_hypernyms())
694
+
695
+ if simulate_root:
696
+ fake_synset = Synset(None)
697
+ fake_synset._name = '*ROOT*'
698
+ path[fake_synset] = max(path.values()) + 1
699
+
700
+ return path
701
+
702
+ def shortest_path_distance(self, other, simulate_root=False):
703
+ """
704
+ Returns the distance of the shortest path linking the two synsets (if
705
+ one exists). For each synset, all the ancestor nodes and their
706
+ distances are recorded and compared. The ancestor node common to both
707
+ synsets that can be reached with the minimum number of traversals is
708
+ used. If no ancestor nodes are common, None is returned. If a node is
709
+ compared with itself 0 is returned.
710
+
711
+ :type other: Synset
712
+ :param other: The Synset to which the shortest path will be found.
713
+ :return: The number of edges in the shortest path connecting the two
714
+ nodes, or None if no path exists.
715
+ """
716
+
717
+ if self == other:
718
+ return 0
719
+
720
+ dist_dict1 = self._shortest_hypernym_paths(simulate_root)
721
+ dist_dict2 = other._shortest_hypernym_paths(simulate_root)
722
+
723
+ # For each ancestor synset common to both subject synsets, find the
724
+ # connecting path length. Return the shortest of these.
725
+
726
+ inf = float('inf')
727
+ path_distance = inf
728
+ for synset, d1 in iteritems(dist_dict1):
729
+ d2 = dist_dict2.get(synset, inf)
730
+ path_distance = min(path_distance, d1 + d2)
731
+
732
+ return None if math.isinf(path_distance) else path_distance
733
+
734
+ def tree(self, rel, depth=-1, cut_mark=None):
735
+ """
736
+ >>> from nltk.corpus import wordnet as wn
737
+ >>> dog = wn.synset('dog.n.01')
738
+ >>> hyp = lambda s:s.hypernyms()
739
+ >>> from pprint import pprint
740
+ >>> pprint(dog.tree(hyp))
741
+ [Synset('dog.n.01'),
742
+ [Synset('canine.n.02'),
743
+ [Synset('carnivore.n.01'),
744
+ [Synset('placental.n.01'),
745
+ [Synset('mammal.n.01'),
746
+ [Synset('vertebrate.n.01'),
747
+ [Synset('chordate.n.01'),
748
+ [Synset('animal.n.01'),
749
+ [Synset('organism.n.01'),
750
+ [Synset('living_thing.n.01'),
751
+ [Synset('whole.n.02'),
752
+ [Synset('object.n.01'),
753
+ [Synset('physical_entity.n.01'),
754
+ [Synset('entity.n.01')]]]]]]]]]]]]],
755
+ [Synset('domestic_animal.n.01'),
756
+ [Synset('animal.n.01'),
757
+ [Synset('organism.n.01'),
758
+ [Synset('living_thing.n.01'),
759
+ [Synset('whole.n.02'),
760
+ [Synset('object.n.01'),
761
+ [Synset('physical_entity.n.01'), [Synset('entity.n.01')]]]]]]]]]
762
+ """
763
+
764
+ tree = [self]
765
+ if depth != 0:
766
+ tree += [x.tree(rel, depth-1, cut_mark) for x in rel(self)]
767
+ elif cut_mark:
768
+ tree += [cut_mark]
769
+ return tree
770
+
771
+ # interface to similarity methods
772
+ def path_similarity(self, other, verbose=False, simulate_root=True):
773
+ """
774
+ Path Distance Similarity:
775
+ Return a score denoting how similar two word senses are, based on the
776
+ shortest path that connects the senses in the is-a (hypernym/hypnoym)
777
+ taxonomy. The score is in the range 0 to 1, except in those cases where
778
+ a path cannot be found (will only be true for verbs as there are many
779
+ distinct verb taxonomies), in which case None is returned. A score of
780
+ 1 represents identity i.e. comparing a sense with itself will return 1.
781
+
782
+ :type other: Synset
783
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
784
+ :type simulate_root: bool
785
+ :param simulate_root: The various verb taxonomies do not
786
+ share a single root which disallows this metric from working for
787
+ synsets that are not connected. This flag (True by default)
788
+ creates a fake root that connects all the taxonomies. Set it
789
+ to false to disable this behavior. For the noun taxonomy,
790
+ there is usually a default root except for WordNet version 1.6.
791
+ If you are using wordnet 1.6, a fake root will be added for nouns
792
+ as well.
793
+ :return: A score denoting the similarity of the two ``Synset`` objects,
794
+ normally between 0 and 1. None is returned if no connecting path
795
+ could be found. 1 is returned if a ``Synset`` is compared with
796
+ itself.
797
+ """
798
+
799
+ distance = self.shortest_path_distance(
800
+ other,
801
+ simulate_root=simulate_root and self._needs_root()
802
+ )
803
+ if distance is None or distance < 0:
804
+ return None
805
+ return 1.0 / (distance + 1)
806
+
807
+ def lch_similarity(self, other, verbose=False, simulate_root=True):
808
+ """
809
+ Leacock Chodorow Similarity:
810
+ Return a score denoting how similar two word senses are, based on the
811
+ shortest path that connects the senses (as above) and the maximum depth
812
+ of the taxonomy in which the senses occur. The relationship is given as
813
+ -log(p/2d) where p is the shortest path length and d is the taxonomy
814
+ depth.
815
+
816
+ :type other: Synset
817
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
818
+ :type simulate_root: bool
819
+ :param simulate_root: The various verb taxonomies do not
820
+ share a single root which disallows this metric from working for
821
+ synsets that are not connected. This flag (True by default)
822
+ creates a fake root that connects all the taxonomies. Set it
823
+ to false to disable this behavior. For the noun taxonomy,
824
+ there is usually a default root except for WordNet version 1.6.
825
+ If you are using wordnet 1.6, a fake root will be added for nouns
826
+ as well.
827
+ :return: A score denoting the similarity of the two ``Synset`` objects,
828
+ normally greater than 0. None is returned if no connecting path
829
+ could be found. If a ``Synset`` is compared with itself, the
830
+ maximum score is returned, which varies depending on the taxonomy
831
+ depth.
832
+ """
833
+
834
+ if self._pos != other._pos:
835
+ raise WordNetError(
836
+ 'Computing the lch similarity requires '
837
+ '%s and %s to have the same part of speech.' %
838
+ (self, other)
839
+ )
840
+
841
+ need_root = self._needs_root()
842
+
843
+ if self._pos not in self._wordnet_corpus_reader._max_depth:
844
+ self._wordnet_corpus_reader._compute_max_depth(
845
+ self._pos, need_root
846
+ )
847
+
848
+ depth = self._wordnet_corpus_reader._max_depth[self._pos]
849
+
850
+ distance = self.shortest_path_distance(
851
+ other,
852
+ simulate_root=simulate_root and need_root
853
+ )
854
+
855
+ if distance is None or distance < 0 or depth == 0:
856
+ return None
857
+ return -math.log((distance + 1) / (2.0 * depth))
858
+
859
+ def wup_similarity(self, other, verbose=False, simulate_root=True):
860
+ """
861
+ Wu-Palmer Similarity:
862
+ Return a score denoting how similar two word senses are, based on the
863
+ depth of the two senses in the taxonomy and that of their Least Common
864
+ Subsumer (most specific ancestor node). Previously, the scores computed
865
+ by this implementation did _not_ always agree with those given by
866
+ Pedersen's Perl implementation of WordNet Similarity. However, with
867
+ the addition of the simulate_root flag (see below), the score for
868
+ verbs now almost always agree but not always for nouns.
869
+
870
+ The LCS does not necessarily feature in the shortest path connecting
871
+ the two senses, as it is by definition the common ancestor deepest in
872
+ the taxonomy, not closest to the two senses. Typically, however, it
873
+ will so feature. Where multiple candidates for the LCS exist, that
874
+ whose shortest path to the root node is the longest will be selected.
875
+ Where the LCS has multiple paths to the root, the longer path is used
876
+ for the purposes of the calculation.
877
+
878
+ :type other: Synset
879
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
880
+ :type simulate_root: bool
881
+ :param simulate_root: The various verb taxonomies do not
882
+ share a single root which disallows this metric from working for
883
+ synsets that are not connected. This flag (True by default)
884
+ creates a fake root that connects all the taxonomies. Set it
885
+ to false to disable this behavior. For the noun taxonomy,
886
+ there is usually a default root except for WordNet version 1.6.
887
+ If you are using wordnet 1.6, a fake root will be added for nouns
888
+ as well.
889
+ :return: A float score denoting the similarity of the two ``Synset``
890
+ objects, normally greater than zero. If no connecting path between
891
+ the two senses can be found, None is returned.
892
+
893
+ """
894
+
895
+ need_root = self._needs_root()
896
+ # Note that to preserve behavior from NLTK2 we set use_min_depth=True
897
+ # It is possible that more accurate results could be obtained by
898
+ # removing this setting and it should be tested later on
899
+ subsumers = self.lowest_common_hypernyms(
900
+ other,
901
+ simulate_root=simulate_root and need_root, use_min_depth=True
902
+ )
903
+
904
+ # If no LCS was found return None
905
+ if len(subsumers) == 0:
906
+ return None
907
+
908
+ subsumer = self if self in subsumers else subsumers[0]
909
+
910
+ # Get the longest path from the LCS to the root,
911
+ # including a correction:
912
+ # - add one because the calculations include both the start and end
913
+ # nodes
914
+ depth = subsumer.max_depth() + 1
915
+
916
+ # Note: No need for an additional add-one correction for non-nouns
917
+ # to account for an imaginary root node because that is now
918
+ # automatically handled by simulate_root
919
+ # if subsumer._pos != NOUN:
920
+ # depth += 1
921
+
922
+ # Get the shortest path from the LCS to each of the synsets it is
923
+ # subsuming. Add this to the LCS path length to get the path
924
+ # length from each synset to the root.
925
+ len1 = self.shortest_path_distance(
926
+ subsumer,
927
+ simulate_root=simulate_root and need_root
928
+ )
929
+ len2 = other.shortest_path_distance(
930
+ subsumer,
931
+ simulate_root=simulate_root and need_root
932
+ )
933
+ if len1 is None or len2 is None:
934
+ return None
935
+ len1 += depth
936
+ len2 += depth
937
+ return (2.0 * depth) / (len1 + len2)
938
+
939
+ def res_similarity(self, other, ic, verbose=False):
940
+ """
941
+ Resnik Similarity:
942
+ Return a score denoting how similar two word senses are, based on the
943
+ Information Content (IC) of the Least Common Subsumer (most specific
944
+ ancestor node).
945
+
946
+ :type other: Synset
947
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
948
+ :type ic: dict
949
+ :param ic: an information content object (as returned by
950
+ ``nltk.corpus.wordnet_ic.ic()``).
951
+ :return: A float score denoting the similarity of the two ``Synset``
952
+ objects. Synsets whose LCS is the root node of the taxonomy will
953
+ have a score of 0 (e.g. N['dog'][0] and N['table'][0]).
954
+ """
955
+
956
+ ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
957
+ return lcs_ic
958
+
959
+ def jcn_similarity(self, other, ic, verbose=False):
960
+ """
961
+ Jiang-Conrath Similarity:
962
+ Return a score denoting how similar two word senses are, based on the
963
+ Information Content (IC) of the Least Common Subsumer (most specific
964
+ ancestor node) and that of the two input Synsets. The relationship is
965
+ given by the equation 1 / (IC(s1) + IC(s2) - 2 * IC(lcs)).
966
+
967
+ :type other: Synset
968
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
969
+ :type ic: dict
970
+ :param ic: an information content object (as returned by
971
+ ``nltk.corpus.wordnet_ic.ic()``).
972
+ :return: A float score denoting the similarity of the two ``Synset``
973
+ objects.
974
+ """
975
+
976
+ if self == other:
977
+ return _INF
978
+
979
+ ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
980
+
981
+ # If either of the input synsets are the root synset, or have a
982
+ # frequency of 0 (sparse data problem), return 0.
983
+ if ic1 == 0 or ic2 == 0:
984
+ return 0
985
+
986
+ ic_difference = ic1 + ic2 - 2 * lcs_ic
987
+
988
+ if ic_difference == 0:
989
+ return _INF
990
+
991
+ return 1 / ic_difference
992
+
993
+ def lin_similarity(self, other, ic, verbose=False):
994
+ """
995
+ Lin Similarity:
996
+ Return a score denoting how similar two word senses are, based on the
997
+ Information Content (IC) of the Least Common Subsumer (most specific
998
+ ancestor node) and that of the two input Synsets. The relationship is
999
+ given by the equation 2 * IC(lcs) / (IC(s1) + IC(s2)).
1000
+
1001
+ :type other: Synset
1002
+ :param other: The ``Synset`` that this ``Synset`` is being compared to.
1003
+ :type ic: dict
1004
+ :param ic: an information content object (as returned by
1005
+ ``nltk.corpus.wordnet_ic.ic()``).
1006
+ :return: A float score denoting the similarity of the two ``Synset``
1007
+ objects, in the range 0 to 1.
1008
+ """
1009
+
1010
+ ic1, ic2, lcs_ic = _lcs_ic(self, other, ic)
1011
+ return (2.0 * lcs_ic) / (ic1 + ic2)
1012
+
1013
+ def _iter_hypernym_lists(self):
1014
+ """
1015
+ :return: An iterator over ``Synset`` objects that are either proper
1016
+ hypernyms or instance of hypernyms of the synset.
1017
+ """
1018
+ todo = [self]
1019
+ seen = set()
1020
+ while todo:
1021
+ for synset in todo:
1022
+ seen.add(synset)
1023
+ yield todo
1024
+ todo = [hypernym
1025
+ for synset in todo
1026
+ for hypernym in (
1027
+ synset.hypernyms() + synset.instance_hypernyms()
1028
+ )
1029
+ if hypernym not in seen]
1030
+
1031
+ def __repr__(self):
1032
+ return "%s('%s')" % (type(self).__name__, self._name)
1033
+
1034
+ def _related(self, relation_symbol, sort=True):
1035
+ get_synset = self._wordnet_corpus_reader.synset_from_pos_and_offset
1036
+ pointer_tuples = self._pointers[relation_symbol]
1037
+ r = [get_synset(pos, offset) for pos, offset in pointer_tuples]
1038
+ if sort:
1039
+ r.sort()
1040
+ return r
1041
+
1042
+
1043
+ ######################################################################
1044
+ # WordNet Corpus Reader
1045
+ ######################################################################
1046
+
1047
+ class WordNetCorpusReader(CorpusReader):
1048
+ """
1049
+ A corpus reader used to access wordnet or its variants.
1050
+ """
1051
+
1052
+ _ENCODING = 'utf8'
1053
+
1054
+ # { Part-of-speech constants
1055
+ ADJ, ADJ_SAT, ADV, NOUN, VERB = 'a', 's', 'r', 'n', 'v'
1056
+ # }
1057
+
1058
+ # { Filename constants
1059
+ _FILEMAP = {ADJ: 'adj', ADV: 'adv', NOUN: 'noun', VERB: 'verb'}
1060
+ # }
1061
+
1062
+ # { Part of speech constants
1063
+ _pos_numbers = {NOUN: 1, VERB: 2, ADJ: 3, ADV: 4, ADJ_SAT: 5}
1064
+ _pos_names = dict(tup[::-1] for tup in _pos_numbers.items())
1065
+ # }
1066
+
1067
+ #: A list of file identifiers for all the fileids used by this
1068
+ #: corpus reader.
1069
+ _FILES = ('cntlist.rev', 'lexnames', 'index.sense',
1070
+ 'index.adj', 'index.adv', 'index.noun', 'index.verb',
1071
+ 'data.adj', 'data.adv', 'data.noun', 'data.verb',
1072
+ 'adj.exc', 'adv.exc', 'noun.exc', 'verb.exc', )
1073
+
1074
+ def __init__(self, root, omw_reader):
1075
+ """
1076
+ Construct a new wordnet corpus reader, with the given root
1077
+ directory.
1078
+ """
1079
+ super(WordNetCorpusReader, self).__init__(root, self._FILES,
1080
+ encoding=self._ENCODING)
1081
+
1082
+ # A index that provides the file offset
1083
+ # Map from lemma -> pos -> synset_index -> offset
1084
+ self._lemma_pos_offset_map = defaultdict(dict)
1085
+
1086
+ # A cache so we don't have to reconstuct synsets
1087
+ # Map from pos -> offset -> synset
1088
+ self._synset_offset_cache = defaultdict(dict)
1089
+
1090
+ # A lookup for the maximum depth of each part of speech. Useful for
1091
+ # the lch similarity metric.
1092
+ self._max_depth = defaultdict(dict)
1093
+
1094
+ # Corpus reader containing omw data.
1095
+ self._omw_reader = omw_reader
1096
+
1097
+ # A cache to store the wordnet data of multiple languages
1098
+ self._lang_data = defaultdict(list)
1099
+
1100
+ self._data_file_map = {}
1101
+ self._exception_map = {}
1102
+ self._lexnames = []
1103
+ self._key_count_file = None
1104
+ self._key_synset_file = None
1105
+
1106
+ # Load the lexnames
1107
+ for i, line in enumerate(self.open('lexnames')):
1108
+ index, lexname, _ = line.split()
1109
+ assert int(index) == i
1110
+ self._lexnames.append(lexname)
1111
+
1112
+ # Load the indices for lemmas and synset offsets
1113
+ self._load_lemma_pos_offset_map()
1114
+
1115
+ # load the exception file data into memory
1116
+ self._load_exception_map()
1117
+
1118
+ # Open Multilingual WordNet functions, contributed by
1119
+ # Nasruddin A’aidil Shari, Sim Wei Ying Geraldine, and Soe Lynn
1120
+
1121
+ def of2ss(self, of):
1122
+ ''' take an id and return the synsets '''
1123
+ return self.synset_from_pos_and_offset(of[-1], int(of[:8]))
1124
+
1125
+ def ss2of(self, ss):
1126
+ ''' return the ID of the synset '''
1127
+ return ("{:08d}-{}".format(ss.offset(), ss.pos()))
1128
+
1129
+ def _load_lang_data(self, lang):
1130
+ ''' load the wordnet data of the requested language from the file to
1131
+ the cache, _lang_data '''
1132
+
1133
+ if lang in self._lang_data.keys():
1134
+ return
1135
+
1136
+ if lang not in self.langs():
1137
+ raise WordNetError("Language is not supported.")
1138
+
1139
+ f = self._omw_reader.open('{0:}/wn-data-{0:}.tab'.format(lang))
1140
+ self.custom_lemmas(f, lang)
1141
+ f.close()
1142
+
1143
+ def langs(self):
1144
+ ''' return a list of languages supported by Multilingual Wordnet '''
1145
+ import os
1146
+ langs = ['eng']
1147
+ fileids = self._omw_reader.fileids()
1148
+ for fileid in fileids:
1149
+ file_name, file_extension = os.path.splitext(fileid)
1150
+ if file_extension == '.tab':
1151
+ langs.append(file_name.split('-')[-1])
1152
+
1153
+ return langs
1154
+
1155
+ def _load_lemma_pos_offset_map(self):
1156
+ for suffix in self._FILEMAP.values():
1157
+
1158
+ # parse each line of the file (ignoring comment lines)
1159
+ for i, line in enumerate(self.open('index.%s' % suffix)):
1160
+ if line.startswith(' '):
1161
+ continue
1162
+
1163
+ _iter = iter(line.split())
1164
+
1165
+ def _next_token(): return next(_iter)
1166
+
1167
+ try:
1168
+
1169
+ # get the lemma and part-of-speech
1170
+ lemma = _next_token()
1171
+ pos = _next_token()
1172
+
1173
+ # get the number of synsets for this lemma
1174
+ n_synsets = int(_next_token())
1175
+ assert n_synsets > 0
1176
+
1177
+ # get and ignore the pointer symbols for all synsets of
1178
+ # this lemma
1179
+ n_pointers = int(_next_token())
1180
+ [_next_token() for _ in range(n_pointers)]
1181
+
1182
+ # same as number of synsets
1183
+ n_senses = int(_next_token())
1184
+ assert n_synsets == n_senses
1185
+
1186
+ # get and ignore number of senses ranked according to
1187
+ # frequency
1188
+ _next_token()
1189
+
1190
+ # get synset offsets
1191
+ synset_offsets = [
1192
+ int(_next_token()) for _ in range(n_synsets)
1193
+ ]
1194
+
1195
+ # raise more informative error with file name and line number
1196
+ except (AssertionError, ValueError) as e:
1197
+ tup = ('index.%s' % suffix), (i + 1), e
1198
+ raise WordNetError('file %s, line %i: %s' % tup)
1199
+
1200
+ # map lemmas and parts of speech to synsets
1201
+ self._lemma_pos_offset_map[lemma][pos] = synset_offsets
1202
+ if pos == ADJ:
1203
+ self._lemma_pos_offset_map[lemma][ADJ_SAT] = synset_offsets
1204
+
1205
+ def _load_exception_map(self):
1206
+ # load the exception file data into memory
1207
+ for pos, suffix in self._FILEMAP.items():
1208
+ self._exception_map[pos] = {}
1209
+ for line in self.open('%s.exc' % suffix):
1210
+ terms = line.split()
1211
+ self._exception_map[pos][terms[0]] = terms[1:]
1212
+ self._exception_map[ADJ_SAT] = self._exception_map[ADJ]
1213
+
1214
+ def _compute_max_depth(self, pos, simulate_root):
1215
+ """
1216
+ Compute the max depth for the given part of speech. This is
1217
+ used by the lch similarity metric.
1218
+ """
1219
+ depth = 0
1220
+ for ii in self.all_synsets(pos):
1221
+ try:
1222
+ depth = max(depth, ii.max_depth())
1223
+ except RuntimeError:
1224
+ print(ii)
1225
+ if simulate_root:
1226
+ depth += 1
1227
+ self._max_depth[pos] = depth
1228
+
1229
+ def get_version(self):
1230
+ fh = self._data_file(ADJ)
1231
+ for line in fh:
1232
+ match = re.search(r'WordNet (\d+\.\d+) Copyright', line)
1233
+ if match is not None:
1234
+ version = match.group(1)
1235
+ fh.seek(0)
1236
+ return version
1237
+
1238
+ #############################################################
1239
+ # Loading Lemmas
1240
+ #############################################################
1241
+
1242
+ def lemma(self, name, lang='eng'):
1243
+ '''Return lemma object that matches the name'''
1244
+ # cannot simply split on first '.',
1245
+ # e.g.: '.45_caliber.a.01..45_caliber'
1246
+ separator = SENSENUM_RE.search(name).start()
1247
+
1248
+ leadingZero = int(name[separator+1]) == 0
1249
+ if (leadingZero):
1250
+ synset_name, lemma_name = name[:separator+3], name[separator+4:]
1251
+ else:
1252
+ synset_name, lemma_name = name[:separator+2], name[separator+3:]
1253
+
1254
+ synset = self.synset(synset_name)
1255
+ for lemma in synset.lemmas(lang):
1256
+ if lemma._name == lemma_name:
1257
+ return lemma
1258
+ raise WordNetError('no lemma %r in %r' % (lemma_name, synset_name))
1259
+
1260
+ def lemma_from_key(self, key):
1261
+ # Keys are case sensitive and always lower-case
1262
+ key = key.lower()
1263
+
1264
+ lemma_name, lex_sense = key.split('%')
1265
+ pos_number, lexname_index, lex_id, _, _ = lex_sense.split(':')
1266
+ pos = self._pos_names[int(pos_number)]
1267
+
1268
+ # open the key -> synset file if necessary
1269
+ if self._key_synset_file is None:
1270
+ self._key_synset_file = self.open('index.sense')
1271
+
1272
+ # Find the synset for the lemma.
1273
+ synset_line = _binary_search_file(self._key_synset_file, key)
1274
+ if not synset_line:
1275
+ raise WordNetError("No synset found for key %r" % key)
1276
+ offset = int(synset_line.split()[1])
1277
+ synset = self.synset_from_pos_and_offset(pos, offset)
1278
+
1279
+ # return the corresponding lemma
1280
+ for lemma in synset._lemmas:
1281
+ if lemma._key == key:
1282
+ return lemma
1283
+ raise WordNetError("No lemma found for for key %r" % key)
1284
+
1285
+ #############################################################
1286
+ # Loading Synsets
1287
+ #############################################################
1288
+ def synset(self, name):
1289
+ # split name into lemma, part of speech and synset number
1290
+ lemma, pos, synset_index_str = name.lower().rsplit('.', 2)
1291
+ synset_index = int(synset_index_str) - 1
1292
+
1293
+ # get the offset for this synset
1294
+ try:
1295
+ offset = self._lemma_pos_offset_map[lemma][pos][synset_index]
1296
+ except KeyError:
1297
+ message = 'no lemma %r with part of speech %r'
1298
+ raise WordNetError(message % (lemma, pos))
1299
+ except IndexError:
1300
+ n_senses = len(self._lemma_pos_offset_map[lemma][pos])
1301
+ message = "lemma %r with part of speech %r has only %i %s"
1302
+ if n_senses == 1:
1303
+ tup = lemma, pos, n_senses, "sense"
1304
+ else:
1305
+ tup = lemma, pos, n_senses, "senses"
1306
+ raise WordNetError(message % tup)
1307
+
1308
+ # load synset information from the appropriate file
1309
+ synset = self.synset_from_pos_and_offset(pos, offset)
1310
+
1311
+ # some basic sanity checks on loaded attributes
1312
+ if pos == 's' and synset._pos == 'a':
1313
+ message = ('adjective satellite requested but only plain '
1314
+ 'adjective found for lemma %r')
1315
+ raise WordNetError(message % lemma)
1316
+ assert synset._pos == pos or (pos == 'a' and synset._pos == 's')
1317
+
1318
+ # Return the synset object.
1319
+ return synset
1320
+
1321
+ def _data_file(self, pos):
1322
+ """
1323
+ Return an open file pointer for the data file for the given
1324
+ part of speech.
1325
+ """
1326
+ if pos == ADJ_SAT:
1327
+ pos = ADJ
1328
+ if self._data_file_map.get(pos) is None:
1329
+ fileid = 'data.%s' % self._FILEMAP[pos]
1330
+ self._data_file_map[pos] = self.open(fileid)
1331
+ return self._data_file_map[pos]
1332
+
1333
+ def synset_from_pos_and_offset(self, pos, offset):
1334
+ # Check to see if the synset is in the cache
1335
+ if offset in self._synset_offset_cache[pos]:
1336
+ return self._synset_offset_cache[pos][offset]
1337
+
1338
+ data_file = self._data_file(pos)
1339
+ data_file.seek(offset)
1340
+ data_file_line = data_file.readline()
1341
+ synset = self._synset_from_pos_and_line(pos, data_file_line)
1342
+ assert synset._offset == offset
1343
+ self._synset_offset_cache[pos][offset] = synset
1344
+ return synset
1345
+
1346
+ @deprecated('Use public method synset_from_pos_and_offset() instead')
1347
+ def _synset_from_pos_and_offset(self, *args, **kwargs):
1348
+ """
1349
+ Hack to help people like the readers of
1350
+ http://stackoverflow.com/a/27145655/1709587
1351
+ who were using this function before it was officially a public method
1352
+ """
1353
+ return self.synset_from_pos_and_offset(*args, **kwargs)
1354
+
1355
+ def _synset_from_pos_and_line(self, pos, data_file_line):
1356
+ # Construct a new (empty) synset.
1357
+ synset = Synset(self)
1358
+
1359
+ # parse the entry for this synset
1360
+ try:
1361
+
1362
+ # parse out the definitions and examples from the gloss
1363
+ columns_str, gloss = data_file_line.split('|')
1364
+ gloss = gloss.strip()
1365
+ definitions = []
1366
+ for gloss_part in gloss.split(';'):
1367
+ gloss_part = gloss_part.strip()
1368
+ if gloss_part.startswith('"'):
1369
+ synset._examples.append(gloss_part.strip('"'))
1370
+ else:
1371
+ definitions.append(gloss_part)
1372
+ synset._definition = '; '.join(definitions)
1373
+
1374
+ # split the other info into fields
1375
+ _iter = iter(columns_str.split())
1376
+
1377
+ def _next_token(): return next(_iter)
1378
+
1379
+ # get the offset
1380
+ synset._offset = int(_next_token())
1381
+
1382
+ # determine the lexicographer file name
1383
+ lexname_index = int(_next_token())
1384
+ synset._lexname = self._lexnames[lexname_index]
1385
+
1386
+ # get the part of speech
1387
+ synset._pos = _next_token()
1388
+
1389
+ # create Lemma objects for each lemma
1390
+ n_lemmas = int(_next_token(), 16)
1391
+ for _ in range(n_lemmas):
1392
+ # get the lemma name
1393
+ lemma_name = _next_token()
1394
+ # get the lex_id (used for sense_keys)
1395
+ lex_id = int(_next_token(), 16)
1396
+ # If the lemma has a syntactic marker, extract it.
1397
+ m = re.match(r'(.*?)(\(.*\))?$', lemma_name)
1398
+ lemma_name, syn_mark = m.groups()
1399
+ # create the lemma object
1400
+ lemma = Lemma(self, synset, lemma_name, lexname_index,
1401
+ lex_id, syn_mark)
1402
+ synset._lemmas.append(lemma)
1403
+ synset._lemma_names.append(lemma._name)
1404
+
1405
+ # collect the pointer tuples
1406
+ n_pointers = int(_next_token())
1407
+ for _ in range(n_pointers):
1408
+ symbol = _next_token()
1409
+ offset = int(_next_token())
1410
+ pos = _next_token()
1411
+ lemma_ids_str = _next_token()
1412
+ if lemma_ids_str == '0000':
1413
+ synset._pointers[symbol].add((pos, offset))
1414
+ else:
1415
+ source_index = int(lemma_ids_str[:2], 16) - 1
1416
+ target_index = int(lemma_ids_str[2:], 16) - 1
1417
+ source_lemma_name = synset._lemmas[source_index]._name
1418
+ lemma_pointers = synset._lemma_pointers
1419
+ tups = lemma_pointers[source_lemma_name, symbol]
1420
+ tups.append((pos, offset, target_index))
1421
+
1422
+ # read the verb frames
1423
+ try:
1424
+ frame_count = int(_next_token())
1425
+ except StopIteration:
1426
+ pass
1427
+ else:
1428
+ for _ in range(frame_count):
1429
+ # read the plus sign
1430
+ plus = _next_token()
1431
+ assert plus == '+'
1432
+ # read the frame and lemma number
1433
+ frame_number = int(_next_token())
1434
+ frame_string_fmt = VERB_FRAME_STRINGS[frame_number]
1435
+ lemma_number = int(_next_token(), 16)
1436
+ # lemma number of 00 means all words in the synset
1437
+ if lemma_number == 0:
1438
+ synset._frame_ids.append(frame_number)
1439
+ for lemma in synset._lemmas:
1440
+ lemma._frame_ids.append(frame_number)
1441
+ lemma._frame_strings.append(
1442
+ frame_string_fmt % lemma._name
1443
+ )
1444
+ # only a specific word in the synset
1445
+ else:
1446
+ lemma = synset._lemmas[lemma_number - 1]
1447
+ lemma._frame_ids.append(frame_number)
1448
+ lemma._frame_strings.append(
1449
+ frame_string_fmt % lemma._name
1450
+ )
1451
+
1452
+ # raise a more informative error with line text
1453
+ except ValueError as e:
1454
+ raise WordNetError('line %r: %s' % (data_file_line, e))
1455
+
1456
+ # set sense keys for Lemma objects - note that this has to be
1457
+ # done afterwards so that the relations are available
1458
+ for lemma in synset._lemmas:
1459
+ if synset._pos == ADJ_SAT:
1460
+ head_lemma = synset.similar_tos()[0]._lemmas[0]
1461
+ head_name = head_lemma._name
1462
+ head_id = '%02d' % head_lemma._lex_id
1463
+ else:
1464
+ head_name = head_id = ''
1465
+ tup = (lemma._name, WordNetCorpusReader._pos_numbers[synset._pos],
1466
+ lemma._lexname_index, lemma._lex_id, head_name, head_id)
1467
+ lemma._key = ('%s%%%d:%02d:%02d:%s:%s' % tup).lower()
1468
+
1469
+ # the canonical name is based on the first lemma
1470
+ lemma_name = synset._lemmas[0]._name.lower()
1471
+ offsets = self._lemma_pos_offset_map[lemma_name][synset._pos]
1472
+ sense_index = offsets.index(synset._offset)
1473
+ tup = lemma_name, synset._pos, sense_index + 1
1474
+ synset._name = '%s.%s.%02i' % tup
1475
+
1476
+ return synset
1477
+
1478
+ def synset_from_sense_key(self, sense_key):
1479
+ """
1480
+ Retrieves synset based on a given sense_key. Sense keys can be
1481
+ obtained from lemma.key()
1482
+
1483
+ From https://wordnet.princeton.edu/wordnet/man/senseidx.5WN.html:
1484
+ A sense_key is represented as:
1485
+ lemma % lex_sense (e.g. 'dog%1:18:01::')
1486
+ where lex_sense is encoded as:
1487
+ ss_type:lex_filenum:lex_id:head_word:head_id
1488
+
1489
+ lemma: ASCII text of word/collocation, in lower case
1490
+ ss_type: synset type for the sense (1 digit int)
1491
+ The synset type is encoded as follows:
1492
+ 1 NOUN
1493
+ 2 VERB
1494
+ 3 ADJECTIVE
1495
+ 4 ADVERB
1496
+ 5 ADJECTIVE SATELLITE
1497
+ lex_filenum: name of lexicographer file containing the synset for the sense (2 digit int)
1498
+ lex_id: when paired with lemma, uniquely identifies a sense in the lexicographer file (2 digit int)
1499
+ head_word: lemma of the first word in satellite's head synset
1500
+ Only used if sense is in an adjective satellite synset
1501
+ head_id: uniquely identifies sense in a lexicographer file when paired with head_word
1502
+ Only used if head_word is present (2 digit int)
1503
+ """
1504
+ sense_key_regex = re.compile(r"(.*)\%(.*):(.*):(.*):(.*):(.*)")
1505
+ synset_types = {1:NOUN, 2:VERB, 3:ADJ, 4:ADV, 5:ADJ_SAT}
1506
+ lemma, ss_type, _, lex_id, _, _ = sense_key_regex.match(sense_key).groups()
1507
+
1508
+ # check that information extracted from sense_key is valid
1509
+ error = None
1510
+ if not lemma:
1511
+ error = "lemma"
1512
+ elif int(ss_type) not in synset_types:
1513
+ error = "ss_type"
1514
+ elif int(lex_id) < 0 or int(lex_id) > 99:
1515
+ error = "lex_id"
1516
+ if error:
1517
+ raise WordNetError("valid {} could not be extracted from the sense key".format(error))
1518
+
1519
+ synset_id = '.'.join([lemma, synset_types[int(ss_type)], lex_id])
1520
+ return self.synset(synset_id)
1521
+
1522
+ #############################################################
1523
+ # Retrieve synsets and lemmas.
1524
+ #############################################################
1525
+
1526
+ def synsets(self, lemma, pos=None, lang='eng', check_exceptions=True):
1527
+ """Load all synsets with a given lemma and part of speech tag.
1528
+ If no pos is specified, all synsets for all parts of speech
1529
+ will be loaded.
1530
+ If lang is specified, all the synsets associated with the lemma name
1531
+ of that language will be returned.
1532
+ """
1533
+ lemma = lemma.lower()
1534
+
1535
+ if lang == 'eng':
1536
+ get_synset = self.synset_from_pos_and_offset
1537
+ index = self._lemma_pos_offset_map
1538
+ if pos is None:
1539
+ pos = POS_LIST
1540
+ return [get_synset(p, offset)
1541
+ for p in pos
1542
+ for form in self._morphy(lemma, p, check_exceptions)
1543
+ for offset in index[form].get(p, [])]
1544
+
1545
+ else:
1546
+ self._load_lang_data(lang)
1547
+ synset_list = []
1548
+ for l in self._lang_data[lang][1][lemma]:
1549
+ if pos is not None and l[-1] != pos:
1550
+ continue
1551
+ synset_list.append(self.of2ss(l))
1552
+ return synset_list
1553
+
1554
+ def lemmas(self, lemma, pos=None, lang='eng'):
1555
+ """Return all Lemma objects with a name matching the specified lemma
1556
+ name and part of speech tag. Matches any part of speech tag if none is
1557
+ specified."""
1558
+
1559
+ lemma = lemma.lower()
1560
+ if lang == 'eng':
1561
+ return [lemma_obj
1562
+ for synset in self.synsets(lemma, pos)
1563
+ for lemma_obj in synset.lemmas()
1564
+ if lemma_obj.name().lower() == lemma]
1565
+
1566
+ else:
1567
+ self._load_lang_data(lang)
1568
+ lemmas = []
1569
+ syn = self.synsets(lemma, lang=lang)
1570
+ for s in syn:
1571
+ if pos is not None and s.pos() != pos:
1572
+ continue
1573
+ for lemma_obj in s.lemmas(lang=lang):
1574
+ if lemma_obj.name().lower() == lemma:
1575
+ lemmas.append(lemma_obj)
1576
+ return lemmas
1577
+
1578
+ def all_lemma_names(self, pos=None, lang='eng'):
1579
+ """Return all lemma names for all synsets for the given
1580
+ part of speech tag and language or languages. If pos is
1581
+ not specified, all synsets for all parts of speech will
1582
+ be used."""
1583
+
1584
+ if lang == 'eng':
1585
+ if pos is None:
1586
+ return iter(self._lemma_pos_offset_map)
1587
+ else:
1588
+ return (
1589
+ lemma for lemma in self._lemma_pos_offset_map
1590
+ if pos in self._lemma_pos_offset_map[lemma]
1591
+ )
1592
+ else:
1593
+ self._load_lang_data(lang)
1594
+ lemma = []
1595
+ for i in self._lang_data[lang][0]:
1596
+ if pos is not None and i[-1] != pos:
1597
+ continue
1598
+ lemma.extend(self._lang_data[lang][0][i])
1599
+
1600
+ lemma = list(set(lemma))
1601
+ return lemma
1602
+
1603
+ def all_synsets(self, pos=None):
1604
+ """Iterate over all synsets with a given part of speech tag.
1605
+ If no pos is specified, all synsets for all parts of speech
1606
+ will be loaded.
1607
+ """
1608
+ if pos is None:
1609
+ pos_tags = self._FILEMAP.keys()
1610
+ else:
1611
+ pos_tags = [pos]
1612
+
1613
+ cache = self._synset_offset_cache
1614
+ from_pos_and_line = self._synset_from_pos_and_line
1615
+
1616
+ # generate all synsets for each part of speech
1617
+ for pos_tag in pos_tags:
1618
+ # Open the file for reading. Note that we can not re-use
1619
+ # the file poitners from self._data_file_map here, because
1620
+ # we're defining an iterator, and those file pointers might
1621
+ # be moved while we're not looking.
1622
+ if pos_tag == ADJ_SAT:
1623
+ pos_tag = ADJ
1624
+ fileid = 'data.%s' % self._FILEMAP[pos_tag]
1625
+ data_file = self.open(fileid)
1626
+
1627
+ try:
1628
+ # generate synsets for each line in the POS file
1629
+ offset = data_file.tell()
1630
+ line = data_file.readline()
1631
+ while line:
1632
+ if not line[0].isspace():
1633
+ if offset in cache[pos_tag]:
1634
+ # See if the synset is cached
1635
+ synset = cache[pos_tag][offset]
1636
+ else:
1637
+ # Otherwise, parse the line
1638
+ synset = from_pos_and_line(pos_tag, line)
1639
+ cache[pos_tag][offset] = synset
1640
+
1641
+ # adjective satellites are in the same file as
1642
+ # adjectives so only yield the synset if it's actually
1643
+ # a satellite
1644
+ if synset._pos == ADJ_SAT:
1645
+ yield synset
1646
+
1647
+ # for all other POS tags, yield all synsets (this means
1648
+ # that adjectives also include adjective satellites)
1649
+ else:
1650
+ yield synset
1651
+ offset = data_file.tell()
1652
+ line = data_file.readline()
1653
+
1654
+ # close the extra file handle we opened
1655
+ except:
1656
+ data_file.close()
1657
+ raise
1658
+ else:
1659
+ data_file.close()
1660
+
1661
+ def words(self, lang='eng'):
1662
+ """return lemmas of the given language as list of words"""
1663
+ return self.all_lemma_names(lang=lang)
1664
+
1665
+ def license(self, lang='eng'):
1666
+ """Return the contents of LICENSE (for omw)
1667
+ use lang=lang to get the license for an individual language"""
1668
+ if lang == 'eng':
1669
+ return self.open("LICENSE").read()
1670
+ elif lang in self.langs():
1671
+ return self._omw_reader.open("{}/LICENSE".format(lang)).read()
1672
+ elif lang == 'omw':
1673
+ # under the assumption you don't mean Omwunra-Toqura
1674
+ return self._omw_reader.open("LICENSE").read()
1675
+ elif lang in self._lang_data:
1676
+ raise WordNetError(
1677
+ "Cannot determine license for user-provided tab file"
1678
+ )
1679
+ else:
1680
+ raise WordNetError("Language is not supported.")
1681
+
1682
+ def readme(self, lang='omw'):
1683
+ """Return the contents of README (for omw)
1684
+ use lang=lang to get the readme for an individual language"""
1685
+ if lang == 'eng':
1686
+ return self.open("README").read()
1687
+ elif lang in self.langs():
1688
+ return self._omw_reader.open("{}/README".format(lang)).read()
1689
+ elif lang == 'omw':
1690
+ # under the assumption you don't mean Omwunra-Toqura
1691
+ return self._omw_reader.open("README").read()
1692
+ elif lang in self._lang_data:
1693
+ raise WordNetError("No README for user-provided tab file")
1694
+ else:
1695
+ raise WordNetError("Language is not supported.")
1696
+
1697
+ def citation(self, lang='omw'):
1698
+ """Return the contents of citation.bib file (for omw)
1699
+ use lang=lang to get the citation for an individual language"""
1700
+ if lang == 'eng':
1701
+ return self.open("citation.bib").read()
1702
+ elif lang in self.langs():
1703
+ return self._omw_reader.open("{}/citation.bib".format(lang)).read()
1704
+ elif lang == 'omw':
1705
+ # under the assumption you don't mean Omwunra-Toqura
1706
+ return self._omw_reader.open("citation.bib").read()
1707
+ elif lang in self._lang_data:
1708
+ raise WordNetError("citation not known for user-provided tab file")
1709
+ else:
1710
+ raise WordNetError("Language is not supported.")
1711
+
1712
+ #############################################################
1713
+ # Misc
1714
+ #############################################################
1715
+ def lemma_count(self, lemma):
1716
+ """Return the frequency count for this Lemma"""
1717
+ # Currently, count is only work for English
1718
+ if lemma._lang != 'eng':
1719
+ return 0
1720
+ # open the count file if we haven't already
1721
+ if self._key_count_file is None:
1722
+ self._key_count_file = self.open('cntlist.rev')
1723
+ # find the key in the counts file and return the count
1724
+ line = _binary_search_file(self._key_count_file, lemma._key)
1725
+ if line:
1726
+ return int(line.rsplit(' ', 1)[-1])
1727
+ else:
1728
+ return 0
1729
+
1730
+ def path_similarity(
1731
+ self, synset1, synset2, verbose=False, simulate_root=True
1732
+ ):
1733
+ return synset1.path_similarity(synset2, verbose, simulate_root)
1734
+ path_similarity.__doc__ = Synset.path_similarity.__doc__
1735
+
1736
+ def lch_similarity(
1737
+ self, synset1, synset2, verbose=False, simulate_root=True
1738
+ ):
1739
+ return synset1.lch_similarity(synset2, verbose, simulate_root)
1740
+ lch_similarity.__doc__ = Synset.lch_similarity.__doc__
1741
+
1742
+ def wup_similarity(
1743
+ self, synset1, synset2, verbose=False, simulate_root=True
1744
+ ):
1745
+ return synset1.wup_similarity(synset2, verbose, simulate_root)
1746
+ wup_similarity.__doc__ = Synset.wup_similarity.__doc__
1747
+
1748
+ def res_similarity(self, synset1, synset2, ic, verbose=False):
1749
+ return synset1.res_similarity(synset2, ic, verbose)
1750
+ res_similarity.__doc__ = Synset.res_similarity.__doc__
1751
+
1752
+ def jcn_similarity(self, synset1, synset2, ic, verbose=False):
1753
+ return synset1.jcn_similarity(synset2, ic, verbose)
1754
+ jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__
1755
+
1756
+ def lin_similarity(self, synset1, synset2, ic, verbose=False):
1757
+ return synset1.lin_similarity(synset2, ic, verbose)
1758
+ lin_similarity.__doc__ = Synset.lin_similarity.__doc__
1759
+
1760
+ #############################################################
1761
+ # Morphy
1762
+ #############################################################
1763
+ # Morphy, adapted from Oliver Steele's pywordnet
1764
+ def morphy(self, form, pos=None, check_exceptions=True):
1765
+ """
1766
+ Find a possible base form for the given form, with the given
1767
+ part of speech, by checking WordNet's list of exceptional
1768
+ forms, and by recursively stripping affixes for this part of
1769
+ speech until a form in WordNet is found.
1770
+
1771
+ >>> from nltk.corpus import wordnet as wn
1772
+ >>> print(wn.morphy('dogs'))
1773
+ dog
1774
+ >>> print(wn.morphy('churches'))
1775
+ church
1776
+ >>> print(wn.morphy('aardwolves'))
1777
+ aardwolf
1778
+ >>> print(wn.morphy('abaci'))
1779
+ abacus
1780
+ >>> wn.morphy('hardrock', wn.ADV)
1781
+ >>> print(wn.morphy('book', wn.NOUN))
1782
+ book
1783
+ >>> wn.morphy('book', wn.ADJ)
1784
+ """
1785
+
1786
+ if pos is None:
1787
+ morphy = self._morphy
1788
+ analyses = chain(a for p in POS_LIST for a in morphy(form, p))
1789
+ else:
1790
+ analyses = self._morphy(form, pos, check_exceptions)
1791
+
1792
+ # get the first one we find
1793
+ first = list(islice(analyses, 1))
1794
+ if len(first) == 1:
1795
+ return first[0]
1796
+ else:
1797
+ return None
1798
+
1799
+ MORPHOLOGICAL_SUBSTITUTIONS = {
1800
+ NOUN: [('s', ''), ('ses', 's'), ('ves', 'f'), ('xes', 'x'),
1801
+ ('zes', 'z'), ('ches', 'ch'), ('shes', 'sh'),
1802
+ ('men', 'man'), ('ies', 'y')],
1803
+ VERB: [('s', ''), ('ies', 'y'), ('es', 'e'), ('es', ''),
1804
+ ('ed', 'e'), ('ed', ''), ('ing', 'e'), ('ing', '')],
1805
+ ADJ: [('er', ''), ('est', ''), ('er', 'e'), ('est', 'e')],
1806
+ ADV: []}
1807
+
1808
+ MORPHOLOGICAL_SUBSTITUTIONS[ADJ_SAT] = MORPHOLOGICAL_SUBSTITUTIONS[ADJ]
1809
+
1810
+ def _morphy(self, form, pos, check_exceptions=True):
1811
+ # from jordanbg:
1812
+ # Given an original string x
1813
+ # 1. Apply rules once to the input to get y1, y2, y3, etc.
1814
+ # 2. Return all that are in the database
1815
+ # 3. If there are no matches, keep applying rules until you either
1816
+ # find a match or you can't go any further
1817
+
1818
+ exceptions = self._exception_map[pos]
1819
+ substitutions = self.MORPHOLOGICAL_SUBSTITUTIONS[pos]
1820
+
1821
+ def apply_rules(forms):
1822
+ return [form[:-len(old)] + new
1823
+ for form in forms
1824
+ for old, new in substitutions
1825
+ if form.endswith(old)]
1826
+
1827
+ def filter_forms(forms):
1828
+ result = []
1829
+ seen = set()
1830
+ for form in forms:
1831
+ if form in self._lemma_pos_offset_map:
1832
+ if pos in self._lemma_pos_offset_map[form]:
1833
+ if form not in seen:
1834
+ result.append(form)
1835
+ seen.add(form)
1836
+ return result
1837
+
1838
+ # 0. Check the exception lists
1839
+ if check_exceptions:
1840
+ if form in exceptions:
1841
+ return filter_forms([form] + exceptions[form])
1842
+
1843
+ # 1. Apply rules once to the input to get y1, y2, y3, etc.
1844
+ forms = apply_rules([form])
1845
+
1846
+ # 2. Return all that are in the database (and check the original too)
1847
+ results = filter_forms([form] + forms)
1848
+ if results:
1849
+ return results
1850
+
1851
+ # 3. If there are no matches, keep applying rules until we find a match
1852
+ while forms:
1853
+ forms = apply_rules(forms)
1854
+ results = filter_forms(forms)
1855
+ if results:
1856
+ return results
1857
+
1858
+ # Return an empty list if we can't find anything
1859
+ return []
1860
+
1861
+ #############################################################
1862
+ # Create information content from corpus
1863
+ #############################################################
1864
+ def ic(self, corpus, weight_senses_equally=False, smoothing=1.0):
1865
+ """
1866
+ Creates an information content lookup dictionary from a corpus.
1867
+
1868
+ :type corpus: CorpusReader
1869
+ :param corpus: The corpus from which we create an information
1870
+ content dictionary.
1871
+ :type weight_senses_equally: bool
1872
+ :param weight_senses_equally: If this is True, gives all
1873
+ possible senses equal weight rather than dividing by the
1874
+ number of possible senses. (If a word has 3 synses, each
1875
+ sense gets 0.3333 per appearance when this is False, 1.0 when
1876
+ it is true.)
1877
+ :param smoothing: How much do we smooth synset counts (default is 1.0)
1878
+ :type smoothing: float
1879
+ :return: An information content dictionary
1880
+ """
1881
+ counts = FreqDist()
1882
+ for ww in corpus.words():
1883
+ counts[ww] += 1
1884
+
1885
+ ic = {}
1886
+ for pp in POS_LIST:
1887
+ ic[pp] = defaultdict(float)
1888
+
1889
+ # Initialize the counts with the smoothing value
1890
+ if smoothing > 0.0:
1891
+ for ss in self.all_synsets():
1892
+ pos = ss._pos
1893
+ if pos == ADJ_SAT:
1894
+ pos = ADJ
1895
+ ic[pos][ss._offset] = smoothing
1896
+
1897
+ for ww in counts:
1898
+ possible_synsets = self.synsets(ww)
1899
+ if len(possible_synsets) == 0:
1900
+ continue
1901
+
1902
+ # Distribute weight among possible synsets
1903
+ weight = float(counts[ww])
1904
+ if not weight_senses_equally:
1905
+ weight /= float(len(possible_synsets))
1906
+
1907
+ for ss in possible_synsets:
1908
+ pos = ss._pos
1909
+ if pos == ADJ_SAT:
1910
+ pos = ADJ
1911
+ for level in ss._iter_hypernym_lists():
1912
+ for hh in level:
1913
+ ic[pos][hh._offset] += weight
1914
+ # Add the weight to the root
1915
+ ic[pos][0] += weight
1916
+ return ic
1917
+
1918
+ def custom_lemmas(self, tab_file, lang):
1919
+ """
1920
+ Reads a custom tab file containing mappings of lemmas in the given
1921
+ language to Princeton WordNet 3.0 synset offsets, allowing NLTK's
1922
+ WordNet functions to then be used with that language.
1923
+
1924
+ See the "Tab files" section at http://compling.hss.ntu.edu.sg/omw/ for
1925
+ documentation on the Multilingual WordNet tab file format.
1926
+
1927
+ :param tab_file: Tab file as a file or file-like object
1928
+ :type lang str
1929
+ :param lang ISO 639-3 code of the language of the tab file
1930
+ """
1931
+ if len(lang) != 3:
1932
+ raise ValueError('lang should be a (3 character) ISO 639-3 code')
1933
+ self._lang_data[lang] = [defaultdict(list), defaultdict(list)]
1934
+ for l in tab_file.readlines():
1935
+ if isinstance(l, bytes):
1936
+ # Support byte-stream files (e.g. as returned by Python 2's
1937
+ # open() function) as well as text-stream ones
1938
+ l = l.decode('utf-8')
1939
+ l = l.replace('\n', '')
1940
+ l = l.replace(' ', '_')
1941
+ if l[0] != '#':
1942
+ word = l.split('\t')
1943
+ self._lang_data[lang][0][word[0]].append(word[2])
1944
+ self._lang_data[lang][1][word[2].lower()].append(word[0])
1945
+
1946
+
1947
+ ######################################################################
1948
+ # WordNet Information Content Corpus Reader
1949
+ ######################################################################
1950
+
1951
+ class WordNetICCorpusReader(CorpusReader):
1952
+ """
1953
+ A corpus reader for the WordNet information content corpus.
1954
+ """
1955
+
1956
+ def __init__(self, root, fileids):
1957
+ CorpusReader.__init__(self, root, fileids, encoding='utf8')
1958
+
1959
+ # this load function would be more efficient if the data was pickled
1960
+ # Note that we can't use NLTK's frequency distributions because
1961
+ # synsets are overlapping (each instance of a synset also counts
1962
+ # as an instance of its hypernyms)
1963
+ def ic(self, icfile):
1964
+ """
1965
+ Load an information content file from the wordnet_ic corpus
1966
+ and return a dictionary. This dictionary has just two keys,
1967
+ NOUN and VERB, whose values are dictionaries that map from
1968
+ synsets to information content values.
1969
+
1970
+ :type icfile: str
1971
+ :param icfile: The name of the wordnet_ic file (e.g. "ic-brown.dat")
1972
+ :return: An information content dictionary
1973
+ """
1974
+ ic = {}
1975
+ ic[NOUN] = defaultdict(float)
1976
+ ic[VERB] = defaultdict(float)
1977
+ for num, line in enumerate(self.open(icfile)):
1978
+ if num == 0: # skip the header
1979
+ continue
1980
+ fields = line.split()
1981
+ offset = int(fields[0][:-1])
1982
+ value = float(fields[1])
1983
+ pos = _get_pos(fields[0])
1984
+ if len(fields) == 3 and fields[2] == "ROOT":
1985
+ # Store root count.
1986
+ ic[pos][0] += value
1987
+ if value != 0:
1988
+ ic[pos][offset] = value
1989
+ return ic
1990
+
1991
+
1992
+ ######################################################################
1993
+ # Similarity metrics
1994
+ ######################################################################
1995
+
1996
+ # TODO: Add in the option to manually add a new root node; this will be
1997
+ # useful for verb similarity as there exist multiple verb taxonomies.
1998
+
1999
+ # More information about the metrics is available at
2000
+ # http://marimba.d.umn.edu/similarity/measures.html
2001
+
2002
+ def path_similarity(synset1, synset2, verbose=False, simulate_root=True):
2003
+ return synset1.path_similarity(synset2, verbose, simulate_root)
2004
+
2005
+
2006
+ def lch_similarity(synset1, synset2, verbose=False, simulate_root=True):
2007
+ return synset1.lch_similarity(synset2, verbose, simulate_root)
2008
+
2009
+
2010
+ def wup_similarity(synset1, synset2, verbose=False, simulate_root=True):
2011
+ return synset1.wup_similarity(synset2, verbose, simulate_root)
2012
+
2013
+
2014
+ def res_similarity(synset1, synset2, ic, verbose=False):
2015
+ return synset1.res_similarity(synset2, verbose)
2016
+
2017
+
2018
+ def jcn_similarity(synset1, synset2, ic, verbose=False):
2019
+ return synset1.jcn_similarity(synset2, verbose)
2020
+
2021
+
2022
+ def lin_similarity(synset1, synset2, ic, verbose=False):
2023
+ return synset1.lin_similarity(synset2, verbose)
2024
+
2025
+
2026
+ path_similarity.__doc__ = Synset.path_similarity.__doc__
2027
+ lch_similarity.__doc__ = Synset.lch_similarity.__doc__
2028
+ wup_similarity.__doc__ = Synset.wup_similarity.__doc__
2029
+ res_similarity.__doc__ = Synset.res_similarity.__doc__
2030
+ jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__
2031
+ lin_similarity.__doc__ = Synset.lin_similarity.__doc__
2032
+
2033
+
2034
+ def _lcs_ic(synset1, synset2, ic, verbose=False):
2035
+ """
2036
+ Get the information content of the least common subsumer that has
2037
+ the highest information content value. If two nodes have no
2038
+ explicit common subsumer, assume that they share an artificial
2039
+ root node that is the hypernym of all explicit roots.
2040
+
2041
+ :type synset1: Synset
2042
+ :param synset1: First input synset.
2043
+ :type synset2: Synset
2044
+ :param synset2: Second input synset. Must be the same part of
2045
+ speech as the first synset.
2046
+ :type ic: dict
2047
+ :param ic: an information content object (as returned by ``load_ic()``).
2048
+ :return: The information content of the two synsets and their most
2049
+ informative subsumer
2050
+ """
2051
+ if synset1._pos != synset2._pos:
2052
+ raise WordNetError(
2053
+ 'Computing the least common subsumer requires '
2054
+ '%s and %s to have the same part of speech.' %
2055
+ (synset1, synset2)
2056
+ )
2057
+
2058
+ ic1 = information_content(synset1, ic)
2059
+ ic2 = information_content(synset2, ic)
2060
+ subsumers = synset1.common_hypernyms(synset2)
2061
+ if len(subsumers) == 0:
2062
+ subsumer_ic = 0
2063
+ else:
2064
+ subsumer_ic = max(information_content(s, ic) for s in subsumers)
2065
+
2066
+ if verbose:
2067
+ print("> LCS Subsumer by content:", subsumer_ic)
2068
+
2069
+ return ic1, ic2, subsumer_ic
2070
+
2071
+
2072
+ # Utility functions
2073
+
2074
+ def information_content(synset, ic):
2075
+ try:
2076
+ icpos = ic[synset._pos]
2077
+ except KeyError:
2078
+ msg = 'Information content file has no entries for part-of-speech: %s'
2079
+ raise WordNetError(msg % synset._pos)
2080
+
2081
+ counts = icpos[synset._offset]
2082
+ if counts == 0:
2083
+ return _INF
2084
+ else:
2085
+ return -math.log(counts / icpos[0])
2086
+
2087
+
2088
+ # get the part of speech (NOUN or VERB) from the information content record
2089
+ # (each identifier has a 'n' or 'v' suffix)
2090
+
2091
+ def _get_pos(field):
2092
+ if field[-1] == 'n':
2093
+ return NOUN
2094
+ elif field[-1] == 'v':
2095
+ return VERB
2096
+ else:
2097
+ msg = (
2098
+ "Unidentified part of speech in WordNet Information Content file "
2099
+ "for field %s" % field
2100
+ )
2101
+ raise ValueError(msg)
2102
+
2103
+
2104
+ # unload corpus after tests
2105
+ def teardown_module(module=None):
2106
+ from nltk.corpus import wordnet
2107
+ wordnet._unload()
2108
+