Skip to content

Commit 3d89f5e

Browse files
authored
Merge pull request #243 from jkittner/calm_fix
fix y-axis scaling when `calm_limit` is provided
2 parents 3255434 + 7d715a6 commit 3d89f5e

7 files changed

+85
-49
lines changed
Loading
Loading
Loading
Loading
Loading

tests/test_windrose_np_mpl_oo.py

+45
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ def test_windrose_stacked_histogram_not_normed_binned():
4545
return ax.figure
4646

4747

48+
@pytest.mark.mpl_image_compare(baseline_dir="output/oo", tolerance=6.5)
49+
def test_windrose_stacked_histogram_not_normed_binned_calm_limit():
50+
# Another stacked histogram representation, not normed, with bins limits and a calm limit
51+
ax = WindroseAxes.from_ax()
52+
ax.box(wd, ws, bins=bins, calm_limit=0.2)
53+
ax.set_legend()
54+
return ax.figure
55+
56+
57+
@pytest.mark.mpl_image_compare(baseline_dir="output/oo", tolerance=15.5)
58+
def test_windrose_stacked_histogram_normed_calm_limit():
59+
# windrose like a stacked histogram with normed (displayed in percent) results and a calm limit
60+
ax = WindroseAxes.from_ax()
61+
ax.bar(wd, ws, normed=True, opening=0.8, edgecolor="white", calm_limit=0.2)
62+
ax.set_legend()
63+
return ax.figure
64+
65+
4866
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
4967
def test_filled_with_colormap():
5068
# A windrose in filled representation, with a controlled colormap
@@ -54,6 +72,15 @@ def test_filled_with_colormap():
5472
return ax.figure
5573

5674

75+
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
76+
def test_filled_with_colormap_calm_limit():
77+
# A windrose in filled representation, with a controlled colormap and a calm limit
78+
ax = WindroseAxes.from_ax()
79+
ax.contourf(wd, ws, bins=bins, cmap=cm.hot, calm_limit=0.2)
80+
ax.set_legend()
81+
return ax.figure
82+
83+
5784
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
5885
def test_filled_with_colormap_contours():
5986
# Same as above, but with contours over each filled region...
@@ -64,6 +91,16 @@ def test_filled_with_colormap_contours():
6491
return ax.figure
6592

6693

94+
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
95+
def test_filled_with_colormap_contours_calm_limit():
96+
# Same as above, but with contours over each filled region...
97+
ax = WindroseAxes.from_ax()
98+
ax.contourf(wd, ws, bins=bins, cmap=cm.hot, calm_limit=0.2)
99+
ax.contour(wd, ws, bins=bins, colors="black", calm_limit=0.2)
100+
ax.set_legend()
101+
return ax.figure
102+
103+
67104
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
68105
def test_without_filled_with_colormap_contours():
69106
ax = WindroseAxes.from_ax()
@@ -72,6 +109,14 @@ def test_without_filled_with_colormap_contours():
72109
return ax.figure
73110

74111

112+
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
113+
def test_without_filled_with_colormap_contours_calm_limit():
114+
ax = WindroseAxes.from_ax()
115+
ax.contour(wd, ws, bins=bins, cmap=cm.hot, lw=3, calm_limit=0.2)
116+
ax.set_legend()
117+
return ax.figure
118+
119+
75120
@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
76121
def test_pdf():
77122
ax = WindAxes.from_ax()

windrose/windrose.py

+40-49
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
DIR_DEFAULT = "direction"
1515
FIGSIZE_DEFAULT = (8, 8)
1616
DPI_DEFAULT = 80
17-
CALM_CIRCLE_COLOR = "red"
18-
CALM_CIRCLE_ALPHA = 0.4
1917
DEFAULT_THETA_LABELS = ["E", "N-E", "N", "N-W", "W", "S-W", "S", "S-E"]
2018

2119

@@ -309,6 +307,20 @@ def _init_plot(self, direction, var, **kwargs):
309307
Any argument accepted by :obj:`matplotlib.pyplot.plot`.
310308
"""
311309

310+
normed = kwargs.pop("normed", False)
311+
blowto = kwargs.pop("blowto", False)
312+
313+
# Calm condition, mask data if needed
314+
calm_limit = kwargs.pop("calm_limit", None)
315+
total = len(var)
316+
if calm_limit is not None:
317+
mask = var > calm_limit
318+
self.calm_count = len(var) - np.count_nonzero(mask)
319+
if normed:
320+
self.calm_count = self.calm_count * 100 / len(var)
321+
var = var[mask]
322+
direction = direction[mask]
323+
312324
# if weibull factors are entered overwrite direction and var
313325
if "weibull_factors" in kwargs or "mean_values" in kwargs:
314326
if "weibull_factors" in kwargs and "mean_values" in kwargs:
@@ -383,19 +395,6 @@ def _init_plot(self, direction, var, **kwargs):
383395
# Building the angles list
384396
angles = np.arange(0, -2 * np.pi, -2 * np.pi / nsector) + np.pi / 2
385397

386-
normed = kwargs.pop("normed", False)
387-
blowto = kwargs.pop("blowto", False)
388-
389-
# Calm condition
390-
calm_limit = kwargs.pop("calm_limit", None)
391-
if calm_limit is not None:
392-
mask = var > calm_limit
393-
self.calm_count = len(var) - np.count_nonzero(mask)
394-
if normed:
395-
self.calm_count = self.calm_count * 100 / len(var)
396-
var = var[mask]
397-
direction = direction[mask]
398-
399398
# Set the global information dictionary
400399
self._info["dir"], self._info["bins"], self._info["table"] = histogram(
401400
direction,
@@ -404,25 +403,15 @@ def _init_plot(self, direction, var, **kwargs):
404403
nsector,
405404
normed,
406405
blowto,
406+
total,
407407
)
408408

409409
return bins, nbins, nsector, colors, angles, kwargs
410410

411411
def _calm_circle(self):
412-
"""
413-
Draw the calm centered circle
414-
and return the initial offset for plots methods
415-
"""
412+
"""Draw the calm centered circle"""
416413
if self.calm_count and self.calm_count > 0:
417-
circle = mpl.patches.Circle(
418-
(0.0, 0.0),
419-
self.calm_count,
420-
transform=self.transData._b,
421-
color=CALM_CIRCLE_COLOR,
422-
alpha=CALM_CIRCLE_ALPHA,
423-
)
424-
self.add_artist(circle)
425-
return self.calm_count or 0
414+
self.set_rorigin(-(np.sqrt(self.calm_count / np.pi)))
426415

427416
def contour(self, direction, var, **kwargs):
428417
"""
@@ -440,9 +429,9 @@ def contour(self, direction, var, **kwargs):
440429
441430
Other Parameters
442431
----------------
443-
sector : integer, optional
432+
nsector : integer, optional
444433
number of sectors used to compute the windrose table. If not set,
445-
nsectors=16, then each sector will be 360/16=22.5°, and the
434+
nsector=16, then each sector will be 360/16=22.5°, and the
446435
resulting computed table will be aligned with the cardinals points.
447436
bins : 1D array or integer, optional
448437
number of bins, or a sequence of bins variable. If not set, bins=6,
@@ -482,10 +471,11 @@ def contour(self, direction, var, **kwargs):
482471
),
483472
)
484473

485-
offset = self._calm_circle()
474+
self._calm_circle()
475+
origin = 0
486476
for i in range(nbins):
487-
val = vals[i, :] + offset
488-
offset += vals[i, :]
477+
val = vals[i, :] + origin
478+
origin += vals[i, :]
489479
zorder = ZBASE + nbins - i
490480
patch = self.plot(angles, val, color=colors[i], zorder=zorder, **kwargs)
491481
self.patches_list.extend(patch)
@@ -509,7 +499,7 @@ def contourf(self, direction, var, **kwargs):
509499
----------------
510500
nsector: integer, optional
511501
number of sectors used to compute the windrose table. If not set,
512-
nsectors=16, then each sector will be 360/16=22.5°, and the
502+
nsector=16, then each sector will be 360/16=22.5°, and the
513503
resulting computed table will be aligned with the cardinals points.
514504
bins : 1D array or integer, optional
515505
number of bins, or a sequence of bins variable. If not set, bins=6,
@@ -550,10 +540,11 @@ def contourf(self, direction, var, **kwargs):
550540
),
551541
),
552542
)
553-
offset = self._calm_circle()
543+
self._calm_circle()
544+
origin = 0
554545
for i in range(nbins):
555-
val = vals[i, :] + offset
556-
offset += vals[i, :]
546+
val = vals[i, :] + origin
547+
origin += vals[i, :]
557548
zorder = ZBASE + nbins - i
558549
patch = self.fill(
559550
np.append(angles, 0),
@@ -582,7 +573,7 @@ def bar(self, direction, var, **kwargs):
582573
----------------
583574
nsector : integer, optional
584575
number of sectors used to compute the windrose table. If not set,
585-
nsectors=16, then each sector will be 360/16=22.5°, and the
576+
nsector=16, then each sector will be 360/16=22.5°, and the
586577
resulting computed table will be aligned with the cardinals points.
587578
bins : 1D array or integer, optional
588579
number of bins, or a sequence of bins variable. If not set, bins=6
@@ -624,17 +615,17 @@ def bar(self, direction, var, **kwargs):
624615
dtheta = 2 * np.pi / nsector
625616
opening = dtheta * opening
626617

627-
offs = self._calm_circle()
618+
self._calm_circle()
628619

629620
for j in range(nsector):
630-
offset = offs
621+
origin = 0
631622
for i in range(nbins):
632623
if i > 0:
633-
offset += self._info["table"][i - 1, j]
624+
origin += self._info["table"][i - 1, j]
634625
val = self._info["table"][i, j]
635626
zorder = ZBASE + nbins - i
636627
patch = mpl.patches.Rectangle(
637-
(angles[j] - opening / 2, offset),
628+
(angles[j] - opening / 2, origin),
638629
opening,
639630
val,
640631
facecolor=colors[i],
@@ -663,7 +654,7 @@ def box(self, direction, var, **kwargs):
663654
----------------
664655
nsector: integer, optional
665656
number of sectors used to compute the windrose table. If not set,
666-
nsectors=16, then each sector will be 360/16=22.5°, and the
657+
nsector=16, then each sector will be 360/16=22.5°, and the
667658
resulting computed table will be aligned with the cardinals points.
668659
bins : 1D array or integer, optional
669660
number of bins, or a sequence of bins variable. If not set, bins=6
@@ -698,17 +689,17 @@ def box(self, direction, var, **kwargs):
698689
raise ValueError("edgecolor must be a string color")
699690
opening = np.linspace(0.0, np.pi / 16, nbins)
700691

701-
offs = self._calm_circle()
692+
self._calm_circle()
702693

703694
for j in range(nsector):
704-
offset = offs
695+
origin = 0
705696
for i in range(nbins):
706697
if i > 0:
707-
offset += self._info["table"][i - 1, j]
698+
origin += self._info["table"][i - 1, j]
708699
val = self._info["table"][i, j]
709700
zorder = ZBASE + nbins - i
710701
patch = mpl.patches.Rectangle(
711-
(angles[j] - opening[i] / 2, offset),
702+
(angles[j] - opening[i] / 2, origin),
712703
opening[i],
713704
val,
714705
facecolor=colors[i],
@@ -769,7 +760,7 @@ def pdf(
769760
return (self, params)
770761

771762

772-
def histogram(direction, var, bins, nsector, normed=False, blowto=False):
763+
def histogram(direction, var, bins, nsector, normed=False, blowto=False, total=0):
773764
"""
774765
Returns an array where, for each sector of wind
775766
(centred on the north), we have the number of time the wind comes with a
@@ -819,7 +810,7 @@ def histogram(direction, var, bins, nsector, normed=False, blowto=False):
819810
# and remove the last col
820811
table = table[:, :-1]
821812
if normed:
822-
table = table * 100 / table.sum()
813+
table = table * 100 / total
823814

824815
return dir_edges, var_bins, table
825816

0 commit comments

Comments
 (0)