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,1603 +1,1605 @@
1
- # -*- coding: utf-8 -*-
2
- # Natural Language Toolkit: Text Trees
3
- #
4
- # Copyright (C) 2001-2015 NLTK Project
5
- # Author: Edward Loper <edloper@gmail.com>
6
- # Steven Bird <stevenbird1@gmail.com>
7
- # Peter Ljunglöf <peter.ljunglof@gu.se>
8
- # Nathan Bodenstab <bodenstab@cslu.ogi.edu> (tree transforms)
9
- # URL: <http://nltk.org/>
10
- # For license information, see LICENSE.TXT
11
-
12
- """
13
- Class for representing hierarchical language structures, such as
14
- syntax trees and morphological trees.
15
- """
16
- from __future__ import print_function, unicode_literals
17
-
18
- # TODO: add LabelledTree (can be used for dependency trees)
19
-
20
- import re
21
-
22
- from nltk.grammar import Production, Nonterminal
23
- from nltk.probability import ProbabilisticMixIn
24
- from nltk.util import slice_bounds
25
- from nltk.compat import string_types, python_2_unicode_compatible, unicode_repr
26
- from nltk.internals import raise_unorderable_types
27
-
28
- ######################################################################
29
- ## Trees
30
- ######################################################################
31
-
32
- @python_2_unicode_compatible
33
- class Tree(list):
34
- """
35
- A Tree represents a hierarchical grouping of leaves and subtrees.
36
- For example, each constituent in a syntax tree is represented by a single Tree.
37
-
38
- A tree's children are encoded as a list of leaves and subtrees,
39
- where a leaf is a basic (non-tree) value; and a subtree is a
40
- nested Tree.
41
-
42
- >>> from nltk.tree import Tree
43
- >>> print(Tree(1, [2, Tree(3, [4]), 5]))
44
- (1 2 (3 4) 5)
45
- >>> vp = Tree('VP', [Tree('V', ['saw']),
46
- ... Tree('NP', ['him'])])
47
- >>> s = Tree('S', [Tree('NP', ['I']), vp])
48
- >>> print(s)
49
- (S (NP I) (VP (V saw) (NP him)))
50
- >>> print(s[1])
51
- (VP (V saw) (NP him))
52
- >>> print(s[1,1])
53
- (NP him)
54
- >>> t = Tree.fromstring("(S (NP I) (VP (V saw) (NP him)))")
55
- >>> s == t
56
- True
57
- >>> t[1][1].set_label('X')
58
- >>> t[1][1].label()
59
- 'X'
60
- >>> print(t)
61
- (S (NP I) (VP (V saw) (X him)))
62
- >>> t[0], t[1,1] = t[1,1], t[0]
63
- >>> print(t)
64
- (S (X him) (VP (V saw) (NP I)))
65
-
66
- The length of a tree is the number of children it has.
67
-
68
- >>> len(t)
69
- 2
70
-
71
- The set_label() and label() methods allow individual constituents
72
- to be labeled. For example, syntax trees use this label to specify
73
- phrase tags, such as "NP" and "VP".
74
-
75
- Several Tree methods use "tree positions" to specify
76
- children or descendants of a tree. Tree positions are defined as
77
- follows:
78
-
79
- - The tree position *i* specifies a Tree's *i*\ th child.
80
- - The tree position ``()`` specifies the Tree itself.
81
- - If *p* is the tree position of descendant *d*, then
82
- *p+i* specifies the *i*\ th child of *d*.
83
-
84
- I.e., every tree position is either a single index *i*,
85
- specifying ``tree[i]``; or a sequence *i1, i2, ..., iN*,
86
- specifying ``tree[i1][i2]...[iN]``.
87
-
88
- Construct a new tree. This constructor can be called in one
89
- of two ways:
90
-
91
- - ``Tree(label, children)`` constructs a new tree with the
92
- specified label and list of children.
93
-
94
- - ``Tree.fromstring(s)`` constructs a new tree by parsing the string ``s``.
95
- """
96
- def __init__(self, node, children=None):
97
- if children is None:
98
- raise TypeError("%s: Expected a node value and child list "
99
- % type(self).__name__)
100
- elif isinstance(children, string_types):
101
- raise TypeError("%s() argument 2 should be a list, not a "
102
- "string" % type(self).__name__)
103
- else:
104
- list.__init__(self, children)
105
- self._label = node
106
-
107
- #////////////////////////////////////////////////////////////
108
- # Comparison operators
109
- #////////////////////////////////////////////////////////////
110
-
111
- def __eq__(self, other):
112
- return (self.__class__ is other.__class__ and
113
- (self._label, list(self)) == (other._label, list(other)))
114
-
115
- def __lt__(self, other):
116
- if not isinstance(other, Tree):
117
- # raise_unorderable_types("<", self, other)
118
- # Sometimes children can be pure strings,
119
- # so we need to be able to compare with non-trees:
120
- return self.__class__.__name__ < other.__class__.__name__
121
- elif self.__class__ is other.__class__:
122
- return (self._label, list(self)) < (other._label, list(other))
123
- else:
124
- return self.__class__.__name__ < other.__class__.__name__
125
-
126
- # @total_ordering doesn't work here, since the class inherits from a builtin class
127
- __ne__ = lambda self, other: not self == other
128
- __gt__ = lambda self, other: not (self < other or self == other)
129
- __le__ = lambda self, other: self < other or self == other
130
- __ge__ = lambda self, other: not self < other
131
-
132
- #////////////////////////////////////////////////////////////
133
- # Disabled list operations
134
- #////////////////////////////////////////////////////////////
135
-
136
- def __mul__(self, v):
137
- raise TypeError('Tree does not support multiplication')
138
- def __rmul__(self, v):
139
- raise TypeError('Tree does not support multiplication')
140
- def __add__(self, v):
141
- raise TypeError('Tree does not support addition')
142
- def __radd__(self, v):
143
- raise TypeError('Tree does not support addition')
144
-
145
- #////////////////////////////////////////////////////////////
146
- # Indexing (with support for tree positions)
147
- #////////////////////////////////////////////////////////////
148
-
149
- def __getitem__(self, index):
150
- if isinstance(index, (int, slice)):
151
- return list.__getitem__(self, index)
152
- elif isinstance(index, (list, tuple)):
153
- if len(index) == 0:
154
- return self
155
- elif len(index) == 1:
156
- return self[index[0]]
157
- else:
158
- return self[index[0]][index[1:]]
159
- else:
160
- raise TypeError("%s indices must be integers, not %s" %
161
- (type(self).__name__, type(index).__name__))
162
-
163
- def __setitem__(self, index, value):
164
- if isinstance(index, (int, slice)):
165
- return list.__setitem__(self, index, value)
166
- elif isinstance(index, (list, tuple)):
167
- if len(index) == 0:
168
- raise IndexError('The tree position () may not be '
169
- 'assigned to.')
170
- elif len(index) == 1:
171
- self[index[0]] = value
172
- else:
173
- self[index[0]][index[1:]] = value
174
- else:
175
- raise TypeError("%s indices must be integers, not %s" %
176
- (type(self).__name__, type(index).__name__))
177
-
178
- def __delitem__(self, index):
179
- if isinstance(index, (int, slice)):
180
- return list.__delitem__(self, index)
181
- elif isinstance(index, (list, tuple)):
182
- if len(index) == 0:
183
- raise IndexError('The tree position () may not be deleted.')
184
- elif len(index) == 1:
185
- del self[index[0]]
186
- else:
187
- del self[index[0]][index[1:]]
188
- else:
189
- raise TypeError("%s indices must be integers, not %s" %
190
- (type(self).__name__, type(index).__name__))
191
-
192
- #////////////////////////////////////////////////////////////
193
- # Basic tree operations
194
- #////////////////////////////////////////////////////////////
195
-
196
- def _get_node(self):
197
- """Outdated method to access the node value; use the label() method instead."""
198
- raise NotImplementedError("Use label() to access a node label.")
199
- def _set_node(self, value):
200
- """Outdated method to set the node value; use the set_label() method instead."""
201
- raise NotImplementedError("Use set_label() method to set a node label.")
202
- node = property(_get_node, _set_node)
203
-
204
- def label(self):
205
- """
206
- Return the node label of the tree.
207
-
208
- >>> t = Tree.fromstring('(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))')
209
- >>> t.label()
210
- 'S'
211
-
212
- :return: the node label (typically a string)
213
- :rtype: any
214
- """
215
- return self._label
216
-
217
- def set_label(self, label):
218
- """
219
- Set the node label of the tree.
220
-
221
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
222
- >>> t.set_label("T")
223
- >>> print(t)
224
- (T (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))
225
-
226
- :param label: the node label (typically a string)
227
- :type label: any
228
- """
229
- self._label = label
230
-
231
- def leaves(self):
232
- """
233
- Return the leaves of the tree.
234
-
235
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
236
- >>> t.leaves()
237
- ['the', 'dog', 'chased', 'the', 'cat']
238
-
239
- :return: a list containing this tree's leaves.
240
- The order reflects the order of the
241
- leaves in the tree's hierarchical structure.
242
- :rtype: list
243
- """
244
- leaves = []
245
- for child in self:
246
- if isinstance(child, Tree):
247
- leaves.extend(child.leaves())
248
- else:
249
- leaves.append(child)
250
- return leaves
251
-
252
- def flatten(self):
253
- """
254
- Return a flat version of the tree, with all non-root non-terminals removed.
255
-
256
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
257
- >>> print(t.flatten())
258
- (S the dog chased the cat)
259
-
260
- :return: a tree consisting of this tree's root connected directly to
261
- its leaves, omitting all intervening non-terminal nodes.
262
- :rtype: Tree
263
- """
264
- return Tree(self.label(), self.leaves())
265
-
266
- def height(self):
267
- """
268
- Return the height of the tree.
269
-
270
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
271
- >>> t.height()
272
- 5
273
- >>> print(t[0,0])
274
- (D the)
275
- >>> t[0,0].height()
276
- 2
277
-
278
- :return: The height of this tree. The height of a tree
279
- containing no children is 1; the height of a tree
280
- containing only leaves is 2; and the height of any other
281
- tree is one plus the maximum of its children's
282
- heights.
283
- :rtype: int
284
- """
285
- max_child_height = 0
286
- for child in self:
287
- if isinstance(child, Tree):
288
- max_child_height = max(max_child_height, child.height())
289
- else:
290
- max_child_height = max(max_child_height, 1)
291
- return 1 + max_child_height
292
-
293
- def treepositions(self, order='preorder'):
294
- """
295
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
296
- >>> t.treepositions() # doctest: +ELLIPSIS
297
- [(), (0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1,), (1, 0), (1, 0, 0), ...]
298
- >>> for pos in t.treepositions('leaves'):
299
- ... t[pos] = t[pos][::-1].upper()
300
- >>> print(t)
301
- (S (NP (D EHT) (N GOD)) (VP (V DESAHC) (NP (D EHT) (N TAC))))
302
-
303
- :param order: One of: ``preorder``, ``postorder``, ``bothorder``,
304
- ``leaves``.
305
- """
306
- positions = []
307
- if order in ('preorder', 'bothorder'): positions.append( () )
308
- for i, child in enumerate(self):
309
- if isinstance(child, Tree):
310
- childpos = child.treepositions(order)
311
- positions.extend((i,)+p for p in childpos)
312
- else:
313
- positions.append( (i,) )
314
- if order in ('postorder', 'bothorder'): positions.append( () )
315
- return positions
316
-
317
- def subtrees(self, filter=None):
318
- """
319
- Generate all the subtrees of this tree, optionally restricted
320
- to trees matching the filter function.
321
-
322
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
323
- >>> for s in t.subtrees(lambda t: t.height() == 2):
324
- ... print(s)
325
- (D the)
326
- (N dog)
327
- (V chased)
328
- (D the)
329
- (N cat)
330
-
331
- :type filter: function
332
- :param filter: the function to filter all local trees
333
- """
334
- if not filter or filter(self):
335
- yield self
336
- for child in self:
337
- if isinstance(child, Tree):
338
- for subtree in child.subtrees(filter):
339
- yield subtree
340
-
341
- def productions(self):
342
- """
343
- Generate the productions that correspond to the non-terminal nodes of the tree.
344
- For each subtree of the form (P: C1 C2 ... Cn) this produces a production of the
345
- form P -> C1 C2 ... Cn.
346
-
347
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
348
- >>> t.productions()
349
- [S -> NP VP, NP -> D N, D -> 'the', N -> 'dog', VP -> V NP, V -> 'chased',
350
- NP -> D N, D -> 'the', N -> 'cat']
351
-
352
- :rtype: list(Production)
353
- """
354
-
355
- if not isinstance(self._label, string_types):
356
- raise TypeError('Productions can only be generated from trees having node labels that are strings')
357
-
358
- prods = [Production(Nonterminal(self._label), _child_names(self))]
359
- for child in self:
360
- if isinstance(child, Tree):
361
- prods += child.productions()
362
- return prods
363
-
364
- def pos(self):
365
- """
366
- Return a sequence of pos-tagged words extracted from the tree.
367
-
368
- >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
369
- >>> t.pos()
370
- [('the', 'D'), ('dog', 'N'), ('chased', 'V'), ('the', 'D'), ('cat', 'N')]
371
-
372
- :return: a list of tuples containing leaves and pre-terminals (part-of-speech tags).
373
- The order reflects the order of the leaves in the tree's hierarchical structure.
374
- :rtype: list(tuple)
375
- """
376
- pos = []
377
- for child in self:
378
- if isinstance(child, Tree):
379
- pos.extend(child.pos())
380
- else:
381
- pos.append((child, self._label))
382
- return pos
383
-
384
- def leaf_treeposition(self, index):
385
- """
386
- :return: The tree position of the ``index``-th leaf in this
387
- tree. I.e., if ``tp=self.leaf_treeposition(i)``, then
388
- ``self[tp]==self.leaves()[i]``.
389
-
390
- :raise IndexError: If this tree contains fewer than ``index+1``
391
- leaves, or if ``index<0``.
392
- """
393
- if index < 0: raise IndexError('index must be non-negative')
394
-
395
- stack = [(self, ())]
396
- while stack:
397
- value, treepos = stack.pop()
398
- if not isinstance(value, Tree):
399
- if index == 0: return treepos
400
- else: index -= 1
401
- else:
402
- for i in range(len(value)-1, -1, -1):
403
- stack.append( (value[i], treepos+(i,)) )
404
-
405
- raise IndexError('index must be less than or equal to len(self)')
406
-
407
- def treeposition_spanning_leaves(self, start, end):
408
- """
409
- :return: The tree position of the lowest descendant of this
410
- tree that dominates ``self.leaves()[start:end]``.
411
- :raise ValueError: if ``end <= start``
412
- """
413
- if end <= start:
414
- raise ValueError('end must be greater than start')
415
- # Find the tree positions of the start & end leaves, and
416
- # take the longest common subsequence.
417
- start_treepos = self.leaf_treeposition(start)
418
- end_treepos = self.leaf_treeposition(end-1)
419
- # Find the first index where they mismatch:
420
- for i in range(len(start_treepos)):
421
- if i == len(end_treepos) or start_treepos[i] != end_treepos[i]:
422
- return start_treepos[:i]
423
- return start_treepos
424
-
425
- #////////////////////////////////////////////////////////////
426
- # Transforms
427
- #////////////////////////////////////////////////////////////
428
-
429
- def chomsky_normal_form(self, factor="right", horzMarkov=None, vertMarkov=0, childChar="|", parentChar="^"):
430
- """
431
- This method can modify a tree in three ways:
432
-
433
- 1. Convert a tree into its Chomsky Normal Form (CNF)
434
- equivalent -- Every subtree has either two non-terminals
435
- or one terminal as its children. This process requires
436
- the creation of more"artificial" non-terminal nodes.
437
- 2. Markov (vertical) smoothing of children in new artificial
438
- nodes
439
- 3. Horizontal (parent) annotation of nodes
440
-
441
- :param factor: Right or left factoring method (default = "right")
442
- :type factor: str = [left|right]
443
- :param horzMarkov: Markov order for sibling smoothing in artificial nodes (None (default) = include all siblings)
444
- :type horzMarkov: int | None
445
- :param vertMarkov: Markov order for parent smoothing (0 (default) = no vertical annotation)
446
- :type vertMarkov: int | None
447
- :param childChar: A string used in construction of the artificial nodes, separating the head of the
448
- original subtree from the child nodes that have yet to be expanded (default = "|")
449
- :type childChar: str
450
- :param parentChar: A string used to separate the node representation from its vertical annotation
451
- :type parentChar: str
452
- """
453
- from nltk.treetransforms import chomsky_normal_form
454
- chomsky_normal_form(self, factor, horzMarkov, vertMarkov, childChar, parentChar)
455
-
456
- def un_chomsky_normal_form(self, expandUnary = True, childChar = "|", parentChar = "^", unaryChar = "+"):
457
- """
458
- This method modifies the tree in three ways:
459
-
460
- 1. Transforms a tree in Chomsky Normal Form back to its
461
- original structure (branching greater than two)
462
- 2. Removes any parent annotation (if it exists)
463
- 3. (optional) expands unary subtrees (if previously
464
- collapsed with collapseUnary(...) )
465
-
466
- :param expandUnary: Flag to expand unary or not (default = True)
467
- :type expandUnary: bool
468
- :param childChar: A string separating the head node from its children in an artificial node (default = "|")
469
- :type childChar: str
470
- :param parentChar: A sting separating the node label from its parent annotation (default = "^")
471
- :type parentChar: str
472
- :param unaryChar: A string joining two non-terminals in a unary production (default = "+")
473
- :type unaryChar: str
474
- """
475
- from nltk.treetransforms import un_chomsky_normal_form
476
- un_chomsky_normal_form(self, expandUnary, childChar, parentChar, unaryChar)
477
-
478
- def collapse_unary(self, collapsePOS = False, collapseRoot = False, joinChar = "+"):
479
- """
480
- Collapse subtrees with a single child (ie. unary productions)
481
- into a new non-terminal (Tree node) joined by 'joinChar'.
482
- This is useful when working with algorithms that do not allow
483
- unary productions, and completely removing the unary productions
484
- would require loss of useful information. The Tree is modified
485
- directly (since it is passed by reference) and no value is returned.
486
-
487
- :param collapsePOS: 'False' (default) will not collapse the parent of leaf nodes (ie.
488
- Part-of-Speech tags) since they are always unary productions
489
- :type collapsePOS: bool
490
- :param collapseRoot: 'False' (default) will not modify the root production
491
- if it is unary. For the Penn WSJ treebank corpus, this corresponds
492
- to the TOP -> productions.
493
- :type collapseRoot: bool
494
- :param joinChar: A string used to connect collapsed node values (default = "+")
495
- :type joinChar: str
496
- """
497
- from nltk.treetransforms import collapse_unary
498
- collapse_unary(self, collapsePOS, collapseRoot, joinChar)
499
-
500
- #////////////////////////////////////////////////////////////
501
- # Convert, copy
502
- #////////////////////////////////////////////////////////////
503
-
504
- @classmethod
505
- def convert(cls, tree):
506
- """
507
- Convert a tree between different subtypes of Tree. ``cls`` determines
508
- which class will be used to encode the new tree.
509
-
510
- :type tree: Tree
511
- :param tree: The tree that should be converted.
512
- :return: The new Tree.
513
- """
514
- if isinstance(tree, Tree):
515
- children = [cls.convert(child) for child in tree]
516
- return cls(tree._label, children)
517
- else:
518
- return tree
519
-
520
- def copy(self, deep=False):
521
- if not deep: return type(self)(self._label, self)
522
- else: return type(self).convert(self)
523
-
524
- def _frozen_class(self): return ImmutableTree
525
- def freeze(self, leaf_freezer=None):
526
- frozen_class = self._frozen_class()
527
- if leaf_freezer is None:
528
- newcopy = frozen_class.convert(self)
529
- else:
530
- newcopy = self.copy(deep=True)
531
- for pos in newcopy.treepositions('leaves'):
532
- newcopy[pos] = leaf_freezer(newcopy[pos])
533
- newcopy = frozen_class.convert(newcopy)
534
- hash(newcopy) # Make sure the leaves are hashable.
535
- return newcopy
536
-
537
- #////////////////////////////////////////////////////////////
538
- # Parsing
539
- #////////////////////////////////////////////////////////////
540
-
541
- @classmethod
542
- def fromstring(cls, s, brackets='()', read_node=None, read_leaf=None,
543
- node_pattern=None, leaf_pattern=None,
544
- remove_empty_top_bracketing=False):
545
- """
546
- Read a bracketed tree string and return the resulting tree.
547
- Trees are represented as nested brackettings, such as::
548
-
549
- (S (NP (NNP John)) (VP (V runs)))
550
-
551
- :type s: str
552
- :param s: The string to read
553
-
554
- :type brackets: str (length=2)
555
- :param brackets: The bracket characters used to mark the
556
- beginning and end of trees and subtrees.
557
-
558
- :type read_node: function
559
- :type read_leaf: function
560
- :param read_node, read_leaf: If specified, these functions
561
- are applied to the substrings of ``s`` corresponding to
562
- nodes and leaves (respectively) to obtain the values for
563
- those nodes and leaves. They should have the following
564
- signature:
565
-
566
- read_node(str) -> value
567
-
568
- For example, these functions could be used to process nodes
569
- and leaves whose values should be some type other than
570
- string (such as ``FeatStruct``).
571
- Note that by default, node strings and leaf strings are
572
- delimited by whitespace and brackets; to override this
573
- default, use the ``node_pattern`` and ``leaf_pattern``
574
- arguments.
575
-
576
- :type node_pattern: str
577
- :type leaf_pattern: str
578
- :param node_pattern, leaf_pattern: Regular expression patterns
579
- used to find node and leaf substrings in ``s``. By
580
- default, both nodes patterns are defined to match any
581
- sequence of non-whitespace non-bracket characters.
582
-
583
- :type remove_empty_top_bracketing: bool
584
- :param remove_empty_top_bracketing: If the resulting tree has
585
- an empty node label, and is length one, then return its
586
- single child instead. This is useful for treebank trees,
587
- which sometimes contain an extra level of bracketing.
588
-
589
- :return: A tree corresponding to the string representation ``s``.
590
- If this class method is called using a subclass of Tree,
591
- then it will return a tree of that type.
592
- :rtype: Tree
593
- """
594
- if not isinstance(brackets, string_types) or len(brackets) != 2:
595
- raise TypeError('brackets must be a length-2 string')
596
- if re.search('\s', brackets):
597
- raise TypeError('whitespace brackets not allowed')
598
- # Construct a regexp that will tokenize the string.
599
- open_b, close_b = brackets
600
- open_pattern, close_pattern = (re.escape(open_b), re.escape(close_b))
601
- if node_pattern is None:
602
- node_pattern = '[^\s%s%s]+' % (open_pattern, close_pattern)
603
- if leaf_pattern is None:
604
- leaf_pattern = '[^\s%s%s]+' % (open_pattern, close_pattern)
605
- token_re = re.compile('%s\s*(%s)?|%s|(%s)' % (
606
- open_pattern, node_pattern, close_pattern, leaf_pattern))
607
- # Walk through each token, updating a stack of trees.
608
- stack = [(None, [])] # list of (node, children) tuples
609
- for match in token_re.finditer(s):
610
- token = match.group()
611
- # Beginning of a tree/subtree
612
- if token[0] == open_b:
613
- if len(stack) == 1 and len(stack[0][1]) > 0:
614
- cls._parse_error(s, match, 'end-of-string')
615
- label = token[1:].lstrip()
616
- if read_node is not None: label = read_node(label)
617
- stack.append((label, []))
618
- # End of a tree/subtree
619
- elif token == close_b:
620
- if len(stack) == 1:
621
- if len(stack[0][1]) == 0:
622
- cls._parse_error(s, match, open_b)
623
- else:
624
- cls._parse_error(s, match, 'end-of-string')
625
- label, children = stack.pop()
626
- stack[-1][1].append(cls(label, children))
627
- # Leaf node
628
- else:
629
- if len(stack) == 1:
630
- cls._parse_error(s, match, open_b)
631
- if read_leaf is not None: token = read_leaf(token)
632
- stack[-1][1].append(token)
633
-
634
- # check that we got exactly one complete tree.
635
- if len(stack) > 1:
636
- cls._parse_error(s, 'end-of-string', close_b)
637
- elif len(stack[0][1]) == 0:
638
- cls._parse_error(s, 'end-of-string', open_b)
639
- else:
640
- assert stack[0][0] is None
641
- assert len(stack[0][1]) == 1
642
- tree = stack[0][1][0]
643
-
644
- # If the tree has an extra level with node='', then get rid of
645
- # it. E.g.: "((S (NP ...) (VP ...)))"
646
- if remove_empty_top_bracketing and tree._label == '' and len(tree) == 1:
647
- tree = tree[0]
648
- # return the tree.
649
- return tree
650
-
651
- @classmethod
652
- def _parse_error(cls, s, match, expecting):
653
- """
654
- Display a friendly error message when parsing a tree string fails.
655
- :param s: The string we're parsing.
656
- :param match: regexp match of the problem token.
657
- :param expecting: what we expected to see instead.
658
- """
659
- # Construct a basic error message
660
- if match == 'end-of-string':
661
- pos, token = len(s), 'end-of-string'
662
- else:
663
- pos, token = match.start(), match.group()
664
- msg = '%s.read(): expected %r but got %r\n%sat index %d.' % (
665
- cls.__name__, expecting, token, ' '*12, pos)
666
- # Add a display showing the error token itsels:
667
- s = s.replace('\n', ' ').replace('\t', ' ')
668
- offset = pos
669
- if len(s) > pos+10:
670
- s = s[:pos+10]+'...'
671
- if pos > 10:
672
- s = '...'+s[pos-10:]
673
- offset = 13
674
- msg += '\n%s"%s"\n%s^' % (' '*16, s, ' '*(17+offset))
675
- raise ValueError(msg)
676
-
677
- #////////////////////////////////////////////////////////////
678
- # Visualization & String Representation
679
- #////////////////////////////////////////////////////////////
680
-
681
- def draw(self):
682
- """
683
- Open a new window containing a graphical diagram of this tree.
684
- """
685
- from nltk.draw.tree import draw_trees
686
- draw_trees(self)
687
-
688
- def pretty_print(self, sentence=None, highlight=(), stream=None, **kwargs):
689
- """
690
- Pretty-print this tree as ASCII or Unicode art.
691
- For explanation of the arguments, see the documentation for
692
- `nltk.treeprettyprinter.TreePrettyPrinter`.
693
- """
694
- from nltk.treeprettyprinter import TreePrettyPrinter
695
- print(TreePrettyPrinter(self, sentence, highlight).text(**kwargs),
696
- file=stream)
697
-
698
- def __repr__(self):
699
- childstr = ", ".join(unicode_repr(c) for c in self)
700
- return '%s(%s, [%s])' % (type(self).__name__, unicode_repr(self._label), childstr)
701
-
702
- def _repr_png_(self):
703
- """
704
- Draws and outputs in PNG for ipython.
705
- PNG is used instead of PDF, since it can be displayed in the qt console and
706
- has wider browser support.
707
- """
708
- import os
709
- import base64
710
- import subprocess
711
- import tempfile
712
- from nltk.draw.tree import tree_to_treesegment
713
- from nltk.draw.util import CanvasFrame
714
- from nltk.internals import find_binary
715
- _canvas_frame = CanvasFrame()
716
- widget = tree_to_treesegment(_canvas_frame.canvas(), self)
717
- _canvas_frame.add_widget(widget)
718
- x, y, w, h = widget.bbox()
719
- # print_to_file uses scrollregion to set the width and height of the pdf.
720
- _canvas_frame.canvas()['scrollregion'] = (0, 0, w, h)
721
- with tempfile.NamedTemporaryFile() as file:
722
- in_path = '{0:}.ps'.format(file.name)
723
- out_path = '{0:}.png'.format(file.name)
724
- _canvas_frame.print_to_file(in_path)
725
- _canvas_frame.destroy_widget(widget)
726
- subprocess.call([find_binary('gs', binary_names=['gswin32c.exe', 'gswin64c.exe'], env_vars=['PATH'], verbose=False)] +
727
- '-q -dEPSCrop -sDEVICE=png16m -r90 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -dSAFER -dBATCH -dNOPAUSE -sOutputFile={0:} {1:}'
728
- .format(out_path, in_path).split())
729
- with open(out_path, 'rb') as sr:
730
- res = sr.read()
731
- os.remove(in_path)
732
- os.remove(out_path)
733
- return base64.b64encode(res).decode()
734
-
735
- def __str__(self):
736
- return self.pformat()
737
-
738
- def pprint(self, **kwargs):
739
- """
740
- Print a string representation of this Tree to 'stream'
741
- """
742
-
743
- if "stream" in kwargs:
744
- stream = kwargs["stream"]
745
- del kwargs["stream"]
746
- else:
747
- stream = None
748
- print(self.pformat(**kwargs), file=stream)
749
-
750
- def pformat(self, margin=70, indent=0, nodesep='', parens='()', quotes=False):
751
- """
752
- :return: A pretty-printed string representation of this tree.
753
- :rtype: str
754
- :param margin: The right margin at which to do line-wrapping.
755
- :type margin: int
756
- :param indent: The indentation level at which printing
757
- begins. This number is used to decide how far to indent
758
- subsequent lines.
759
- :type indent: int
760
- :param nodesep: A string that is used to separate the node
761
- from the children. E.g., the default value ``':'`` gives
762
- trees like ``(S: (NP: I) (VP: (V: saw) (NP: it)))``.
763
- """
764
-
765
- # Try writing it on one line.
766
- s = self._pformat_flat(nodesep, parens, quotes)
767
- if len(s) + indent < margin:
768
- return s
769
-
770
- # If it doesn't fit on one line, then write it on multi-lines.
771
- if isinstance(self._label, string_types):
772
- s = '%s%s%s' % (parens[0], self._label, nodesep)
773
- else:
774
- s = '%s%s%s' % (parens[0], unicode_repr(self._label), nodesep)
775
- for child in self:
776
- if isinstance(child, Tree):
777
- s += '\n'+' '*(indent+2)+child.pformat(margin, indent+2,
778
- nodesep, parens, quotes)
779
- elif isinstance(child, tuple):
780
- s += '\n'+' '*(indent+2)+ "/".join(child)
781
- elif isinstance(child, string_types) and not quotes:
782
- s += '\n'+' '*(indent+2)+ '%s' % child
783
- else:
784
- s += '\n'+' '*(indent+2)+ unicode_repr(child)
785
- return s+parens[1]
786
-
787
- def pformat_latex_qtree(self):
788
- r"""
789
- Returns a representation of the tree compatible with the
790
- LaTeX qtree package. This consists of the string ``\Tree``
791
- followed by the tree represented in bracketed notation.
792
-
793
- For example, the following result was generated from a parse tree of
794
- the sentence ``The announcement astounded us``::
795
-
796
- \Tree [.I'' [.N'' [.D The ] [.N' [.N announcement ] ] ]
797
- [.I' [.V'' [.V' [.V astounded ] [.N'' [.N' [.N us ] ] ] ] ] ] ]
798
-
799
- See http://www.ling.upenn.edu/advice/latex.html for the LaTeX
800
- style file for the qtree package.
801
-
802
- :return: A latex qtree representation of this tree.
803
- :rtype: str
804
- """
805
- reserved_chars = re.compile('([#\$%&~_\{\}])')
806
-
807
- pformat = self.pformat(indent=6, nodesep='', parens=('[.', ' ]'))
808
- return r'\Tree ' + re.sub(reserved_chars, r'\\\1', pformat)
809
-
810
- def _pformat_flat(self, nodesep, parens, quotes):
811
- childstrs = []
812
- for child in self:
813
- if isinstance(child, Tree):
814
- childstrs.append(child._pformat_flat(nodesep, parens, quotes))
815
- elif isinstance(child, tuple):
816
- childstrs.append("/".join(child))
817
- elif isinstance(child, string_types) and not quotes:
818
- childstrs.append('%s' % child)
819
- else:
820
- childstrs.append(unicode_repr(child))
821
- if isinstance(self._label, string_types):
822
- return '%s%s%s %s%s' % (parens[0], self._label, nodesep,
823
- " ".join(childstrs), parens[1])
824
- else:
825
- return '%s%s%s %s%s' % (parens[0], unicode_repr(self._label), nodesep,
826
- " ".join(childstrs), parens[1])
827
-
828
-
829
- class ImmutableTree(Tree):
830
- def __init__(self, node, children=None):
831
- super(ImmutableTree, self).__init__(node, children)
832
- # Precompute our hash value. This ensures that we're really
833
- # immutable. It also means we only have to calculate it once.
834
- try:
835
- self._hash = hash((self._label, tuple(self)))
836
- except (TypeError, ValueError):
837
- raise ValueError("%s: node value and children "
838
- "must be immutable" % type(self).__name__)
839
-
840
- def __setitem__(self, index, value):
841
- raise ValueError('%s may not be modified' % type(self).__name__)
842
- def __setslice__(self, i, j, value):
843
- raise ValueError('%s may not be modified' % type(self).__name__)
844
- def __delitem__(self, index):
845
- raise ValueError('%s may not be modified' % type(self).__name__)
846
- def __delslice__(self, i, j):
847
- raise ValueError('%s may not be modified' % type(self).__name__)
848
- def __iadd__(self, other):
849
- raise ValueError('%s may not be modified' % type(self).__name__)
850
- def __imul__(self, other):
851
- raise ValueError('%s may not be modified' % type(self).__name__)
852
- def append(self, v):
853
- raise ValueError('%s may not be modified' % type(self).__name__)
854
- def extend(self, v):
855
- raise ValueError('%s may not be modified' % type(self).__name__)
856
- def pop(self, v=None):
857
- raise ValueError('%s may not be modified' % type(self).__name__)
858
- def remove(self, v):
859
- raise ValueError('%s may not be modified' % type(self).__name__)
860
- def reverse(self):
861
- raise ValueError('%s may not be modified' % type(self).__name__)
862
- def sort(self):
863
- raise ValueError('%s may not be modified' % type(self).__name__)
864
- def __hash__(self):
865
- return self._hash
866
-
867
- def set_label(self, value):
868
- """
869
- Set the node label. This will only succeed the first time the
870
- node label is set, which should occur in ImmutableTree.__init__().
871
- """
872
- if hasattr(self, '_label'):
873
- raise ValueError('%s may not be modified' % type(self).__name__)
874
- self._label = value
875
-
876
-
877
- ######################################################################
878
- ## Parented trees
879
- ######################################################################
880
-
881
- class AbstractParentedTree(Tree):
882
- """
883
- An abstract base class for a ``Tree`` that automatically maintains
884
- pointers to parent nodes. These parent pointers are updated
885
- whenever any change is made to a tree's structure. Two subclasses
886
- are currently defined:
887
-
888
- - ``ParentedTree`` is used for tree structures where each subtree
889
- has at most one parent. This class should be used in cases
890
- where there is no"sharing" of subtrees.
891
-
892
- - ``MultiParentedTree`` is used for tree structures where a
893
- subtree may have zero or more parents. This class should be
894
- used in cases where subtrees may be shared.
895
-
896
- Subclassing
897
- ===========
898
- The ``AbstractParentedTree`` class redefines all operations that
899
- modify a tree's structure to call two methods, which are used by
900
- subclasses to update parent information:
901
-
902
- - ``_setparent()`` is called whenever a new child is added.
903
- - ``_delparent()`` is called whenever a child is removed.
904
- """
905
-
906
- def __init__(self, node, children=None):
907
- super(AbstractParentedTree, self).__init__(node, children)
908
- # If children is None, the tree is read from node, and
909
- # all parents will be set during parsing.
910
- if children is not None:
911
- # Otherwise we have to set the parent of the children.
912
- # Iterate over self, and *not* children, because children
913
- # might be an iterator.
914
- for i, child in enumerate(self):
915
- if isinstance(child, Tree):
916
- self._setparent(child, i, dry_run=True)
917
- for i, child in enumerate(self):
918
- if isinstance(child, Tree):
919
- self._setparent(child, i)
920
-
921
- #////////////////////////////////////////////////////////////
922
- # Parent management
923
- #////////////////////////////////////////////////////////////
924
-
925
- def _setparent(self, child, index, dry_run=False):
926
- """
927
- Update the parent pointer of ``child`` to point to ``self``. This
928
- method is only called if the type of ``child`` is ``Tree``;
929
- i.e., it is not called when adding a leaf to a tree. This method
930
- is always called before the child is actually added to the
931
- child list of ``self``.
932
-
933
- :type child: Tree
934
- :type index: int
935
- :param index: The index of ``child`` in ``self``.
936
- :raise TypeError: If ``child`` is a tree with an impropriate
937
- type. Typically, if ``child`` is a tree, then its type needs
938
- to match the type of ``self``. This prevents mixing of
939
- different tree types (single-parented, multi-parented, and
940
- non-parented).
941
- :param dry_run: If true, the don't actually set the child's
942
- parent pointer; just check for any error conditions, and
943
- raise an exception if one is found.
944
- """
945
- raise NotImplementedError()
946
-
947
- def _delparent(self, child, index):
948
- """
949
- Update the parent pointer of ``child`` to not point to self. This
950
- method is only called if the type of ``child`` is ``Tree``; i.e., it
951
- is not called when removing a leaf from a tree. This method
952
- is always called before the child is actually removed from the
953
- child list of ``self``.
954
-
955
- :type child: Tree
956
- :type index: int
957
- :param index: The index of ``child`` in ``self``.
958
- """
959
- raise NotImplementedError()
960
-
961
- #////////////////////////////////////////////////////////////
962
- # Methods that add/remove children
963
- #////////////////////////////////////////////////////////////
964
- # Every method that adds or removes a child must make
965
- # appropriate calls to _setparent() and _delparent().
966
-
967
- def __delitem__(self, index):
968
- # del ptree[start:stop]
969
- if isinstance(index, slice):
970
- start, stop, step = slice_bounds(self, index, allow_step=True)
971
- # Clear all the children pointers.
972
- for i in range(start, stop, step):
973
- if isinstance(self[i], Tree):
974
- self._delparent(self[i], i)
975
- # Delete the children from our child list.
976
- super(AbstractParentedTree, self).__delitem__(index)
977
-
978
- # del ptree[i]
979
- elif isinstance(index, int):
980
- if index < 0: index += len(self)
981
- if index < 0: raise IndexError('index out of range')
982
- # Clear the child's parent pointer.
983
- if isinstance(self[index], Tree):
984
- self._delparent(self[index], index)
985
- # Remove the child from our child list.
986
- super(AbstractParentedTree, self).__delitem__(index)
987
-
988
- elif isinstance(index, (list, tuple)):
989
- # del ptree[()]
990
- if len(index) == 0:
991
- raise IndexError('The tree position () may not be deleted.')
992
- # del ptree[(i,)]
993
- elif len(index) == 1:
994
- del self[index[0]]
995
- # del ptree[i1, i2, i3]
996
- else:
997
- del self[index[0]][index[1:]]
998
-
999
- else:
1000
- raise TypeError("%s indices must be integers, not %s" %
1001
- (type(self).__name__, type(index).__name__))
1002
-
1003
- def __setitem__(self, index, value):
1004
- # ptree[start:stop] = value
1005
- if isinstance(index, slice):
1006
- start, stop, step = slice_bounds(self, index, allow_step=True)
1007
- # make a copy of value, in case it's an iterator
1008
- if not isinstance(value, (list, tuple)):
1009
- value = list(value)
1010
- # Check for any error conditions, so we can avoid ending
1011
- # up in an inconsistent state if an error does occur.
1012
- for i, child in enumerate(value):
1013
- if isinstance(child, Tree):
1014
- self._setparent(child, start + i*step, dry_run=True)
1015
- # clear the child pointers of all parents we're removing
1016
- for i in range(start, stop, step):
1017
- if isinstance(self[i], Tree):
1018
- self._delparent(self[i], i)
1019
- # set the child pointers of the new children. We do this
1020
- # after clearing *all* child pointers, in case we're e.g.
1021
- # reversing the elements in a tree.
1022
- for i, child in enumerate(value):
1023
- if isinstance(child, Tree):
1024
- self._setparent(child, start + i*step)
1025
- # finally, update the content of the child list itself.
1026
- super(AbstractParentedTree, self).__setitem__(index, value)
1027
-
1028
- # ptree[i] = value
1029
- elif isinstance(index, int):
1030
- if index < 0: index += len(self)
1031
- if index < 0: raise IndexError('index out of range')
1032
- # if the value is not changing, do nothing.
1033
- if value is self[index]:
1034
- return
1035
- # Set the new child's parent pointer.
1036
- if isinstance(value, Tree):
1037
- self._setparent(value, index)
1038
- # Remove the old child's parent pointer
1039
- if isinstance(self[index], Tree):
1040
- self._delparent(self[index], index)
1041
- # Update our child list.
1042
- super(AbstractParentedTree, self).__setitem__(index, value)
1043
-
1044
- elif isinstance(index, (list, tuple)):
1045
- # ptree[()] = value
1046
- if len(index) == 0:
1047
- raise IndexError('The tree position () may not be assigned to.')
1048
- # ptree[(i,)] = value
1049
- elif len(index) == 1:
1050
- self[index[0]] = value
1051
- # ptree[i1, i2, i3] = value
1052
- else:
1053
- self[index[0]][index[1:]] = value
1054
-
1055
- else:
1056
- raise TypeError("%s indices must be integers, not %s" %
1057
- (type(self).__name__, type(index).__name__))
1058
-
1059
- def append(self, child):
1060
- if isinstance(child, Tree):
1061
- self._setparent(child, len(self))
1062
- super(AbstractParentedTree, self).append(child)
1063
-
1064
- def extend(self, children):
1065
- for child in children:
1066
- if isinstance(child, Tree):
1067
- self._setparent(child, len(self))
1068
- super(AbstractParentedTree, self).append(child)
1069
-
1070
- def insert(self, index, child):
1071
- # Handle negative indexes. Note that if index < -len(self),
1072
- # we do *not* raise an IndexError, unlike __getitem__. This
1073
- # is done for consistency with list.__getitem__ and list.index.
1074
- if index < 0: index += len(self)
1075
- if index < 0: index = 0
1076
- # Set the child's parent, and update our child list.
1077
- if isinstance(child, Tree):
1078
- self._setparent(child, index)
1079
- super(AbstractParentedTree, self).insert(index, child)
1080
-
1081
- def pop(self, index=-1):
1082
- if index < 0: index += len(self)
1083
- if index < 0: raise IndexError('index out of range')
1084
- if isinstance(self[index], Tree):
1085
- self._delparent(self[index], index)
1086
- return super(AbstractParentedTree, self).pop(index)
1087
-
1088
- # n.b.: like `list`, this is done by equality, not identity!
1089
- # To remove a specific child, use del ptree[i].
1090
- def remove(self, child):
1091
- index = self.index(child)
1092
- if isinstance(self[index], Tree):
1093
- self._delparent(self[index], index)
1094
- super(AbstractParentedTree, self).remove(child)
1095
-
1096
- # We need to implement __getslice__ and friends, even though
1097
- # they're deprecated, because otherwise list.__getslice__ will get
1098
- # called (since we're subclassing from list). Just delegate to
1099
- # __getitem__ etc., but use max(0, start) and max(0, stop) because
1100
- # because negative indices are already handled *before*
1101
- # __getslice__ is called; and we don't want to double-count them.
1102
- if hasattr(list, '__getslice__'):
1103
- def __getslice__(self, start, stop):
1104
- return self.__getitem__(slice(max(0, start), max(0, stop)))
1105
- def __delslice__(self, start, stop):
1106
- return self.__delitem__(slice(max(0, start), max(0, stop)))
1107
- def __setslice__(self, start, stop, value):
1108
- return self.__setitem__(slice(max(0, start), max(0, stop)), value)
1109
-
1110
- class ParentedTree(AbstractParentedTree):
1111
- """
1112
- A ``Tree`` that automatically maintains parent pointers for
1113
- single-parented trees. The following are methods for querying
1114
- the structure of a parented tree: ``parent``, ``parent_index``,
1115
- ``left_sibling``, ``right_sibling``, ``root``, ``treeposition``.
1116
-
1117
- Each ``ParentedTree`` may have at most one parent. In
1118
- particular, subtrees may not be shared. Any attempt to reuse a
1119
- single ``ParentedTree`` as a child of more than one parent (or
1120
- as multiple children of the same parent) will cause a
1121
- ``ValueError`` exception to be raised.
1122
-
1123
- ``ParentedTrees`` should never be used in the same tree as ``Trees``
1124
- or ``MultiParentedTrees``. Mixing tree implementations may result
1125
- in incorrect parent pointers and in ``TypeError`` exceptions.
1126
- """
1127
- def __init__(self, node, children=None):
1128
- self._parent = None
1129
- """The parent of this Tree, or None if it has no parent."""
1130
- super(ParentedTree, self).__init__(node, children)
1131
- if children is None:
1132
- # If children is None, the tree is read from node.
1133
- # After parsing, the parent of the immediate children
1134
- # will point to an intermediate tree, not self.
1135
- # We fix this by brute force:
1136
- for i, child in enumerate(self):
1137
- if isinstance(child, Tree):
1138
- child._parent = None
1139
- self._setparent(child, i)
1140
-
1141
- def _frozen_class(self): return ImmutableParentedTree
1142
-
1143
- #/////////////////////////////////////////////////////////////////
1144
- # Methods
1145
- #/////////////////////////////////////////////////////////////////
1146
-
1147
- def parent(self):
1148
- """The parent of this tree, or None if it has no parent."""
1149
- return self._parent
1150
-
1151
- def parent_index(self):
1152
- """
1153
- The index of this tree in its parent. I.e.,
1154
- ``ptree.parent()[ptree.parent_index()] is ptree``. Note that
1155
- ``ptree.parent_index()`` is not necessarily equal to
1156
- ``ptree.parent.index(ptree)``, since the ``index()`` method
1157
- returns the first child that is equal to its argument.
1158
- """
1159
- if self._parent is None: return None
1160
- for i, child in enumerate(self._parent):
1161
- if child is self: return i
1162
- assert False, 'expected to find self in self._parent!'
1163
-
1164
- def left_sibling(self):
1165
- """The left sibling of this tree, or None if it has none."""
1166
- parent_index = self.parent_index()
1167
- if self._parent and parent_index > 0:
1168
- return self._parent[parent_index-1]
1169
- return None # no left sibling
1170
-
1171
- def right_sibling(self):
1172
- """The right sibling of this tree, or None if it has none."""
1173
- parent_index = self.parent_index()
1174
- if self._parent and parent_index < (len(self._parent)-1):
1175
- return self._parent[parent_index+1]
1176
- return None # no right sibling
1177
-
1178
- def root(self):
1179
- """
1180
- The root of this tree. I.e., the unique ancestor of this tree
1181
- whose parent is None. If ``ptree.parent()`` is None, then
1182
- ``ptree`` is its own root.
1183
- """
1184
- root = self
1185
- while root.parent() is not None:
1186
- root = root.parent()
1187
- return root
1188
-
1189
- def treeposition(self):
1190
- """
1191
- The tree position of this tree, relative to the root of the
1192
- tree. I.e., ``ptree.root[ptree.treeposition] is ptree``.
1193
- """
1194
- if self.parent() is None:
1195
- return ()
1196
- else:
1197
- return self.parent().treeposition() + (self.parent_index(),)
1198
-
1199
-
1200
- #/////////////////////////////////////////////////////////////////
1201
- # Parent Management
1202
- #/////////////////////////////////////////////////////////////////
1203
-
1204
- def _delparent(self, child, index):
1205
- # Sanity checks
1206
- assert isinstance(child, ParentedTree)
1207
- assert self[index] is child
1208
- assert child._parent is self
1209
-
1210
- # Delete child's parent pointer.
1211
- child._parent = None
1212
-
1213
- def _setparent(self, child, index, dry_run=False):
1214
- # If the child's type is incorrect, then complain.
1215
- if not isinstance(child, ParentedTree):
1216
- raise TypeError('Can not insert a non-ParentedTree '+
1217
- 'into a ParentedTree')
1218
-
1219
- # If child already has a parent, then complain.
1220
- if child._parent is not None:
1221
- raise ValueError('Can not insert a subtree that already '
1222
- 'has a parent.')
1223
-
1224
- # Set child's parent pointer & index.
1225
- if not dry_run:
1226
- child._parent = self
1227
-
1228
-
1229
- class MultiParentedTree(AbstractParentedTree):
1230
- """
1231
- A ``Tree`` that automatically maintains parent pointers for
1232
- multi-parented trees. The following are methods for querying the
1233
- structure of a multi-parented tree: ``parents()``, ``parent_indices()``,
1234
- ``left_siblings()``, ``right_siblings()``, ``roots``, ``treepositions``.
1235
-
1236
- Each ``MultiParentedTree`` may have zero or more parents. In
1237
- particular, subtrees may be shared. If a single
1238
- ``MultiParentedTree`` is used as multiple children of the same
1239
- parent, then that parent will appear multiple times in its
1240
- ``parents()`` method.
1241
-
1242
- ``MultiParentedTrees`` should never be used in the same tree as
1243
- ``Trees`` or ``ParentedTrees``. Mixing tree implementations may
1244
- result in incorrect parent pointers and in ``TypeError`` exceptions.
1245
- """
1246
- def __init__(self, node, children=None):
1247
- self._parents = []
1248
- """A list of this tree's parents. This list should not
1249
- contain duplicates, even if a parent contains this tree
1250
- multiple times."""
1251
- super(MultiParentedTree, self).__init__(node, children)
1252
- if children is None:
1253
- # If children is None, the tree is read from node.
1254
- # After parsing, the parent(s) of the immediate children
1255
- # will point to an intermediate tree, not self.
1256
- # We fix this by brute force:
1257
- for i, child in enumerate(self):
1258
- if isinstance(child, Tree):
1259
- child._parents = []
1260
- self._setparent(child, i)
1261
-
1262
- def _frozen_class(self): return ImmutableMultiParentedTree
1263
-
1264
- #/////////////////////////////////////////////////////////////////
1265
- # Methods
1266
- #/////////////////////////////////////////////////////////////////
1267
-
1268
- def parents(self):
1269
- """
1270
- The set of parents of this tree. If this tree has no parents,
1271
- then ``parents`` is the empty set. To check if a tree is used
1272
- as multiple children of the same parent, use the
1273
- ``parent_indices()`` method.
1274
-
1275
- :type: list(MultiParentedTree)
1276
- """
1277
- return list(self._parents)
1278
-
1279
- def left_siblings(self):
1280
- """
1281
- A list of all left siblings of this tree, in any of its parent
1282
- trees. A tree may be its own left sibling if it is used as
1283
- multiple contiguous children of the same parent. A tree may
1284
- appear multiple times in this list if it is the left sibling
1285
- of this tree with respect to multiple parents.
1286
-
1287
- :type: list(MultiParentedTree)
1288
- """
1289
- return [parent[index-1]
1290
- for (parent, index) in self._get_parent_indices()
1291
- if index > 0]
1292
-
1293
- def right_siblings(self):
1294
- """
1295
- A list of all right siblings of this tree, in any of its parent
1296
- trees. A tree may be its own right sibling if it is used as
1297
- multiple contiguous children of the same parent. A tree may
1298
- appear multiple times in this list if it is the right sibling
1299
- of this tree with respect to multiple parents.
1300
-
1301
- :type: list(MultiParentedTree)
1302
- """
1303
- return [parent[index+1]
1304
- for (parent, index) in self._get_parent_indices()
1305
- if index < (len(parent)-1)]
1306
-
1307
- def _get_parent_indices(self):
1308
- return [(parent, index)
1309
- for parent in self._parents
1310
- for index, child in enumerate(parent)
1311
- if child is self]
1312
-
1313
- def roots(self):
1314
- """
1315
- The set of all roots of this tree. This set is formed by
1316
- tracing all possible parent paths until trees with no parents
1317
- are found.
1318
-
1319
- :type: list(MultiParentedTree)
1320
- """
1321
- return list(self._get_roots_helper({}).values())
1322
-
1323
- def _get_roots_helper(self, result):
1324
- if self._parents:
1325
- for parent in self._parents:
1326
- parent._get_roots_helper(result)
1327
- else:
1328
- result[id(self)] = self
1329
- return result
1330
-
1331
- def parent_indices(self, parent):
1332
- """
1333
- Return a list of the indices where this tree occurs as a child
1334
- of ``parent``. If this child does not occur as a child of
1335
- ``parent``, then the empty list is returned. The following is
1336
- always true::
1337
-
1338
- for parent_index in ptree.parent_indices(parent):
1339
- parent[parent_index] is ptree
1340
- """
1341
- if parent not in self._parents: return []
1342
- else: return [index for (index, child) in enumerate(parent)
1343
- if child is self]
1344
-
1345
- def treepositions(self, root):
1346
- """
1347
- Return a list of all tree positions that can be used to reach
1348
- this multi-parented tree starting from ``root``. I.e., the
1349
- following is always true::
1350
-
1351
- for treepos in ptree.treepositions(root):
1352
- root[treepos] is ptree
1353
- """
1354
- if self is root:
1355
- return [()]
1356
- else:
1357
- return [treepos+(index,)
1358
- for parent in self._parents
1359
- for treepos in parent.treepositions(root)
1360
- for (index, child) in enumerate(parent) if child is self]
1361
-
1362
-
1363
- #/////////////////////////////////////////////////////////////////
1364
- # Parent Management
1365
- #/////////////////////////////////////////////////////////////////
1366
-
1367
- def _delparent(self, child, index):
1368
- # Sanity checks
1369
- assert isinstance(child, MultiParentedTree)
1370
- assert self[index] is child
1371
- assert len([p for p in child._parents if p is self]) == 1
1372
-
1373
- # If the only copy of child in self is at index, then delete
1374
- # self from child's parent list.
1375
- for i, c in enumerate(self):
1376
- if c is child and i != index: break
1377
- else:
1378
- child._parents.remove(self)
1379
-
1380
- def _setparent(self, child, index, dry_run=False):
1381
- # If the child's type is incorrect, then complain.
1382
- if not isinstance(child, MultiParentedTree):
1383
- raise TypeError('Can not insert a non-MultiParentedTree '+
1384
- 'into a MultiParentedTree')
1385
-
1386
- # Add self as a parent pointer if it's not already listed.
1387
- if not dry_run:
1388
- for parent in child._parents:
1389
- if parent is self: break
1390
- else:
1391
- child._parents.append(self)
1392
-
1393
- class ImmutableParentedTree(ImmutableTree, ParentedTree):
1394
- pass
1395
-
1396
- class ImmutableMultiParentedTree(ImmutableTree, MultiParentedTree):
1397
- pass
1398
-
1399
-
1400
- ######################################################################
1401
- ## Probabilistic trees
1402
- ######################################################################
1403
-
1404
- @python_2_unicode_compatible
1405
- class ProbabilisticTree(Tree, ProbabilisticMixIn):
1406
- def __init__(self, node, children=None, **prob_kwargs):
1407
- Tree.__init__(self, node, children)
1408
- ProbabilisticMixIn.__init__(self, **prob_kwargs)
1409
-
1410
- # We have to patch up these methods to make them work right:
1411
- def _frozen_class(self): return ImmutableProbabilisticTree
1412
- def __repr__(self):
1413
- return '%s (p=%r)' % (Tree.unicode_repr(self), self.prob())
1414
- def __str__(self):
1415
- return '%s (p=%.6g)' % (self.pformat(margin=60), self.prob())
1416
- def copy(self, deep=False):
1417
- if not deep: return type(self)(self._label, self, prob=self.prob())
1418
- else: return type(self).convert(self)
1419
- @classmethod
1420
- def convert(cls, val):
1421
- if isinstance(val, Tree):
1422
- children = [cls.convert(child) for child in val]
1423
- if isinstance(val, ProbabilisticMixIn):
1424
- return cls(val._label, children, prob=val.prob())
1425
- else:
1426
- return cls(val._label, children, prob=1.0)
1427
- else:
1428
- return val
1429
-
1430
- def __eq__(self, other):
1431
- return (self.__class__ is other.__class__ and
1432
- (self._label, list(self), self.prob()) ==
1433
- (other._label, list(other), other.prob()))
1434
-
1435
- def __lt__(self, other):
1436
- if not isinstance(other, Tree):
1437
- raise_unorderable_types("<", self, other)
1438
- if self.__class__ is other.__class__:
1439
- return ((self._label, list(self), self.prob()) <
1440
- (other._label, list(other), other.prob()))
1441
- else:
1442
- return self.__class__.__name__ < other.__class__.__name__
1443
-
1444
-
1445
- @python_2_unicode_compatible
1446
- class ImmutableProbabilisticTree(ImmutableTree, ProbabilisticMixIn):
1447
- def __init__(self, node, children=None, **prob_kwargs):
1448
- ImmutableTree.__init__(self, node, children)
1449
- ProbabilisticMixIn.__init__(self, **prob_kwargs)
1450
- self._hash = hash((self._label, tuple(self), self.prob()))
1451
-
1452
- # We have to patch up these methods to make them work right:
1453
- def _frozen_class(self): return ImmutableProbabilisticTree
1454
- def __repr__(self):
1455
- return '%s [%s]' % (Tree.unicode_repr(self), self.prob())
1456
- def __str__(self):
1457
- return '%s [%s]' % (self.pformat(margin=60), self.prob())
1458
- def copy(self, deep=False):
1459
- if not deep: return type(self)(self._label, self, prob=self.prob())
1460
- else: return type(self).convert(self)
1461
- @classmethod
1462
- def convert(cls, val):
1463
- if isinstance(val, Tree):
1464
- children = [cls.convert(child) for child in val]
1465
- if isinstance(val, ProbabilisticMixIn):
1466
- return cls(val._label, children, prob=val.prob())
1467
- else:
1468
- return cls(val._label, children, prob=1.0)
1469
- else:
1470
- return val
1471
-
1472
-
1473
- def _child_names(tree):
1474
- names = []
1475
- for child in tree:
1476
- if isinstance(child, Tree):
1477
- names.append(Nonterminal(child._label))
1478
- else:
1479
- names.append(child)
1480
- return names
1481
-
1482
- ######################################################################
1483
- ## Parsing
1484
- ######################################################################
1485
-
1486
- def bracket_parse(s):
1487
- """
1488
- Use Tree.read(s, remove_empty_top_bracketing=True) instead.
1489
- """
1490
- raise NameError("Use Tree.read(s, remove_empty_top_bracketing=True) instead.")
1491
-
1492
- def sinica_parse(s):
1493
- """
1494
- Parse a Sinica Treebank string and return a tree. Trees are represented as nested brackettings,
1495
- as shown in the following example (X represents a Chinese character):
1496
- S(goal:NP(Head:Nep:XX)|theme:NP(Head:Nhaa:X)|quantity:Dab:X|Head:VL2:X)#0(PERIODCATEGORY)
1497
-
1498
- :return: A tree corresponding to the string representation.
1499
- :rtype: Tree
1500
- :param s: The string to be converted
1501
- :type s: str
1502
- """
1503
- tokens = re.split(r'([()| ])', s)
1504
- for i in range(len(tokens)):
1505
- if tokens[i] == '(':
1506
- tokens[i-1], tokens[i] = tokens[i], tokens[i-1] # pull nonterminal inside parens
1507
- elif ':' in tokens[i]:
1508
- fields = tokens[i].split(':')
1509
- if len(fields) == 2: # non-terminal
1510
- tokens[i] = fields[1]
1511
- else:
1512
- tokens[i] = "(" + fields[-2] + " " + fields[-1] + ")"
1513
- elif tokens[i] == '|':
1514
- tokens[i] = ''
1515
-
1516
- treebank_string = " ".join(tokens)
1517
- return Tree.fromstring(treebank_string, remove_empty_top_bracketing=True)
1518
-
1519
- # s = re.sub(r'^#[^\s]*\s', '', s) # remove leading identifier
1520
- # s = re.sub(r'\w+:', '', s) # remove role tags
1521
-
1522
- # return s
1523
-
1524
- ######################################################################
1525
- ## Demonstration
1526
- ######################################################################
1527
-
1528
- def demo():
1529
- """
1530
- A demonstration showing how Trees and Trees can be
1531
- used. This demonstration creates a Tree, and loads a
1532
- Tree from the Treebank corpus,
1533
- and shows the results of calling several of their methods.
1534
- """
1535
-
1536
- from nltk import Tree, ProbabilisticTree
1537
-
1538
- # Demonstrate tree parsing.
1539
- s = '(S (NP (DT the) (NN cat)) (VP (VBD ate) (NP (DT a) (NN cookie))))'
1540
- t = Tree.fromstring(s)
1541
- print("Convert bracketed string into tree:")
1542
- print(t)
1543
- print(t.__repr__())
1544
-
1545
- print("Display tree properties:")
1546
- print(t.label()) # tree's constituent type
1547
- print(t[0]) # tree's first child
1548
- print(t[1]) # tree's second child
1549
- print(t.height())
1550
- print(t.leaves())
1551
- print(t[1])
1552
- print(t[1,1])
1553
- print(t[1,1,0])
1554
-
1555
- # Demonstrate tree modification.
1556
- the_cat = t[0]
1557
- the_cat.insert(1, Tree.fromstring('(JJ big)'))
1558
- print("Tree modification:")
1559
- print(t)
1560
- t[1,1,1] = Tree.fromstring('(NN cake)')
1561
- print(t)
1562
- print()
1563
-
1564
- # Tree transforms
1565
- print("Collapse unary:")
1566
- t.collapse_unary()
1567
- print(t)
1568
- print("Chomsky normal form:")
1569
- t.chomsky_normal_form()
1570
- print(t)
1571
- print()
1572
-
1573
- # Demonstrate probabilistic trees.
1574
- pt = ProbabilisticTree('x', ['y', 'z'], prob=0.5)
1575
- print("Probabilistic Tree:")
1576
- print(pt)
1577
- print()
1578
-
1579
- # Demonstrate parsing of treebank output format.
1580
- t = Tree.fromstring(t.pformat())
1581
- print("Convert tree to bracketed string and back again:")
1582
- print(t)
1583
- print()
1584
-
1585
- # Demonstrate LaTeX output
1586
- print("LaTeX output:")
1587
- print(t.pformat_latex_qtree())
1588
- print()
1589
-
1590
- # Demonstrate Productions
1591
- print("Production output:")
1592
- print(t.productions())
1593
- print()
1594
-
1595
- # Demonstrate tree nodes containing objects other than strings
1596
- t.set_label(('test', 3))
1597
- print(t)
1598
-
1599
- __all__ = ['ImmutableProbabilisticTree', 'ImmutableTree', 'ProbabilisticMixIn',
1600
- 'ProbabilisticTree', 'Tree', 'bracket_parse',
1601
- 'sinica_parse', 'ParentedTree', 'MultiParentedTree',
1602
- 'ImmutableParentedTree', 'ImmutableMultiParentedTree']
1603
-
1
+ # -*- coding: utf-8 -*-
2
+ # Natural Language Toolkit: Text Trees
3
+ #
4
+ # Copyright (C) 2001-2018 NLTK Project
5
+ # Author: Edward Loper <edloper@gmail.com>
6
+ # Steven Bird <stevenbird1@gmail.com>
7
+ # Peter Ljunglöf <peter.ljunglof@gu.se>
8
+ # Nathan Bodenstab <bodenstab@cslu.ogi.edu> (tree transforms)
9
+ # URL: <http://nltk.org/>
10
+ # For license information, see LICENSE.TXT
11
+
12
+ """
13
+ Class for representing hierarchical language structures, such as
14
+ syntax trees and morphological trees.
15
+ """
16
+ from __future__ import print_function, unicode_literals
17
+ from abc import ABCMeta, abstractmethod
18
+ from six import add_metaclass
19
+
20
+ # TODO: add LabelledTree (can be used for dependency trees)
21
+
22
+ import re
23
+
24
+ from six import string_types
25
+
26
+ from nltk.grammar import Production, Nonterminal
27
+ from nltk.probability import ProbabilisticMixIn
28
+ from nltk.util import slice_bounds
29
+ from nltk.compat import python_2_unicode_compatible, unicode_repr
30
+ from nltk.internals import raise_unorderable_types
31
+
32
+ ######################################################################
33
+ ## Trees
34
+ ######################################################################
35
+
36
+ @python_2_unicode_compatible
37
+ class Tree(list):
38
+ """
39
+ A Tree represents a hierarchical grouping of leaves and subtrees.
40
+ For example, each constituent in a syntax tree is represented by a single Tree.
41
+
42
+ A tree's children are encoded as a list of leaves and subtrees,
43
+ where a leaf is a basic (non-tree) value; and a subtree is a
44
+ nested Tree.
45
+
46
+ >>> from nltk.tree import Tree
47
+ >>> print(Tree(1, [2, Tree(3, [4]), 5]))
48
+ (1 2 (3 4) 5)
49
+ >>> vp = Tree('VP', [Tree('V', ['saw']),
50
+ ... Tree('NP', ['him'])])
51
+ >>> s = Tree('S', [Tree('NP', ['I']), vp])
52
+ >>> print(s)
53
+ (S (NP I) (VP (V saw) (NP him)))
54
+ >>> print(s[1])
55
+ (VP (V saw) (NP him))
56
+ >>> print(s[1,1])
57
+ (NP him)
58
+ >>> t = Tree.fromstring("(S (NP I) (VP (V saw) (NP him)))")
59
+ >>> s == t
60
+ True
61
+ >>> t[1][1].set_label('X')
62
+ >>> t[1][1].label()
63
+ 'X'
64
+ >>> print(t)
65
+ (S (NP I) (VP (V saw) (X him)))
66
+ >>> t[0], t[1,1] = t[1,1], t[0]
67
+ >>> print(t)
68
+ (S (X him) (VP (V saw) (NP I)))
69
+
70
+ The length of a tree is the number of children it has.
71
+
72
+ >>> len(t)
73
+ 2
74
+
75
+ The set_label() and label() methods allow individual constituents
76
+ to be labeled. For example, syntax trees use this label to specify
77
+ phrase tags, such as "NP" and "VP".
78
+
79
+ Several Tree methods use "tree positions" to specify
80
+ children or descendants of a tree. Tree positions are defined as
81
+ follows:
82
+
83
+ - The tree position *i* specifies a Tree's *i*\ th child.
84
+ - The tree position ``()`` specifies the Tree itself.
85
+ - If *p* is the tree position of descendant *d*, then
86
+ *p+i* specifies the *i*\ th child of *d*.
87
+
88
+ I.e., every tree position is either a single index *i*,
89
+ specifying ``tree[i]``; or a sequence *i1, i2, ..., iN*,
90
+ specifying ``tree[i1][i2]...[iN]``.
91
+
92
+ Construct a new tree. This constructor can be called in one
93
+ of two ways:
94
+
95
+ - ``Tree(label, children)`` constructs a new tree with the
96
+ specified label and list of children.
97
+
98
+ - ``Tree.fromstring(s)`` constructs a new tree by parsing the string ``s``.
99
+ """
100
+ def __init__(self, node, children=None):
101
+ if children is None:
102
+ raise TypeError("%s: Expected a node value and child list "
103
+ % type(self).__name__)
104
+ elif isinstance(children, string_types):
105
+ raise TypeError("%s() argument 2 should be a list, not a "
106
+ "string" % type(self).__name__)
107
+ else:
108
+ list.__init__(self, children)
109
+ self._label = node
110
+
111
+ #////////////////////////////////////////////////////////////
112
+ # Comparison operators
113
+ #////////////////////////////////////////////////////////////
114
+
115
+ def __eq__(self, other):
116
+ return (self.__class__ is other.__class__ and
117
+ (self._label, list(self)) == (other._label, list(other)))
118
+
119
+ def __lt__(self, other):
120
+ if not isinstance(other, Tree):
121
+ # raise_unorderable_types("<", self, other)
122
+ # Sometimes children can be pure strings,
123
+ # so we need to be able to compare with non-trees:
124
+ return self.__class__.__name__ < other.__class__.__name__
125
+ elif self.__class__ is other.__class__:
126
+ return (self._label, list(self)) < (other._label, list(other))
127
+ else:
128
+ return self.__class__.__name__ < other.__class__.__name__
129
+
130
+ # @total_ordering doesn't work here, since the class inherits from a builtin class
131
+ __ne__ = lambda self, other: not self == other
132
+ __gt__ = lambda self, other: not (self < other or self == other)
133
+ __le__ = lambda self, other: self < other or self == other
134
+ __ge__ = lambda self, other: not self < other
135
+
136
+ #////////////////////////////////////////////////////////////
137
+ # Disabled list operations
138
+ #////////////////////////////////////////////////////////////
139
+
140
+ def __mul__(self, v):
141
+ raise TypeError('Tree does not support multiplication')
142
+ def __rmul__(self, v):
143
+ raise TypeError('Tree does not support multiplication')
144
+ def __add__(self, v):
145
+ raise TypeError('Tree does not support addition')
146
+ def __radd__(self, v):
147
+ raise TypeError('Tree does not support addition')
148
+
149
+ #////////////////////////////////////////////////////////////
150
+ # Indexing (with support for tree positions)
151
+ #////////////////////////////////////////////////////////////
152
+
153
+ def __getitem__(self, index):
154
+ if isinstance(index, (int, slice)):
155
+ return list.__getitem__(self, index)
156
+ elif isinstance(index, (list, tuple)):
157
+ if len(index) == 0:
158
+ return self
159
+ elif len(index) == 1:
160
+ return self[index[0]]
161
+ else:
162
+ return self[index[0]][index[1:]]
163
+ else:
164
+ raise TypeError("%s indices must be integers, not %s" %
165
+ (type(self).__name__, type(index).__name__))
166
+
167
+ def __setitem__(self, index, value):
168
+ if isinstance(index, (int, slice)):
169
+ return list.__setitem__(self, index, value)
170
+ elif isinstance(index, (list, tuple)):
171
+ if len(index) == 0:
172
+ raise IndexError('The tree position () may not be '
173
+ 'assigned to.')
174
+ elif len(index) == 1:
175
+ self[index[0]] = value
176
+ else:
177
+ self[index[0]][index[1:]] = value
178
+ else:
179
+ raise TypeError("%s indices must be integers, not %s" %
180
+ (type(self).__name__, type(index).__name__))
181
+
182
+ def __delitem__(self, index):
183
+ if isinstance(index, (int, slice)):
184
+ return list.__delitem__(self, index)
185
+ elif isinstance(index, (list, tuple)):
186
+ if len(index) == 0:
187
+ raise IndexError('The tree position () may not be deleted.')
188
+ elif len(index) == 1:
189
+ del self[index[0]]
190
+ else:
191
+ del self[index[0]][index[1:]]
192
+ else:
193
+ raise TypeError("%s indices must be integers, not %s" %
194
+ (type(self).__name__, type(index).__name__))
195
+
196
+ #////////////////////////////////////////////////////////////
197
+ # Basic tree operations
198
+ #////////////////////////////////////////////////////////////
199
+
200
+ def _get_node(self):
201
+ """Outdated method to access the node value; use the label() method instead."""
202
+ raise NotImplementedError("Use label() to access a node label.")
203
+ def _set_node(self, value):
204
+ """Outdated method to set the node value; use the set_label() method instead."""
205
+ raise NotImplementedError("Use set_label() method to set a node label.")
206
+ node = property(_get_node, _set_node)
207
+
208
+ def label(self):
209
+ """
210
+ Return the node label of the tree.
211
+
212
+ >>> t = Tree.fromstring('(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))')
213
+ >>> t.label()
214
+ 'S'
215
+
216
+ :return: the node label (typically a string)
217
+ :rtype: any
218
+ """
219
+ return self._label
220
+
221
+ def set_label(self, label):
222
+ """
223
+ Set the node label of the tree.
224
+
225
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
226
+ >>> t.set_label("T")
227
+ >>> print(t)
228
+ (T (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))
229
+
230
+ :param label: the node label (typically a string)
231
+ :type label: any
232
+ """
233
+ self._label = label
234
+
235
+ def leaves(self):
236
+ """
237
+ Return the leaves of the tree.
238
+
239
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
240
+ >>> t.leaves()
241
+ ['the', 'dog', 'chased', 'the', 'cat']
242
+
243
+ :return: a list containing this tree's leaves.
244
+ The order reflects the order of the
245
+ leaves in the tree's hierarchical structure.
246
+ :rtype: list
247
+ """
248
+ leaves = []
249
+ for child in self:
250
+ if isinstance(child, Tree):
251
+ leaves.extend(child.leaves())
252
+ else:
253
+ leaves.append(child)
254
+ return leaves
255
+
256
+ def flatten(self):
257
+ """
258
+ Return a flat version of the tree, with all non-root non-terminals removed.
259
+
260
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
261
+ >>> print(t.flatten())
262
+ (S the dog chased the cat)
263
+
264
+ :return: a tree consisting of this tree's root connected directly to
265
+ its leaves, omitting all intervening non-terminal nodes.
266
+ :rtype: Tree
267
+ """
268
+ return Tree(self.label(), self.leaves())
269
+
270
+ def height(self):
271
+ """
272
+ Return the height of the tree.
273
+
274
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
275
+ >>> t.height()
276
+ 5
277
+ >>> print(t[0,0])
278
+ (D the)
279
+ >>> t[0,0].height()
280
+ 2
281
+
282
+ :return: The height of this tree. The height of a tree
283
+ containing no children is 1; the height of a tree
284
+ containing only leaves is 2; and the height of any other
285
+ tree is one plus the maximum of its children's
286
+ heights.
287
+ :rtype: int
288
+ """
289
+ max_child_height = 0
290
+ for child in self:
291
+ if isinstance(child, Tree):
292
+ max_child_height = max(max_child_height, child.height())
293
+ else:
294
+ max_child_height = max(max_child_height, 1)
295
+ return 1 + max_child_height
296
+
297
+ def treepositions(self, order='preorder'):
298
+ """
299
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
300
+ >>> t.treepositions() # doctest: +ELLIPSIS
301
+ [(), (0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1,), (1, 0), (1, 0, 0), ...]
302
+ >>> for pos in t.treepositions('leaves'):
303
+ ... t[pos] = t[pos][::-1].upper()
304
+ >>> print(t)
305
+ (S (NP (D EHT) (N GOD)) (VP (V DESAHC) (NP (D EHT) (N TAC))))
306
+
307
+ :param order: One of: ``preorder``, ``postorder``, ``bothorder``,
308
+ ``leaves``.
309
+ """
310
+ positions = []
311
+ if order in ('preorder', 'bothorder'): positions.append( () )
312
+ for i, child in enumerate(self):
313
+ if isinstance(child, Tree):
314
+ childpos = child.treepositions(order)
315
+ positions.extend((i,)+p for p in childpos)
316
+ else:
317
+ positions.append( (i,) )
318
+ if order in ('postorder', 'bothorder'): positions.append( () )
319
+ return positions
320
+
321
+ def subtrees(self, filter=None):
322
+ """
323
+ Generate all the subtrees of this tree, optionally restricted
324
+ to trees matching the filter function.
325
+
326
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
327
+ >>> for s in t.subtrees(lambda t: t.height() == 2):
328
+ ... print(s)
329
+ (D the)
330
+ (N dog)
331
+ (V chased)
332
+ (D the)
333
+ (N cat)
334
+
335
+ :type filter: function
336
+ :param filter: the function to filter all local trees
337
+ """
338
+ if not filter or filter(self):
339
+ yield self
340
+ for child in self:
341
+ if isinstance(child, Tree):
342
+ for subtree in child.subtrees(filter):
343
+ yield subtree
344
+
345
+ def productions(self):
346
+ """
347
+ Generate the productions that correspond to the non-terminal nodes of the tree.
348
+ For each subtree of the form (P: C1 C2 ... Cn) this produces a production of the
349
+ form P -> C1 C2 ... Cn.
350
+
351
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
352
+ >>> t.productions()
353
+ [S -> NP VP, NP -> D N, D -> 'the', N -> 'dog', VP -> V NP, V -> 'chased',
354
+ NP -> D N, D -> 'the', N -> 'cat']
355
+
356
+ :rtype: list(Production)
357
+ """
358
+
359
+ if not isinstance(self._label, string_types):
360
+ raise TypeError('Productions can only be generated from trees having node labels that are strings')
361
+
362
+ prods = [Production(Nonterminal(self._label), _child_names(self))]
363
+ for child in self:
364
+ if isinstance(child, Tree):
365
+ prods += child.productions()
366
+ return prods
367
+
368
+ def pos(self):
369
+ """
370
+ Return a sequence of pos-tagged words extracted from the tree.
371
+
372
+ >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))")
373
+ >>> t.pos()
374
+ [('the', 'D'), ('dog', 'N'), ('chased', 'V'), ('the', 'D'), ('cat', 'N')]
375
+
376
+ :return: a list of tuples containing leaves and pre-terminals (part-of-speech tags).
377
+ The order reflects the order of the leaves in the tree's hierarchical structure.
378
+ :rtype: list(tuple)
379
+ """
380
+ pos = []
381
+ for child in self:
382
+ if isinstance(child, Tree):
383
+ pos.extend(child.pos())
384
+ else:
385
+ pos.append((child, self._label))
386
+ return pos
387
+
388
+ def leaf_treeposition(self, index):
389
+ """
390
+ :return: The tree position of the ``index``-th leaf in this
391
+ tree. I.e., if ``tp=self.leaf_treeposition(i)``, then
392
+ ``self[tp]==self.leaves()[i]``.
393
+
394
+ :raise IndexError: If this tree contains fewer than ``index+1``
395
+ leaves, or if ``index<0``.
396
+ """
397
+ if index < 0: raise IndexError('index must be non-negative')
398
+
399
+ stack = [(self, ())]
400
+ while stack:
401
+ value, treepos = stack.pop()
402
+ if not isinstance(value, Tree):
403
+ if index == 0: return treepos
404
+ else: index -= 1
405
+ else:
406
+ for i in range(len(value)-1, -1, -1):
407
+ stack.append( (value[i], treepos+(i,)) )
408
+
409
+ raise IndexError('index must be less than or equal to len(self)')
410
+
411
+ def treeposition_spanning_leaves(self, start, end):
412
+ """
413
+ :return: The tree position of the lowest descendant of this
414
+ tree that dominates ``self.leaves()[start:end]``.
415
+ :raise ValueError: if ``end <= start``
416
+ """
417
+ if end <= start:
418
+ raise ValueError('end must be greater than start')
419
+ # Find the tree positions of the start & end leaves, and
420
+ # take the longest common subsequence.
421
+ start_treepos = self.leaf_treeposition(start)
422
+ end_treepos = self.leaf_treeposition(end-1)
423
+ # Find the first index where they mismatch:
424
+ for i in range(len(start_treepos)):
425
+ if i == len(end_treepos) or start_treepos[i] != end_treepos[i]:
426
+ return start_treepos[:i]
427
+ return start_treepos
428
+
429
+ #////////////////////////////////////////////////////////////
430
+ # Transforms
431
+ #////////////////////////////////////////////////////////////
432
+
433
+ def chomsky_normal_form(self, factor="right", horzMarkov=None, vertMarkov=0, childChar="|", parentChar="^"):
434
+ """
435
+ This method can modify a tree in three ways:
436
+
437
+ 1. Convert a tree into its Chomsky Normal Form (CNF)
438
+ equivalent -- Every subtree has either two non-terminals
439
+ or one terminal as its children. This process requires
440
+ the creation of more"artificial" non-terminal nodes.
441
+ 2. Markov (vertical) smoothing of children in new artificial
442
+ nodes
443
+ 3. Horizontal (parent) annotation of nodes
444
+
445
+ :param factor: Right or left factoring method (default = "right")
446
+ :type factor: str = [left|right]
447
+ :param horzMarkov: Markov order for sibling smoothing in artificial nodes (None (default) = include all siblings)
448
+ :type horzMarkov: int | None
449
+ :param vertMarkov: Markov order for parent smoothing (0 (default) = no vertical annotation)
450
+ :type vertMarkov: int | None
451
+ :param childChar: A string used in construction of the artificial nodes, separating the head of the
452
+ original subtree from the child nodes that have yet to be expanded (default = "|")
453
+ :type childChar: str
454
+ :param parentChar: A string used to separate the node representation from its vertical annotation
455
+ :type parentChar: str
456
+ """
457
+ from nltk.treetransforms import chomsky_normal_form
458
+ chomsky_normal_form(self, factor, horzMarkov, vertMarkov, childChar, parentChar)
459
+
460
+ def un_chomsky_normal_form(self, expandUnary = True, childChar = "|", parentChar = "^", unaryChar = "+"):
461
+ """
462
+ This method modifies the tree in three ways:
463
+
464
+ 1. Transforms a tree in Chomsky Normal Form back to its
465
+ original structure (branching greater than two)
466
+ 2. Removes any parent annotation (if it exists)
467
+ 3. (optional) expands unary subtrees (if previously
468
+ collapsed with collapseUnary(...) )
469
+
470
+ :param expandUnary: Flag to expand unary or not (default = True)
471
+ :type expandUnary: bool
472
+ :param childChar: A string separating the head node from its children in an artificial node (default = "|")
473
+ :type childChar: str
474
+ :param parentChar: A sting separating the node label from its parent annotation (default = "^")
475
+ :type parentChar: str
476
+ :param unaryChar: A string joining two non-terminals in a unary production (default = "+")
477
+ :type unaryChar: str
478
+ """
479
+ from nltk.treetransforms import un_chomsky_normal_form
480
+ un_chomsky_normal_form(self, expandUnary, childChar, parentChar, unaryChar)
481
+
482
+ def collapse_unary(self, collapsePOS = False, collapseRoot = False, joinChar = "+"):
483
+ """
484
+ Collapse subtrees with a single child (ie. unary productions)
485
+ into a new non-terminal (Tree node) joined by 'joinChar'.
486
+ This is useful when working with algorithms that do not allow
487
+ unary productions, and completely removing the unary productions
488
+ would require loss of useful information. The Tree is modified
489
+ directly (since it is passed by reference) and no value is returned.
490
+
491
+ :param collapsePOS: 'False' (default) will not collapse the parent of leaf nodes (ie.
492
+ Part-of-Speech tags) since they are always unary productions
493
+ :type collapsePOS: bool
494
+ :param collapseRoot: 'False' (default) will not modify the root production
495
+ if it is unary. For the Penn WSJ treebank corpus, this corresponds
496
+ to the TOP -> productions.
497
+ :type collapseRoot: bool
498
+ :param joinChar: A string used to connect collapsed node values (default = "+")
499
+ :type joinChar: str
500
+ """
501
+ from nltk.treetransforms import collapse_unary
502
+ collapse_unary(self, collapsePOS, collapseRoot, joinChar)
503
+
504
+ #////////////////////////////////////////////////////////////
505
+ # Convert, copy
506
+ #////////////////////////////////////////////////////////////
507
+
508
+ @classmethod
509
+ def convert(cls, tree):
510
+ """
511
+ Convert a tree between different subtypes of Tree. ``cls`` determines
512
+ which class will be used to encode the new tree.
513
+
514
+ :type tree: Tree
515
+ :param tree: The tree that should be converted.
516
+ :return: The new Tree.
517
+ """
518
+ if isinstance(tree, Tree):
519
+ children = [cls.convert(child) for child in tree]
520
+ return cls(tree._label, children)
521
+ else:
522
+ return tree
523
+
524
+ def copy(self, deep=False):
525
+ if not deep: return type(self)(self._label, self)
526
+ else: return type(self).convert(self)
527
+
528
+ def _frozen_class(self): return ImmutableTree
529
+ def freeze(self, leaf_freezer=None):
530
+ frozen_class = self._frozen_class()
531
+ if leaf_freezer is None:
532
+ newcopy = frozen_class.convert(self)
533
+ else:
534
+ newcopy = self.copy(deep=True)
535
+ for pos in newcopy.treepositions('leaves'):
536
+ newcopy[pos] = leaf_freezer(newcopy[pos])
537
+ newcopy = frozen_class.convert(newcopy)
538
+ hash(newcopy) # Make sure the leaves are hashable.
539
+ return newcopy
540
+
541
+ #////////////////////////////////////////////////////////////
542
+ # Parsing
543
+ #////////////////////////////////////////////////////////////
544
+
545
+ @classmethod
546
+ def fromstring(cls, s, brackets='()', read_node=None, read_leaf=None,
547
+ node_pattern=None, leaf_pattern=None,
548
+ remove_empty_top_bracketing=False):
549
+ """
550
+ Read a bracketed tree string and return the resulting tree.
551
+ Trees are represented as nested brackettings, such as::
552
+
553
+ (S (NP (NNP John)) (VP (V runs)))
554
+
555
+ :type s: str
556
+ :param s: The string to read
557
+
558
+ :type brackets: str (length=2)
559
+ :param brackets: The bracket characters used to mark the
560
+ beginning and end of trees and subtrees.
561
+
562
+ :type read_node: function
563
+ :type read_leaf: function
564
+ :param read_node, read_leaf: If specified, these functions
565
+ are applied to the substrings of ``s`` corresponding to
566
+ nodes and leaves (respectively) to obtain the values for
567
+ those nodes and leaves. They should have the following
568
+ signature:
569
+
570
+ read_node(str) -> value
571
+
572
+ For example, these functions could be used to process nodes
573
+ and leaves whose values should be some type other than
574
+ string (such as ``FeatStruct``).
575
+ Note that by default, node strings and leaf strings are
576
+ delimited by whitespace and brackets; to override this
577
+ default, use the ``node_pattern`` and ``leaf_pattern``
578
+ arguments.
579
+
580
+ :type node_pattern: str
581
+ :type leaf_pattern: str
582
+ :param node_pattern, leaf_pattern: Regular expression patterns
583
+ used to find node and leaf substrings in ``s``. By
584
+ default, both nodes patterns are defined to match any
585
+ sequence of non-whitespace non-bracket characters.
586
+
587
+ :type remove_empty_top_bracketing: bool
588
+ :param remove_empty_top_bracketing: If the resulting tree has
589
+ an empty node label, and is length one, then return its
590
+ single child instead. This is useful for treebank trees,
591
+ which sometimes contain an extra level of bracketing.
592
+
593
+ :return: A tree corresponding to the string representation ``s``.
594
+ If this class method is called using a subclass of Tree,
595
+ then it will return a tree of that type.
596
+ :rtype: Tree
597
+ """
598
+ if not isinstance(brackets, string_types) or len(brackets) != 2:
599
+ raise TypeError('brackets must be a length-2 string')
600
+ if re.search('\s', brackets):
601
+ raise TypeError('whitespace brackets not allowed')
602
+ # Construct a regexp that will tokenize the string.
603
+ open_b, close_b = brackets
604
+ open_pattern, close_pattern = (re.escape(open_b), re.escape(close_b))
605
+ if node_pattern is None:
606
+ node_pattern = '[^\s%s%s]+' % (open_pattern, close_pattern)
607
+ if leaf_pattern is None:
608
+ leaf_pattern = '[^\s%s%s]+' % (open_pattern, close_pattern)
609
+ token_re = re.compile('%s\s*(%s)?|%s|(%s)' % (
610
+ open_pattern, node_pattern, close_pattern, leaf_pattern))
611
+ # Walk through each token, updating a stack of trees.
612
+ stack = [(None, [])] # list of (node, children) tuples
613
+ for match in token_re.finditer(s):
614
+ token = match.group()
615
+ # Beginning of a tree/subtree
616
+ if token[0] == open_b:
617
+ if len(stack) == 1 and len(stack[0][1]) > 0:
618
+ cls._parse_error(s, match, 'end-of-string')
619
+ label = token[1:].lstrip()
620
+ if read_node is not None: label = read_node(label)
621
+ stack.append((label, []))
622
+ # End of a tree/subtree
623
+ elif token == close_b:
624
+ if len(stack) == 1:
625
+ if len(stack[0][1]) == 0:
626
+ cls._parse_error(s, match, open_b)
627
+ else:
628
+ cls._parse_error(s, match, 'end-of-string')
629
+ label, children = stack.pop()
630
+ stack[-1][1].append(cls(label, children))
631
+ # Leaf node
632
+ else:
633
+ if len(stack) == 1:
634
+ cls._parse_error(s, match, open_b)
635
+ if read_leaf is not None: token = read_leaf(token)
636
+ stack[-1][1].append(token)
637
+
638
+ # check that we got exactly one complete tree.
639
+ if len(stack) > 1:
640
+ cls._parse_error(s, 'end-of-string', close_b)
641
+ elif len(stack[0][1]) == 0:
642
+ cls._parse_error(s, 'end-of-string', open_b)
643
+ else:
644
+ assert stack[0][0] is None
645
+ assert len(stack[0][1]) == 1
646
+ tree = stack[0][1][0]
647
+
648
+ # If the tree has an extra level with node='', then get rid of
649
+ # it. E.g.: "((S (NP ...) (VP ...)))"
650
+ if remove_empty_top_bracketing and tree._label == '' and len(tree) == 1:
651
+ tree = tree[0]
652
+ # return the tree.
653
+ return tree
654
+
655
+ @classmethod
656
+ def _parse_error(cls, s, match, expecting):
657
+ """
658
+ Display a friendly error message when parsing a tree string fails.
659
+ :param s: The string we're parsing.
660
+ :param match: regexp match of the problem token.
661
+ :param expecting: what we expected to see instead.
662
+ """
663
+ # Construct a basic error message
664
+ if match == 'end-of-string':
665
+ pos, token = len(s), 'end-of-string'
666
+ else:
667
+ pos, token = match.start(), match.group()
668
+ msg = '%s.read(): expected %r but got %r\n%sat index %d.' % (
669
+ cls.__name__, expecting, token, ' '*12, pos)
670
+ # Add a display showing the error token itsels:
671
+ s = s.replace('\n', ' ').replace('\t', ' ')
672
+ offset = pos
673
+ if len(s) > pos+10:
674
+ s = s[:pos+10]+'...'
675
+ if pos > 10:
676
+ s = '...'+s[pos-10:]
677
+ offset = 13
678
+ msg += '\n%s"%s"\n%s^' % (' '*16, s, ' '*(17+offset))
679
+ raise ValueError(msg)
680
+
681
+ #////////////////////////////////////////////////////////////
682
+ # Visualization & String Representation
683
+ #////////////////////////////////////////////////////////////
684
+
685
+ def draw(self):
686
+ """
687
+ Open a new window containing a graphical diagram of this tree.
688
+ """
689
+ from nltk.draw.tree import draw_trees
690
+ draw_trees(self)
691
+
692
+ def pretty_print(self, sentence=None, highlight=(), stream=None, **kwargs):
693
+ """
694
+ Pretty-print this tree as ASCII or Unicode art.
695
+ For explanation of the arguments, see the documentation for
696
+ `nltk.treeprettyprinter.TreePrettyPrinter`.
697
+ """
698
+ from nltk.treeprettyprinter import TreePrettyPrinter
699
+ print(TreePrettyPrinter(self, sentence, highlight).text(**kwargs),
700
+ file=stream)
701
+
702
+ def __repr__(self):
703
+ childstr = ", ".join(unicode_repr(c) for c in self)
704
+ return '%s(%s, [%s])' % (type(self).__name__, unicode_repr(self._label), childstr)
705
+
706
+ def _repr_png_(self):
707
+ """
708
+ Draws and outputs in PNG for ipython.
709
+ PNG is used instead of PDF, since it can be displayed in the qt console and
710
+ has wider browser support.
711
+ """
712
+ import os
713
+ import base64
714
+ import subprocess
715
+ import tempfile
716
+ from nltk.draw.tree import tree_to_treesegment
717
+ from nltk.draw.util import CanvasFrame
718
+ from nltk.internals import find_binary
719
+ _canvas_frame = CanvasFrame()
720
+ widget = tree_to_treesegment(_canvas_frame.canvas(), self)
721
+ _canvas_frame.add_widget(widget)
722
+ x, y, w, h = widget.bbox()
723
+ # print_to_file uses scrollregion to set the width and height of the pdf.
724
+ _canvas_frame.canvas()['scrollregion'] = (0, 0, w, h)
725
+ with tempfile.NamedTemporaryFile() as file:
726
+ in_path = '{0:}.ps'.format(file.name)
727
+ out_path = '{0:}.png'.format(file.name)
728
+ _canvas_frame.print_to_file(in_path)
729
+ _canvas_frame.destroy_widget(widget)
730
+ subprocess.call([find_binary('gs', binary_names=['gswin32c.exe', 'gswin64c.exe'], env_vars=['PATH'], verbose=False)] +
731
+ '-q -dEPSCrop -sDEVICE=png16m -r90 -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -dSAFER -dBATCH -dNOPAUSE -sOutputFile={0:} {1:}'
732
+ .format(out_path, in_path).split())
733
+ with open(out_path, 'rb') as sr:
734
+ res = sr.read()
735
+ os.remove(in_path)
736
+ os.remove(out_path)
737
+ return base64.b64encode(res).decode()
738
+
739
+ def __str__(self):
740
+ return self.pformat()
741
+
742
+ def pprint(self, **kwargs):
743
+ """
744
+ Print a string representation of this Tree to 'stream'
745
+ """
746
+
747
+ if "stream" in kwargs:
748
+ stream = kwargs["stream"]
749
+ del kwargs["stream"]
750
+ else:
751
+ stream = None
752
+ print(self.pformat(**kwargs), file=stream)
753
+
754
+ def pformat(self, margin=70, indent=0, nodesep='', parens='()', quotes=False):
755
+ """
756
+ :return: A pretty-printed string representation of this tree.
757
+ :rtype: str
758
+ :param margin: The right margin at which to do line-wrapping.
759
+ :type margin: int
760
+ :param indent: The indentation level at which printing
761
+ begins. This number is used to decide how far to indent
762
+ subsequent lines.
763
+ :type indent: int
764
+ :param nodesep: A string that is used to separate the node
765
+ from the children. E.g., the default value ``':'`` gives
766
+ trees like ``(S: (NP: I) (VP: (V: saw) (NP: it)))``.
767
+ """
768
+
769
+ # Try writing it on one line.
770
+ s = self._pformat_flat(nodesep, parens, quotes)
771
+ if len(s) + indent < margin:
772
+ return s
773
+
774
+ # If it doesn't fit on one line, then write it on multi-lines.
775
+ if isinstance(self._label, string_types):
776
+ s = '%s%s%s' % (parens[0], self._label, nodesep)
777
+ else:
778
+ s = '%s%s%s' % (parens[0], unicode_repr(self._label), nodesep)
779
+ for child in self:
780
+ if isinstance(child, Tree):
781
+ s += '\n'+' '*(indent+2)+child.pformat(margin, indent+2,
782
+ nodesep, parens, quotes)
783
+ elif isinstance(child, tuple):
784
+ s += '\n'+' '*(indent+2)+ "/".join(child)
785
+ elif isinstance(child, string_types) and not quotes:
786
+ s += '\n'+' '*(indent+2)+ '%s' % child
787
+ else:
788
+ s += '\n'+' '*(indent+2)+ unicode_repr(child)
789
+ return s+parens[1]
790
+
791
+ def pformat_latex_qtree(self):
792
+ r"""
793
+ Returns a representation of the tree compatible with the
794
+ LaTeX qtree package. This consists of the string ``\Tree``
795
+ followed by the tree represented in bracketed notation.
796
+
797
+ For example, the following result was generated from a parse tree of
798
+ the sentence ``The announcement astounded us``::
799
+
800
+ \Tree [.I'' [.N'' [.D The ] [.N' [.N announcement ] ] ]
801
+ [.I' [.V'' [.V' [.V astounded ] [.N'' [.N' [.N us ] ] ] ] ] ] ]
802
+
803
+ See http://www.ling.upenn.edu/advice/latex.html for the LaTeX
804
+ style file for the qtree package.
805
+
806
+ :return: A latex qtree representation of this tree.
807
+ :rtype: str
808
+ """
809
+ reserved_chars = re.compile('([#\$%&~_\{\}])')
810
+
811
+ pformat = self.pformat(indent=6, nodesep='', parens=('[.', ' ]'))
812
+ return r'\Tree ' + re.sub(reserved_chars, r'\\\1', pformat)
813
+
814
+ def _pformat_flat(self, nodesep, parens, quotes):
815
+ childstrs = []
816
+ for child in self:
817
+ if isinstance(child, Tree):
818
+ childstrs.append(child._pformat_flat(nodesep, parens, quotes))
819
+ elif isinstance(child, tuple):
820
+ childstrs.append("/".join(child))
821
+ elif isinstance(child, string_types) and not quotes:
822
+ childstrs.append('%s' % child)
823
+ else:
824
+ childstrs.append(unicode_repr(child))
825
+ if isinstance(self._label, string_types):
826
+ return '%s%s%s %s%s' % (parens[0], self._label, nodesep,
827
+ " ".join(childstrs), parens[1])
828
+ else:
829
+ return '%s%s%s %s%s' % (parens[0], unicode_repr(self._label), nodesep,
830
+ " ".join(childstrs), parens[1])
831
+
832
+
833
+ class ImmutableTree(Tree):
834
+ def __init__(self, node, children=None):
835
+ super(ImmutableTree, self).__init__(node, children)
836
+ # Precompute our hash value. This ensures that we're really
837
+ # immutable. It also means we only have to calculate it once.
838
+ try:
839
+ self._hash = hash((self._label, tuple(self)))
840
+ except (TypeError, ValueError):
841
+ raise ValueError("%s: node value and children "
842
+ "must be immutable" % type(self).__name__)
843
+
844
+ def __setitem__(self, index, value):
845
+ raise ValueError('%s may not be modified' % type(self).__name__)
846
+ def __setslice__(self, i, j, value):
847
+ raise ValueError('%s may not be modified' % type(self).__name__)
848
+ def __delitem__(self, index):
849
+ raise ValueError('%s may not be modified' % type(self).__name__)
850
+ def __delslice__(self, i, j):
851
+ raise ValueError('%s may not be modified' % type(self).__name__)
852
+ def __iadd__(self, other):
853
+ raise ValueError('%s may not be modified' % type(self).__name__)
854
+ def __imul__(self, other):
855
+ raise ValueError('%s may not be modified' % type(self).__name__)
856
+ def append(self, v):
857
+ raise ValueError('%s may not be modified' % type(self).__name__)
858
+ def extend(self, v):
859
+ raise ValueError('%s may not be modified' % type(self).__name__)
860
+ def pop(self, v=None):
861
+ raise ValueError('%s may not be modified' % type(self).__name__)
862
+ def remove(self, v):
863
+ raise ValueError('%s may not be modified' % type(self).__name__)
864
+ def reverse(self):
865
+ raise ValueError('%s may not be modified' % type(self).__name__)
866
+ def sort(self):
867
+ raise ValueError('%s may not be modified' % type(self).__name__)
868
+ def __hash__(self):
869
+ return self._hash
870
+
871
+ def set_label(self, value):
872
+ """
873
+ Set the node label. This will only succeed the first time the
874
+ node label is set, which should occur in ImmutableTree.__init__().
875
+ """
876
+ if hasattr(self, '_label'):
877
+ raise ValueError('%s may not be modified' % type(self).__name__)
878
+ self._label = value
879
+
880
+
881
+ ######################################################################
882
+ ## Parented trees
883
+ ######################################################################
884
+ @add_metaclass(ABCMeta)
885
+ class AbstractParentedTree(Tree):
886
+ """
887
+ An abstract base class for a ``Tree`` that automatically maintains
888
+ pointers to parent nodes. These parent pointers are updated
889
+ whenever any change is made to a tree's structure. Two subclasses
890
+ are currently defined:
891
+
892
+ - ``ParentedTree`` is used for tree structures where each subtree
893
+ has at most one parent. This class should be used in cases
894
+ where there is no"sharing" of subtrees.
895
+
896
+ - ``MultiParentedTree`` is used for tree structures where a
897
+ subtree may have zero or more parents. This class should be
898
+ used in cases where subtrees may be shared.
899
+
900
+ Subclassing
901
+ ===========
902
+ The ``AbstractParentedTree`` class redefines all operations that
903
+ modify a tree's structure to call two methods, which are used by
904
+ subclasses to update parent information:
905
+
906
+ - ``_setparent()`` is called whenever a new child is added.
907
+ - ``_delparent()`` is called whenever a child is removed.
908
+ """
909
+
910
+ def __init__(self, node, children=None):
911
+ super(AbstractParentedTree, self).__init__(node, children)
912
+ # If children is None, the tree is read from node, and
913
+ # all parents will be set during parsing.
914
+ if children is not None:
915
+ # Otherwise we have to set the parent of the children.
916
+ # Iterate over self, and *not* children, because children
917
+ # might be an iterator.
918
+ for i, child in enumerate(self):
919
+ if isinstance(child, Tree):
920
+ self._setparent(child, i, dry_run=True)
921
+ for i, child in enumerate(self):
922
+ if isinstance(child, Tree):
923
+ self._setparent(child, i)
924
+
925
+ #////////////////////////////////////////////////////////////
926
+ # Parent management
927
+ #////////////////////////////////////////////////////////////
928
+ @abstractmethod
929
+ def _setparent(self, child, index, dry_run=False):
930
+ """
931
+ Update the parent pointer of ``child`` to point to ``self``. This
932
+ method is only called if the type of ``child`` is ``Tree``;
933
+ i.e., it is not called when adding a leaf to a tree. This method
934
+ is always called before the child is actually added to the
935
+ child list of ``self``.
936
+
937
+ :type child: Tree
938
+ :type index: int
939
+ :param index: The index of ``child`` in ``self``.
940
+ :raise TypeError: If ``child`` is a tree with an impropriate
941
+ type. Typically, if ``child`` is a tree, then its type needs
942
+ to match the type of ``self``. This prevents mixing of
943
+ different tree types (single-parented, multi-parented, and
944
+ non-parented).
945
+ :param dry_run: If true, the don't actually set the child's
946
+ parent pointer; just check for any error conditions, and
947
+ raise an exception if one is found.
948
+ """
949
+
950
+ @abstractmethod
951
+ def _delparent(self, child, index):
952
+ """
953
+ Update the parent pointer of ``child`` to not point to self. This
954
+ method is only called if the type of ``child`` is ``Tree``; i.e., it
955
+ is not called when removing a leaf from a tree. This method
956
+ is always called before the child is actually removed from the
957
+ child list of ``self``.
958
+
959
+ :type child: Tree
960
+ :type index: int
961
+ :param index: The index of ``child`` in ``self``.
962
+ """
963
+
964
+ #////////////////////////////////////////////////////////////
965
+ # Methods that add/remove children
966
+ #////////////////////////////////////////////////////////////
967
+ # Every method that adds or removes a child must make
968
+ # appropriate calls to _setparent() and _delparent().
969
+
970
+ def __delitem__(self, index):
971
+ # del ptree[start:stop]
972
+ if isinstance(index, slice):
973
+ start, stop, step = slice_bounds(self, index, allow_step=True)
974
+ # Clear all the children pointers.
975
+ for i in range(start, stop, step):
976
+ if isinstance(self[i], Tree):
977
+ self._delparent(self[i], i)
978
+ # Delete the children from our child list.
979
+ super(AbstractParentedTree, self).__delitem__(index)
980
+
981
+ # del ptree[i]
982
+ elif isinstance(index, int):
983
+ if index < 0: index += len(self)
984
+ if index < 0: raise IndexError('index out of range')
985
+ # Clear the child's parent pointer.
986
+ if isinstance(self[index], Tree):
987
+ self._delparent(self[index], index)
988
+ # Remove the child from our child list.
989
+ super(AbstractParentedTree, self).__delitem__(index)
990
+
991
+ elif isinstance(index, (list, tuple)):
992
+ # del ptree[()]
993
+ if len(index) == 0:
994
+ raise IndexError('The tree position () may not be deleted.')
995
+ # del ptree[(i,)]
996
+ elif len(index) == 1:
997
+ del self[index[0]]
998
+ # del ptree[i1, i2, i3]
999
+ else:
1000
+ del self[index[0]][index[1:]]
1001
+
1002
+ else:
1003
+ raise TypeError("%s indices must be integers, not %s" %
1004
+ (type(self).__name__, type(index).__name__))
1005
+
1006
+ def __setitem__(self, index, value):
1007
+ # ptree[start:stop] = value
1008
+ if isinstance(index, slice):
1009
+ start, stop, step = slice_bounds(self, index, allow_step=True)
1010
+ # make a copy of value, in case it's an iterator
1011
+ if not isinstance(value, (list, tuple)):
1012
+ value = list(value)
1013
+ # Check for any error conditions, so we can avoid ending
1014
+ # up in an inconsistent state if an error does occur.
1015
+ for i, child in enumerate(value):
1016
+ if isinstance(child, Tree):
1017
+ self._setparent(child, start + i*step, dry_run=True)
1018
+ # clear the child pointers of all parents we're removing
1019
+ for i in range(start, stop, step):
1020
+ if isinstance(self[i], Tree):
1021
+ self._delparent(self[i], i)
1022
+ # set the child pointers of the new children. We do this
1023
+ # after clearing *all* child pointers, in case we're e.g.
1024
+ # reversing the elements in a tree.
1025
+ for i, child in enumerate(value):
1026
+ if isinstance(child, Tree):
1027
+ self._setparent(child, start + i*step)
1028
+ # finally, update the content of the child list itself.
1029
+ super(AbstractParentedTree, self).__setitem__(index, value)
1030
+
1031
+ # ptree[i] = value
1032
+ elif isinstance(index, int):
1033
+ if index < 0: index += len(self)
1034
+ if index < 0: raise IndexError('index out of range')
1035
+ # if the value is not changing, do nothing.
1036
+ if value is self[index]:
1037
+ return
1038
+ # Set the new child's parent pointer.
1039
+ if isinstance(value, Tree):
1040
+ self._setparent(value, index)
1041
+ # Remove the old child's parent pointer
1042
+ if isinstance(self[index], Tree):
1043
+ self._delparent(self[index], index)
1044
+ # Update our child list.
1045
+ super(AbstractParentedTree, self).__setitem__(index, value)
1046
+
1047
+ elif isinstance(index, (list, tuple)):
1048
+ # ptree[()] = value
1049
+ if len(index) == 0:
1050
+ raise IndexError('The tree position () may not be assigned to.')
1051
+ # ptree[(i,)] = value
1052
+ elif len(index) == 1:
1053
+ self[index[0]] = value
1054
+ # ptree[i1, i2, i3] = value
1055
+ else:
1056
+ self[index[0]][index[1:]] = value
1057
+
1058
+ else:
1059
+ raise TypeError("%s indices must be integers, not %s" %
1060
+ (type(self).__name__, type(index).__name__))
1061
+
1062
+ def append(self, child):
1063
+ if isinstance(child, Tree):
1064
+ self._setparent(child, len(self))
1065
+ super(AbstractParentedTree, self).append(child)
1066
+
1067
+ def extend(self, children):
1068
+ for child in children:
1069
+ if isinstance(child, Tree):
1070
+ self._setparent(child, len(self))
1071
+ super(AbstractParentedTree, self).append(child)
1072
+
1073
+ def insert(self, index, child):
1074
+ # Handle negative indexes. Note that if index < -len(self),
1075
+ # we do *not* raise an IndexError, unlike __getitem__. This
1076
+ # is done for consistency with list.__getitem__ and list.index.
1077
+ if index < 0: index += len(self)
1078
+ if index < 0: index = 0
1079
+ # Set the child's parent, and update our child list.
1080
+ if isinstance(child, Tree):
1081
+ self._setparent(child, index)
1082
+ super(AbstractParentedTree, self).insert(index, child)
1083
+
1084
+ def pop(self, index=-1):
1085
+ if index < 0: index += len(self)
1086
+ if index < 0: raise IndexError('index out of range')
1087
+ if isinstance(self[index], Tree):
1088
+ self._delparent(self[index], index)
1089
+ return super(AbstractParentedTree, self).pop(index)
1090
+
1091
+ # n.b.: like `list`, this is done by equality, not identity!
1092
+ # To remove a specific child, use del ptree[i].
1093
+ def remove(self, child):
1094
+ index = self.index(child)
1095
+ if isinstance(self[index], Tree):
1096
+ self._delparent(self[index], index)
1097
+ super(AbstractParentedTree, self).remove(child)
1098
+
1099
+ # We need to implement __getslice__ and friends, even though
1100
+ # they're deprecated, because otherwise list.__getslice__ will get
1101
+ # called (since we're subclassing from list). Just delegate to
1102
+ # __getitem__ etc., but use max(0, start) and max(0, stop) because
1103
+ # because negative indices are already handled *before*
1104
+ # __getslice__ is called; and we don't want to double-count them.
1105
+ if hasattr(list, '__getslice__'):
1106
+ def __getslice__(self, start, stop):
1107
+ return self.__getitem__(slice(max(0, start), max(0, stop)))
1108
+ def __delslice__(self, start, stop):
1109
+ return self.__delitem__(slice(max(0, start), max(0, stop)))
1110
+ def __setslice__(self, start, stop, value):
1111
+ return self.__setitem__(slice(max(0, start), max(0, stop)), value)
1112
+
1113
+ class ParentedTree(AbstractParentedTree):
1114
+ """
1115
+ A ``Tree`` that automatically maintains parent pointers for
1116
+ single-parented trees. The following are methods for querying
1117
+ the structure of a parented tree: ``parent``, ``parent_index``,
1118
+ ``left_sibling``, ``right_sibling``, ``root``, ``treeposition``.
1119
+
1120
+ Each ``ParentedTree`` may have at most one parent. In
1121
+ particular, subtrees may not be shared. Any attempt to reuse a
1122
+ single ``ParentedTree`` as a child of more than one parent (or
1123
+ as multiple children of the same parent) will cause a
1124
+ ``ValueError`` exception to be raised.
1125
+
1126
+ ``ParentedTrees`` should never be used in the same tree as ``Trees``
1127
+ or ``MultiParentedTrees``. Mixing tree implementations may result
1128
+ in incorrect parent pointers and in ``TypeError`` exceptions.
1129
+ """
1130
+ def __init__(self, node, children=None):
1131
+ self._parent = None
1132
+ """The parent of this Tree, or None if it has no parent."""
1133
+ super(ParentedTree, self).__init__(node, children)
1134
+ if children is None:
1135
+ # If children is None, the tree is read from node.
1136
+ # After parsing, the parent of the immediate children
1137
+ # will point to an intermediate tree, not self.
1138
+ # We fix this by brute force:
1139
+ for i, child in enumerate(self):
1140
+ if isinstance(child, Tree):
1141
+ child._parent = None
1142
+ self._setparent(child, i)
1143
+
1144
+ def _frozen_class(self): return ImmutableParentedTree
1145
+
1146
+ #/////////////////////////////////////////////////////////////////
1147
+ # Methods
1148
+ #/////////////////////////////////////////////////////////////////
1149
+
1150
+ def parent(self):
1151
+ """The parent of this tree, or None if it has no parent."""
1152
+ return self._parent
1153
+
1154
+ def parent_index(self):
1155
+ """
1156
+ The index of this tree in its parent. I.e.,
1157
+ ``ptree.parent()[ptree.parent_index()] is ptree``. Note that
1158
+ ``ptree.parent_index()`` is not necessarily equal to
1159
+ ``ptree.parent.index(ptree)``, since the ``index()`` method
1160
+ returns the first child that is equal to its argument.
1161
+ """
1162
+ if self._parent is None: return None
1163
+ for i, child in enumerate(self._parent):
1164
+ if child is self: return i
1165
+ assert False, 'expected to find self in self._parent!'
1166
+
1167
+ def left_sibling(self):
1168
+ """The left sibling of this tree, or None if it has none."""
1169
+ parent_index = self.parent_index()
1170
+ if self._parent and parent_index > 0:
1171
+ return self._parent[parent_index-1]
1172
+ return None # no left sibling
1173
+
1174
+ def right_sibling(self):
1175
+ """The right sibling of this tree, or None if it has none."""
1176
+ parent_index = self.parent_index()
1177
+ if self._parent and parent_index < (len(self._parent)-1):
1178
+ return self._parent[parent_index+1]
1179
+ return None # no right sibling
1180
+
1181
+ def root(self):
1182
+ """
1183
+ The root of this tree. I.e., the unique ancestor of this tree
1184
+ whose parent is None. If ``ptree.parent()`` is None, then
1185
+ ``ptree`` is its own root.
1186
+ """
1187
+ root = self
1188
+ while root.parent() is not None:
1189
+ root = root.parent()
1190
+ return root
1191
+
1192
+ def treeposition(self):
1193
+ """
1194
+ The tree position of this tree, relative to the root of the
1195
+ tree. I.e., ``ptree.root[ptree.treeposition] is ptree``.
1196
+ """
1197
+ if self.parent() is None:
1198
+ return ()
1199
+ else:
1200
+ return self.parent().treeposition() + (self.parent_index(),)
1201
+
1202
+
1203
+ #/////////////////////////////////////////////////////////////////
1204
+ # Parent Management
1205
+ #/////////////////////////////////////////////////////////////////
1206
+
1207
+ def _delparent(self, child, index):
1208
+ # Sanity checks
1209
+ assert isinstance(child, ParentedTree)
1210
+ assert self[index] is child
1211
+ assert child._parent is self
1212
+
1213
+ # Delete child's parent pointer.
1214
+ child._parent = None
1215
+
1216
+ def _setparent(self, child, index, dry_run=False):
1217
+ # If the child's type is incorrect, then complain.
1218
+ if not isinstance(child, ParentedTree):
1219
+ raise TypeError('Can not insert a non-ParentedTree '+
1220
+ 'into a ParentedTree')
1221
+
1222
+ # If child already has a parent, then complain.
1223
+ if child._parent is not None:
1224
+ raise ValueError('Can not insert a subtree that already '
1225
+ 'has a parent.')
1226
+
1227
+ # Set child's parent pointer & index.
1228
+ if not dry_run:
1229
+ child._parent = self
1230
+
1231
+
1232
+ class MultiParentedTree(AbstractParentedTree):
1233
+ """
1234
+ A ``Tree`` that automatically maintains parent pointers for
1235
+ multi-parented trees. The following are methods for querying the
1236
+ structure of a multi-parented tree: ``parents()``, ``parent_indices()``,
1237
+ ``left_siblings()``, ``right_siblings()``, ``roots``, ``treepositions``.
1238
+
1239
+ Each ``MultiParentedTree`` may have zero or more parents. In
1240
+ particular, subtrees may be shared. If a single
1241
+ ``MultiParentedTree`` is used as multiple children of the same
1242
+ parent, then that parent will appear multiple times in its
1243
+ ``parents()`` method.
1244
+
1245
+ ``MultiParentedTrees`` should never be used in the same tree as
1246
+ ``Trees`` or ``ParentedTrees``. Mixing tree implementations may
1247
+ result in incorrect parent pointers and in ``TypeError`` exceptions.
1248
+ """
1249
+ def __init__(self, node, children=None):
1250
+ self._parents = []
1251
+ """A list of this tree's parents. This list should not
1252
+ contain duplicates, even if a parent contains this tree
1253
+ multiple times."""
1254
+ super(MultiParentedTree, self).__init__(node, children)
1255
+ if children is None:
1256
+ # If children is None, the tree is read from node.
1257
+ # After parsing, the parent(s) of the immediate children
1258
+ # will point to an intermediate tree, not self.
1259
+ # We fix this by brute force:
1260
+ for i, child in enumerate(self):
1261
+ if isinstance(child, Tree):
1262
+ child._parents = []
1263
+ self._setparent(child, i)
1264
+
1265
+ def _frozen_class(self): return ImmutableMultiParentedTree
1266
+
1267
+ #/////////////////////////////////////////////////////////////////
1268
+ # Methods
1269
+ #/////////////////////////////////////////////////////////////////
1270
+
1271
+ def parents(self):
1272
+ """
1273
+ The set of parents of this tree. If this tree has no parents,
1274
+ then ``parents`` is the empty set. To check if a tree is used
1275
+ as multiple children of the same parent, use the
1276
+ ``parent_indices()`` method.
1277
+
1278
+ :type: list(MultiParentedTree)
1279
+ """
1280
+ return list(self._parents)
1281
+
1282
+ def left_siblings(self):
1283
+ """
1284
+ A list of all left siblings of this tree, in any of its parent
1285
+ trees. A tree may be its own left sibling if it is used as
1286
+ multiple contiguous children of the same parent. A tree may
1287
+ appear multiple times in this list if it is the left sibling
1288
+ of this tree with respect to multiple parents.
1289
+
1290
+ :type: list(MultiParentedTree)
1291
+ """
1292
+ return [parent[index-1]
1293
+ for (parent, index) in self._get_parent_indices()
1294
+ if index > 0]
1295
+
1296
+ def right_siblings(self):
1297
+ """
1298
+ A list of all right siblings of this tree, in any of its parent
1299
+ trees. A tree may be its own right sibling if it is used as
1300
+ multiple contiguous children of the same parent. A tree may
1301
+ appear multiple times in this list if it is the right sibling
1302
+ of this tree with respect to multiple parents.
1303
+
1304
+ :type: list(MultiParentedTree)
1305
+ """
1306
+ return [parent[index+1]
1307
+ for (parent, index) in self._get_parent_indices()
1308
+ if index < (len(parent)-1)]
1309
+
1310
+ def _get_parent_indices(self):
1311
+ return [(parent, index)
1312
+ for parent in self._parents
1313
+ for index, child in enumerate(parent)
1314
+ if child is self]
1315
+
1316
+ def roots(self):
1317
+ """
1318
+ The set of all roots of this tree. This set is formed by
1319
+ tracing all possible parent paths until trees with no parents
1320
+ are found.
1321
+
1322
+ :type: list(MultiParentedTree)
1323
+ """
1324
+ return list(self._get_roots_helper({}).values())
1325
+
1326
+ def _get_roots_helper(self, result):
1327
+ if self._parents:
1328
+ for parent in self._parents:
1329
+ parent._get_roots_helper(result)
1330
+ else:
1331
+ result[id(self)] = self
1332
+ return result
1333
+
1334
+ def parent_indices(self, parent):
1335
+ """
1336
+ Return a list of the indices where this tree occurs as a child
1337
+ of ``parent``. If this child does not occur as a child of
1338
+ ``parent``, then the empty list is returned. The following is
1339
+ always true::
1340
+
1341
+ for parent_index in ptree.parent_indices(parent):
1342
+ parent[parent_index] is ptree
1343
+ """
1344
+ if parent not in self._parents: return []
1345
+ else: return [index for (index, child) in enumerate(parent)
1346
+ if child is self]
1347
+
1348
+ def treepositions(self, root):
1349
+ """
1350
+ Return a list of all tree positions that can be used to reach
1351
+ this multi-parented tree starting from ``root``. I.e., the
1352
+ following is always true::
1353
+
1354
+ for treepos in ptree.treepositions(root):
1355
+ root[treepos] is ptree
1356
+ """
1357
+ if self is root:
1358
+ return [()]
1359
+ else:
1360
+ return [treepos+(index,)
1361
+ for parent in self._parents
1362
+ for treepos in parent.treepositions(root)
1363
+ for (index, child) in enumerate(parent) if child is self]
1364
+
1365
+
1366
+ #/////////////////////////////////////////////////////////////////
1367
+ # Parent Management
1368
+ #/////////////////////////////////////////////////////////////////
1369
+
1370
+ def _delparent(self, child, index):
1371
+ # Sanity checks
1372
+ assert isinstance(child, MultiParentedTree)
1373
+ assert self[index] is child
1374
+ assert len([p for p in child._parents if p is self]) == 1
1375
+
1376
+ # If the only copy of child in self is at index, then delete
1377
+ # self from child's parent list.
1378
+ for i, c in enumerate(self):
1379
+ if c is child and i != index: break
1380
+ else:
1381
+ child._parents.remove(self)
1382
+
1383
+ def _setparent(self, child, index, dry_run=False):
1384
+ # If the child's type is incorrect, then complain.
1385
+ if not isinstance(child, MultiParentedTree):
1386
+ raise TypeError('Can not insert a non-MultiParentedTree '+
1387
+ 'into a MultiParentedTree')
1388
+
1389
+ # Add self as a parent pointer if it's not already listed.
1390
+ if not dry_run:
1391
+ for parent in child._parents:
1392
+ if parent is self: break
1393
+ else:
1394
+ child._parents.append(self)
1395
+
1396
+ class ImmutableParentedTree(ImmutableTree, ParentedTree):
1397
+ pass
1398
+
1399
+ class ImmutableMultiParentedTree(ImmutableTree, MultiParentedTree):
1400
+ pass
1401
+
1402
+
1403
+ ######################################################################
1404
+ ## Probabilistic trees
1405
+ ######################################################################
1406
+
1407
+ @python_2_unicode_compatible
1408
+ class ProbabilisticTree(Tree, ProbabilisticMixIn):
1409
+ def __init__(self, node, children=None, **prob_kwargs):
1410
+ Tree.__init__(self, node, children)
1411
+ ProbabilisticMixIn.__init__(self, **prob_kwargs)
1412
+
1413
+ # We have to patch up these methods to make them work right:
1414
+ def _frozen_class(self): return ImmutableProbabilisticTree
1415
+ def __repr__(self):
1416
+ return '%s (p=%r)' % (Tree.unicode_repr(self), self.prob())
1417
+ def __str__(self):
1418
+ return '%s (p=%.6g)' % (self.pformat(margin=60), self.prob())
1419
+ def copy(self, deep=False):
1420
+ if not deep: return type(self)(self._label, self, prob=self.prob())
1421
+ else: return type(self).convert(self)
1422
+ @classmethod
1423
+ def convert(cls, val):
1424
+ if isinstance(val, Tree):
1425
+ children = [cls.convert(child) for child in val]
1426
+ if isinstance(val, ProbabilisticMixIn):
1427
+ return cls(val._label, children, prob=val.prob())
1428
+ else:
1429
+ return cls(val._label, children, prob=1.0)
1430
+ else:
1431
+ return val
1432
+
1433
+ def __eq__(self, other):
1434
+ return (self.__class__ is other.__class__ and
1435
+ (self._label, list(self), self.prob()) ==
1436
+ (other._label, list(other), other.prob()))
1437
+
1438
+ def __lt__(self, other):
1439
+ if not isinstance(other, Tree):
1440
+ raise_unorderable_types("<", self, other)
1441
+ if self.__class__ is other.__class__:
1442
+ return ((self._label, list(self), self.prob()) <
1443
+ (other._label, list(other), other.prob()))
1444
+ else:
1445
+ return self.__class__.__name__ < other.__class__.__name__
1446
+
1447
+
1448
+ @python_2_unicode_compatible
1449
+ class ImmutableProbabilisticTree(ImmutableTree, ProbabilisticMixIn):
1450
+ def __init__(self, node, children=None, **prob_kwargs):
1451
+ ImmutableTree.__init__(self, node, children)
1452
+ ProbabilisticMixIn.__init__(self, **prob_kwargs)
1453
+ self._hash = hash((self._label, tuple(self), self.prob()))
1454
+
1455
+ # We have to patch up these methods to make them work right:
1456
+ def _frozen_class(self): return ImmutableProbabilisticTree
1457
+ def __repr__(self):
1458
+ return '%s [%s]' % (Tree.unicode_repr(self), self.prob())
1459
+ def __str__(self):
1460
+ return '%s [%s]' % (self.pformat(margin=60), self.prob())
1461
+ def copy(self, deep=False):
1462
+ if not deep: return type(self)(self._label, self, prob=self.prob())
1463
+ else: return type(self).convert(self)
1464
+ @classmethod
1465
+ def convert(cls, val):
1466
+ if isinstance(val, Tree):
1467
+ children = [cls.convert(child) for child in val]
1468
+ if isinstance(val, ProbabilisticMixIn):
1469
+ return cls(val._label, children, prob=val.prob())
1470
+ else:
1471
+ return cls(val._label, children, prob=1.0)
1472
+ else:
1473
+ return val
1474
+
1475
+
1476
+ def _child_names(tree):
1477
+ names = []
1478
+ for child in tree:
1479
+ if isinstance(child, Tree):
1480
+ names.append(Nonterminal(child._label))
1481
+ else:
1482
+ names.append(child)
1483
+ return names
1484
+
1485
+ ######################################################################
1486
+ ## Parsing
1487
+ ######################################################################
1488
+
1489
+ def bracket_parse(s):
1490
+ """
1491
+ Use Tree.read(s, remove_empty_top_bracketing=True) instead.
1492
+ """
1493
+ raise NameError("Use Tree.read(s, remove_empty_top_bracketing=True) instead.")
1494
+
1495
+ def sinica_parse(s):
1496
+ """
1497
+ Parse a Sinica Treebank string and return a tree. Trees are represented as nested brackettings,
1498
+ as shown in the following example (X represents a Chinese character):
1499
+ S(goal:NP(Head:Nep:XX)|theme:NP(Head:Nhaa:X)|quantity:Dab:X|Head:VL2:X)#0(PERIODCATEGORY)
1500
+
1501
+ :return: A tree corresponding to the string representation.
1502
+ :rtype: Tree
1503
+ :param s: The string to be converted
1504
+ :type s: str
1505
+ """
1506
+ tokens = re.split(r'([()| ])', s)
1507
+ for i in range(len(tokens)):
1508
+ if tokens[i] == '(':
1509
+ tokens[i-1], tokens[i] = tokens[i], tokens[i-1] # pull nonterminal inside parens
1510
+ elif ':' in tokens[i]:
1511
+ fields = tokens[i].split(':')
1512
+ if len(fields) == 2: # non-terminal
1513
+ tokens[i] = fields[1]
1514
+ else:
1515
+ tokens[i] = "(" + fields[-2] + " " + fields[-1] + ")"
1516
+ elif tokens[i] == '|':
1517
+ tokens[i] = ''
1518
+
1519
+ treebank_string = " ".join(tokens)
1520
+ return Tree.fromstring(treebank_string, remove_empty_top_bracketing=True)
1521
+
1522
+ # s = re.sub(r'^#[^\s]*\s', '', s) # remove leading identifier
1523
+ # s = re.sub(r'\w+:', '', s) # remove role tags
1524
+
1525
+ # return s
1526
+
1527
+ ######################################################################
1528
+ ## Demonstration
1529
+ ######################################################################
1530
+
1531
+ def demo():
1532
+ """
1533
+ A demonstration showing how Trees and Trees can be
1534
+ used. This demonstration creates a Tree, and loads a
1535
+ Tree from the Treebank corpus,
1536
+ and shows the results of calling several of their methods.
1537
+ """
1538
+
1539
+ from nltk import Tree, ProbabilisticTree
1540
+
1541
+ # Demonstrate tree parsing.
1542
+ s = '(S (NP (DT the) (NN cat)) (VP (VBD ate) (NP (DT a) (NN cookie))))'
1543
+ t = Tree.fromstring(s)
1544
+ print("Convert bracketed string into tree:")
1545
+ print(t)
1546
+ print(t.__repr__())
1547
+
1548
+ print("Display tree properties:")
1549
+ print(t.label()) # tree's constituent type
1550
+ print(t[0]) # tree's first child
1551
+ print(t[1]) # tree's second child
1552
+ print(t.height())
1553
+ print(t.leaves())
1554
+ print(t[1])
1555
+ print(t[1,1])
1556
+ print(t[1,1,0])
1557
+
1558
+ # Demonstrate tree modification.
1559
+ the_cat = t[0]
1560
+ the_cat.insert(1, Tree.fromstring('(JJ big)'))
1561
+ print("Tree modification:")
1562
+ print(t)
1563
+ t[1,1,1] = Tree.fromstring('(NN cake)')
1564
+ print(t)
1565
+ print()
1566
+
1567
+ # Tree transforms
1568
+ print("Collapse unary:")
1569
+ t.collapse_unary()
1570
+ print(t)
1571
+ print("Chomsky normal form:")
1572
+ t.chomsky_normal_form()
1573
+ print(t)
1574
+ print()
1575
+
1576
+ # Demonstrate probabilistic trees.
1577
+ pt = ProbabilisticTree('x', ['y', 'z'], prob=0.5)
1578
+ print("Probabilistic Tree:")
1579
+ print(pt)
1580
+ print()
1581
+
1582
+ # Demonstrate parsing of treebank output format.
1583
+ t = Tree.fromstring(t.pformat())
1584
+ print("Convert tree to bracketed string and back again:")
1585
+ print(t)
1586
+ print()
1587
+
1588
+ # Demonstrate LaTeX output
1589
+ print("LaTeX output:")
1590
+ print(t.pformat_latex_qtree())
1591
+ print()
1592
+
1593
+ # Demonstrate Productions
1594
+ print("Production output:")
1595
+ print(t.productions())
1596
+ print()
1597
+
1598
+ # Demonstrate tree nodes containing objects other than strings
1599
+ t.set_label(('test', 3))
1600
+ print(t)
1601
+
1602
+ __all__ = ['ImmutableProbabilisticTree', 'ImmutableTree', 'ProbabilisticMixIn',
1603
+ 'ProbabilisticTree', 'Tree', 'bracket_parse',
1604
+ 'sinica_parse', 'ParentedTree', 'MultiParentedTree',
1605
+ 'ImmutableParentedTree', 'ImmutableMultiParentedTree']