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

Adds Figure.setLayout and Figure.setConfig #690

Merged
merged 9 commits into from
Feb 23, 2020

Conversation

emilianbold
Copy link
Contributor

Thanks for contributing.

Description

The jsplot APIs don't allow a whole lot of UI flexibility. Allowing callers to send a Layout instance would help a lot with UI customizations. The most basic customization being changing the chart width/height.

This PR adds some simple methods to accomplish this for VerticalBarPlot and HorizontalBarPlot.

I am open to suggestions on this.

I see that the api package is supposed to create an API boundary so I'm not certain making Layout public API is the best solution. It doesn't seem such a bad solution though since the APIs already expose tech.tablesaw.plotly.components.Figure and Layout.BarMode so the components package isn't entirely hidden.

One idea would be to create a separate tech.tablesaw.plotly.api.LayoutOptions of sorts but it would more or less be a copy paste of what components.Layout has and in the end we would just add all the options into the main Layout instance.

If this idea is good (or we settle on something else) I can go through the other plot classes (api.LinePlot, etc.) and add methods with Layout.

Testing

Manually tested.

@lwhite1
Copy link
Collaborator

lwhite1 commented Oct 23, 2019 via email

@emilianbold
Copy link
Contributor Author

So far the API seems sufficient to nicely display data. But the 1st change is to make it look good or fit somewhere which means Layout tweaks and the API doesn't allow that.

I don't need (yet?) to change how any of the Traces are being built. I'm happy with that part being hidden from me.

Note that all the tech.tablesaw.plotly.components classes (such as Layout, Axis, etc.) have builders and seem quite fit to be a public API. Maybe we could just rename the package to tech.tablesaw.plotly.api.components

If we add Layout arguments to the API then you don't really need a builder to set a few chart inputs like table, xCol, yCol.

I don't understand how the Plotly python enhancements would help. Do you mean I should copy the API style? If I look at a bar plot:

px.bar(tips, x="sex", y="total_bill", color='smoker', barmode='group', height=400)

they seem to use Python named arguments. I guess we could add another method to HorizontalBarPlot with an int height argument but it would pollute the class much more than exposing Layout. As for each Layout tweak I would need to add more methods. Named arguments are quite neat in Python...

@emilianbold
Copy link
Contributor Author

I noticed that although not part of the api package users are encouraged to use the Layout, etc. classes when needed in this user guide page: https://jtablesaw.github.io/tablesaw/userguide/Visualization_custom

So, I think allowing Layout as an argument to existing plot API classes would help users (like me) that want some basic customization and are fine with how the chart is built otherwise (no need to re-do the Trace instances, etc)

@lwhite1
Copy link
Collaborator

lwhite1 commented Oct 27, 2019

I will try to cover some of the design considerations here and then look at the code specifics.

The jsplot APIs don't allow a whole lot of UI flexibility. Allowing callers to send a Layout instance would help a lot with UI customizations.

Yeah, part of the problem (as you suggested elsewhere) is that api is not really a good description of the intended functionality here. It should probably have been eda (exploratory data analysis) or something like that. The goal was to make it easy to do one-liners for interactive analysis. The theory was that you wouldn't worry too much about height and width, etc.

The most basic customization being changing the chart width/height.

Absolutely. My experience as a Tablesaw user, even doing interactive analysis, is exactly that.

OTOH, when I recently worked on a project where I made hundreds of plots, I ended up writing code to wrap the builders, providing certain defaults, and making it easier to construct more complex graphs.

For example: I frequently wanted to make scatter plots of more than two variables. With Tablesaw, you can do up to 5 numeric variables and two categorical variables: (a 3D bubble chart (x, y, z, plus bubble size, with color spectrum for the fifth numeric variable, plus shape and 'conditioning' (i.e., making separate plots for each item in a category) for two categorical variables). Or you could do 4 numeric and three categorical, or whatever.

A problem with the current implementation is that doing any of this involves heavy use of the builders. While they're easy, they're not concise. This is what led me to believe that a different API was desirable. It should be possible to change the dimensions and create a 2D scatter with color or shape categories without digging into the marker builder. And it should be easy to specify "use color for the category" in one plot and "use shape for the category" in another.

If we add Layout arguments to the API then you don't really need a builder to set a few chart inputs like table, xCol, yCol.

That's true, but you will double the size of the API by doing that (or break the existing api) because you can't add an optional argument in java. The API is already pretty large and not very elegant (in the sense of providing a lot of power through a few, uniformly applied constructs).

[Sorry. Have to break this off at this point as i need to be driving. Will come back to it as time allows]

@emilianbold
Copy link
Contributor Author

This is what led me to believe that a different API was desirable. It should be possible to change the dimensions and create a 2D scatter with color or shape categories without digging into the marker builder. And it should be easy to specify "use color for the category" in one plot and "use shape for the category" in another.

You thought about this much more than me, clearly.

What you are saying makes sense: the plotly configuration is too low level, even if we wrap it in nice fluent builders. We could offer another high-level API where users can customize their charts without getting into the plotly implementation details.

I don't have a feeling about how that could look though. If you give me some pointers I can think about it and get back to you. Seems like it might depend on chart type, as in, determine some usual chart "templates"/use-cases which we could support.

Speaking of APIs, what you want would be a 3rd API package since we can't remove the existing api package nor the builders.

As for the api package methods, adding Layout (and Config!) does double the methods but each *Plot class has 1-3 methods so getting to 2-6 methods doesn't seem to make such a mess.

In a way what I'm trying to avoid with the Layout parameter is similar to your idea: I don't want to deal with the low level Trace, Marker and TableSliceGroup, I just want to spruce things up.

@lwhite1
Copy link
Collaborator

lwhite1 commented Oct 29, 2019

What you are saying makes sense: the plotly configuration is too low level, even if we wrap it in nice fluent builders. We could offer another high-level API where users can customize their charts without getting into the plotly implementation details.

I don't have a feeling about how that could look though. If you give me some pointers I can think about it and get back to you. Seems like it might depend on chart type, as in, determine some usual chart "templates"/use-cases which we could support.

There are a few different perspectives that might be worth looking at. Let me give it a little thought/research and get back to you.

Speaking of APIs, what you want would be a 3rd API package since we can't remove the existing api package nor the builders.

True, but I would deprecate the current API if we had something better.

Having the low-level stuff accessible via builders is fine, I think, because someone will always want to create something that leverages all the flexibility you get in Plot.ly. We just don't want to make people build everything from the ground up if they just want to plot X by Y.

As for the api package methods, adding Layout (and Config!) does double the methods but each *Plot class has 1-3 methods so getting to 2-6 methods doesn't seem to make such a mess.

I was thinking more about adding layout to, say, the BoxPlot class. BoxPlot looks like this:

  public static Figure create(String title, Table table, String groupCol, String numCol) {

    Layout layout = Layout.builder().title(title).height(HEIGHT).width(WIDTH).build();

    BoxTrace trace =
        BoxTrace.builder(table.categoricalColumn(groupCol), table.nCol(numCol)).build();
    return new Figure(layout, trace);
  }

To add a custom layout you'd need a new method here.

You're suggesting adding a layout to Plot, but that seems a little awkward (although easier to implement). If I were looking for a shortcut here, I would make Figure have a layout setter that overrides the default layout and leave Plot the way it is.

In a way what I'm trying to avoid with the Layout parameter is similar to your idea: I don't want to deal with the low level Trace, Marker and TableSliceGroup, I just want to spruce things up.

I would be ok with a PR that lets you create a plot with the existing API classes like BoxPlot and override the Layout as described above. What do you think of that option?

@lwhite1
Copy link
Collaborator

lwhite1 commented Oct 29, 2019

just wanted to add that one model for a simpler api might be the "plotly express" code
https://plot.ly/python/plotly-express/

@emilianbold
Copy link
Contributor Author

If I were looking for a shortcut here, I would make Figure have a layout setter that overrides the default layout and leave Plot the way it is.

That was an idea I also had then I saw the final Layout layout in Figure and thought the API might be better. But I guess a setLayout in Figure would also do. Let me update the PR.

@emilianbold
Copy link
Contributor Author

I've changed this branch to only add Figure.setLayout. Please squash the commits, no need to keep that history.

just wanted to add that one model for a simpler api might be the "plotly express" code
https://plot.ly/python/plotly-express/

I'll look into this.

@emilianbold emilianbold changed the title Allows Layout argument to Vertical- and HorizontalBarPlot Adds Figure.setLayout Nov 4, 2019
@emilianbold emilianbold changed the title Adds Figure.setLayout Adds Figure.setLayout and Figure.setConfig Nov 4, 2019
@emilianbold
Copy link
Contributor Author

emilianbold commented Nov 11, 2019

Actually, there's no need for Figure to be such a tight class. Its whole purpose is to generate that asJavascript but by making more fields accessible / modifiable I think it can help with customizations. What do you think @lwhite1?

@benmccann benmccann force-pushed the master branch 2 times, most recently from 6baaa1c to 23716aa Compare January 13, 2020 02:51
@lwhite1 lwhite1 merged commit 1cb254b into jtablesaw:master Feb 23, 2020
@lwhite1
Copy link
Collaborator

lwhite1 commented Feb 23, 2020

sorry for the huge delay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants