Computing histogram with datetime axis throws TypeError: can't compare offset-naive and offset-aware datetimes
chrisjcameron opened this issue · 5 comments
This is a minimal example that produces the error. Leaving out any single row in the data table will result in the expected plot without throwing the error. Similarly, specifying + gg.scale_x_datetime(breaks='12 hours', date_labels='%m-%d %H:%M')
will produce a plot without errors.
import pandas as pd
import plotnine as gg
Registration_Time = '''
2022-10-19 04:42:04
2022-10-19 10:16:29
2022-10-19 13:27:09
2022-10-19 14:40:24
2022-10-19 15:20:31
2022-10-19 15:47:18
2022-10-19 17:00:23
2022-10-19 22:45:01
'''.strip().splitlines()
org = '''
other
wcm
wcm
cu
wcm
wcm
cu
cu
'''.strip().splitlines()
df = pd.DataFrame({'Registration_Time':Registration_Time, 'org':org})
df['Registration_Time'] = pd.to_datetime(df['Registration_Time'])
df['org'] = pd.Categorical(df.org, categories=['wcm', 'cu', 'other'])
plot_df = (
df
.sort_values("Registration_Time")
)#.iloc[:-1]
# plotnine to plot (using grammar of graphics like ggplot2)
plot = (
gg.ggplot(plot_df, gg.aes(x='Registration_Time', fill='org', group='org'))
+ gg.geom_histogram(binwidth=4/24, position='stack', color='black', show_legend=False)
)
plot.show()
Full traceback:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[126], line 19
6 # plotnine to plot (using grammar of graphics like ggplot2)
7 plot = (
8 gg.ggplot(plot_df, gg.aes(x='Registration_Time', fill='org', group='org'))
9 + gg.geom_histogram(binwidth=4[/24](http://localhost:51124/24), position='stack', color='black', show_legend=False)
(...)
17 # + gg.ggtitle("Registrations for R workshop in first 48 hours")
18 )
---> 19 plot.show()
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:150](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=149), in ggplot.show(self)
143 def show(self):
144 """
145 Show plot using the matplotlib backend set by the user
146
147 Users should prefer this method instead of printing or repring
148 the object.
149 """
--> 150 self._display() if is_inline_backend() else self.draw(show=True)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:175](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=174), in ggplot._display(self)
172 save_format = "png"
174 buf = BytesIO()
--> 175 self.save(buf, format=save_format, verbose=False)
176 display_func = get_display_function(format)
177 display_func(buf.getvalue())
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:663](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=662), in ggplot.save(self, filename, format, path, width, height, units, dpi, limitsize, verbose, **kwargs)
615 def save(
616 self,
617 filename: Optional[str | Path | BytesIO] = None,
(...)
626 **kwargs: Any,
627 ):
628 """
629 Save a ggplot object as an image file
630
(...)
661 Additional arguments to pass to matplotlib `savefig()`.
662 """
--> 663 sv = self.save_helper(
664 filename=filename,
665 format=format,
666 path=path,
667 width=width,
668 height=height,
669 units=units,
670 dpi=dpi,
671 limitsize=limitsize,
672 verbose=verbose,
673 **kwargs,
674 )
676 with plot_context(self).rc_context:
677 sv.figure.savefig(**sv.kwargs)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:612](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=611), in ggplot.save_helper(self, filename, format, path, width, height, units, dpi, limitsize, verbose, **kwargs)
609 if dpi is not None:
610 self.theme = self.theme + theme(dpi=dpi)
--> 612 figure = self.draw(show=False)
613 return mpl_save_view(figure, fig_kwargs)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:272](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=271), in ggplot.draw(self, show)
270 self = deepcopy(self)
271 with plot_context(self, show=show):
--> 272 self._build()
274 # setup
275 self.figure, self.axs = self.facet.setup(self)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py:400](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/ggplot.py#line=399), in ggplot._build(self)
397 layers.map(npscales)
399 # Train coordinate system
--> 400 layout.setup_panel_params(self.coordinates)
402 # fill in the defaults
403 layers.use_defaults()
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/facets/layout.py:198](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/facets/layout.py#line=197), in Layout.setup_panel_params(self, coord)
196 for i, j in self.layout[cols].itertuples(index=False):
197 i, j = i - 1, j - 1
--> 198 params = coord.setup_panel_params(
199 self.panel_scales_x[i], self.panel_scales_y[j]
200 )
201 self.panel_params.append(params)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/coords/coord_cartesian.py:80](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/coords/coord_cartesian.py#line=79), in coord_cartesian.setup_panel_params(self, scale_x, scale_y)
76 sv = scale.view(limits=coord_limits, range=ranges.range)
77 return sv
79 out = panel_view(
---> 80 x=get_scale_view(scale_x, self.limits.x),
81 y=get_scale_view(scale_y, self.limits.y),
82 )
83 return out
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/coords/coord_cartesian.py:76](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/coords/coord_cartesian.py#line=75), in coord_cartesian.setup_panel_params.<locals>.get_scale_view(scale, coord_limits)
72 expansion = scale.default_expansion(expand=self.expand)
73 ranges = scale.expand_limits(
74 scale.limits, expansion, coord_limits, identity_trans
75 )
---> 76 sv = scale.view(limits=coord_limits, range=ranges.range)
77 return sv
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py:302](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py#line=301), in scale_continuous.view(self, limits, range)
299 if range is None:
300 range = self.dimension(limits=limits)
--> 302 breaks = self.get_bounded_breaks(range)
303 labels = self.get_labels(breaks)
305 ubreaks = self.get_breaks(range)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py:403](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py#line=402), in scale_continuous.get_bounded_breaks(self, limits)
401 if limits is None:
402 limits = self.limits
--> 403 breaks = self.get_breaks(limits)
404 strict_breaks = [b for b in breaks if limits[0] <= b <= limits[1]]
405 return strict_breaks
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py:383](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/plotnine/scales/scale_continuous.py#line=382), in scale_continuous.get_breaks(self, limits)
379 breaks = []
380 elif self.breaks is True:
381 # TODO: Fix this type mismatch in mizani with
382 # a typevar so that type-in = type-out
--> 383 _tlimits = self.trans.breaks(_limits)
384 breaks: ScaleContinuousBreaks = _tlimits # pyright: ignore
385 elif zero_range(_limits):
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/transforms.py:224](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/transforms.py#line=223), in trans.breaks(self, limits)
218 # clip the breaks to the domain,
219 # e.g. probabilities will be in [0, 1] domain
220 limits = (
221 max(self.domain[0], limits[0]),
222 min(self.domain[1], limits[1]),
223 )
--> 224 breaks = np.asarray(self.breaks_(limits))
226 # Some methods (e.g. breaks_extended) that
227 # calculate breaks take the limits as guide posts and
228 # not hard limits.
229 breaks = breaks.compress(
230 (breaks >= self.domain[0]) & (breaks <= self.domain[1])
231 )
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/breaks.py:481](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/breaks.py#line=480), in breaks_date.__call__(self, limits)
477 return calculate_date_breaks_byunits(
478 limits, self.units, self.width
479 )
480 else:
--> 481 return calculate_date_breaks_auto(limits, self.n)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/dates.py:280](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/dates.py#line=279), in calculate_date_breaks_auto(limits, n)
276 def calculate_date_breaks_auto(limits, n: int = 5) -> Sequence[datetime]:
277 """
278 Calcuate date breaks using appropriate units
279 """
--> 280 info = calculate_date_breaks_info(limits, n=n)
281 lookup = {
282 DF.YEARLY: yearly_breaks,
283 DF.MONTHLY: monthly_breaks,
(...)
288 DF.MICROSECONDLY: microsecondly_breaks,
289 }
290 return lookup[info.frequency](info)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/dates.py:232](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/dates.py#line=231), in calculate_date_breaks_info(limits, n)
225 # Widen the duration at each granularity
226 itv = Interval(*limits)
227 unit_durations = (
228 itv.y_wide,
229 itv.M_wide,
230 itv.d_wide,
231 itv.h_wide,
--> 232 itv.m_wide,
233 itv.s,
234 itv.u,
235 )
236 # Search frequencies from longest (yearly) to the smallest
237 # for one that would a good width between the breaks
238
239 # Defaults
240 freq = DF.YEARLY # makes pyright happy
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/date_utils.py:131](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/date_utils.py#line=130), in Interval.m_wide(self)
126 @property
127 def m_wide(self) -> int:
128 """
129 Minutes (enclosing the original)
130 """
--> 131 return Interval(*self.limits_minute()).m
File <string>:5, in __init__(self, start, end)
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/date_utils.py:46](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/mizani/_core/date_utils.py#line=45), in Interval.__post_init__(self)
43 if isinstance(self.end, date):
44 self.end = datetime.fromisoformat(self.end.isoformat())
---> 46 self._delta = relativedelta(self.end, self.start)
47 self._tdelta = self.end - self.start
File [~/miniforge3/envs/cforge/lib/python3.11/site-packages/dateutil/relativedelta.py:154](http://localhost:51124/lab/tree/~/miniforge3/envs/cforge/lib/python3.11/site-packages/dateutil/relativedelta.py#line=153), in relativedelta.__init__(self, dt1, dt2, years, months, days, leapdays, weeks, hours, minutes, seconds, microseconds, year, month, day, weekday, yearday, nlyearday, hour, minute, second, microsecond)
151 dtm = self.__radd__(dt2)
153 # If we've overshot our target, make an adjustment
--> 154 if dt1 < dt2:
155 compare = operator.gt
156 increment = 1
TypeError: can't compare offset-naive and offset-aware datetimes
What versions of plotnine
and mizani
do you have installed? Try upgrading to the latest versions.
Originally:
python 3.11.9 h932a869_0_cpython conda-forge
mizani 0.11.2 pyhd8ed1ab_0 conda-forge
plotnine 0.13.5 pyhd8ed1ab_0 conda-forge
conda update plotnine mizani
updated mizani and downgraded plotnine:
mizani 0.12.2 pyhd8ed1ab_0 conda-forge
plotnine 0.12.2 pyhd8ed1ab_0 conda-forge
With these versions, plotnine complained about not having a "show()" method.
pip install -U mizani plotnine
installed:
mizani-0.11.4
plotnine-0.13.6
For this set, I got the same TypeError
Okay got it. This is solved in the current mizani-0.12.2
which can only be installed by the next version of plotnine; vO.14.0
will be out in a couple of days.
If you really need it fixed, you can install the development version (main branch) of plotnine.
v0.14.0 is out.