Repository navigation
mpl_to_plotly does not preserve axis labels (bar plots are useless) #5059
Copy link
Copy link
Open
Labels
Description
While mpl_to_plotly is little known and receives little love, this bug is pretty easy to fix. Would you be open to a PR?
Details
import matplotlib.pyplot as plt
from plotly.tools import mpl_to_plotly
from plotly.offline import plotIn matplotlib this produces a barplot with labels:
labels = ['a', 'b', 'c']
values = [1, 2, 3]
f = plt.figure(figsize=(6, 4))
plt.bar(labels, values)
plt.tight_layout()But conversion to plotly looses the labels:
plotly_fig = mpl_to_plotly(f)
plot(plotly_fig)
A minimal fix would be to modify prep_ticks by appending:
if axis_dict.get("type") == "date":
return axis_dict
vals = []
texts = []
for tick in axis.majorTicks:
vals.append(tick.get_loc())
texts.append(tick.label1.get_text())
if texts:
axis_dict = {}
axis_dict['tickmode'] = 'array'
axis_dict['tickvals'] = vals
axis_dict['ticktext'] = texts
return axis_dictwhich produces:
prep_ticks is defined in:
plotly.py/plotly/matplotlylib/mpltools.py
Lines 428 to 514 in c54a2bd
| def prep_ticks(ax, index, ax_type, props): | |
| """Prepare axis obj belonging to axes obj. | |
| positional arguments: | |
| ax - the mpl axes instance | |
| index - the index of the axis in `props` | |
| ax_type - 'x' or 'y' (for now) | |
| props - an mplexporter poperties dictionary | |
| """ | |
| axis_dict = dict() | |
| if ax_type == "x": | |
| axis = ax.get_xaxis() | |
| elif ax_type == "y": | |
| axis = ax.get_yaxis() | |
| else: | |
| return dict() # whoops! | |
| scale = props["axes"][index]["scale"] | |
| if scale == "linear": | |
| # get tick location information | |
| try: | |
| tickvalues = props["axes"][index]["tickvalues"] | |
| tick0 = tickvalues[0] | |
| dticks = [ | |
| round(tickvalues[i] - tickvalues[i - 1], 12) | |
| for i in range(1, len(tickvalues) - 1) | |
| ] | |
| if all([dticks[i] == dticks[i - 1] for i in range(1, len(dticks) - 1)]): | |
| dtick = tickvalues[1] - tickvalues[0] | |
| else: | |
| warnings.warn( | |
| "'linear' {0}-axis tick spacing not even, " | |
| "ignoring mpl tick formatting.".format(ax_type) | |
| ) | |
| raise TypeError | |
| except (IndexError, TypeError): | |
| axis_dict["nticks"] = props["axes"][index]["nticks"] | |
| else: | |
| axis_dict["tick0"] = tick0 | |
| axis_dict["dtick"] = dtick | |
| axis_dict["tickmode"] = None | |
| elif scale == "log": | |
| try: | |
| axis_dict["tick0"] = props["axes"][index]["tickvalues"][0] | |
| axis_dict["dtick"] = ( | |
| props["axes"][index]["tickvalues"][1] | |
| - props["axes"][index]["tickvalues"][0] | |
| ) | |
| axis_dict["tickmode"] = None | |
| except (IndexError, TypeError): | |
| axis_dict = dict(nticks=props["axes"][index]["nticks"]) | |
| base = axis.get_transform().base | |
| if base == 10: | |
| if ax_type == "x": | |
| axis_dict["range"] = [ | |
| math.log10(props["xlim"][0]), | |
| math.log10(props["xlim"][1]), | |
| ] | |
| elif ax_type == "y": | |
| axis_dict["range"] = [ | |
| math.log10(props["ylim"][0]), | |
| math.log10(props["ylim"][1]), | |
| ] | |
| else: | |
| axis_dict = dict(range=None, type="linear") | |
| warnings.warn( | |
| "Converted non-base10 {0}-axis log scale to 'linear'" "".format(ax_type) | |
| ) | |
| else: | |
| return dict() | |
| # get tick label formatting information | |
| formatter = axis.get_major_formatter().__class__.__name__ | |
| if ax_type == "x" and "DateFormatter" in formatter: | |
| axis_dict["type"] = "date" | |
| try: | |
| axis_dict["tick0"] = mpl_dates_to_datestrings(axis_dict["tick0"], formatter) | |
| except KeyError: | |
| pass | |
| finally: | |
| axis_dict.pop("dtick", None) | |
| axis_dict.pop("tickmode", None) | |
| axis_dict["range"] = mpl_dates_to_datestrings(props["xlim"], formatter) | |
| if formatter == "LogFormatterMathtext": | |
| axis_dict["exponentformat"] = "e" | |
| return axis_dict |
Reactions are currently unavailable



Activity
gvwilson commentedon Mar 3, 2025
thanks very much @krassowski - yes, we would be very happy to get a PR. thanks - @gvwilson
willtryagain commentedon Sep 16, 2025
Hi @krassowski,
Thanks for looking into this issue. I noticed you have a fix ready for this and wanted to check in on its status. Are you still actively working on it or do you have plans to submit it for review soon?
I'm asking because this bug is an upstream dependency blocker for me, where
wandbrelies on Plotly internally.If you're no longer able to work on it, would you be open to me submitting a separate PR for a fix?
Let me know what works best. Thanks!
krassowski commentedon Sep 16, 2025
@willtryagain absolutely please submit sorry I forgot to open the PR earlier!