Skip to content
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

Facet plot #1068

Open
rolyp opened this issue Aug 24, 2024 · 7 comments
Open

Facet plot #1068

rolyp opened this issue Aug 24, 2024 · 7 comments

Comments

@rolyp
Copy link
Collaborator

rolyp commented Aug 24, 2024

A kind of composite plot which consists of a list of parameter values and for each value of the parameter, another kind of visualisation instantiated with that parameter value. For example, given a list of sources of methane emissions [Agriculture, Energy Sector, Forest Burning], a line chart plotting historical and projected methane emissions. The facet would support tabular layout of the individual subplots, but also a carousel-like interface which allows tabbing between a “current” subplot.

See also:

@JosephBond
Copy link
Collaborator

JosephBond commented Aug 27, 2024

Current proposal is to construct this functionality in two main stages:

Step 1

  • A parameter grid field for FigSpec's
    • Add field grid :: Array (Bind (Array Val)) or more likely grid :: Array (Bind (Array (Expr a))), to identify the sets of variables we wish to range over when constructing a faceted visualization
    • Modify loadFig to evaluate the combinations of parameter values all at once
      • Requires consideration as to how we send values to the fluid file: my gut feeling is we just use variable names in the code, and then evaluate the various parameter combinations, sending them into the environment in which the .fld file is evaluated. This will eventually have to be revisited (see below and TODO: issue for backquote and synthesizing code fragments)
    • TabularView interface, which takes Array View or Array2 View (maybe just Array View, defer the number of dimensions til later, it's entirely possible we'd need to generalize it to higher dimensions if you're faceting over 3 or more variables).
      • TabularView start off in 2-d grid form
      • Construct selectors for TabularView's, most likely just listElement composed with the appropriate selector for a given member of a TabularView
    • Interface for displaying only one plot at a time
      • Could be as simple as a div which has tabs at the top, with each tab comprising of a combination of parameter values
      • Alternatively, each parameter value creates one "level" of the tabulation, so you'd select a tab for the value of the first parameter, then inside the pane that creates, show a table of the remaining options or tabs for the remaining options, and so on, to allow the user/reader to sequentially narrow down the plots they are seeing

@rolyp
Copy link
Collaborator Author

rolyp commented Aug 27, 2024

@JosephBond Wondering if we need to modify loadfig, or whether we can think of this as an internal detail of a new kind of View (e.g. FacetView). Maybe you could construct a FacetView by supplying:

  • a grid as a Fluid value of type List (or perhaps Dict, if we want keys)
  • a Fluid function taking a value in that list to a value of type View

@JosephBond
Copy link
Collaborator

Step 2

  • For efficiency, only evaluate the plots that are required based on the parameter values the user has selected: the grid will become unwieldy for more than 2-3 parameterized values (see: combinatorial explosion)
    • We need to re-examine the pipeline of loading and evaluating fluid expressions, and how that interacts with user-interfaces. We want to be able to only evaluate versions of the program that the user is going to interact with.
      • For discrete value choices it's not too hard, especially if they're hard-coded, but could be more complicated if we allow for more arbitrary interactions, for example Achintya mentioned the idea of a dumbbell selector like you often get when online shopping, contracting the ends of a given range to adjust search results. In my mind, the ability to dynamically evaluate a restricted form of a fluid program would allow us to implement this sort of thing for, for example, faceting a time-series over various ranges of time interactively.
    • Start by setting a combination of parameter values that are defaults, and then re-evaluate the program everytime the user interacts with a Facet menu.

Note: Re-evaluating fluid programs interactively dove-tails with the planned next steps of #1040, since we need to examine how we do this. In my mind we will eventually want the backquote mechanism (and future synthesis of explanatory expressions) to be operate similarly to Faceting, (perhaps an interface like Github issues, with a preview tab showing the view you get from running the code.

@JosephBond
Copy link
Collaborator

@rolyp I think a dict makes more sense, with each faceted parameter being a key in the dict, then instead of setting variable names we could use the syntax for accessing dicts. Unfortunately, we have no special surface syntax for working with dicts.

For a contrived example, faceting a line-chart for a time series by a range of years, where get_points is an accessor function that behaves as you'd expect, and the dict grid binds the key "range" to a list of possible ranges, we'd need something like:
LinePlot { name: "Example", data: get_points (nth choice (dict_get "range" grid)) }

In other words, it works but we'd probably want to add syntax for it at a later date. I'm still uncertain how we would then change the value of the variable choice to match the user picking a different entry in the set of allowed ranges. Maybe I'm overcomplicating it, but it feels like even then we will need to spend time with the data-bindings, and use them to modify the values being returned to fluid, as well as the selections. Maybe it's not that difficult/I'm overcomplicating things though

@rolyp
Copy link
Collaborator Author

rolyp commented Aug 27, 2024

Spitballing here (erring on the side of over-simplification rather than over-complication!), but perhaps FacetView can be a list of {index, view} records, where index has some primitive type, and view is an arbitrary view that may depend on index. The renderer for FacetView would provide custom UI for switching between different {index, view} pairs, or perhaps arranging the various facets all in a column or table layout. The Fluid code for your example would be something like:

let series = [ … ]; // list of records with `year` field
    years = [2015..2019] in
FacetView {
   caption: “Example facet view”,
   facets: [{ 
      index: year, 
      view: LinePlot { 
         name: “Plot for “ ++ numToStr year, 
         data: [row | row <- series, row.year = year] 
      }
   } | year <- years]
}

Here the “grid” isn’t explicit but rather just implicit in the various values of index.

The naive repeated filtering here would be expensive, so some kind groupBy or pivot library function would help express this in an efficient way.

See also:

@rolyp
Copy link
Collaborator Author

rolyp commented Aug 28, 2024

Thinking of FacetView as simply another kind of View would allow us to e.g. reimplement LineChart as a FacetView where each facet is a LinePlot, with presentation options like:

  • a carousel-like interface for switching between a single LinePlot rendered on the axes of the enclosing LineChart
  • a view of any subset of the LinePlots all superimposed on the axes provided by the enclosing LineChart
  • a view for each LinePlot rendered into its own copy of the LineChart, like a traditional faceting

I think the question about interaction and what it might suggest in terms of more granular or demand-driven execution models is probably a distraction at this stage. Call-by-value programs in general do more work than strictly necessary, and this would just be another example of that – not fundamentally different from computing a list of 1,000 elements but only returning the first 10 elements to the user, or (as a more interactive example closer to the intuition you have in mind) presenting a view of the 1,000 records where only a window of 10 records is visible at any point in time (perhaps through a scrollable viewport).

@rolyp rolyp moved this from Proposed to Planned in Fluid Nov 7, 2024
@rolyp
Copy link
Collaborator Author

rolyp commented Nov 7, 2024

@JosephBond Bumping this to Planned too as I think it is pretty close to what we want for an initial multiverse implementation, with nested facet plots allowing for a branching multiverse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Planned
Development

No branches or pull requests

2 participants