Repository: explosion/spaCy

Issue:
displacy arc labels are place incorrectly in Jupyter notebook

| Software | Version |
|----------|---------|
| Python   | 3.6.4   |
| notebook | 5.4.1   |
| spacy    | 2.0.16  |

The SVG contents is the same when I copy the HTML from the inspector both in
jupyter and from the `displacy.serve` webpage.

When viewed through `displacy.serve`:

![displacy-serve](https://user-images.githubusercontent.com/966205/48501958-58c53f80-e836-11e8-9a11-af5de1d67a41.png)

And in Jupyter:
![displacy jupyter](https://user-images.githubusercontent.com/966205/48501957-58c53f80-e836-11e8-81f9-87f97eed32b8.png)

The SVG generated is the same, so I suspect some CSS issue is overriding the default, I don't know whether that should be solved within Jupyter, or whether it's spaCy's responsibility to make portable SVGs.  

Generated SVG here: https://gist.github.com/willprice/66d9ff961b693abe19e94d541eaacb99

This happens both in Firefox and Chrome.

I had a poke twiddling CSS rules off and on but couldn't seem to find any rules that affected the placement. Let me know if there's anything I can do providing additional info to help resolve the issue.

Thanks for the awesome library!

displacy arc labels are place incorrectly in Jupyter notebook

| Software | Version |
|----------|---------|
| Python   | 3.6.4   |
| notebook | 5.4.1   |
| spacy    | 2.0.16  |

The SVG contents is the same when I copy the HTML from the inspector both in
jupyter and from the `displacy.serve` webpage.

When viewed through `displacy.serve`:

![displacy-serve](https://user-images.githubusercontent.com/966205/48501958-58c53f80-e836-11e8-9a11-af5de1d67a41.png)

And in Jupyter:
![displacy jupyter](https://user-images.githubusercontent.com/966205/48501957-58c53f80-e836-11e8-81f9-87f97eed32b8.png)

The SVG generated is the same, so I suspect some CSS issue is overriding the default, I don't know whether that should be solved within Jupyter, or whether it's spaCy's responsibility to make portable SVGs.  

Generated SVG here: https://gist.github.com/willprice/66d9ff961b693abe19e94d541eaacb99

This happens both in Firefox and Chrome.

I had a poke twiddling CSS rules off and on but couldn't seem to find any rules that affected the placement. Let me know if there's anything I can do providing additional info to help resolve the issue.

Thanks for the awesome library!



Discussion:
Thanks for the detailed report!

This is very strange indeed... The labels are positioned with the paths using `xlink:href` and the path `id`. Did you render any other displaCy dependency visualizations in your notebook by any chance? Maybe what happened here is that it aligns the labels with the paths from a *previous* SVG, because the path `id`s are identical?

In the method that renders the SVG, we currently use the index of the document to create the unique ID for each SVG ([see here](https://github.com/explosion/spaCy/blob/87ce435affb972f6b1b9dde2cf953212a8e5d825/spacy/displacy/render.py#L41)). This works well with `displacy.serve`, but if you make several calls to `displacy.render` with one `Doc` in a notebook, it'd mean that all SVGs would receive the ID `0`.

If this is the case, we should probably generate a random visualization ID on each call to `displacy.render` – so instead of `"arrow-0-1"` the arcs would then have IDs like `arrow-34039420-1`, which should prevent clashes.
Ah! Yes, that could well be the issue. I've got a whole bunch of visualisations in the same notebook, most of which are minor variants (I'm trying to understand the different dependency relations that arise). 

Looking at the label placement I can now see that the text spans are reusing previous positions.

Randomising the ID sounds like a good idea, perhaps use a [UUID](https://docs.python.org/3/library/uuid.html).

Thanks for the super quick response and root causing, impressive!
Thanks! And yes, this all makes sense then!

UUID is a nice idea, maybe spaCy should just expose this by default (like, `doc.uuid` or something). Then again, in displaCy, this would mean that two identical parses would still receive the same ID, right? So if you render the same doc twice in a notebook (for whatever reason), the IDs would still clash – it won't be noticable, but I guess we might as well prevent it for consistency.

I'll experiment with this and will push a fix, so we can ship it with the upcoming `v2.0.17`.
Thanks for the detailed report!

This is very strange indeed... The labels are positioned with the paths using `xlink:href` and the path `id`. Did you render any other displaCy dependency visualizations in your notebook by any chance? Maybe what happened here is that it aligns the labels with the paths from a *previous* SVG, because the path `id`s are identical?

In the method that renders the SVG, we currently use the index of the document to create the unique ID for each SVG ([see here](https://github.com/explosion/spaCy/blob/87ce435affb972f6b1b9dde2cf953212a8e5d825/spacy/displacy/render.py#L41)). This works well with `displacy.serve`, but if you make several calls to `displacy.render` with one `Doc` in a notebook, it'd mean that all SVGs would receive the ID `0`.

If this is the case, we should probably generate a random visualization ID on each call to `displacy.render` – so instead of `"arrow-0-1"` the arcs would then have IDs like `arrow-34039420-1`, which should prevent clashes.
Ah! Yes, that could well be the issue. I've got a whole bunch of visualisations in the same notebook, most of which are minor variants (I'm trying to understand the different dependency relations that arise). 

Looking at the label placement I can now see that the text spans are reusing previous positions.

Randomising the ID sounds like a good idea, perhaps use a [UUID](https://docs.python.org/3/library/uuid.html).

Thanks for the super quick response and root causing, impressive!
Thanks! And yes, this all makes sense then!

UUID is a nice idea, maybe spaCy should just expose this by default (like, `doc.uuid` or something). Then again, in displaCy, this would mean that two identical parses would still receive the same ID, right? So if you render the same doc twice in a notebook (for whatever reason), the IDs would still clash – it won't be noticable, but I guess we might as well prevent it for consistency.

I'll experiment with this and will push a fix, so we can ship it with the upcoming `v2.0.17`.

Resolving patch:
diff --git a/spacy/displacy/render.py b/spacy/displacy/render.py
--- a/spacy/displacy/render.py
+++ b/spacy/displacy/render.py
@@ -1,6 +1,8 @@
 # coding: utf8
 from __future__ import unicode_literals
 
+import random
+
 from .templates import TPL_DEP_SVG, TPL_DEP_WORDS, TPL_DEP_ARCS
 from .templates import TPL_ENT, TPL_ENTS, TPL_FIGURE, TPL_TITLE, TPL_PAGE
 from ..util import minify_html, escape_html
@@ -38,7 +40,10 @@ def render(self, parsed, page=False, minify=False):
         minify (bool): Minify HTML markup.
         RETURNS (unicode): Rendered SVG or HTML markup.
         """
-        rendered = [self.render_svg(i, p['words'], p['arcs'])
+        # Create a random ID prefix to make sure parses don't receive the
+        # same ID, even if they're identical
+        id_prefix = random.randint(0, 999)
+        rendered = [self.render_svg('{}-{}'.format(id_prefix, i), p['words'], p['arcs'])
                     for i, p in enumerate(parsed)]
         if page:
             content = ''.join([TPL_FIGURE.format(content=svg)


Test patch: