-
Notifications
You must be signed in to change notification settings - Fork 590
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
Syntax for Wrapped/Flow Layout Support for Repeat / Facet / Concat #4457
Comments
I like a) a lot. It seems much more intuitive than b). I still struggle to understand when to use column or row and a) makes it less important. Another future extension of either syntax could be to support faceting by multiple fields at once (multiple group by's). Not saying that we should do this soon but we are not preventing this extension.
|
One thing that I don't like about a) is that the repeater syntax will use "repeat" as both key and value: x: {field: {repeat: "repeat"}, ...} because now repeat is a repeat channel name just like row/column x: {field: {repeat: "row/column"}, ...} That said, I guess this is probably fine. |
@jheer @arvind @domoritz Given that we will do a), I have a follow up question: For the shorthand, should the composition layout properties be in fieldDef or in the outer spec? a-1) in fieldDef
{
...
"encoding": {
"row/column": {
"field": "Origin",
"type": "nominal",
"spacing": 20 // this would be only either row/column spacing
"center": ... // this would be only either row/column center
"align": ... // this would be only either row/column align
}
}
} a-2) outside
{
...
"columns": 4,
"spacing": 20, // number | {row: number, column: number}
"center": ..., // boolean | {row: boolean, column: boolean}
"align": ..., // "all" | `"each" | "none"` | {row: ..., column: ...}
"mark": ...,
"encoding": {
"facet": {"field": "Origin", "type": "nominal"}
}
}
{
...
"spacing": 20, // number | {row: number, column: number}
"center": ..., // boolean | {row: boolean, column: boolean}
"align": ..., // "all" | `"each" | "none"` | {row: ..., column: ...}
"mark": ...,
"encoding": {
"row/column": {"field": "Origin", "type": "nominal"}
}
} a-1) is good that the layout properties are specific so the properties are quite "localized" to facet channel. However, the facet field def will be quite more complicated. Meanwhile, a-2) offers a more flexible setup and less complicated schema. I'm leaning towards a-2) for the sake of simplicity. Plus, even if we add a-1), a-2) would still be available, which means we offer multiple ways of doing things. |
a-2 seems cleaner to me as well since you need to add objects in the general facet case anyway and the nesting gets pretty deep. |
Cool thanks -- This issue will be finished in #4541 |
Just noting here that after a discussing with @davidanthoff we noted that these two are equivalent and so this can help resolve #393 (comment). {
"name": "trellis_barley",
"data": {"url": "data/barley.json"},
"columns": 2,
"facet": {
"field": "site",
"type": "ordinal",
"sort": {"op": "median", "field": "yield"}
},
"spec" : {
"mark": "point",
"encoding": {
"x": {
"aggregate": "median",
"field": "yield",
"type": "quantitative",
"scale": {"zero": false}
},
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
}
}
} {
"name": "trellis_barley",
"data": {"url": "data/barley.json"},
"mark": "point",
"columns": 2,
"encoding": {
"facet": {
"field": "site",
"type": "ordinal",
"sort": {"op": "median", "field": "yield"}
},
"x": {
"aggregate": "median",
"field": "yield",
"type": "quantitative",
"scale": {"zero": false}
},
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
}
} |
Somehow I am only getting to this issue now. I apologize I missed it earlier! The design arguments above strike me as rather system-developer-centric. It is my strong suspicion that users would find the localized a-1 examples easier to understand (I certainly do). In any case, I naturally assumed that facet wrap would place the parameters within the facet definition itself, not outside it. The outside placement confused me as it seems to unnecessarily disconnect logically connected things. (Or, does it make sense to define any of those properties outside specific composition operators?) From the standpoint of user-centric API design I would argue for including support for a-1. |
Unfortunately, the use of |
It's not too late to add support for a-1 and deprecate a-2 (with backward compatibility support). The |
We previously discussed this in #393, but that discussion has been too long to follow (and sidetracked by comments about a slightly different discussion).
So I'd like to start a new issue to summarize proposed solutions.
We have considered two possible syntaxes for supporting wrappable facet/repeat concat so far
A) Using unnested
facet
/repeat
/concat
as the "general"facet
/repeat
/concat
syntax that supports wrappingBasically, a general 1D facet spec could look like this:
then we can have the following syntax as our shorthand (just like
row
/column
encoding channel).Note that we discussed the possibility of a
{facet: {wrap: ...}, ...}
syntax earlier. In that case, the macro's channel name could be"wrap"
. However, @jakevdp suggests that it could be confusing, especially in Altair. I agree with him.We can do the same unnested
repeat
:This could work well in a consistent fashion with a wrapped
concat
.Note that I thought about the name for wrapped concat for a while, but
wrappedConcat
,flow
,grid
all sound worse than simply a genericconcat
, which can serve as a generic concat (vconcat whencolumns=1
andhconcat
whencolumns
=undefined
/ Infinity or wrapped concat for other numbers ofcolumns
).By using the term
concat
, we can preservegrid
for a possible macro/shorthand like this:(This
grid
thing has a lower priority though).Pro
The syntax is quite concise. It does not require an additional level of nesting when we only facet / repeat by one field (rather than two). Faceting by one field is gonna be much more common use case, so opting for concision for this use case is favorable.
We can achieve a consistent syntax for generic facet/repeat/concat (which can wrap / or not depending on the number of
columns
).Cons
B) Extend existing row/column/hconcat/vconcat syntax to support
columns
(orrows
)The other alternative is to extend existing row/column syntax:
If both facet.row and facet.column are specified, columns or rows will be ignored and there will no wrapping.
One minor note is that the latter (
rows
) requires some extension to Vega. (We could try to do the similar math in Vega-Lite, but I don't think we could as doing the same in Vega will benefit both abstraction level.)We could do the same for repeat:
and concat:
Pros
Cons
It's semantically misleading. A wrapped facet does not encode data by either row or column but rather both.
hconcat
with a number of columns is also misleading as it's no longer purely "horizontal". I think this is a critical con for this design.It leads to a complex schema when both
facet.row
andfacet.column
are specified, columns or rows will be ignored and there will no wrapping.(Minor point) Supporting
rows
requires some works. This solution requires that we supportrows
right away to make it not weird. (The first solution is fine with justcolumns
for now.)As I summarize these pros and cons for these two approaches, I think a) is a stronger solution.
cc: @domoritz @arvind @jheer @jakevdp -- Please comment if you have any thoughts.
The text was updated successfully, but these errors were encountered: