Comments (6)
Thanks for all the thoughtful ideas. This is sort of what I had in mind already and have started implementing to some extent.
One of the reasons the partial specification is supported in Gosling (and in Vega-Lite I think) is to prevent code repetition, and my guess is that they do not necessarily support the partial specification since this issue (repetition) can be easily handled by the programmatical approach.
Completely agree. I'm thinking that for now, we don't expose the PartialTrack
and instead just work with complete Track
's. This will make the JSON larger at the end, but that is OK IMO. If it becomes an issue, we can always write a script to strip out duplicated (unecessary) fields :)
Additionally, thank you for all the examples and explanation. It helps a lot for me to understand gosling's data-model much better. You have actually identified a bug with the current implementation. The class Track
should only be able to hold fields for a gosling Track
, but the properties
API is sneaky because it allows a hook to add additional fields (which) actually turn the Track into a View! For example,
track4 = Track(multivec).encode(
x=gos.Channel("position:G", axis="top"),
y="peak:Q",
row="sample:N",
color=gos.Channel("sample:N", legend=True),
tooltip=tooltip,
).properties(
alignment="overlay",
tracks=[
gos.PartialTrack().mark_line(),
gos.PartialTrack().mark_point().encode(
size=gos.Channel("peak:Q", range=[0, 2]),
)
],
)
"Works" because the constructor for Track
is called with proper arguments and passes validation. The assignment in the .properties
method adds these fields to the Track
instance, but effectively changes the type from a track to a SingleView
. The way that the validation ends up working, this is still valid in the end. But for example if you call Track
like below,
gos.Track(
multivec,
alignment="overlay",
tracks=[
gos.PartialTrack().mark_line(),
gos.PartialTrack().mark_point().encode(
size=gos.Channel("peak:Q", range=[0, 2]),
)
],
)
# gosling.schemapi.SchemaValidationError: Invalid specification
#
# gosling.api.Track->0, validating 'additionalProperties'
#
# Additional properties are not allowed ('alignment', 'tracks' were unexpected)
It results in an error. The idea with properties
is to have a nice shorthand modify the class instance. So technically,
gos.Track(data, ...)
# and
gos.Track(data).properties(...)
should be equivalent. The fact that the example still compiled with the Track(data).properties(alignemnt=..., tracks=...)
confused me about what the types in gosling actually are. But now I can see clearly what's going on.
Thanks for all your help. I'm goign to work on the API today and will ping you when I have something cleaned up :)
from gos.
The challenge here is that gosling.Chart
inherits from gosling.schema.Root
(and additional "mixin" classes that add short-hands for setting properties / displaying the Root in a notebook). The Root
type is different from both SingleView
and MultipleViews
(adds title
, subtitle
, description
), so I think it's important to establish a "root" type unambiguously.
In general, I'm happy to reconsider the naming, but I do think this "root" type isn't exactly a "View". Similar to gos.Track
we could extend the SingleView
and MultipleView
methods with a .chart
mixin:
import gosling as gos
gos.MultipleViews(...).chart(title='Overview and Detail Views')
gos.SingleView(...).chart()
I see that View
is defined in https://github.com/gosling-lang/gosling.js/blob/eafc64c22000c2c140fd003aa5980acbb39d7db8/src/core/gosling.schema.ts#L19, but since the type isn't referenced anywhere as a dependency in GoslingSpec
, the definition isn't added to generated schema.json
. If it were, we could use that type to generate:
gos.View(arrangement='horizontal', views=[overview, detail]).chart()
from gos.
I didn't realize that the View
is not directly included in the GoslingSpec
. I think I can include View
somewhere, e.g., at the root level of GoslingSpec
, so that it can be included in the python package as well. The two terms "tracks" and "views" are taken from the taxonomy of genomics visualization, so we probably would need to keep that as is in the package.
My one concern was that the name of Chart()
could be confusing to users given that we already have two terms, i.e., "track" and "view", that denote visualizations or sets of visualizations. If we want to keep the "root" type unambiguously, I think we could use another name (e.g., "Root" or something else).
Similar to #12, my initial thought was to somehow combine the View
and "root" types and expose only View
to users (e.g., when a user defines "title" and/or "subtitle" at the root level, then the package considers this as the "root" type). But, I agree we may want to distinguish the two, and not sure what would be better at this point.
I think one main difference between gosling.js (JSON-based) and the Python package (programmatical way) from the users' perspective is that in the python package users might need to care about the types (e.g., Track, PatialTrack, MultipleViews, SingleView), which can be an extra burden. But, in JSON specs, the compiler determines the types for users. So, in general, I thought it might be better if we expose a small set of types/methods to users by merging very similar types/methods so that users do not necessarily have to memorize a larger number of types/methods.
from gos.
If we want to keep the "root" type unambiguously, I think we could use another name (e.g., "Root" or something else).
I see your point, and mostly agree. Perhaps Root
is likely a good option. I liked Chart
because then there was a nice link between gos.Chart
and the Track(...).chart()
methods. To me, I thought it was more intuitive than gos.Track().root()
for example.
I think one main difference between gosling.js (JSON-based) and the Python package (programmatical way) from the users' perspective is that in the python package users might need to care about the types (e.g., Track, PatialTrack, MultipleViews, SingleView), which can be an extra burden.
The generated python code performs validation, which is why it is difficult to group these classes in the way you suggest. Every generated class derives from SchemaBase
and has many utility methods (e.g. to_json
, to_dict
, __setattr__
) as well as a strict constructor. If you try to pass data as arguments that are invalid, the code will raise an Exception.
import gosling as gos
gos.Chart(tracks=[gos.PartialTrack()]) # raises an exception because a Track is required
gos.Chart(tracks=[gos.Track()]) # raises an excpetion because no data/width/height are passed to constructor
The generated classes unambiguously define types, meaning you can't pass and object that is maybe a Track
or a PartialTrack
into a constructor that requires a Track
. Similarly, you cannot pass title
or subtitle
on a SingleView
because these fields are not defined on a single view.
from gos.
Thank you for the clarification! It helped me to understand better the technical aspect.
Since overlay
ing tracks in Gosling is very similar with layer
ing in Vega-Lite, I wondered how they support layer
ing in the Altair.
Overlaying tracks in Gosling:
{
alignment: 'overlay',
data: {...}, // common properties of Track A-B that should be inherited are defined here
tracks: [
{ /* Track A */ }, // partial spec
{ /* Track B */ } // partial spec
]
}
Overlaying charts in Vega-Lite (link):
{
data: {...}, // common properties of Chart A-B that should be inherited are defined here
layer: [
{ /* Chart A */ }, // partial spec
{ /* Chart B */ } // partial spec
]
}
Overlaying charts in Altair:
chartA = alt.Chart(df).encode(...) # Complete spec that requires data, mark, etc
chartB = alt.Chart(df).encode(...) # Complete spec that requires data, mark, etc
alt.layer(chartA, chartB) # or `chartA + chartB`
It looks like Altair does not seem to expose Vega-Lite's partial specification (at least in the docs). One of the reasons the partial specification is supported in Gosling (and in Vega-Lite I think) is to prevent code repetition, and my guess is that they do not necessarily support the partial specification since this issue (repetition) can be easily handled by the programmatical approach. I guess we could consider letting users use Track()
consistently if we want to keep fewer types/methods:
overlay_base = gos.Track(data=mv)
bar_base.properties(
...,
alignment='overlay',
tracks=[
overlay_base.mark_bar().encode(...), # overlaying `Track` is already possible in the package
overlay_base.mark_brush().encode(...),
overlay_base.mark_brush().encode(...)
]
)
Similarly, in theory, we could let users use Chart()
for all variants of views to users (Root, MultipleViews, SingleView), and it will be the users' responsibility to define title
and subtitle
only in the root level. But, in this way, we can let users remember/use a fewer number of types. (I used this approach in my notebook).
So, the questions, I think, are which types we want to advertise users to use (in the docs and example codes) and what parts we will need to update in the Gosling schema for this if any.
from gos.
Another thing I realized is that Altair does not seem to expose types at all (or may a few) that are beyond a chart level. For example, instead of alt.HConcatSpec()
, Altair let users use |
or alt.hconcat()
to generate { hconcat: [{...}, {...}] }
specs. As I personally find them really useful, I wonder if we want to (or you are planning to) support such operations and probably deprecate the use of tracks=[...]
:
a = overlay_base.mark_bar().encode(...)
b = overlay_base.mark_brush().encode(...)
c = overlay_base.mark_brush().encode(...)
d = (a + b + c) # SingleTrack
If we want to support both the track and view composition, we could do something like the following?:
v1 = gos.overlay(a, b) # { alignemet: "overlay", tracks: [ /* a */, /* b */ ] }
v2 = gos.stack(a, b) # { alignemet: "stack", tracks: [ /* a */, /* b */ ] }
gos.horizontal(v1, v2) # { arrangement: "horizontal", views: [/* v1 */, /* v2 */] }
gos.vertical(v1, v2) # { arrangement: "vertical", views: [/* v1 */, /* v2 */] }
gos.parallel(v1, v2) # { arrangement: "parallel", views: [/* v1 */, /* v2 */] }
gos.serial(v1, v2) # { arrangement: "serial", views: [/* v1 */, /* v2 */] }
# specify additional properties
gos.stack(a, b, spacing=30, layout='linear', xDomain={...})
...
(Sorry, comments became too long than I expected 😅 )
from gos.
Related Issues (20)
- Non-human genomic data HOT 3
- User guide on exporting plots HOT 3
- Examples of pile displacement HOT 2
- visibility_lt (or other visibility functions) will not accept "zoomLevel" as a "measure" argument HOT 2
- How to provide constant value for y? HOT 4
- Plotting Single Co-ordinate BED file on custom assembly HOT 3
- Using local data for heatmap HOT 7
- Loading data from URL isn't always a success HOT 3
- Blank canvas plotting local BED file HOT 4
- How can you check which version of gos is compatible with given version of gosling? HOT 4
- Scroll to zoom broken in VSCode
- Why is built gos using python rather than js? HOT 1
- feat: Widget that exposes gosling.js API HOT 1
- Enable themes configuration
- How to visualize my own data? HOT 7
- potential CORs issue with local server? HOT 4
- race conditions with HTML renderer
- feat: (responsive) defaults (e.g. width, height) when not explicitly provided
- Extract data-server for gos/hg
- Support for Ibisdata HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gos.