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

feat: add VegaLite.Data.polar_plot #69

Closed
wants to merge 12 commits into from

Conversation

polvalente
Copy link

Adds the ability for plotting polar data

lib/vega_lite/data.ex Outdated Show resolved Hide resolved
@cristineguadelupe
Copy link
Contributor

Hey @polvalente! I like the idea of supporting it, but I agree with @josevalim. How about a new module? 🤔

@polvalente
Copy link
Author

Hey @polvalente! I like the idea of supporting it, but I agree with @josevalim. How about a new module? 🤔

I'm not set on keeping this in VegaLite.Data. It just seemed like the right place to do it.

lib/vega_lite/data.ex Outdated Show resolved Hide resolved
radius_marks_vl ++ radius_ruler_vl
end

defp polar_infer_radius_marks(data) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do data processing. And we can't assume data is an enum nor fields in the data. We only assume data implements Table.Reader.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a deal breaker in terms of usability, but again, can be extracted out.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the preprocessing is done through VegaLite calculations, so it's fine

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is up to you. I am mostly dropping comments in favor of consistency with the remaining APIs in this module, which I would like to strictly adhere to for now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me.

If we:

  • remove the max radius inference
  • refactor the data keys so that we don't assume the keys in data
  • only accept a single data collection instead of a list of those

Is the feature good to go? Or would it still be in the "can be denied" realm?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I think we can accept it, if you still think it is useful. But I can't promise other blockers won't be found as you refactor. I can't promise to merge something based on code i can't see.

@cristineguadelupe
Copy link
Contributor

Hey @polvalente! I like the idea of supporting it, but I agree with @josevalim. How about a new module? 🤔

I'm not set on keeping this in VegaLite.Data. It just seemed like the right place to do it.

I'd say that VegaLite.Data should be reserved for data that implements Table.Reader.
A new module seems ok to me, I don't think VegaLite will ever support it directly, they're restricted to operating on top of grammars rather than abstractions.

opts =
case opts[:radius_marks] do
nil ->
radius_marks = polar_infer_radius_marks(data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One alternative is to infer the min and max using VegaLite, if at all possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be because we plot separate layers for each radius mark

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind though you can keep the data on the root, outside of the layers, then all layers will have access to it. You can see how other functions do it. My suggestion would be to trim this down into the smallest possible function, that follows the other patterns in this module, and then you can add all of the conveniences later in a pull request.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wrong about them being separate layers, so maybe we can do some kind of vega-lite calculation for this. Though I don't see this inference missing as a blocker for the feature

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the data in the root hurts composability in a sense because I have different r/theta definitions depending on whether we're dealing with the angle marks, the radius marks or the data layers

legend_key: legend_key
)
"""
def polar_grid(vl \\ Vl.new(), opts \\ []) do
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josevalim @cristineguadelupe I was able to split polar_grid and polar_plot (docs still pending, I'll leave this for later).

The idea is that polar_grid will just generate an empty polar grid, while polar_plot will convert polar coordinates data into a cartesian plot.

Both can be combined to plot multiple datasets onto a given grid.

I also implemented the append_layers defp which we might want to extract.

Screenshot 2023-08-19 at 9 53 21 AM
Screenshot 2023-08-19 at 9 53 24 AM

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to show just vl_grid:

Screenshot 2023-08-19 at 9 56 27 AM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks pretty good. I think we are on a path to merging this 🎉

lib/vega_lite/data.ex Outdated Show resolved Hide resolved
vl_polar_config =
case vl.spec do
%{"_vl_polar_config" => conf} -> Enum.to_list(conf)
_ -> [radius_marks: [1, 2, 3, 4, 5], angle_offset: 0, direction: :counter_clockwise]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should raise if there is no config.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's no config, we can't do the standalone plot (the one where we get the cartesian-converted plot for polar data). Maybe we can require that either the config is present or those three options are present.
If both are present, we could make it so that the config takes precedence.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's no config, we can't do the standalone plot (the one where we get the cartesian-converted plot for polar data).

That's fine by me. Let's raise for now. It is always easier to add features later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I managed to use the fields along with a check for the presence of the :radius_marks config to allow for the plot to still be used in a standalone fashion.

I'll commit it like this and we can remove the code if you feel it's bloat

end

def polar_plot(vl \\ Vl.new(), data, mark, fields, opts \\ []) do
opts = Keyword.validate!(opts, scheme: "turbo", hide_axes: false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we set the scheme? I don't think so.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to be optional

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not about being optional. I don't think we should change the scheme unless the user asks us to. We don't do that for any other the other plots.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the color field as an optional meta-field for this configuration. It also is now what affects the legend grouping.

lib/vega_lite/data.ex Outdated Show resolved Hide resolved
lib/vega_lite/data.ex Outdated Show resolved Hide resolved
lib/vega_lite/data.ex Outdated Show resolved Hide resolved
case fields[:r] do
opts when is_list(opts) -> opts[:field]
field when is_binary(field) -> field
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a function for normalizing fields. Can you please use it instead? We also have functions to extract information from data and subfields. Please use chart as the foundation for this function and then build on top!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see how to use that, but I don't see how columns_for applies to this plot

Comment on lines 410 to 411
* `:x` - options for the x-axis
* `:y` - options for the y-axis
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `:x` - options for the x-axis
* `:y` - options for the y-axis

Let's remove options for :x and :y for now. The most we can simplify this for now, the better.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make it so we can't have the standalone cartesian-converted plot. This is what you want?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can wait for a use case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

Copy link
Contributor

@josevalim josevalim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! We now need tests and i think we will be ready to go!

@josevalim
Copy link
Contributor

We are deprecating the specialized plots in VegaLite.Data. :) Thank you @polvalente and sorry for all the back and forth on this.

@josevalim josevalim closed this Aug 30, 2023
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.

3 participants