Skip to content

Render matplotlib path collections (violin plots, pcolor, stems, etc.) in plotly - #5702

Open
robertoffmoura wants to merge 1 commit into
plotly:mainfrom
robertoffmoura:rm/fix-violin-plot
Open

Render matplotlib path collections (violin plots, pcolor, stems, etc.) in plotly#5702
robertoffmoura wants to merge 1 commit into
plotly:mainfrom
robertoffmoura:rm/fix-violin-plot

Conversation

@robertoffmoura

Copy link
Copy Markdown
Contributor

mpl_to_plotly silently drops several common plot types. Violin plots, pcolor, event plots, stack plots, fill_between and stem plots all produce PolyCollection/LineCollection artists in data coordinates, which the renderer skipped with "Dang! That path collection is out of this world...". The converted figure ends up with zero traces (except for stem, which gets partially drawn).

Fix: data-coordinate path collections are now drawn:

  • collections with face colors (violin bodies, pcolor cells, stacked areas, fills) → filled polygons (fill="toself")
  • collections without face colors (contour lines, stem lines) → plain line traces
  • per-path colors/widths handled for collections that mix them; empty color arrays and degenerate paths skipped

Before: plots converts to 0 traces (empty figures).
After: plots render correctly.

Snippet to reproduce:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import plotly.tools as tls

x = np.linspace(-3, 3, 30)
X, Y = np.meshgrid(x, x)
t = np.linspace(0, 2 * np.pi, 50)

cases = {
    "violinplot":   lambda: plt.violinplot(np.random.randn(100, 3)),
    "pcolor":       lambda: plt.pcolor(X, Y, np.sin(X) * np.cos(Y)),
    "eventplot":    lambda: plt.eventplot([np.random.randn(20) for _ in range(5)]),
    "stackplot":    lambda: plt.stackplot(np.arange(10), np.random.rand(10), np.random.rand(10), np.random.rand(10)),
    "fill_between": lambda: plt.fill_between(t, np.sin(t), np.cos(t)),
    "stem":         lambda: plt.stem(np.linspace(0, 2 * np.pi, 20), np.sin(np.linspace(0, 2 * np.pi, 20))),
}

for name, build in cases.items():
    fig, ax = plt.subplots()
    build()
    fig.savefig(f"{name}_mpl.png")

    p = tls.mpl_to_plotly(fig)   # 0 traces for all of these before the fix (except stem, which had some traces)
    p.write_image(f"{name}_plotly.png")

    print(f"{name:13s} -> {len(p.data)} traces")
Plot type Converted plotly figure
violinplot (12 traces) violinplot_plotly
pcolor (900 traces) pcolor_plotly
eventplot (100 traces) eventplot_plotly
stackplot (3 traces) stackplot_plotly
fill_between (1 trace) fill_between_plotly
stem (22 traces) stem_plotly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant