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
Dot, Lollipop and Cleveland Dot Plots #842
Conversation
Also a modest refactor to bar rendering.
Note that this commit does make a refactor to the rendering of bar plots ( I'm not sure about showing the values above the bars, but errorbars probably could be refactored out into its own function which would be shared between both vertical and horizontal barcharts. |
I'm wondering if Cleveland Dot Plots should be their own plottable, or a type of bar chart? At first, I didn't think it would work, but bar charts have the Let me know what you think, in terms of code it would be quite elegant, but conceptually I don't know that Cleveland Plots are really in the Bar Plot family. |
With Legend support: Code: double[] home_wins = { 12, 17, 16, 18, 18 }; // Data collected from https://footystats.org/england/premier-league/home-away-league-table
double[] away_wins = { 11, 13, 16, 14, 14 };
away_wins = away_wins.Select((y, i) => y - home_wins[i]).ToArray(); // y2 should be absolute, not in terms of its distance from y1
string[] labels = { "2015/16", "2016/17", "2017/18", "2018/19", "2019/20" };
double[] label_positions = Enumerable.Range(0, labels.Length).Select(i => (double)i).ToArray();
var bar = plt.AddBar(away_wins);
bar.YOffsets = home_wins;
bar.DisplayStyle = BarStyle.ClevelandDot;
plt.XAxis.ManualTickPositions(label_positions, labels);
plt.Title("British Premier League Champion Home vs Away Wins");
bar.ClevelandLabel1 = "Home Wins";
bar.ClevelandLabel2 = "Away Wins";
plt.Legend(); |
Also let me know if a neutral colour (instead of the colour of the bottom dot) would be better for the line between dots in Cleveland dot plot. |
Hey @bclehmann, sorry it took a little while for me to get to this. So far things are looking great! Here are some quick thoughts: Dot PlotsI agree with you this seems pretty low priority / not worth implementing at this time. Users who are really compelled to make these plots could probably do so by getting creative with scatter plots. However, it might make a good example for the FAQ page demonstrating how to create custom plot types? https://github.com/ScottPlot/Website/issues/6 Error Bars
Seems fair! I'd be surprised if users wanted to mix error bars with these new bar styles anyway. If users wanted something custom, they could probably add a scatter plot with no lines or markers (just error bars) and customize that as desired. Extend BarPlot or Inherit it?
My gut reaction is that inheriting EDIT: Whether/how you refactor this way may depend on if/how you implement data object Data ObjectsI suspect we may soon want to refactor bar plots to accomodate a I'm on the fence about if exploring this will make this PR too complicated or not, but if a lot of refactoring goes down it might be an opportunity to experiment with this concept a bit. Rather than suggest implementation details, I'm curious what you think of the idea and if you like it what your preferred implementation what it would look like. Perhaps you could make a data object for bar plots, I'll do the same with scatter plots, then we could compare our implementations and identify the best parts of both and converge onto a common style before creating data objects for other plot types? It sounds good in theory, but it also might just be clash of personal preferences in API design! I'm interested in your thoughts on this front. If you want to discuss it using shorter messages I'm happy to touch base on slack or discord. I'll probably work on this a little bit every day for the next week, so progress may be a bit slow on my end. |
Want to have 3 user-configurable colors? Rather than expose the fields, a setter might be a nice way to let the user control transparency. Thinking out loud we could make everything private and have style methods like this: void SetStyleDot1(Color color, float size, MarkerShape shape) { /* */ }
void SetStyleDot2(Color color, float size, MarkerShape shape) { /* */ }
void SetStyleLine(Color color, float width, LineStyle style, double alpha = 1.0) { /* */ } |
Sorry, I wasn't clear. Error bars work with the new bar styles (although I question their usefulness). The refactoring I was referring to was moving the bar rendering into its own function, while the error bar code is there twice in
I like this idea, they're conveniently implemented as extensions to bar charts but I don't know how intuitive that is from an API perspective. Inheritance addresses both concerns pretty well in my opinion.
I am not particularly fond of making our own data object. I agree that handling growing data is best handled in the datastructure, but I think most of our users want to append (and possibly remove) data either to the beginning or the end. That sounds like a deque to me. C# doesn't provide a deque class but they're not particularly difficult to implement. I think if we create a To me this has a few main benefits:
In principle, one could use I am open to discussing this on discord or slack or wherever else you would like, although unfortunately I'm pretty busy for the next week. |
Yeah that's probably the best option.
Yeah I like that approach, it organizes the different properties of a dot more clearly. |
As an aside it just occurred to me that customizable marker shapes would probably be a good idea. |
I created a For the customization of the Cleveland Dot plot I added a I also didn't touch the |
Hey @bclehmann, thanks again for all your work here! I finally got to start working on this (#813 took me way longer than I anticipated), and I was hoping to finish it today but I wasn't able to. Your modules look great - I'm going to take this opportunity to refactor the |
Those test failures came because |
@bclehmann thanks for pointing that out and stepping in to fix it! Something came up tonight and I tried to wrap up and commit my work before leaving the computer and in hindsight that was an irresponsible / rookie move! I'm super embarrassed I broke the build here and didn't have the time to fix it 😝 Thanks for the quick repair! I think I can finish my work on this PR EDIT: I'm slammed with some other stuff this week so it'll have to wait until the weekend |
The old field names only make sense for vertically-oriented bar graphs. Original field names have been marked obsolete.
The bar plot base now contains data management only. This commit leaves child classes with sub-ideal duplicated code. Plot-type-specific minimal-complexity render methods should be created for these child classes in the future.
Hey @bclehmann, thanks for your work on this PR! I continued to refactor these classes and basically removed the I didn't extensively review/refactor the render code in the new child classes. I suspect large portions of the render code can be deleted, and the render methods can become relatively simple now (e.g., lollipop plots don't need to know how to draw rectangles). I really want to focus on the current high-priority tasks preventing me from releasing ScottPlot 4.1 (#716), so I'll probably put this down and pick it up again in a few weeks. I think this landed in a pretty good place for now, but I look forward to revisiting some of the discussions above about data management (live data, growing and auto-resizing arrays, etc.) after the release. |
New Contributors:
please review CONTRIBUTING.md
Purpose:
#817
New Functionality:
So far this only adds Lollipop plots because I'm still working on it.
I'm also unsure whether dot plots are worth the effort, dot plots offer no benefit compared to bar plots that I know of other than being easy to draw with typewriters or ASCII art. And adding dot plot support looks like it would be non-trivial if one wants to minimize the number of fractional dots drawn. If someone has a compelling use case I can come back to them.
Lollipop