') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Use plotly.js `base64` API to store and pass typed arrays declared by numpy, pandas, etc. by archmoj · Pull Request #4470 · plotly/plotly.py · GitHub
Skip to content

Use plotly.js base64 API to store and pass typed arrays declared by numpy, pandas, etc. - #4470

Merged
marthacryan merged 128 commits into
masterfrom
pass-b64
Oct 21, 2024
Merged

Use plotly.js base64 API to store and pass typed arrays declared by numpy, pandas, etc. #4470
marthacryan merged 128 commits into
masterfrom
pass-b64

Conversation

@archmoj

@archmojarchmoj commented Dec 21, 2023

Copy link
Copy Markdown
Contributor

This PR make use of this plotly.js PR and changes the default behavior to pass numpy arrays as plotly.js base64 spec.

Code Changes

  • Adds to_typed_array_spec function to use for converting numpy arrays to the "typed array spec" type that plotly.js expects when receiving base64 encoded arrays.
  • Updates the validate_coerce function of the DataArrayValidator class to base64 encode numpy arrays and store them in the "typed array spec" format (by calling to_typed_array_spec)
  • Adds thorough testing to validators
  • Adds performance (size and speed) tests

Comment threadpackages/python/plotly/_plotly_utils/basevalidators.py Outdated
Co-authored-by: Liam Connors <liam@plot.ly>
Comment threaddoc/python/b64.md Outdated
Comment threaddoc/python/b64.md Outdated
Comment threaddoc/python/b64.md Outdated
Comment threaddoc/python/b64.md Outdated
archmojand others added 3 commits February 29, 2024 11:10
 - fixed Conflicts in
packages/python/plotly/plotly/tests/test_optional/test_figure_factory/test_figure_factory.py
packages/python/plotly/plotly/tests/test_optional/test_px/test_px_input.py
- Removes a speed comparison that is no longer needed because the base64 encoding makes it faster
- Updates a test to use numpy arrays to account for plotly express automatically converting numerical lists to pandas arrays.
- Remove unnecessary enumerate
with self.assertRaises(TypeError):
_json.dumps({"a": {1}}, cls=utils.PlotlyJSONEncoder)

def test_fast_track_finite_arrays(self):

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marthacryan
Could we revert this change now or we should drop the test?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this because it was failing. Presumably it's just faster now?

@ndreznndrezn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Psyched for this to land! Killer work team.
ezgif-4-d5a21d0496

Comment threadtest/percy/compare-pandas.py Outdated
if any(l1 != l2 for l1, l2 in zip(fig1, fig2)):
print("".join(difflib.unified_diff(fig1, fig2)))
raise ValueError(f"Pandas 1/2 difference in {filename}")
if filename not in [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marthacryan / @archmoj What is the reason for excluding these particular files?

are skipped for conversion to the typed array spec
"""
skipped_keys = ["geojson", "layer", "range"]
return any(skipped_key in key for skipped_key in skipped_keys)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marthacryan Shouldn't the test be skipped_key == key rather than skipped_key in key ?

Or we really do want to be checking that the skipped key is a substring of key?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I think the docstring comment is no longer accurate

v = copy_to_readonly_numpy_array(v)

np = get_module("numpy", should_load=False)
if not isinstance(v, np.ndarray):

@emilyklemilyklOct 17, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this line raise an error if numpy is not installed? (Since get_module will return None in that case.)

Is that ok?

@alippai

Copy link
Copy Markdown

Should this work for SVG render mode too? (render_mode="svg" in px.line)

@gvwilson

Copy link
Copy Markdown
Contributor

cc @marthacryan@emilykl for an answer

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

featuresomething newP1needed for current cycle

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@archmoj@marthacryan@gvwilson@alippai@alexcjohnson@emilykl@LiamConnors@ndrezn