Skip to content

Commit

Permalink
Merge pull request #243 from jkittner/calm_fix
Browse files Browse the repository at this point in the history
fix y-axis scaling when `calm_limit` is provided
  • Loading branch information
ocefpaf committed Jun 12, 2023
2 parents 3255434 + 7d715a6 commit 3d89f5e
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 49 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions tests/test_windrose_np_mpl_oo.py
Expand Up @@ -45,6 +45,24 @@ def test_windrose_stacked_histogram_not_normed_binned():
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo", tolerance=6.5)
def test_windrose_stacked_histogram_not_normed_binned_calm_limit():
# Another stacked histogram representation, not normed, with bins limits and a calm limit
ax = WindroseAxes.from_ax()
ax.box(wd, ws, bins=bins, calm_limit=0.2)
ax.set_legend()
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo", tolerance=15.5)
def test_windrose_stacked_histogram_normed_calm_limit():
# windrose like a stacked histogram with normed (displayed in percent) results and a calm limit
ax = WindroseAxes.from_ax()
ax.bar(wd, ws, normed=True, opening=0.8, edgecolor="white", calm_limit=0.2)
ax.set_legend()
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_filled_with_colormap():
# A windrose in filled representation, with a controlled colormap
Expand All @@ -54,6 +72,15 @@ def test_filled_with_colormap():
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_filled_with_colormap_calm_limit():
# A windrose in filled representation, with a controlled colormap and a calm limit
ax = WindroseAxes.from_ax()
ax.contourf(wd, ws, bins=bins, cmap=cm.hot, calm_limit=0.2)
ax.set_legend()
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_filled_with_colormap_contours():
# Same as above, but with contours over each filled region...
Expand All @@ -64,6 +91,16 @@ def test_filled_with_colormap_contours():
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_filled_with_colormap_contours_calm_limit():
# Same as above, but with contours over each filled region...
ax = WindroseAxes.from_ax()
ax.contourf(wd, ws, bins=bins, cmap=cm.hot, calm_limit=0.2)
ax.contour(wd, ws, bins=bins, colors="black", calm_limit=0.2)
ax.set_legend()
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_without_filled_with_colormap_contours():
ax = WindroseAxes.from_ax()
Expand All @@ -72,6 +109,14 @@ def test_without_filled_with_colormap_contours():
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_without_filled_with_colormap_contours_calm_limit():
ax = WindroseAxes.from_ax()
ax.contour(wd, ws, bins=bins, cmap=cm.hot, lw=3, calm_limit=0.2)
ax.set_legend()
return ax.figure


@pytest.mark.mpl_image_compare(baseline_dir="output/oo")
def test_pdf():
ax = WindAxes.from_ax()
Expand Down
89 changes: 40 additions & 49 deletions windrose/windrose.py
Expand Up @@ -14,8 +14,6 @@
DIR_DEFAULT = "direction"
FIGSIZE_DEFAULT = (8, 8)
DPI_DEFAULT = 80
CALM_CIRCLE_COLOR = "red"
CALM_CIRCLE_ALPHA = 0.4
DEFAULT_THETA_LABELS = ["E", "N-E", "N", "N-W", "W", "S-W", "S", "S-E"]


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

normed = kwargs.pop("normed", False)
blowto = kwargs.pop("blowto", False)

# Calm condition, mask data if needed
calm_limit = kwargs.pop("calm_limit", None)
total = len(var)
if calm_limit is not None:
mask = var > calm_limit
self.calm_count = len(var) - np.count_nonzero(mask)
if normed:
self.calm_count = self.calm_count * 100 / len(var)
var = var[mask]
direction = direction[mask]

# if weibull factors are entered overwrite direction and var
if "weibull_factors" in kwargs or "mean_values" in kwargs:
if "weibull_factors" in kwargs and "mean_values" in kwargs:
Expand Down Expand Up @@ -383,19 +395,6 @@ def _init_plot(self, direction, var, **kwargs):
# Building the angles list
angles = np.arange(0, -2 * np.pi, -2 * np.pi / nsector) + np.pi / 2

normed = kwargs.pop("normed", False)
blowto = kwargs.pop("blowto", False)

# Calm condition
calm_limit = kwargs.pop("calm_limit", None)
if calm_limit is not None:
mask = var > calm_limit
self.calm_count = len(var) - np.count_nonzero(mask)
if normed:
self.calm_count = self.calm_count * 100 / len(var)
var = var[mask]
direction = direction[mask]

# Set the global information dictionary
self._info["dir"], self._info["bins"], self._info["table"] = histogram(
direction,
Expand All @@ -404,25 +403,15 @@ def _init_plot(self, direction, var, **kwargs):
nsector,
normed,
blowto,
total,
)

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

def _calm_circle(self):
"""
Draw the calm centered circle
and return the initial offset for plots methods
"""
"""Draw the calm centered circle"""
if self.calm_count and self.calm_count > 0:
circle = mpl.patches.Circle(
(0.0, 0.0),
self.calm_count,
transform=self.transData._b,
color=CALM_CIRCLE_COLOR,
alpha=CALM_CIRCLE_ALPHA,
)
self.add_artist(circle)
return self.calm_count or 0
self.set_rorigin(-(np.sqrt(self.calm_count / np.pi)))

def contour(self, direction, var, **kwargs):
"""
Expand All @@ -440,9 +429,9 @@ def contour(self, direction, var, **kwargs):
Other Parameters
----------------
sector : integer, optional
nsector : integer, optional
number of sectors used to compute the windrose table. If not set,
nsectors=16, then each sector will be 360/16=22.5°, and the
nsector=16, then each sector will be 360/16=22.5°, and the
resulting computed table will be aligned with the cardinals points.
bins : 1D array or integer, optional
number of bins, or a sequence of bins variable. If not set, bins=6,
Expand Down Expand Up @@ -482,10 +471,11 @@ def contour(self, direction, var, **kwargs):
),
)

offset = self._calm_circle()
self._calm_circle()
origin = 0
for i in range(nbins):
val = vals[i, :] + offset
offset += vals[i, :]
val = vals[i, :] + origin
origin += vals[i, :]
zorder = ZBASE + nbins - i
patch = self.plot(angles, val, color=colors[i], zorder=zorder, **kwargs)
self.patches_list.extend(patch)
Expand All @@ -509,7 +499,7 @@ def contourf(self, direction, var, **kwargs):
----------------
nsector: integer, optional
number of sectors used to compute the windrose table. If not set,
nsectors=16, then each sector will be 360/16=22.5°, and the
nsector=16, then each sector will be 360/16=22.5°, and the
resulting computed table will be aligned with the cardinals points.
bins : 1D array or integer, optional
number of bins, or a sequence of bins variable. If not set, bins=6,
Expand Down Expand Up @@ -550,10 +540,11 @@ def contourf(self, direction, var, **kwargs):
),
),
)
offset = self._calm_circle()
self._calm_circle()
origin = 0
for i in range(nbins):
val = vals[i, :] + offset
offset += vals[i, :]
val = vals[i, :] + origin
origin += vals[i, :]
zorder = ZBASE + nbins - i
patch = self.fill(
np.append(angles, 0),
Expand Down Expand Up @@ -582,7 +573,7 @@ def bar(self, direction, var, **kwargs):
----------------
nsector : integer, optional
number of sectors used to compute the windrose table. If not set,
nsectors=16, then each sector will be 360/16=22.5°, and the
nsector=16, then each sector will be 360/16=22.5°, and the
resulting computed table will be aligned with the cardinals points.
bins : 1D array or integer, optional
number of bins, or a sequence of bins variable. If not set, bins=6
Expand Down Expand Up @@ -624,17 +615,17 @@ def bar(self, direction, var, **kwargs):
dtheta = 2 * np.pi / nsector
opening = dtheta * opening

offs = self._calm_circle()
self._calm_circle()

for j in range(nsector):
offset = offs
origin = 0
for i in range(nbins):
if i > 0:
offset += self._info["table"][i - 1, j]
origin += self._info["table"][i - 1, j]
val = self._info["table"][i, j]
zorder = ZBASE + nbins - i
patch = mpl.patches.Rectangle(
(angles[j] - opening / 2, offset),
(angles[j] - opening / 2, origin),
opening,
val,
facecolor=colors[i],
Expand Down Expand Up @@ -663,7 +654,7 @@ def box(self, direction, var, **kwargs):
----------------
nsector: integer, optional
number of sectors used to compute the windrose table. If not set,
nsectors=16, then each sector will be 360/16=22.5°, and the
nsector=16, then each sector will be 360/16=22.5°, and the
resulting computed table will be aligned with the cardinals points.
bins : 1D array or integer, optional
number of bins, or a sequence of bins variable. If not set, bins=6
Expand Down Expand Up @@ -698,17 +689,17 @@ def box(self, direction, var, **kwargs):
raise ValueError("edgecolor must be a string color")
opening = np.linspace(0.0, np.pi / 16, nbins)

offs = self._calm_circle()
self._calm_circle()

for j in range(nsector):
offset = offs
origin = 0
for i in range(nbins):
if i > 0:
offset += self._info["table"][i - 1, j]
origin += self._info["table"][i - 1, j]
val = self._info["table"][i, j]
zorder = ZBASE + nbins - i
patch = mpl.patches.Rectangle(
(angles[j] - opening[i] / 2, offset),
(angles[j] - opening[i] / 2, origin),
opening[i],
val,
facecolor=colors[i],
Expand Down Expand Up @@ -769,7 +760,7 @@ def pdf(
return (self, params)


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

return dir_edges, var_bins, table

Expand Down

0 comments on commit 3d89f5e

Please sign in to comment.