-
-
Notifications
You must be signed in to change notification settings - Fork 411
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
Add circular histogram and KDE plot #1266
Add circular histogram and KDE plot #1266
Conversation
**hist_kwargs, | ||
) | ||
|
||
ticks = np.linspace(-np.pi, np.pi, 9) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be
ticks = np.linspace(-np.pi, np.pi, len(labels), endpoint=False)
y=0, | ||
inner_radius=0, | ||
outer_radius=np.max(outer_radius) * 1.1, | ||
start_angle=ticks[:-1], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and this could just be start_angle=ticks
inner_radius=0, | ||
outer_radius=np.max(outer_radius) * 1.1, | ||
start_angle=ticks[:-1], | ||
end_angle=ticks[:-1] + 0.002, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need the 0.002 or could we just use end_angle=ticks
?
Thanks @aloctavodia for your comments! |
Nitpicking: The grey radial lines indicating the angles are of the same color as the wedge's borders. We may want to 1)remove the borders (make them the same color as the wedges), 2) make the border black, 3) add a small blank space between wedges. Probably the first option is easier and it will work well. But the last one will be more consistent with the matplotlib histogram (circular and not) |
fix docstring fix changelog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
I have a couple of high level questions too, the comments are minor nits.
Do both degrees and radians units work with bokeh hist, mpl hist and mpl kde? Do you think it would be worth it to create a function in plot_utils
that got is_circular
and returned the corresponding ticks
and ticklabels
?
I understand all polar plots will show the complete circle, do you plan to support other angular ranges? (again, I know close to nothing on circular variables so I am not even sure it would be worth to support half circle or something like this)
if ax is None: | ||
ax = plt.gca() | ||
ax = plt.gca(polar=is_circular) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is using gca
instead of a new axes intended or a forgotten TODO from when bokeh was introduced? cc @ahartikainen @canyon289 @aloctavodia
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is intended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will gca
grab possibly old axis here? Have we created a new figure somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if present, plt.gca
will grab old axes yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is considered a "best practice" with matplotlib, and a pattern they endorse.
Codecov Report
@@ Coverage Diff @@
## master #1266 +/- ##
==========================================
+ Coverage 93.02% 93.12% +0.09%
==========================================
Files 101 101
Lines 9908 10034 +126
==========================================
+ Hits 9217 9344 +127
+ Misses 691 690 -1
Continue to review full report at Codecov.
|
Thanks for your comments @OriolAbril. Yes, both degrees and radians will work. Creating a function to manage ticks and ticklabels can be specially useful for the circular plots in Bokeh. I am almost sure that you can get an angular range plot by working on the axes returned by the Matplotlib function. Not quite sure about Bokeh. Since it does not support polar projection, things can get a bit tricky. |
if ax is None: | ||
ax = plt.gca() | ||
ax = plt.gca(polar=is_circular) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is intended
Co-authored-by: Oriol Abril-Pla <oriol.abril.pla@gmail.com>
minor fix
|
||
if is_circular == "degrees": | ||
edges = np.deg2rad(edges) | ||
labels = ["0°", "45°", "90°", "135°", "180°", "225°", "270°", "315°"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are always showing a full circle plot?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's what everybody does (I know this is not a very good argument, haha)
if ax is None: | ||
ax = plt.gca() | ||
ax = plt.gca(polar=is_circular) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will gca
grab possibly old axis here? Have we created a new figure somewhere?
Co-authored-by: Ari Hartikainen <ahartikainen@users.noreply.github.com>
I think this is ready to merge, right? @ahartikainen @OriolAbril what do you think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is really clean code!
if ax is None: | ||
ax = plt.gca() | ||
ax = plt.gca(polar=is_circular) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is considered a "best practice" with matplotlib, and a pattern they endorse.
if rotated or is_circular: | ||
ax.set_yticks(bins[:-1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In polar plots, isn't the y axis the radial one? could this be setting the radial ticks with x values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for spotting this!
# Skip tests if bokeh not installed | ||
bkp = importorskip("bokeh.plotting") # pylint: disable=invalid-name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will skip all tests in the file if bokeh is not installed. Maybe we should create a test_plot_utils_bokeh
? similarly to what is done with test_stats
and test_utils
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Òr maybe a regular skipif? something similar to https://github.com/arviz-devs/arviz/blob/master/arviz/tests/external_tests/test_data_pystan.py#L22
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used skipif, but I am not sure about the import statement placement. Let me know what you think
Co-authored-by: Oriol Abril-Pla <oriol.abril.pla@gmail.com>
Co-authored-by: Oriol Abril-Pla <oriol.abril.pla@gmail.com>
else: | ||
ax.set_xticks(bins[:-1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should not be removed as it will modify the behaviour of the plot, if it has to be skipped for circular plots I'd suggest a elif not is_circular
Co-authored-by: Oriol Abril-Pla <oriol.abril.pla@gmail.com>
{"is_circular": False}, | ||
{"is_circular": True}, | ||
{"is_circular": "radians"}, | ||
{"is_circular": "degrees"}, | ||
], | ||
) | ||
def test_plot_dist(continuous_model, kwargs): | ||
axes = plot_dist(continuous_model["x"], **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was quite shocked by the low coverage given that there are tests for everything, I was expecting something close to 100% diff coverage. I think the issue (and same happens with bokeh) is that the variables in the model are continuous, so the kde is plotted. I am not sure if it would be better to add a new test of divide the fixture into two so all combinations are tested
Nice! Now everything checks out, thanks! I think it's ready to merge |
Description
This PR adds circular histogram and circular KDE plot.
Histogram example (matplotlib):
Histogram example (bokeh):
Although with this code you can get a circular KDE, there are a couple of things to improve in the KDE that will result in better behavior in a circular setting. We are planning to follow on Tomas Capretto's work on KDE. You can read the details in his report.
Checklist