|
45 | 45 | "trees", |
46 | 46 | "search", |
47 | 47 | "search_trees", |
| 48 | + "to_displacy", |
| 49 | + "render", |
48 | 50 | ] |
49 | 51 |
|
50 | 52 |
|
@@ -157,3 +159,91 @@ def search_trees( |
157 | 159 | else: |
158 | 160 | source = list(source) |
159 | 161 | return py_search_trees(source, query) |
| 162 | + |
| 163 | + |
| 164 | +def to_displacy(tree: Tree) -> dict: |
| 165 | + """Convert a Tree to displaCy's manual rendering format. |
| 166 | +
|
| 167 | + Args: |
| 168 | + tree: A Tree object to convert |
| 169 | +
|
| 170 | + Returns: |
| 171 | + Dictionary in displaCy format with 'words' and 'arcs' keys |
| 172 | +
|
| 173 | + Example: |
| 174 | + >>> tree = next(treesearch.trees("corpus.conllu")) |
| 175 | + >>> data = treesearch.to_displacy(tree) |
| 176 | + >>> from spacy import displacy |
| 177 | + >>> displacy.render(data, style="dep", manual=True) |
| 178 | + """ |
| 179 | + words = [] |
| 180 | + arcs = [] |
| 181 | + |
| 182 | + for i in range(len(tree)): |
| 183 | + word = tree.word(i) |
| 184 | + words.append({"text": word.form, "tag": word.upos}) |
| 185 | + |
| 186 | + if word.head is not None: |
| 187 | + head_idx = word.head |
| 188 | + dep_idx = word.id |
| 189 | + if head_idx < dep_idx: |
| 190 | + arcs.append( |
| 191 | + { |
| 192 | + "start": head_idx, |
| 193 | + "end": dep_idx, |
| 194 | + "label": word.deprel, |
| 195 | + "dir": "right", |
| 196 | + } |
| 197 | + ) |
| 198 | + else: |
| 199 | + arcs.append( |
| 200 | + { |
| 201 | + "start": dep_idx, |
| 202 | + "end": head_idx, |
| 203 | + "label": word.deprel, |
| 204 | + "dir": "left", |
| 205 | + } |
| 206 | + ) |
| 207 | + return {"words": words, "arcs": arcs} |
| 208 | + |
| 209 | + |
| 210 | +def render(tree: Tree, **options) -> str: |
| 211 | + """Render a Tree as an SVG dependency visualization using displaCy. |
| 212 | +
|
| 213 | + Requires spaCy to be installed. |
| 214 | +
|
| 215 | + Args: |
| 216 | + tree: A Tree object to render |
| 217 | + **options: Additional options passed to displacy.render() |
| 218 | + Common options include: |
| 219 | + - jupyter: bool - Return HTML for Jupyter display (default: auto-detect) |
| 220 | + - compact: bool - Use compact visualization mode |
| 221 | + - word_spacing: int - Spacing between words |
| 222 | + - distance: int - Distance between dependency arcs |
| 223 | +
|
| 224 | + Returns: |
| 225 | + SVG markup string (or displays in Jupyter if jupyter=True) |
| 226 | +
|
| 227 | + Raises: |
| 228 | + ImportError: If spaCy is not installed |
| 229 | +
|
| 230 | + Example: |
| 231 | + >>> tree = next(treesearch.trees("corpus.conllu")) |
| 232 | + >>> svg = treesearch.render(tree) |
| 233 | + >>> with open("tree.svg", "w") as f: |
| 234 | + ... f.write(svg) |
| 235 | +
|
| 236 | + # In Jupyter notebook: |
| 237 | + >>> treesearch.render(tree, jupyter=True) |
| 238 | + """ |
| 239 | + try: |
| 240 | + from spacy import displacy |
| 241 | + except ImportError: |
| 242 | + raise ImportError("spaCy is required for rendering. Install it with: pip install spacy") |
| 243 | + |
| 244 | + data = to_displacy(tree) |
| 245 | + return displacy.render(data, style="dep", manual=True, **options) |
| 246 | + |
| 247 | + |
| 248 | +Tree.to_displacy = to_displacy |
| 249 | +Tree.render = render |
0 commit comments