juba / pyobsplot Goto Github PK
View Code? Open in Web Editor NEWObservable Plot in Jupyter notebooks and Quarto documents
Home Page: https://juba.github.io/pyobsplot/
License: MIT License
Observable Plot in Jupyter notebooks and Quarto documents
Home Page: https://juba.github.io/pyobsplot/
License: MIT License
For the moment "static" plots generated as HTML or SVG by the jsdom
renderer cannot have pointer interactions.
Hi,
In line with having a syntax as close as possible to javascript it would nice to be able to use the shorthand syntax as seen here.
import pandas as pd
from pyobsplot import Plot
df = pd.DataFrame({'Date': {0: '1880-01-01', 1: '1880-02-01'}, 'Anomaly': {0: -0.3, 1: -0.21}})
Plot.lineY(df, {"x": "Date", "y": "Anomaly"}).plot()
AttributeError Traceback (most recent call last)
Cell In[15], line 5
2 from pyobsplot import Plot
4 df = pd.DataFrame({'Date': {0: '1880-01-01', 1: '1880-02-01'}, 'Anomaly': {0: -0.3, 1: -0.21}})
----> 5 Plot.lineY(df, {"x": "Date", "y": "Anomaly"}).plot()
AttributeError: 'dict' object has no attribute 'plot'
Not sure how feasible this is and not a dealbreaker for me but thought it was worth noting in case it wasn't on the radar.
Many thanks,
Harry
datetime64[ns] objects aren't converted to javascript date objects. Presume this is intended as per the section on datetimes. Would be great if it were possible!
import pandas as pd
from pyobsplot import Plot
df = pd.DataFrame({'full_date_time': {0: pd.Timestamp('2022-12-01 00:00:00'),
1: pd.Timestamp('2022-12-01 01:00:00')},
'value': {0: 20.0, 1: 18.0}})
Plot.plot({"marks":[Plot.dot(df, {"x":"full_date_time", "y": "value"})]})
It would be great to be able to be able to export a png and have it include the title, subtitle and figcaption as is the case when using the three dots to export a png here.
op = Obsplot(renderer="jsdom")
op(
{
"marks": [
Plot.tickX([0, 5, 10, 15])
],
"title" : "Title test",
},
path="test.png"
)
Split out from this #21
As plots with legends generate HTML output instead of SVG, they are not included in Quarto outputs for formats other than HTML.
A comma is missing in below lines of code after color
object.
Plot.plot(
{
color: {legend: true} #comma missing here
grid: false,
marks: [Plot.dot(data, {x: "x", y: "y", fill: "type", r: 5})]
}
)
op({
"color": {"legend": True} #missig comma here as well
"grid": False,
"marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
})
Look what is the cause of the FATAL: Exception not rethrown
messages in pytest
.
Hi, I have a few questions related to ObservableHQ and how you've built pyobsplot:
I'm curious if I could reproduce something similar to this widget using notebooks I've made on ObservableHQ.
As for the general question, it seems a bit problematic that there is no one-to-one correspondence between
format
andpath
-extension. What I mean is that:
- there is no
format="pdf"
(I assume because displaying PDFs in something like Jupyter notebooks seems problematic)- there are two formats for extension ".html", namely "widget", and "html".
Without knowing the internals it seems weird that
Plot.plot({}, format="png", path="out.svg") => ok Plot.plot({}, format="widget", path="out.svg") => error.
I first thought that it would be best to completely decouple
format
andpath
:format
dictates the display format,path
dictates the export format. But this does not account for thehtml
case...
Originally posted by @wirhabenzeit in #32 (comment)
VSCode doesn't save widget output state when the file is closed, so all cells have to be recomputed to see the output.
See if it would be possible to use the typescript type declarations introduced in Plot 0.6.5 to improve autocompletion and IDE support.
Hello,
I am trying to replicate the visualization here, where a table is displayed right next to a tree.
https://observablehq.com/@d3/indented-tree
Is this out of scope for your library? The key code from that script is:
for (const {label, value, format, x} of columns) {
svg.append("text")
.attr("dy", "0.32em")
.attr("y", -nodeSize)
.attr("x", x)
.attr("text-anchor", "end")
.attr("font-weight", "bold")
.text(label);
node.append("text")
.attr("dy", "0.32em")
.attr("x", x)
.attr("text-anchor", "end")
.attr("fill", d => d.children ? null : "#555")
.data(root.copy().sum(value).descendants())
.text(d => format(d.value, d));
}
I've tried replicating this with Plot.text
but it appears combining these marks disrupts how inset
handles the Plot.tree
. Here is the code for the tree alone.
indent = js("""
() => {
return (root) => {
root.eachBefore((node, i) => {
node.x = i;
node.y = node.depth;
});
};
}
""")
Plot.plot({
'axis': None,
'inset': 10,
'insetRight': 800,
'round': True,
'width': 1000,
'height': 6000,
'marks': [
Plot.tree(path_utility, {
'path': 'Path',
'strokeWidth': 1,
'curve': "step-before",
'treeLayout': indent,
'treeSort': "node:height",
'delimiter': '^',
}),
]
})
I don't actually need all the dynamic logic for calculating sizes. I can precompute all the numbers. Just want to align the table with the tree.
Thanks again!
Update Getting started and Usage pages.
When converting topojson data to geojson in Python there seems to be a global "frame" object that hides everything when filled.
Hi
I am trying to use the jsdom renderer but I get the following error (latest pyobsplot==0.4.2):
ValueError: Server not started: npm ERR! cb.apply is not a function
npm ERR! A complete log of this run can be found in:
npm ERR! [/home/jarvis/.npm/_logs/2024-05-03T08_53_13_050Z-debug.log](https://jupyter.imaging.ms.uhbs.ch/home/jarvis/.npm/_logs/2024-05-03T08_53_13_050Z-debug.log)
Install for [ '[email protected]' ] failed with code 1
0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli '/home/jarvis/.nvm/versions/node/v18.17.1/bin/node',
1 verbose cli '/home/jarvis/.nvm/versions/node/v18.17.1/lib/node_modules/npx/node_modules/npm/bin/npm-cli.js',
1 verbose cli 'install',
1 verbose cli '[email protected]',
1 verbose cli '--global',
1 verbose cli '--prefix',
1 verbose cli '/home/jarvis/.npm/_npx/3055885',
1 verbose cli '--loglevel',
1 verbose cli 'error',
1 verbose cli '--json'
1 verbose cli ]
2 info using [email protected]
3 info using [email protected]
4 verbose npm-session 70f0ac5533eebc63
5 silly install loadCurrentTree
6 silly install readGlobalPackageData
7 http fetch GET 304 https://registry.npmjs.org/pyobsplot 884ms (from cache)
8 http fetch GET 200 https://registry.npmjs.org/pyobsplot/-/pyobsplot-0.4.2.tgz 7ms (from cache)
9 silly pacote version manifest for [email protected] fetched in 930ms
10 verbose stack TypeError: cb.apply is not a function
10 verbose stack at /home/jarvis/.nvm/versions/node/v18.17.1/lib/node_modules/npx/node_modules/npm/node_modules/graceful-fs/polyfills.js:287:18
10 verbose stack at FSReqCallback.oncomplete (node:fs:211:5)
11 verbose cwd /var/www/jarvis/Auswertung/ge-migration
12 verbose Linux 5.10.0-25-amd64
13 verbose argv "/home/jarvis/.nvm/versions/node/v18.17.1/bin/node" "/home/jarvis/.nvm/versions/node/v18.17.1/lib/node_modules/npx/node_modules/npm/bin/npm-cli.js" "install" "[email protected]" "--
global" "--prefix" "/home/jarvis/.npm/_npx/3055885" "--loglevel" "error" "--json"
14 verbose node v18.17.1
15 verbose npm v5.1.0
16 error cb.apply is not a function
17 verbose exit [ 1, true ]
Thanks for any help!
Hi,
Running into the error 'UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 780112: character maps to ' when using the jupyter widget renderer. It doesn't happen when using the jsdom renderer.
from pyobsplot import Plot
import polars as pl
data = pl.DataFrame(
{
"x": [1, 5, 2, 4, 6, 2, 4],
"y": [2, 1, 3, 4, 5, 1, 2],
"type": ["T1", "T2", "T1", "T2", "T1", "T1", "T2"],
}
)
Plot.plot(
{
"grid": True,
"marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
}
)
Here is the error in full.
UnicodeDecodeError Traceback (most recent call last)
Cell In[9], line 12
2 import polars as pl
4 data = pl.DataFrame(
5 {
6 "x": [1, 5, 2, 4, 6, 2, 4],
(...)
9 }
10 )
---> 12 Plot.plot(
13 {
14 "grid": True,
15 "marks": [Plot.dot(data, {"x": "x", "y": "y", "fill": "type", "r": 5})],
16 }
17 )
File ~\AppData\Local\pypoetry\Cache\virtualenvs\pyobsplot-test-JUPV2V8N-py3.11\Lib\site-packages\pyobsplot\jsmodules.py:25, in Plot.plot(*args, **kwargs)
23 if _plot_renderer == "jsdom":
24 op = ObsplotJsdomCreator()
---> 25 return op(*args, **kwargs)
File ~\AppData\Local\pypoetry\Cache\virtualenvs\pyobsplot-test-JUPV2V8N-py3.11\Lib\site-packages\pyobsplot\obsplot.py:141, in ObsplotWidgetCreator.__call__(self, *args, **kwargs)
137 """
138 Method called when an instance is called.
139 """
140 spec = self.get_spec(*args, **kwargs)
--> 141 return ObsplotWidget(
142 spec, theme=self._theme, default=self._default, debug=self._debug
143 )
File ~\AppData\Local\pypoetry\Cache\virtualenvs\pyobsplot-test-JUPV2V8N-py3.11\Lib\site-packages\pyobsplot\widget.py:44, in ObsplotWidget.__init__(self, spec, theme, default, debug)
42 self._default = default
43 # Init widget
---> 44 super().__init__(spec=spec, theme=theme)
File ~\AppData\Local\pypoetry\Cache\virtualenvs\pyobsplot-test-JUPV2V8N-py3.11\Lib\site-packages\anywidget\widget.py:42, in AnyWidget.__init__(self, *args, **kwargs)
40 if hasattr(self, key) and not self.has_trait(key):
41 value = getattr(self, key)
---> 42 anywidget_traits[key] = t.Unicode(str(value)).tag(sync=True)
44 if isinstance(value, FileContents):
45 value.changed.connect(
46 lambda new_contents, key=key: setattr(self, key, new_contents)
47 )
File ~\AppData\Local\pypoetry\Cache\virtualenvs\pyobsplot-test-JUPV2V8N-py3.11\Lib\site-packages\anywidget\_file_contents.py:92, in FileContents.__str__(self)
90 def __str__(self) -> str:
91 if self._contents is None:
---> 92 self._contents = self._path.read_text()
93 return self._contents
File ~\.pyenv\pyenv-win\versions\3.11.1\Lib\pathlib.py:1059, in Path.read_text(self, encoding, errors)
1057 encoding = io.text_encoding(encoding)
1058 with self.open(mode='r', encoding=encoding, errors=errors) as f:
-> 1059 return f.read()
File ~\.pyenv\pyenv-win\versions\3.11.1\Lib\encodings\cp1252.py:23, in IncrementalDecoder.decode(self, input, final)
22 def decode(self, input, final=False):
---> 23 return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 780112: character maps to <undefined>
Let me know if there are any other details I can provide!
Many thanks,
Harry
Same data sources are seen as distinct, generating warning and disabling faceting by default.
Example:
import polars as pl
from pyobsplot import Obsplot, Plot
penguins = pl.read_csv("data/penguins.csv")
Obsplot({
"facet": {"data": penguins, "x": "island"},
"marks": [
Plot.dot(penguins, {"x": "flipper_length_mm", "y": "body_mass_g", "fill": "species"}),
]
})
Add reference pages to documentation with quartodoc.
Hello!
This is an amazing library.
I am interested in making a custom tree layout as shown here for an Indented Tree Plot. As described here: https://observablehq.com/@observablehq/plot-custom-tree-layout
indent = js("""
function indent() {
return (root) => {
root.eachBefore((node, i) => {
node.y = node.depth;
node.x = i;
});
};
}
""")
Plot.plot({
'axis': None,
'inset': 10,
'insetRight': 120,
'round': True,
'width': 2000,
'height': 3600,
'margin': 200,
'marks': Plot.tree([all_paths, {
'strokeWidth': 1,
'curve': "step-before",
'treeLayout': indent,
})
})
However as far as I can tell, this is just ignoring the treeLayout provided. Am I missing something? Thank you!
Hi,
Really excited to see progress being made on making observable plot available in python!
What is the feasibility of integrating Observable Inputs with this to make the following example possible?
https://observablehq.com/@observablehq/plot-density-weighted?intent=fork
Many thanks,
Harry
With an example notebook.
Add !pip install pyobsplot
as first cell. See if it is possible to include jsdom
renderer by installing nodejs.
The following produces an empty chart.
import polars as pl
from pyobsplot import Plot
penguins = pl.read_csv("https://github.com/juba/pyobsplot/raw/main/doc/data/penguins.csv")
Plot.plot({
"marks": [
Plot.rectY(penguins, Plot.groupX({"y": "count"}, {"x": "species"})),
]
})
The following produces the chart as expected.
import polars as pl
from pyobsplot import Plot
penguins = pl.read_csv("https://github.com/juba/pyobsplot/raw/main/doc/data/penguins.csv")
Plot.plot({
"marks": [
Plot.barY(penguins, Plot.groupX({"y": "count"}, {"x": "species"})),
]
})
Using rectY works via the observable website.
https://observablehq.com/d/1536c13b55654b3e
Apologies if I'm missing something obvious!
When rendering plots with jsdom
, the generated SVG or HTML as no padding at all. It could be nice to add some in VSCode or Jupyter lab, but maybe not in Quarto.
How would I export a plot to html/png/svg?
Ideally I would like to be able to do something similar to Plotly i.e. fig.write_html("path/to/file.html")
/ fig.write_image("images/fig1.png")
/ fig.write_image("images/fig1.svg")
Add a setting to allow to generate plots in dark mode.
See if IPC serialization could be replaced with parquet, using hyparquet.
The kwargs
alternative syntax allows to define a plot with keyword arguments instead of a dict:
op(
grid = True,
color = {"legend": True},
marks = [
Plot.dot(data, {"x": "x", "y": "y", "fill": "type"})
],
)
I'm not sure this syntax is very useful, and it tends to make some of the code less readable. Maybe it could be removed ?
Add NEWS.md and integrate with doc/
.
The "model not found" error could be related to:
JupyterLab doesn't save widget output state when the file is closed, so all cells have to be recomputed to see the output.
When using the jsdom
renderer, output is saved between sessions.
As with #2 this is probably an upstream issue!
This appears to be the relevant issue jupyter-widgets/ipywidgets#3823 and it looks like people are making progress to fix it.
I tried downgrading jupyterlab and ipywidgets as suggested here without any luck. jupyterlab/jupyterlab#15361 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.