Skip to content

Commit 1401984

Browse files
committed
add support for dates in x axis
1 parent 6a5ec42 commit 1401984

File tree

6 files changed

+190
-112
lines changed

6 files changed

+190
-112
lines changed

matplotlib2tikz/axes.py

Lines changed: 71 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,13 @@ def _ticks(self, data, obj):
230230
self.axis_options.append("tick align=center")
231231

232232
# Set each rotation for every label
233-
x_tick_rotation_and_horizontal_alignment = _get_label_rotation_and_horizontal_alignment(
233+
x_tick_rotation_and_horizontal_alignment = self._get_label_rotation_and_horizontal_alignment(
234234
obj, data, "x"
235235
)
236236
if x_tick_rotation_and_horizontal_alignment:
237237
self.axis_options.append(x_tick_rotation_and_horizontal_alignment)
238238

239-
y_tick_rotation_and_horizontal_alignment = _get_label_rotation_and_horizontal_alignment(
239+
y_tick_rotation_and_horizontal_alignment = self._get_label_rotation_and_horizontal_alignment(
240240
obj, data, "y"
241241
)
242242
if y_tick_rotation_and_horizontal_alignment:
@@ -397,86 +397,92 @@ def _subplot(self, obj, data):
397397

398398
return
399399

400+
def _get_label_rotation_and_horizontal_alignment(self, obj, data, x_or_y):
401+
tick_label_text_width = None
402+
tick_label_text_width_identifier = "{} tick label text width".format(x_or_y)
403+
if tick_label_text_width_identifier in self.axis_options:
404+
self.axis_options.remove(tick_label_text_width_identifier)
400405

401-
def _get_label_rotation_and_horizontal_alignment(obj, data, x_or_y):
402-
tick_label_text_width = None
403-
tick_label_text_width_identifier = "{} tick label text width".format(x_or_y)
404-
if tick_label_text_width_identifier in data["extra axis options"]:
405-
data["extra axis options"].remove(tick_label_text_width_identifier)
406+
label_style = ""
406407

407-
label_style = ""
408-
409-
major_tick_labels = (
410-
obj.xaxis.get_majorticklabels()
411-
if x_or_y == "x"
412-
else obj.yaxis.get_majorticklabels()
413-
)
414-
415-
if not major_tick_labels:
416-
return None
408+
major_tick_labels = (
409+
obj.xaxis.get_majorticklabels()
410+
if x_or_y == "x"
411+
else obj.yaxis.get_majorticklabels()
412+
)
417413

418-
tick_labels_rotation = [label.get_rotation() for label in major_tick_labels]
419-
tick_labels_rotation_same_value = len(set(tick_labels_rotation)) == 1
414+
if not major_tick_labels:
415+
return None
420416

421-
tick_labels_horizontal_alignment = [
422-
label.get_horizontalalignment() for label in major_tick_labels
423-
]
424-
tick_labels_horizontal_alignment_same_value = (
425-
len(set(tick_labels_horizontal_alignment)) == 1
426-
)
417+
tick_labels_rotation = [label.get_rotation() for label in major_tick_labels]
418+
tick_labels_rotation_same_value = len(set(tick_labels_rotation)) == 1
427419

428-
if tick_labels_rotation_same_value and tick_labels_horizontal_alignment_same_value:
429-
values = []
420+
tick_labels_horizontal_alignment = [
421+
label.get_horizontalalignment() for label in major_tick_labels
422+
]
423+
tick_labels_horizontal_alignment_same_value = (
424+
len(set(tick_labels_horizontal_alignment)) == 1
425+
)
430426

431-
if any(tick_labels_rotation) != 0:
432-
values.append("rotate={}".format(tick_labels_rotation[0]))
427+
if (
428+
tick_labels_rotation_same_value
429+
and tick_labels_horizontal_alignment_same_value
430+
):
431+
values = []
433432

434-
# Horizontal alignment will be ignored if no 'x/y tick label text width' has
435-
# been passed in the 'extra' parameter
436-
if tick_label_text_width:
437-
values.append("align={}".format(tick_labels_horizontal_alignment[0]))
438-
values.append("text width={}".format(tick_label_text_width))
433+
if any(tick_labels_rotation) != 0:
434+
values.append("rotate={}".format(tick_labels_rotation[0]))
439435

440-
if values:
441-
label_style = "{}ticklabel style = {{{}}}".format(x_or_y, ",".join(values))
442-
else:
443-
values = []
436+
# Horizontal alignment will be ignored if no 'x/y tick label text width' has
437+
# been passed in the 'extra' parameter
438+
if tick_label_text_width:
439+
values.append("align={}".format(tick_labels_horizontal_alignment[0]))
440+
values.append("text width={}".format(tick_label_text_width))
444441

445-
if tick_labels_rotation_same_value:
446-
values.append("rotate={}".format(tick_labels_rotation[0]))
447-
else:
448-
values.append(
449-
"rotate={{{},0}}[\\ticknum]".format(
450-
",".join(str(x) for x in tick_labels_rotation)
442+
if values:
443+
label_style = "{}ticklabel style = {{{}}}".format(
444+
x_or_y, ",".join(values)
451445
)
452-
)
446+
else:
447+
values = []
453448

454-
# Ignore horizontal alignment if no '{x,y} tick label text width' has
455-
# been passed in the 'extra' parameter
456-
if tick_label_text_width:
457-
if tick_labels_horizontal_alignment_same_value:
458-
values.append("align={}".format(tick_labels_horizontal_alignment[0]))
459-
values.append("text width={}".format(tick_label_text_width))
449+
if tick_labels_rotation_same_value:
450+
values.append("rotate={}".format(tick_labels_rotation[0]))
460451
else:
461-
for idx, x in enumerate(tick_labels_horizontal_alignment):
462-
label_style += "{}_tick_label_ha_{}/.initial = {}".format(
463-
x_or_y, idx, x
464-
)
465-
466452
values.append(
467-
"align=\\pgfkeysvalueof{{/pgfplots/{}_tick_label_ha_\\ticknum}}".format(
468-
x_or_y
453+
"rotate={{{},0}}[\\ticknum]".format(
454+
",".join(str(x) for x in tick_labels_rotation)
469455
)
470456
)
471-
values.append("text width={}".format(tick_label_text_width))
472457

473-
label_style = (
474-
"every {} tick label/.style = {{\n"
475-
"{}\n"
476-
"}}".format(x_or_y, ",\n".join(values))
477-
)
458+
# Ignore horizontal alignment if no '{x,y} tick label text width' has
459+
# been passed in the 'extra' parameter
460+
if tick_label_text_width:
461+
if tick_labels_horizontal_alignment_same_value:
462+
values.append(
463+
"align={}".format(tick_labels_horizontal_alignment[0])
464+
)
465+
values.append("text width={}".format(tick_label_text_width))
466+
else:
467+
for idx, x in enumerate(tick_labels_horizontal_alignment):
468+
label_style += "{}_tick_label_ha_{}/.initial = {}".format(
469+
x_or_y, idx, x
470+
)
471+
472+
values.append(
473+
"align=\\pgfkeysvalueof{{/pgfplots/{}_tick_label_ha_\\ticknum}}".format(
474+
x_or_y
475+
)
476+
)
477+
values.append("text width={}".format(tick_label_text_width))
478+
479+
label_style = (
480+
"every {} tick label/.style = {{\n"
481+
"{}\n"
482+
"}}".format(x_or_y, ",\n".join(values))
483+
)
478484

479-
return label_style
485+
return label_style
480486

481487

482488
def _get_tick_position(obj, axes_obj):

matplotlib2tikz/legend.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,17 @@ def draw_legend(data, obj):
7676
break
7777

7878
if alignment:
79-
data["extra axis options"].add("legend cell align={{{}}}".format(alignment))
79+
data["current axes"].axis_options.append(
80+
"legend cell align={{{}}}".format(alignment)
81+
)
8082

8183
if obj._ncol != 1:
82-
data["extra axis options"].add("legend columns={}".format(obj._ncol))
84+
data["current axes"].axis_options.append("legend columns={}".format(obj._ncol))
8385

8486
# Write styles to data
8587
if legend_style:
8688
style = "legend style={{{}}}".format(", ".join(legend_style))
87-
data["extra axis options"].add(style)
89+
data["current axes"].axis_options.append(style)
8890

8991
return data
9092

matplotlib2tikz/line2d.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
from __future__ import print_function
44

5+
import datetime
56
import six
67

78
from . import color as mycol
@@ -72,7 +73,8 @@ def draw_line2d(data, obj):
7273
if addplot_options:
7374
content.append("[{}]\n".format(", ".join(addplot_options)))
7475

75-
_table(obj, content, data)
76+
c, axis_options = _table(obj, data)
77+
content += c
7678

7779
if legend_text is not None:
7880
content.append("\\addlegendentry{{{}}}\n".format(legend_text))
@@ -268,13 +270,10 @@ def _marker(
268270
return
269271

270272

271-
def _table(obj, content, data):
272-
content.append("table {%\n")
273-
273+
def _table(obj, data):
274274
# TODO nschloe, Oct 2, 2015:
275-
# The transform call yields warnings and it is unclear why. Perhaps
276-
# the input data is not suitable? Anyhow, this should not happen.
277-
# Comment out for now.
275+
# The transform call yields warnings and it is unclear why. Perhaps the input data
276+
# is not suitable? Anyhow, this should not happen. Comment out for now.
278277
# xdata, ydata = _transform_to_data_coordinates(obj, *obj.get_data())
279278
xdata, ydata = obj.get_data()
280279

@@ -292,22 +291,46 @@ def _table(obj, content, data):
292291
try:
293292
has_mask = ydata.mask.any()
294293
except AttributeError:
295-
has_mask = 0
294+
has_mask = False
295+
296+
axis_options = []
297+
298+
content = []
296299

297300
ff = data["float format"]
301+
if isinstance(xdata[0], datetime.datetime):
302+
xdata = [date.strftime("%Y-%m-%d %H:%M") for date in xdata]
303+
xformat = "{}"
304+
col_sep = ","
305+
content.append("table [col sep=comma] {%\n")
306+
data["current axes"].axis_options.append("date coordinates in=x")
307+
# Remove xmin/xmax until <https://github.com/matplotlib/matplotlib/issues/13727>
308+
# is resolved.
309+
data["current axes"].axis_options = [
310+
option
311+
for option in data["current axes"].axis_options
312+
if not option.startswith("xmin")
313+
]
314+
else:
315+
xformat = ff
316+
col_sep = " "
317+
content.append("table {%\n")
318+
298319
plot_table = []
299320
if has_mask:
300321
# matplotlib jumps at masked images, while PGFPlots by default interpolates.
301322
# Hence, if we have a masked plot, make sure that PGFPlots jumps as well.
302-
data["extra axis options"].add("unbounded coords=jump")
323+
if "unbounded coords=jump" not in data["current axes"].axis_options:
324+
data["current axes"].axis_options.append("unbounded coords=jump")
325+
303326
for (x, y, is_masked) in zip(xdata, ydata, ydata.mask):
304327
if is_masked:
305-
plot_table.append((ff + " nan\n").format(x))
328+
plot_table.append((xformat + col_sep + "nan\n").format(x))
306329
else:
307-
plot_table.append((ff + " " + ff + "\n").format(x, y))
330+
plot_table.append((xformat + col_sep + ff + "\n").format(x, y))
308331
else:
309332
for (x, y) in zip(xdata, ydata):
310-
plot_table.append((ff + " " + ff + "\n").format(x, y))
333+
plot_table.append((xformat + col_sep + ff + "\n").format(x, y))
311334

312335
if data["externalize tables"]:
313336
filename, rel_filepath = files.new_filename(data, "table", ".tsv")
@@ -319,4 +342,4 @@ def _table(obj, content, data):
319342
content.extend(plot_table)
320343

321344
content.append("};\n")
322-
return
345+
return content, axis_options

matplotlib2tikz/save.py

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ def get_tikz_code(
223223
\\usepackage[utf8]{{inputenc}}
224224
\\usepackage{{pgfplots}}
225225
\\usepgfplotslibrary{{groupplots}}
226+
\\usepgfplotslibrary{{dateplot}}
226227
\\pgfplotsset{{compat=newest}}
227228
\\DeclareUnicodeCharacter{{2212}}{{−}}
228229
\\begin{{document}}
@@ -327,38 +328,33 @@ def _recurse(data, obj):
327328
continue
328329

329330
if isinstance(child, mpl.axes.Axes):
330-
# Reset 'extra axis parameters' for every new Axes environment.
331-
data["extra axis options"] = data["extra axis options [base]"].copy()
332-
333331
ax = axes.Axes(data, child)
334-
if not ax.is_colorbar:
335-
# Run through the child objects, gather the content.
336-
data, children_content = _recurse(data, child)
337-
# add extra axis options from children
338-
if data["extra axis options"]:
339-
ax.axis_options.extend(data["extra axis options"])
340-
# populate content and add axis environment if desired
341-
if data["add axis environment"]:
342-
content.extend(
343-
ax.get_begin_code()
344-
+ children_content
345-
+ [ax.get_end_code(data)],
346-
0,
347-
)
348-
else:
349-
content.extend(children_content, 0)
350-
# print axis environment options, if told to show infos
351-
if data["show_info"]:
352-
print(
353-
"========================================================="
354-
)
355-
print(
356-
"These would have been the properties of the environment:"
357-
)
358-
print("".join(ax.get_begin_code()[1:]))
359-
print(
360-
"========================================================="
361-
)
332+
333+
if ax.is_colorbar:
334+
continue
335+
336+
# add extra axis options
337+
if data["extra axis options [base]"]:
338+
ax.axis_options.extend(data["extra axis options [base]"])
339+
340+
data["current axes"] = ax
341+
342+
# Run through the child objects, gather the content.
343+
data, children_content = _recurse(data, child)
344+
345+
# populate content and add axis environment if desired
346+
if data["add axis environment"]:
347+
content.extend(
348+
ax.get_begin_code() + children_content + [ax.get_end_code(data)], 0
349+
)
350+
else:
351+
content.extend(children_content, 0)
352+
# print axis environment options, if told to show infos
353+
if data["show_info"]:
354+
print("=========================================================")
355+
print("These would have been the properties of the environment:")
356+
print("".join(ax.get_begin_code()[1:]))
357+
print("=========================================================")
362358
elif isinstance(child, mpl.lines.Line2D):
363359
data, cont = line2d.draw_line2d(data, child)
364360
content.extend(cont, child.get_zorder())

test/test_datetime.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
import datetime as date
4+
from matplotlib import dates
5+
import matplotlib.pyplot as plt
6+
7+
from helpers import assert_equality
8+
9+
10+
def plot():
11+
fig = plt.figure()
12+
13+
values = [50, 50.02]
14+
time = [date.datetime(2016, 10, 10, 18, 00), date.datetime(2016, 10, 10, 18, 15)]
15+
plt.plot(time, values)
16+
hfmt = dates.DateFormatter("%H:%M")
17+
ax = plt.gca()
18+
ax.xaxis.set_major_formatter(hfmt)
19+
return fig
20+
21+
22+
def test():
23+
assert_equality(plot, __file__[:-3] + "_reference.tex")
24+
return
25+
26+
27+
if __name__ == "__main__":
28+
import helpers
29+
30+
helpers.compare_mpl_latex(plot)
31+
# helpers.print_tree(plot())

0 commit comments

Comments
 (0)