predict-idlab / plotly-resampler

Visualize large time series data with plotly.py
https://predict-idlab.github.io/plotly-resampler/latest
MIT License
1k stars 67 forks source link

Getting an `AttributeError` from a timezone labeled time series #212

Closed darynwhite closed 1 year ago

darynwhite commented 1 year ago

Here is the line referenced in the raised error: https://github.com/predict-idlab/plotly-resampler/blob/8fbb0f421781dac895000faddef2014256fd99bc/plotly_resampler/aggregation/plotly_aggregator_parser.py#LL41C17-L41C17

And here is the traceback that follows the plotly-resampler code:

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figurewidget_resampler.py:180](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figurewidget_resampler.py:180), in FigureWidgetResampler._update_x_ranges(self, layout, force_update, *x_ranges)
    176         self._prev_layout[xaxis_str]["range"] = x_range
    178 if relayout_dict:  # when not empty
    179     # Construct the update data
--> 180     update_data = self.construct_update_data(relayout_dict)
    182     if self._is_no_update(update_data):
    183         # Return when no data update
    184         return

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:1259](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:1259), in AbstractFigureAggregator.construct_update_data(self, relayout_data)
   1257         assert xaxis == t_stop_key.split(".")[0]
   1258         # -> we want to copy the layout on the back-end
-> 1259         updated_trace_indices = self._check_update_figure_dict(
   1260             current_graph,
   1261             start=relayout_data[t_start_key],
   1262             stop=relayout_data[t_stop_key],
   1263             xaxis_filter=xaxis,
   1264             updated_trace_indices=updated_trace_indices,
   1265         )
   1267 # 2. The user clicked on either autorange | reset axes
   1268 autorange_matches = self._re_matches(
   1269     re.compile(r"xaxis\d*.autorange"), cl_k
   1270 )

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:474](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:474), in AbstractFigureAggregator._check_update_figure_dict(self, figure, start, stop, xaxis_filter, updated_trace_indices)
    470         continue
    472 # If we managed to find and update the trace, it will return the trace
    473 # and thus not None.
--> 474 updated_trace = self._check_update_trace_data(trace, start=start, end=stop)
    475 if updated_trace is not None:
    476     updated_trace_indices.append(idx)

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:334](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/figure_resampler/figure_resampler_interface.py:334), in AbstractFigureAggregator._check_update_trace_data(self, trace, start, end)
    331     trace["name"] = hf_trace_data["name"]
    332     return trace
--> 334 start_idx, end_idx = PlotlyAggregatorParser.get_start_end_indices(
    335     hf_trace_data, start, end
    336 )
    338 # Return an invisible, single-point, trace when the sliced hf_series doesn't
    339 # contain any data in the current view
    340 if end_idx == start_idx:

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/aggregation/plotly_aggregator_parser.py:77](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/aggregation/plotly_aggregator_parser.py:77), in PlotlyAggregatorParser.get_start_end_indices(hf_trace_data, start, end)
     75         tz = hf_trace_data["x"].tz
     76         assert start.tz == end.tz
---> 77         start = PlotlyAggregatorParser.to_same_tz(start, tz)
     78         end = PlotlyAggregatorParser.to_same_tz(end, tz)
     80 # Search the index-positions

File [~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/aggregation/plotly_aggregator_parser.py:40](https://file+.vscode-resource.vscode-cdn.net/Users/white/Repos/Dev/GTMBA-FlexProcessingDev/~/conda/envs/tmba/lib/python3.11/site-packages/plotly_resampler/aggregation/plotly_aggregator_parser.py:40), in PlotlyAggregatorParser.to_same_tz(ts, reference_tz)
     38 elif reference_tz is not None:
     39     if ts.tz is not None:
---> 40         assert ts.tz.zone == reference_tz.zone
     41         return ts
     42     else:  # localize -> time remains the same

AttributeError: 'datetime.timezone' object has no attribute 'zone'
jonasvdd commented 1 year ago

Hi @darynwhite, thank you for submitting this issue!

Can you provide a minimal example and the used plotly-resampler version which raises this error. This helps us tremendously to pinpoint and resolve the issue!

Kind regards, Jonas

darynwhite commented 1 year ago

I just updated to the 0.9.0rc2 release and the problem persists from 0.9.0rc0.

I've attached the data that I'm attempting to display via Dash. This example script follows the best I can quickly write that raises the issue and roughly displays what I'm attempting to plot.

import pandas, plotly.graph_objects as plgo, plotly_resampler as resampler
from datetime import timedelta

data = pandas.read_csv("airtemp-test-data.csv", index_col="Observation Time (UTC)")
data.index = pandas.to_datetime(data.index)
data.index.freq = "10T"

fig = plgo.Figure(
    layout=plgo.Layout(
        title=dict(
            text=f"AirT test timeseries", y=0.98, x=0.5, xanchor="center", yanchor="top"
        ),
        xaxis=dict(title=data.index.name, type="date"),
        yaxis=dict(title="Air Temp (ºC)", range=[20, 30], fixedrange=True),
        template="seaborn",
        margin=dict(l=50, r=50, t=50, b=50, pad=5),
        showlegend=True,
    )
)

plot = resampler.FigureResampler(fig)

plot.add_traces(
    [plgo.Scattergl(x=data.index, y=data["AirT"], mode="markers", name=f"AirT")]
)

plot.add_vline(x=data.index[0])
plot.add_vline(x=data.index[-1])

start = data.index[0] - timedelta(hours=48)
end = data.index[-1] + timedelta(hours=48)

plot.update_xaxes(range=[start, end])

plot.show_dash()

airtemp-test-data.csv

jonasvdd commented 1 year ago

@darynwhite,

It seems that the issue is related to the plot.update_xaxes(range=[start, end]) code line and pandas>=2.0 causes this to fail (datetimes and timezones are handled slightly different there).

I will try to fix this issue with a PR and create a test for this, so this will not occur anymore in later versions!

jvdd commented 1 year ago

Hey @darynwhite, can you confirm that plotly-resampler==0.9.0rc3 fixes this issue?

darynwhite commented 1 year ago

The error doesn't show, a new one does tho, but that is attached to an update from dash-extensions