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

fix y-axis scaling when calm_limit is provided #243

Merged
merged 3 commits into from Jun 12, 2023
Merged

fix y-axis scaling when calm_limit is provided #243

merged 3 commits into from Jun 12, 2023

Conversation

jkittner
Copy link
Contributor

continues/supersedes #240

Previously there were issues with how the center calm circle was created and scaled with the y-axis.

matplotlib has the set_rorigin which also can be set to a negative value, shifting the 0 mark outwards.

While calm conditions do not have a direction, they still count into the fraction of wind directions e.g. when having calm conditions for half of the time and northerly winds for the other half, there still aren't 100% northerly winds, but 50%!

In case someone want's to display only non calm conditions only, they can already mask their data beforehand.

The result looks like this, scaling the inner circle with the fraction/number of calm conditions. The inner circle and the outside bars always sum up to 100% or N.

t

Code for the plots
from windrose import WindroseAxes
from matplotlib import pyplot as plt
import numpy as np

ws = np.random.random(500) * 6
wd = np.random.random(500) * 360

_, axs = plt.subplots(ncols=4, subplot_kw={'projection': 'windrose'}, figsize=(20, 5))

ax1 = WindroseAxes.from_ax(ax=axs[0])
ax1.bar(wd, ws, normed=True, opening=0.8, bins=4, edgecolor='white', calm_limit=0.3)
ax1.set_legend()
ax1.annotate(text=f'{ax1.calm_count:.0f} %', xy=(np.pi / 2, - ax1.calm_count), ha='center', va='center')


ax2 = WindroseAxes.from_ax(ax=axs[1])
ax2.contour(wd, ws, normed=True, bins=4, calm_limit=0.3)
ax2.set_legend()
ax2.annotate(text=f'{ax2.calm_count:.0f} %', xy=(np.pi / 2, - ax2.calm_count), ha='center', va='center')


ax3 = WindroseAxes.from_ax(ax=axs[2])
ax3.contourf(wd, ws, normed=True, bins=4, calm_limit=0.3)
ax3.contour(wd, ws, normed=True, bins=4, calm_limit=0.3)
ax3.set_legend()
ax3.annotate(text=f'{ax3.calm_count:.0f} %', xy=(np.pi / 2, - ax3.calm_count), ha='center', va='center')

ax4 = WindroseAxes.from_ax(ax=axs[3])
ax4.box(wd, ws, normed=True, bins=4, calm_limit=0.3)
ax4.set_legend()
ax4.annotate(text=f'{ax4.calm_count:.0f} %', xy=(np.pi / 2, - ax4.calm_count), ha='center', va='center')

plt.subplots_adjust(wspace=.25)

plt.savefig('t.png', bbox_inches='tight')

@jparta
Copy link
Contributor

jparta commented May 19, 2023

Nice! Glad to contribute.

@jkittner
Copy link
Contributor Author

I am having more thoughts about what the correct scaling of the inner (calm) circle is.

  1. Right now we simply set the radius to the calm_count meaning with dataset $d$ we only take values $x$ which are less than $c_{limit}$ (calm_limit) and finally take $n$ of the selected data. (whether it's scaled or not, should not make a difference here)

$$ r_{calm} = n(\{ x \in d \ |\ x < c_{limit}\}) $$

  • This gets ridiculously big with higher fractions of calm conditions
Example plot

radius_count


But the fraction of calm conditions should be the area no? So could we achieve this by dividing by the number of sectors (16 in this case)?

$$ r_{calm} = \frac{n(\{ x \in d \ |\ x < c_{limit} \})}{n_{sectors}} $$

  • this does not handle low fractions of calm conditions very well. Usually calm conditions have a lower share, so low values are the more common case.
Example plots

radius_count_div_nsectors

radius_count_div_nsectors_low


Or alternatively, what if we set calm_count as the area of the inner circle and calculate $r_{calm}$ via the area $A$?

$$ A = n(\{ x \in d \ |\ x < c_{limit} \}) $$

$$ r_{calm} =\sqrt{\frac{A}{\pi}} $$

  • this does handle lower values better
Example plots

radius_count_as_area
radius_count_as_area_low

So I am wondering what the mathematically correct way is, but also what the most readable option is, while being reasonably mathematically correct.

Happy to also hear your thoughts on this @jparta, since you already had a deeper look into this calm issue.

@jparta
Copy link
Contributor

jparta commented May 22, 2023

With small plot, such as in the case of plotting multiple plots on a map, reading percentage may not be viable. In that case, being able to read the approximate calm percentage off the r axis is helpful. The last option you presented (circle area) seems to work quite well.

@jkittner
Copy link
Contributor Author

yeah, the annotations are not part of the implementation, I added them manually afterwards. I also don't think that it makes sense to always include them. Maybe it's worth documenting this.

Reading the calm percentage from the r-axis/y-axis remains tricky, since the scale still starts at 0, since it's "% from direction x". So I think this is simply a shortcoming of the type of graph. But being able to visually approximate the percentage of calm conditions (or add the text annotation somewhere) is still nice and also comparable to the rest.

I think going with the area approach will be a sufficient compromise. Thanks for having a look!

@ocefpaf
Copy link
Collaborator

ocefpaf commented Jun 12, 2023

yeah, the annotations are not part of the implementation, I added them manually afterwards. I also don't think that it makes sense to always include them. Maybe it's worth documenting this.

Agreed. I think that an example in the docs would suffice.

I think going with the area approach will be a sufficient compromise. Thanks for having a look!

LGTM. Thanks for guiding us through you thinking process. Made it a bit easier to review this PR :-)
Also thanks for @jparta for the review!

I'll try to clean up the conflicts here and merge it.

jparta and others added 3 commits June 12, 2023 14:11
This was promised in plotting methods:
"...all data below this value will be removed from the computation."

Previously, the legend would start with '[0.0' in the first label,
even with calm_limit > 0. Now labels are accurate.
The actual parameter is called 'nesctor'.
set_rorigin bascially does what was done manually here without the coloring
- added tests for this also including the previous legend fix patch by Joonatan Partanen
@ocefpaf ocefpaf merged commit 3d89f5e into python-windrose:main Jun 12, 2023
15 checks passed
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.

None yet

3 participants