Skip to content

Commit fcfcee3

Browse files
committed
Log/linear 'scale' for axes work now. If log, base must == 10.
If base != 10 for the LogTransform object from matplotlib, we convert the 'scale' back to linear, autorange the axis, issue a warning, and plot the data on a linear scale.
1 parent 800265b commit fcfcee3

2 files changed

Lines changed: 99 additions & 10 deletions

File tree

plotly/matplotlylib/mpltools.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
66
"""
77

8+
import math
9+
import warnings
810

911
def check_bar_match(old_bar, new_bar):
1012
"""Check if two bars belong in the same collection (bar chart).
@@ -259,6 +261,92 @@ def make_bar(**props):
259261
}
260262

261263

264+
def prep_x_ticks(ax, props):
265+
axis = dict()
266+
scale = props['axes'][0]['scale']
267+
if scale == 'linear':
268+
try:
269+
axis['tick0'] = props['axes'][0]['tickvalues'][0]
270+
axis['dtick'] = props['axes'][0]['tickvalues'][1] - \
271+
props['axes'][0]['tickvalues'][0]
272+
axis['autotick'] = False
273+
except (IndexError, TypeError):
274+
axis = dict(nticks=props['axes'][0]['nticks'])
275+
return axis
276+
elif scale == 'log':
277+
try:
278+
axis['tick0'] = props['axes'][0]['tickvalues'][0]
279+
axis['dtick'] = props['axes'][0]['tickvalues'][1] - \
280+
props['axes'][0]['tickvalues'][0]
281+
axis['autotick'] = False
282+
except (IndexError, TypeError):
283+
axis = dict(nticks=props['axes'][0]['nticks'])
284+
base = ax.get_xaxis().get_transform().base
285+
if base == 10:
286+
axis['range'] = [math.log10(props['xlim'][0]),
287+
math.log10(props['xlim'][1])]
288+
else:
289+
axis = dict(range=None, type='linear')
290+
warnings.warn("Converted non-base10 x-axis log scale to 'linear'")
291+
return axis
292+
else:
293+
return dict()
294+
295+
296+
def prep_y_ticks(ax, props):
297+
axis = dict()
298+
scale = props['axes'][1]['scale']
299+
if scale == 'linear':
300+
try:
301+
axis['tick0'] = props['axes'][1]['tickvalues'][0]
302+
axis['dtick'] = props['axes'][1]['tickvalues'][1] - \
303+
props['axes'][1]['tickvalues'][0]
304+
axis['autotick'] = False
305+
except (IndexError, TypeError):
306+
axis = dict(nticks=props['axes'][1]['nticks'])
307+
return axis
308+
elif scale == 'log':
309+
try:
310+
axis['tick0'] = props['axes'][1]['tickvalues'][0]
311+
axis['dtick'] = props['axes'][1]['tickvalues'][1] - \
312+
props['axes'][1]['tickvalues'][0]
313+
axis['autotick'] = False
314+
except (IndexError, TypeError):
315+
axis = dict(nticks=props['axes'][1]['nticks'])
316+
base = ax.get_yaxis().get_transform().base
317+
if base == 10:
318+
axis['range'] = [math.log10(props['ylim'][0]),
319+
math.log10(props['ylim'][1])]
320+
else:
321+
axis = dict(range=None, type='linear')
322+
warnings.warn("Converted non-base10 y-axis log scale to 'linear'")
323+
return axis
324+
else:
325+
return dict()
326+
327+
328+
def prep_xy_axis(ax, props, x_bounds, y_bounds):
329+
xaxis = dict(
330+
type=props['axes'][0]['scale'],
331+
range=props['xlim'],
332+
showgrid=props['axes'][0]['grid']['gridOn'],
333+
domain=convert_x_domain(props['bounds'], x_bounds),
334+
side=props['axes'][0]['position'],
335+
tickfont=dict(size=props['axes'][0]['fontsize'])
336+
)
337+
xaxis.update(prep_x_ticks(ax, props))
338+
yaxis = dict(
339+
type=props['axes'][1]['scale'],
340+
range=props['ylim'],
341+
showgrid=props['axes'][1]['grid']['gridOn'],
342+
domain=convert_y_domain(props['bounds'], y_bounds),
343+
side=props['axes'][1]['position'],
344+
tickfont=dict(size=props['axes'][1]['fontsize'])
345+
)
346+
yaxis.update(prep_y_ticks(ax, props))
347+
return xaxis, yaxis
348+
349+
262350
DASH_MAP = {
263351
'10,0': 'solid',
264352
'6,6': 'dash',

plotly/matplotlylib/renderer.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,27 @@ def open_axes(self, ax, props):
129129
"""
130130
self.msg += " Opening axes\n"
131131
self.axis_ct += 1
132+
# set defaults in axes
132133
xaxis = XAxis(
133-
range=props['xlim'],
134-
showgrid=props['axes'][1]['grid']['gridOn'],
135-
domain=mpltools.convert_x_domain(props['bounds'],
136-
self.mpl_x_bounds),
137134
anchor='y{}'.format(self.axis_ct),
138135
zeroline=False,
139136
showline=True,
140-
mirror=True,
137+
mirror='ticks',
141138
ticks='inside')
142139
yaxis = YAxis(
143-
range=props['ylim'],
144-
showgrid=props['axes'][0]['grid']['gridOn'],
145-
domain=mpltools.convert_y_domain(props['bounds'],
146-
self.mpl_y_bounds),
147140
anchor='x{}'.format(self.axis_ct),
148141
zeroline=False,
149142
showline=True,
150-
mirror=True,
143+
mirror='ticks',
151144
ticks='inside')
145+
# update defaults with things set in mpl
146+
mpl_xaxis, mpl_yaxis = mpltools.prep_xy_axis(ax=ax,
147+
props=props,
148+
x_bounds=self.mpl_x_bounds,
149+
y_bounds=self.mpl_y_bounds)
150+
xaxis.update(mpl_xaxis)
151+
yaxis.update(mpl_yaxis)
152+
# put axes in our figure
152153
self.plotly_fig['layout']['xaxis{}'.format(self.axis_ct)] = xaxis
153154
self.plotly_fig['layout']['yaxis{}'.format(self.axis_ct)] = yaxis
154155

0 commit comments

Comments
 (0)