major_label_overrides dict does not work with numpy scalar keys
See original GitHub issueThis issue is very strange but using a programmatically generated dictionary with xaxis.major_label_overrides
fails, whereas using it with a hard-coded dictionary works fine. Even stranger, setting first the hard-coded, and then the programmatically generated also works. Here is an example
Example:
print(sys.version)
print(bokeh.__version__)
x = np.array([1, 2, 3])
y = np.array([5, 3, 8])
a = list("ABC")
d = dict(zip(x, a))
plot = figure(width=300, height=300)
plot.vbar(x=x, top=y, width=1, bottom=0, color=["red", "green", "blue"])
plot.xaxis.ticker = x
assert d == {1: "A", 2: "B", 3: "C"} # this assert passes
# plot.xaxis.major_label_overrides = {1: "A", 2: "B", 3: "C"} # this works
plot.xaxis.major_label_overrides = d # this doesn't
show(plot)
Stdout:
3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 18:10:19) [GCC 7.2.0] 0.13.0
Stacktrace:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-52-6f355b807b3e> in <module>()
13 # this doesn't
14 plot.xaxis.major_label_overrides = d
---> 15 show(plot)
~/anaconda3/lib/python3.6/site-packages/bokeh/io/showing.py in show(obj, browser, new, notebook_handle, notebook_url)
135 if obj not in state.document.roots:
136 state.document.add_root(obj)
--> 137 return _show_with_state(obj, state, browser, new, notebook_handle=notebook_handle)
138
139 #-----------------------------------------------------------------------------
~/anaconda3/lib/python3.6/site-packages/bokeh/io/showing.py in _show_with_state(obj, state, browser, new, notebook_handle)
169
170 if state.notebook:
--> 171 comms_handle = run_notebook_hook(state.notebook_type, 'doc', obj, state, notebook_handle)
172 shown = True
173
~/anaconda3/lib/python3.6/site-packages/bokeh/io/notebook.py in run_notebook_hook(notebook_type, action, *args, **kw)
286 if _HOOKS[notebook_type][action] is None:
287 raise RuntimeError("notebook hook for %r did not install %r action" % notebook_type, action)
--> 288 return _HOOKS[notebook_type][action](*args, **kw)
289
290 #-----------------------------------------------------------------------------
~/anaconda3/lib/python3.6/site-packages/bokeh/io/notebook.py in show_doc(obj, state, notebook_handle)
491 from ..embed.notebook import notebook_content
492 comms_target = make_id() if notebook_handle else None
--> 493 (script, div, cell_doc) = notebook_content(obj, comms_target)
494
495 publish_display_data({HTML_MIME_TYPE: div})
~/anaconda3/lib/python3.6/site-packages/bokeh/embed/notebook.py in notebook_content(model, notebook_comms_target, theme)
80 # has models with the same IDs as they were started with
81 with _ModelInEmptyDocument(model, apply_theme=theme) as new_doc:
---> 82 (docs_json, [render_item]) = standalone_docs_json_and_render_items([model])
83
84 div = div_for_render_item(render_item)
~/anaconda3/lib/python3.6/site-packages/bokeh/embed/util.py in standalone_docs_json_and_render_items(models)
338 docs_json = {}
339 for doc, (docid, _) in docs.items():
--> 340 docs_json[docid] = doc.to_json()
341
342 render_items = []
~/anaconda3/lib/python3.6/site-packages/bokeh/document/document.py in to_json(self)
779 # this is a total hack to go via a string, needed because
780 # our BokehJSONEncoder goes straight to a string.
--> 781 doc_json = self.to_json_string()
782 return loads(doc_json)
783
~/anaconda3/lib/python3.6/site-packages/bokeh/document/document.py in to_json_string(self, indent)
808 }
809
--> 810 return serialize_json(json, indent=indent)
811
812 def validate(self):
~/anaconda3/lib/python3.6/site-packages/bokeh/core/json_encoder.py in serialize_json(obj, pretty, indent, **kwargs)
211 indent = 2
212
--> 213 return json.dumps(obj, cls=BokehJSONEncoder, allow_nan=False, indent=indent, separators=separators, sort_keys=True, **kwargs)
~/anaconda3/lib/python3.6/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
236 check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237 separators=separators, default=default, sort_keys=sort_keys,
--> 238 **kw).encode(obj)
239
240
~/anaconda3/lib/python3.6/json/encoder.py in encode(self, o)
197 # exceptions aren't as detailed. The list call should be roughly
198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
200 if not isinstance(chunks, (list, tuple)):
201 chunks = list(chunks)
~/anaconda3/lib/python3.6/json/encoder.py in iterencode(self, o, _one_shot)
255 self.key_separator, self.item_separator, self.sort_keys,
256 self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)
258
259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
TypeError: keys must be a string
Issue Analytics
- State:
- Created 5 years ago
- Comments:10 (8 by maintainers)
Top Results From Across the Web
Using scalar ndarrays as keys - Stack Overflow
Since the data in a numpy array is mutable, we could not use that data ... Similarly, dictionary keys can be mutable provided...
Read more >Structured arrays — NumPy v1.23 Manual
Record arrays use a special datatype, numpy. record , that allows field access by attribute on the structured scalars obtained from the array....
Read more >DICTIONARY - L3HarrisGeospatial.com
The key is a case-insensitive scalar string and must be a valid IDL variable name (i.e., no spaces or special characters). You can...
Read more >dictdump: Dumping and loading dictionaries - silx
If it is any other data type, it is cast into a numpy array and written as a ... If dictionary keys are...
Read more >sklearn.feature_extraction.DictVectorizer
This transformer turns lists of mappings (dict-like objects) of feature names to feature values into Numpy arrays or scipy.sparse matrices for use with ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I’m copying over from gitter a related discussion with @bryevdv in case it helps with contributing towards getting to the bottom of this issue.
I also started with numpy when getting tripped up. But later, I removed numpy and observed the following:
including the “.0” in the the latter two dict_keys prevents the override from happening, while removing the “.0” does activate the override
This is unexpected, since one might generate the dictionary keys using the original list of ticks values.
The fact that the key=1.5 works, suggests a possible reason: the float values ending in .0’s are likely being stripped of their decimals when converted to string, here? (My .ts knowledge is only from a cursory googling but that seems to be a difference compared to the python side, here, where the key can be either float or str)
workaround:
Convert any whole-number floats to integers when creating the dictionary keys (the tick values themselves need not be converted). I think my typical use case would be: I already know the major_ticks to specify and their corresponding labels --> the dictionary of overrides would be procedurally generated, so that would be the time to do this protection, rather than fixing the tick values themselves.
Ok, I am inclined to close this again. While the above diff did suppress the error, evidently it did not actually work to display the overrides. The reason is because the
accepts
always converts numpy scalar to floats (it has to convert them to something), but if the fixed ticks are provided as ints, then they do not get applied. I.e. completely independent of Numpy usage, this works:but this does not work:
Which is presumably because the BokehJS axis code does:
There’s no way to know ahead of time whether the ticks chosen are or will be floats. So there is no way to know whether numpy scalars would need to be converted to ints or to floats. It’s just a pick-one-and-hope situation. In fact, there is no reason the fixed ticks could not be both, in which case there is no way to pick a single converted type that will work for all the fixed ticks. That makes this "solution:
The only way to make it work fully would be to change the axis JS code above to be some N^2 loop with a further double loop inside that tries out different pairwise type combinations against each other. There is no way this one use case justifies a mess like that, in addition to all the other changes above. Closing with
wontfix