Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: support plotly backend for pandas plotting #249

Open
hdavid16 opened this issue Aug 16, 2024 · 5 comments
Open

Feature request: support plotly backend for pandas plotting #249

hdavid16 opened this issue Aug 16, 2024 · 5 comments

Comments

@hdavid16
Copy link

This package is amazing. I have been struggling to use the plotly backend for pandas plots when my dataframe has units associated with it. I have had to strip out the units to get the plotting to work. Is there a way to plot a pint_dataframe with plotly?

I'm happy to provide a MWE if that helps.

Thanks.

@andrewgsavage
Copy link
Collaborator

what errors do you get?

@hdavid16
Copy link
Author

hdavid16 commented Aug 16, 2024

MWE:

import pandas as pd
import pint_pandas as ppi
import matplotlib.pyplot as plt
pd.options.plotting.backend = "plotly"
ppi.PintType.ureg.setup_matplotlib()

df = pd.DataFrame({'A': pd.Series([1,2,3], dtype="pint[m]")})
df['A'].plot()

AttributeError: 'int' object has no attribute 'tolist'

This works:

df['A'].pint.magnitude.plot()

Package versions (on Python 3.10):

        'pandas>=2.2.2',
        'numpy>=2.0.1',
        'Pint>=0.24.3',
        'Pint-Pandas>=0.6.2',
        'matplotlib>=3.9.2',
        'plotly>=5.23.0',
        'nbformat>=4.2.0'

@andrewgsavage
Copy link
Collaborator

andrewgsavage commented Aug 16, 2024


AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1424, in PlainQuantity.tolist(self)
1423 try:
-> 1424 values = self._magnitude.tolist()
1425 if not isinstance(values, list):

AttributeError: 'float' object has no attribute 'tolist'

During handling of the above exception, another exception occurred:

AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\IPython\core\formatters.py:925, in IPythonDisplayFormatter.call(self, obj)
923 method = get_real_method(obj, self.print_method)
924 if method is not None:
--> 925 method()
926 return True

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\basedatatypes.py:832, in BaseFigure.ipython_display(self)
829 import plotly.io as pio
831 if pio.renderers.render_on_display and pio.renderers.default:
--> 832 pio.show(self)
833 else:
834 print(repr(self))

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:386, in show(fig, renderer, validate, **kwargs)
383 fig_dict = validate_coerce_fig_to_dict(fig, validate)
385 # Mimetype renderers
--> 386 bundle = renderers._build_mime_bundle(fig_dict, renderers_string=renderer, **kwargs)
387 if bundle:
388 if not ipython_display:

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:294, in RenderersConfig._build_mime_bundle(self, fig_dict, renderers_string, **kwargs)
291 if hasattr(renderer, k):
292 setattr(renderer, k, v)
--> 294 bundle.update(renderer.to_mimebundle(fig_dict))
296 return bundle

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_base_renderers.py:578, in IFrameRenderer.to_mimebundle(self, fig_dict)
575 if not isdir(self.html_directory):
576 raise
--> 578 write_html(
579 fig_dict,
580 filename,
581 config=self.config,
582 auto_play=self.auto_play,
583 include_plotlyjs=self.include_plotlyjs,
584 include_mathjax="cdn",
585 auto_open=False,
586 post_script=self.post_script,
587 animation_opts=self.animation_opts,
588 default_width="100%",
589 default_height=525,
590 validate=False,
591 )
593 # Build IFrame
594 iframe_html = """
595 <iframe
596 scrolling="no"
(...)
604 width=iframe_width, height=iframe_height, src=self.build_url(filename)
605 )

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:505, in write_html(fig, file, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, validate, default_width, default_height, auto_open, div_id)
393 """
394 Write a figure to an HTML file representation
395
(...)
501 Representation of figure as an HTML div string
502 """
504 # Build HTML string
--> 505 html_str = to_html(
506 fig,
507 config=config,
508 auto_play=auto_play,
509 include_plotlyjs=include_plotlyjs,
510 include_mathjax=include_mathjax,
511 post_script=post_script,
512 full_html=full_html,
513 animation_opts=animation_opts,
514 default_width=default_width,
515 default_height=default_height,
516 validate=validate,
517 div_id=div_id,
518 )
520 # Check if file is a string
521 if isinstance(file, str):
522 # Use the standard pathlib constructor to make a pathlib object.

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:142, in to_html(fig, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, default_width, default_height, validate, div_id)
139 plotdivid = div_id or str(uuid.uuid4())
141 # ## Serialize figure ##
--> 142 jdata = to_json_plotly(fig_dict.get("data", []))
143 jlayout = to_json_plotly(fig_dict.get("layout", {}))
145 if fig_dict.get("frames", None):

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_json.py:142, in to_json_plotly(plotly_object, pretty, engine)
137 opts["separators"] = (",", ":")
139 from _plotly_utils.utils import PlotlyJSONEncoder
141 return _safe(
--> 142 json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts), _swap_json
143 )
144 elif engine == "orjson":
145 JsonConfig.validate_orjson()

File ~\mambaforge\envs\pp_aug241\Lib\json_init_.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
232 if cls is None:
233 cls = JSONEncoder
234 return cls(
235 skipkeys=skipkeys, ensure_ascii=ensure_ascii,
236 check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237 separators=separators, default=default, sort_keys=sort_keys,
--> 238 **kw).encode(obj)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:56, in PlotlyJSONEncoder.encode(self, o)
49 """
50 Load and then dump the result using parse_constant kwarg
51
52 Note that setting invalid separators will cause a failure at this step.
53
54 """
55 # this will raise errors in a normal-expected way
---> 56 encoded_o = super(PlotlyJSONEncoder, self).encode(o)
57 # Brute force guessing whether NaN or Infinity values are in the string
58 # We catch false positive cases (e.g. strings such as titles, labels etc.)
59 # but this is ok since the intention is to skip the decoding / reencoding
60 # step when it's completely safe
62 if not ("NaN" in encoded_o or "Infinity" in encoded_o):

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:200, in JSONEncoder.encode(self, o)
196 return encode_basestring(o)
197 # This doesn't pass the iterator directly to ''.join() because the
198 # exceptions aren't as detailed. The list call should be roughly
199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
201 if not isinstance(chunks, (list, tuple)):
202 chunks = list(chunks)

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
253 else:
254 _iterencode = _make_iterencode(
255 markers, self.default, _encoder, self.indent, floatstr,
256 self.key_separator, self.item_separator, self.sort_keys,
257 self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:130, in PlotlyJSONEncoder.default(self, obj)
128 for encoding_method in encoding_methods:
129 try:
--> 130 return encoding_method(obj)
131 except NotEncodable:
132 pass

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:147, in PlotlyJSONEncoder.encode_as_list(obj)
145 """Attempt to use tolist method to convert to normal Python list."""
146 if hasattr(obj, "tolist"):
--> 147 return obj.tolist()
148 else:
149 raise NotEncodable

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1435, in PlainQuantity.tolist(self)
1428 return [
1429 self.class(value, units).tolist()
1430 if isinstance(value, list)
1431 else self.class(value, units)
1432 for value in self._magnitude.tolist()
1433 ]
1434 except AttributeError:
-> 1435 raise AttributeError(
1436 f"Magnitude '{type(self._magnitude).name}' does not support tolist."
1437 )

AttributeError: Magnitude 'float' does not support tolist.

AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1424, in PlainQuantity.tolist(self)
1423 try:
-> 1424 values = self._magnitude.tolist()
1425 if not isinstance(values, list):

AttributeError: 'float' object has no attribute 'tolist'

During handling of the above exception, another exception occurred:

AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\IPython\core\formatters.py:977, in MimeBundleFormatter.call(self, obj, include, exclude)
974 method = get_real_method(obj, self.print_method)
976 if method is not None:
--> 977 return method(include=include, exclude=exclude)
978 return None
979 else:

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\basedatatypes.py:823, in BaseFigure.repr_mimebundle(self, include, exclude, validate, **kwargs)
820 from plotly.io._utils import validate_coerce_fig_to_dict
822 fig_dict = validate_coerce_fig_to_dict(self, validate)
--> 823 return renderers._build_mime_bundle(fig_dict, renderer_str, **kwargs)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:294, in RenderersConfig._build_mime_bundle(self, fig_dict, renderers_string, **kwargs)
291 if hasattr(renderer, k):
292 setattr(renderer, k, v)
--> 294 bundle.update(renderer.to_mimebundle(fig_dict))
296 return bundle

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_base_renderers.py:578, in IFrameRenderer.to_mimebundle(self, fig_dict)
575 if not isdir(self.html_directory):
576 raise
--> 578 write_html(
579 fig_dict,
580 filename,
581 config=self.config,
582 auto_play=self.auto_play,
583 include_plotlyjs=self.include_plotlyjs,
584 include_mathjax="cdn",
585 auto_open=False,
586 post_script=self.post_script,
587 animation_opts=self.animation_opts,
588 default_width="100%",
589 default_height=525,
590 validate=False,
591 )
593 # Build IFrame
594 iframe_html = """
595 <iframe
596 scrolling="no"
(...)
604 width=iframe_width, height=iframe_height, src=self.build_url(filename)
605 )

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:505, in write_html(fig, file, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, validate, default_width, default_height, auto_open, div_id)
393 """
394 Write a figure to an HTML file representation
395
(...)
501 Representation of figure as an HTML div string
502 """
504 # Build HTML string
--> 505 html_str = to_html(
506 fig,
507 config=config,
508 auto_play=auto_play,
509 include_plotlyjs=include_plotlyjs,
510 include_mathjax=include_mathjax,
511 post_script=post_script,
512 full_html=full_html,
513 animation_opts=animation_opts,
514 default_width=default_width,
515 default_height=default_height,
516 validate=validate,
517 div_id=div_id,
518 )
520 # Check if file is a string
521 if isinstance(file, str):
522 # Use the standard pathlib constructor to make a pathlib object.

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:142, in to_html(fig, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, default_width, default_height, validate, div_id)
139 plotdivid = div_id or str(uuid.uuid4())
141 # ## Serialize figure ##
--> 142 jdata = to_json_plotly(fig_dict.get("data", []))
143 jlayout = to_json_plotly(fig_dict.get("layout", {}))
145 if fig_dict.get("frames", None):

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_json.py:142, in to_json_plotly(plotly_object, pretty, engine)
137 opts["separators"] = (",", ":")
139 from _plotly_utils.utils import PlotlyJSONEncoder
141 return _safe(
--> 142 json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts), _swap_json
143 )
144 elif engine == "orjson":
145 JsonConfig.validate_orjson()

File ~\mambaforge\envs\pp_aug241\Lib\json_init_.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
232 if cls is None:
233 cls = JSONEncoder
234 return cls(
235 skipkeys=skipkeys, ensure_ascii=ensure_ascii,
236 check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237 separators=separators, default=default, sort_keys=sort_keys,
--> 238 **kw).encode(obj)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:56, in PlotlyJSONEncoder.encode(self, o)
49 """
50 Load and then dump the result using parse_constant kwarg
51
52 Note that setting invalid separators will cause a failure at this step.
53
54 """
55 # this will raise errors in a normal-expected way
---> 56 encoded_o = super(PlotlyJSONEncoder, self).encode(o)
57 # Brute force guessing whether NaN or Infinity values are in the string
58 # We catch false positive cases (e.g. strings such as titles, labels etc.)
59 # but this is ok since the intention is to skip the decoding / reencoding
60 # step when it's completely safe
62 if not ("NaN" in encoded_o or "Infinity" in encoded_o):

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:200, in JSONEncoder.encode(self, o)
196 return encode_basestring(o)
197 # This doesn't pass the iterator directly to ''.join() because the
198 # exceptions aren't as detailed. The list call should be roughly
199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
201 if not isinstance(chunks, (list, tuple)):
202 chunks = list(chunks)

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
253 else:
254 _iterencode = _make_iterencode(
255 markers, self.default, _encoder, self.indent, floatstr,
256 self.key_separator, self.item_separator, self.sort_keys,
257 self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:130, in PlotlyJSONEncoder.default(self, obj)
128 for encoding_method in encoding_methods:
129 try:
--> 130 return encoding_method(obj)
131 except NotEncodable:
132 pass

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:147, in PlotlyJSONEncoder.encode_as_list(obj)
145 """Attempt to use tolist method to convert to normal Python list."""
146 if hasattr(obj, "tolist"):
--> 147 return obj.tolist()
148 else:
149 raise NotEncodable

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1435, in PlainQuantity.tolist(self)
1428 return [
1429 self.class(value, units).tolist()
1430 if isinstance(value, list)
1431 else self.class(value, units)
1432 for value in self._magnitude.tolist()
1433 ]
1434 except AttributeError:
-> 1435 raise AttributeError(
1436 f"Magnitude '{type(self._magnitude).name}' does not support tolist."
1437 )

AttributeError: Magnitude 'float' does not support tolist.

AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1424, in PlainQuantity.tolist(self)
1423 try:
-> 1424 values = self._magnitude.tolist()
1425 if not isinstance(values, list):

AttributeError: 'float' object has no attribute 'tolist'

During handling of the above exception, another exception occurred:

AttributeError Traceback (most recent call last)
File ~\mambaforge\envs\pp_aug241\Lib\site-packages\IPython\core\formatters.py:347, in BaseFormatter.call(self, obj)
345 method = get_real_method(obj, self.print_method)
346 if method is not None:
--> 347 return method()
348 return None
349 else:

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\basedatatypes.py:806, in BaseFigure.repr_html(self)
802 def repr_html(self):
803 """
804 Customize html representation
805 """
--> 806 bundle = self.repr_mimebundle()
807 if "text/html" in bundle:
808 return bundle["text/html"]

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\basedatatypes.py:823, in BaseFigure.repr_mimebundle(self, include, exclude, validate, **kwargs)
820 from plotly.io._utils import validate_coerce_fig_to_dict
822 fig_dict = validate_coerce_fig_to_dict(self, validate)
--> 823 return renderers._build_mime_bundle(fig_dict, renderer_str, **kwargs)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:294, in RenderersConfig._build_mime_bundle(self, fig_dict, renderers_string, **kwargs)
291 if hasattr(renderer, k):
292 setattr(renderer, k, v)
--> 294 bundle.update(renderer.to_mimebundle(fig_dict))
296 return bundle

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_base_renderers.py:578, in IFrameRenderer.to_mimebundle(self, fig_dict)
575 if not isdir(self.html_directory):
576 raise
--> 578 write_html(
579 fig_dict,
580 filename,
581 config=self.config,
582 auto_play=self.auto_play,
583 include_plotlyjs=self.include_plotlyjs,
584 include_mathjax="cdn",
585 auto_open=False,
586 post_script=self.post_script,
587 animation_opts=self.animation_opts,
588 default_width="100%",
589 default_height=525,
590 validate=False,
591 )
593 # Build IFrame
594 iframe_html = """
595 <iframe
596 scrolling="no"
(...)
604 width=iframe_width, height=iframe_height, src=self.build_url(filename)
605 )

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:505, in write_html(fig, file, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, validate, default_width, default_height, auto_open, div_id)
393 """
394 Write a figure to an HTML file representation
395
(...)
501 Representation of figure as an HTML div string
502 """
504 # Build HTML string
--> 505 html_str = to_html(
506 fig,
507 config=config,
508 auto_play=auto_play,
509 include_plotlyjs=include_plotlyjs,
510 include_mathjax=include_mathjax,
511 post_script=post_script,
512 full_html=full_html,
513 animation_opts=animation_opts,
514 default_width=default_width,
515 default_height=default_height,
516 validate=validate,
517 div_id=div_id,
518 )
520 # Check if file is a string
521 if isinstance(file, str):
522 # Use the standard pathlib constructor to make a pathlib object.

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:142, in to_html(fig, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, default_width, default_height, validate, div_id)
139 plotdivid = div_id or str(uuid.uuid4())
141 # ## Serialize figure ##
--> 142 jdata = to_json_plotly(fig_dict.get("data", []))
143 jlayout = to_json_plotly(fig_dict.get("layout", {}))
145 if fig_dict.get("frames", None):

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_json.py:142, in to_json_plotly(plotly_object, pretty, engine)
137 opts["separators"] = (",", ":")
139 from _plotly_utils.utils import PlotlyJSONEncoder
141 return _safe(
--> 142 json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts), _swap_json
143 )
144 elif engine == "orjson":
145 JsonConfig.validate_orjson()

File ~\mambaforge\envs\pp_aug241\Lib\json_init_.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
232 if cls is None:
233 cls = JSONEncoder
234 return cls(
235 skipkeys=skipkeys, ensure_ascii=ensure_ascii,
236 check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237 separators=separators, default=default, sort_keys=sort_keys,
--> 238 **kw).encode(obj)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:56, in PlotlyJSONEncoder.encode(self, o)
49 """
50 Load and then dump the result using parse_constant kwarg
51
52 Note that setting invalid separators will cause a failure at this step.
53
54 """
55 # this will raise errors in a normal-expected way
---> 56 encoded_o = super(PlotlyJSONEncoder, self).encode(o)
57 # Brute force guessing whether NaN or Infinity values are in the string
58 # We catch false positive cases (e.g. strings such as titles, labels etc.)
59 # but this is ok since the intention is to skip the decoding / reencoding
60 # step when it's completely safe
62 if not ("NaN" in encoded_o or "Infinity" in encoded_o):

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:200, in JSONEncoder.encode(self, o)
196 return encode_basestring(o)
197 # This doesn't pass the iterator directly to ''.join() because the
198 # exceptions aren't as detailed. The list call should be roughly
199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
201 if not isinstance(chunks, (list, tuple)):
202 chunks = list(chunks)

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
253 else:
254 _iterencode = _make_iterencode(
255 markers, self.default, _encoder, self.indent, floatstr,
256 self.key_separator, self.item_separator, self.sort_keys,
257 self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:130, in PlotlyJSONEncoder.default(self, obj)
128 for encoding_method in encoding_methods:
129 try:
--> 130 return encoding_method(obj)
131 except NotEncodable:
132 pass

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:147, in PlotlyJSONEncoder.encode_as_list(obj)
145 """Attempt to use tolist method to convert to normal Python list."""
146 if hasattr(obj, "tolist"):
--> 147 return obj.tolist()
148 else:
149 raise NotEncodable

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1435, in PlainQuantity.tolist(self)
1428 return [
1429 self.class(value, units).tolist()
1430 if isinstance(value, list)
1431 else self.class(value, units)
1432 for value in self._magnitude.tolist()
1433 ]
1434 except AttributeError:
-> 1435 raise AttributeError(
1436 f"Magnitude '{type(self._magnitude).name}' does not support tolist."
1437 )

AttributeError: Magnitude 'float' does not support tolist.
Figure({
'data': [{'hovertemplate': 'variable=A
index=%{x}
value=%{y}',
'legendgroup': 'A',
'line': {'color': '#636efa', 'dash': 'solid'},
'marker': {'symbol': 'circle'},
'mode': 'lines',
'name': 'A',
'orientation': 'v',
'showlegend': True,
'type': 'scatter',
'x': array([0, 1, 2]),
'xaxis': 'x',
'y': array([<Quantity(1.0, 'meter')>, <Quantity(2.0, 'meter')>,
<Quantity(3.0, 'meter')>], dtype=object),
'yaxis': 'y'}],
'layout': {'legend': {'title': {'text': 'variable'}, 'tracegroupgap': 0},
'margin': {'t': 60},
'template': '...',
'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'index'}},
'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'value'}}}
})

Output:
Figure({
    'data': [{'hovertemplate': 'variable=A<br>index=%{x}<br>value=%{y}<extra></extra>',
              'legendgroup': 'A',
              'line': {'color': '#636efa', 'dash': 'solid'},
              'marker': {'symbol': 'circle'},
              'mode': 'lines',
              'name': 'A',
              'orientation': 'v',
              'showlegend': True,
              'type': 'scatter',
              'x': array([0, 1, 2]),
              'xaxis': 'x',
              'y': array([<Quantity(1.0, 'meter')>, <Quantity(2.0, 'meter')>,
                          <Quantity(3.0, 'meter')>], dtype=object),
              'yaxis': 'y'}],
    'layout': {'legend': {'title': {'text': 'variable'}, 'tracegroupgap': 0},
               'margin': {'t': 60},
               'template': '...',
               'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'index'}},
               'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'value'}}}
})

I note that it's showing the ydata as an array of qty


              'y': array([<Quantity(1.0, 'meter')>, <Quantity(2.0, 'meter')>,
                          <Quantity(3.0, 'meter')>], dtype=object),

@andrewgsavage
Copy link
Collaborator

I get the same tolist error when trying to plot an array of qty:

import plotly.express as px
import pint
import numpy as np
Q_ = pint.Quantity
fig = px.line(x=["a","b","c"], y=np.array([Q_(1,"m"),Q_(1,"m"),Q_(1,"m")],dtype="object"), title="sample figure")
print(fig)
fig.show()

Figure({
    'data': [{'hovertemplate': 'x=%{x}<br>y=%{y}<extra></extra>',
              'legendgroup': '',
              'line': {'color': '#636efa', 'dash': 'solid'},
              'marker': {'symbol': 'circle'},
              'mode': 'lines',
              'name': '',
              'orientation': 'v',
              'showlegend': False,
              'type': 'scatter',
              'x': array(['a', 'b', 'c'], dtype=object),
              'xaxis': 'x',
              'y': array([<Quantity(1, 'meter')>, <Quantity(1, 'meter')>, <Quantity(1, 'meter')>],
                         dtype=object),
              'yaxis': 'y'}],
    'layout': {'legend': {'tracegroupgap': 0},
               'template': '...',
               'title': {'text': 'sample figure'},
               'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'x'}},
               'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'y'}}}
})
```python3 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1424, in PlainQuantity.tolist(self) 1423 try: -> 1424 values = self._magnitude.tolist() 1425 if not isinstance(values, list):

AttributeError: 'int' object has no attribute 'tolist'

During handling of the above exception, another exception occurred:

AttributeError Traceback (most recent call last)
Cell In[8], line 7
5 fig = px.line(x=["a","b","c"], y=np.array([Q_(1,"m"),Q_(1,"m"),Q_(1,"m")],dtype="object"), title="sample figure")
6 print(fig)
----> 7 fig.show()

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\basedatatypes.py:3410, in BaseFigure.show(self, *args, **kwargs)
3377 """
3378 Show a figure using either the default renderer(s) or the renderer(s)
3379 specified by the renderer argument
(...)
3406 None
3407 """
3408 import plotly.io as pio
-> 3410 return pio.show(self, *args, **kwargs)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:386, in show(fig, renderer, validate, **kwargs)
383 fig_dict = validate_coerce_fig_to_dict(fig, validate)
385 # Mimetype renderers
--> 386 bundle = renderers._build_mime_bundle(fig_dict, renderers_string=renderer, **kwargs)
387 if bundle:
388 if not ipython_display:

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_renderers.py:294, in RenderersConfig._build_mime_bundle(self, fig_dict, renderers_string, **kwargs)
291 if hasattr(renderer, k):
292 setattr(renderer, k, v)
--> 294 bundle.update(renderer.to_mimebundle(fig_dict))
296 return bundle

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_base_renderers.py:578, in IFrameRenderer.to_mimebundle(self, fig_dict)
575 if not isdir(self.html_directory):
576 raise
--> 578 write_html(
579 fig_dict,
580 filename,
581 config=self.config,
582 auto_play=self.auto_play,
583 include_plotlyjs=self.include_plotlyjs,
584 include_mathjax="cdn",
585 auto_open=False,
586 post_script=self.post_script,
587 animation_opts=self.animation_opts,
588 default_width="100%",
589 default_height=525,
590 validate=False,
591 )
593 # Build IFrame
594 iframe_html = """
595 <iframe
596 scrolling="no"
(...)
604 width=iframe_width, height=iframe_height, src=self.build_url(filename)
605 )

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:505, in write_html(fig, file, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, validate, default_width, default_height, auto_open, div_id)
393 """
394 Write a figure to an HTML file representation
395
(...)
501 Representation of figure as an HTML div string
502 """
504 # Build HTML string
--> 505 html_str = to_html(
506 fig,
507 config=config,
508 auto_play=auto_play,
509 include_plotlyjs=include_plotlyjs,
510 include_mathjax=include_mathjax,
511 post_script=post_script,
512 full_html=full_html,
513 animation_opts=animation_opts,
514 default_width=default_width,
515 default_height=default_height,
516 validate=validate,
517 div_id=div_id,
518 )
520 # Check if file is a string
521 if isinstance(file, str):
522 # Use the standard pathlib constructor to make a pathlib object.

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_html.py:142, in to_html(fig, config, auto_play, include_plotlyjs, include_mathjax, post_script, full_html, animation_opts, default_width, default_height, validate, div_id)
139 plotdivid = div_id or str(uuid.uuid4())
141 # ## Serialize figure ##
--> 142 jdata = to_json_plotly(fig_dict.get("data", []))
143 jlayout = to_json_plotly(fig_dict.get("layout", {}))
145 if fig_dict.get("frames", None):

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\plotly\io_json.py:142, in to_json_plotly(plotly_object, pretty, engine)
137 opts["separators"] = (",", ":")
139 from _plotly_utils.utils import PlotlyJSONEncoder
141 return _safe(
--> 142 json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts), _swap_json
143 )
144 elif engine == "orjson":
145 JsonConfig.validate_orjson()

File ~\mambaforge\envs\pp_aug241\Lib\json_init_.py:238, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
232 if cls is None:
233 cls = JSONEncoder
234 return cls(
235 skipkeys=skipkeys, ensure_ascii=ensure_ascii,
236 check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237 separators=separators, default=default, sort_keys=sort_keys,
--> 238 **kw).encode(obj)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:56, in PlotlyJSONEncoder.encode(self, o)
49 """
50 Load and then dump the result using parse_constant kwarg
51
52 Note that setting invalid separators will cause a failure at this step.
53
54 """
55 # this will raise errors in a normal-expected way
---> 56 encoded_o = super(PlotlyJSONEncoder, self).encode(o)
57 # Brute force guessing whether NaN or Infinity values are in the string
58 # We catch false positive cases (e.g. strings such as titles, labels etc.)
59 # but this is ok since the intention is to skip the decoding / reencoding
60 # step when it's completely safe
62 if not ("NaN" in encoded_o or "Infinity" in encoded_o):

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:200, in JSONEncoder.encode(self, o)
196 return encode_basestring(o)
197 # This doesn't pass the iterator directly to ''.join() because the
198 # exceptions aren't as detailed. The list call should be roughly
199 # equivalent to the PySequence_Fast that ''.join() would do.
--> 200 chunks = self.iterencode(o, _one_shot=True)
201 if not isinstance(chunks, (list, tuple)):
202 chunks = list(chunks)

File ~\mambaforge\envs\pp_aug241\Lib\json\encoder.py:258, in JSONEncoder.iterencode(self, o, _one_shot)
253 else:
254 _iterencode = _make_iterencode(
255 markers, self.default, _encoder, self.indent, floatstr,
256 self.key_separator, self.item_separator, self.sort_keys,
257 self.skipkeys, _one_shot)
--> 258 return _iterencode(o, 0)

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:130, in PlotlyJSONEncoder.default(self, obj)
128 for encoding_method in encoding_methods:
129 try:
--> 130 return encoding_method(obj)
131 except NotEncodable:
132 pass

File ~\mambaforge\envs\pp_aug241\Lib\site-packages_plotly_utils\utils.py:147, in PlotlyJSONEncoder.encode_as_list(obj)
145 """Attempt to use tolist method to convert to normal Python list."""
146 if hasattr(obj, "tolist"):
--> 147 return obj.tolist()
148 else:
149 raise NotEncodable

File ~\mambaforge\envs\pp_aug241\Lib\site-packages\pint\facets\plain\quantity.py:1435, in PlainQuantity.tolist(self)
1428 return [
1429 self.class(value, units).tolist()
1430 if isinstance(value, list)
1431 else self.class(value, units)
1432 for value in self._magnitude.tolist()
1433 ]
1434 except AttributeError:
-> 1435 raise AttributeError(
1436 f"Magnitude '{type(self._magnitude).name}' does not support tolist."
1437 )

AttributeError: Magnitude 'int' does not support tolist.

</details>

@andrewgsavage
Copy link
Collaborator

this looks like plotly is not recognising pint scalars as scalars, so tries to convert them to list. I'd try opening an issue in plotly with my last post as the MWE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants