Skip to content

Commit

Permalink
Calm conditions
Browse files Browse the repository at this point in the history
* Update README.md

* Add calm limit parameter and representative circle

* PEP8

* Address style issues in setup.py

* Address style issues in windrose.py

* Do not use opening parens for raising exceptions

* Do not use 'o' as a variable name
  • Loading branch information
petebachant authored and scls19fr committed Oct 4, 2018
1 parent fcf258c commit a2607da
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ we encourage you to follow our [code of conduct](https://github.com/python-windr
### Contributing

If you discover issues, have ideas for improvements or new features, please report them.
[CONTRIBUTING.md](https://github.com/python-windrose/windrose/blob/master/CONTRIBUTING.md) explains
[CONTRIBUTING.md](https://github.com/python-windrose/windrose/blob/master/CONTRIBUTING.md) explains
how to contribute to this project.

### List of contributors and/or notable users
Expand Down
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup, find_packages # Always prefer setuptools over distutils
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from os import path
import io

Expand All @@ -23,7 +24,8 @@
# version='0.0.2',
version=__version__,

description='Python Matplotlib, Numpy library to manage wind data, draw windrose (also known as a polar rose plot)',
description=('Python Matplotlib, Numpy library to manage wind data, '
'draw windrose (also known as a polar rose plot)',)
long_description=long_description,
long_description_content_type='text/markdown',

Expand Down Expand Up @@ -88,7 +90,7 @@
# List additional groups of dependencies here (e.g. development dependencies).
# You can install these using the following syntax, for example:
# $ pip install -e .[dev,test]
extras_require = {
extras_require={
'dev': ['check-manifest', 'pytest'],
'test': ['coverage', 'pytest'],
'advanced': ['pandas', 'scipy']
Expand Down
139 changes: 98 additions & 41 deletions windrose/windrose.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
DIR_DEFAULT = 'direction'
FIGSIZE_DEFAULT = (8, 8)
DPI_DEFAULT = 80
CALM_CIRCLE_COLOR = "red"
CALM_CIRCLE_ALPHA = 0.4


def _autogen_docstring(base):
Expand Down Expand Up @@ -67,7 +69,9 @@ def create(typ, ax=None, *args, **kwargs):
ax = cls.from_ax(ax, *args, **kwargs)
return ax
else:
raise(NotImplementedError("typ=%r but it might be in %s" % (typ, d.keys())))
raise NotImplementedError(
"typ=%r but it might be in %s" % (typ, d.keys())
)


class WindroseAxes(PolarAxes):
Expand Down Expand Up @@ -99,9 +103,11 @@ def from_ax(ax=None, fig=None, rmax=None, *args, **kwargs):
"""
if ax is None:
if fig is None:
fig = plt.figure(figsize=FIGSIZE_DEFAULT, dpi=DPI_DEFAULT, facecolor='w', edgecolor='w')
fig = plt.figure(figsize=FIGSIZE_DEFAULT, dpi=DPI_DEFAULT,
facecolor='w', edgecolor='w')
rect = [0.1, 0.1, 0.8, 0.8]
ax = WindroseAxes(fig, rect, facecolor='w', rmax=rmax, *args, **kwargs)
ax = WindroseAxes(fig, rect, facecolor='w', rmax=rmax, *args,
**kwargs)
fig.add_axes(ax)
return ax
else:
Expand All @@ -125,6 +131,8 @@ def cla(self):

self.patches_list = list()

self.calm_count = None

def _colors(self, cmap, n):
"""
Returns a list of n colors based on the colormap cmap
Expand Down Expand Up @@ -155,10 +163,10 @@ def set_radii_angle(self, **kwargs):
angle=self.radii_angle, **kwargs)

def _update(self):
if self.rmax is None:
self.set_rmax(rmax=np.max(np.sum(self._info['table'], axis=0)))
else:
self.set_rmax(rmax=self.rmax)
if not self.rmax:
self.rmax = np.max(np.sum(self._info['table'], axis=0))
calm_count = self.calm_count or 0
self.set_rmax(rmax=self.rmax + calm_count)
self.set_radii_angle(angle=self.radii_angle)

def legend(self, loc='lower left', decimal_places=1, **kwargs):
Expand Down Expand Up @@ -207,7 +215,8 @@ def get_handles():
else:
raise AttributeError("Can't handle patches")
handles.append(mpl.patches.Rectangle((0, 0), 0.2, 0.2,
facecolor=color, edgecolor='black'))
facecolor=color,
edgecolor='black'))
return handles

def get_labels(decimal_places=1):
Expand Down Expand Up @@ -340,11 +349,35 @@ def _init_plot(self, direction, var, **kwargs):
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 dictionnary
self._info['dir'], self._info['bins'], self._info['table'] = histogram(direction, var, bins, nsector, normed, blowto)
self._info['dir'], self._info['bins'], self._info['table'] \
= histogram(direction, var, bins, nsector, normed, blowto)

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

def _calm_circle(self):
"""
Draw the calm centered circle
and return the initial offset for plots methods
"""
if self.calm_count and self.calm_count > 0:
circle = mpl.patches.Circle((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

def contour(self, direction, var, **kwargs):
"""
Plot a windrose in linear mode. For each var bins, a line will be
Expand Down Expand Up @@ -382,16 +415,17 @@ def contour(self, direction, var, **kwargs):
Any supported argument of :obj:`matplotlib.pyplot.plot`
"""
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(direction, var,
**kwargs)
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(
direction, var, **kwargs
)

# closing lines
angles = np.hstack((angles, angles[-1] - 2 * np.pi / nsector))
vals = np.hstack((self._info['table'],
np.reshape(self._info['table'][:, 0],
(self._info['table'].shape[0], 1))))
np.reshape(self._info['table'][:, 0],
(self._info['table'].shape[0], 1))))

offset = 0
offset = self._calm_circle()
for i in range(nbins):
val = vals[i, :] + offset
offset += vals[i, :]
Expand Down Expand Up @@ -439,8 +473,9 @@ def contourf(self, direction, var, **kwargs):
Any supported argument of :obj:`matplotlib.pyplot.plot`
"""

bins, nbins, nsector, colors, angles, kwargs = self._init_plot(direction, var,
**kwargs)
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(
direction, var, **kwargs
)
kwargs.pop('facecolor', None)
kwargs.pop('edgecolor', None)

Expand All @@ -449,7 +484,7 @@ def contourf(self, direction, var, **kwargs):
vals = np.hstack((self._info['table'],
np.reshape(self._info['table'][:, 0],
(self._info['table'].shape[0], 1))))
offset = 0
offset = self._calm_circle()
for i in range(nbins):
val = vals[i, :] + offset
offset += vals[i, :]
Expand All @@ -458,6 +493,7 @@ def contourf(self, direction, var, **kwargs):
patch = self.fill(xs, ys, facecolor=colors[i],
edgecolor=colors[i], zorder=zorder, **kwargs)
self.patches_list.extend(patch)
self._update()

def bar(self, direction, var, **kwargs):
"""
Expand Down Expand Up @@ -499,8 +535,9 @@ def bar(self, direction, var, **kwargs):
"""

bins, nbins, nsector, colors, angles, kwargs = self._init_plot(direction, var,
**kwargs)
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(
direction, var, **kwargs
)
kwargs.pop('facecolor', None)
edgecolor = kwargs.pop('edgecolor', None)
if edgecolor is not None:
Expand All @@ -512,8 +549,10 @@ def bar(self, direction, var, **kwargs):
dtheta = 2 * np.pi / nsector
opening = dtheta * opening

offs = self._calm_circle()

for j in range(nsector):
offset = 0
offset = offs
for i in range(nbins):
if i > 0:
offset += self._info['table'][i - 1, j]
Expand All @@ -530,8 +569,8 @@ def bar(self, direction, var, **kwargs):

def box(self, direction, var, **kwargs):
"""
Plot a windrose in proportional box mode. For each var bins and for each
sector, a colored box will be draw on the axes.
Plot a windrose in proportional box mode. For each var bins and for
each sector, a colored box will be draw on the axes.
Parameters
----------
Expand Down Expand Up @@ -565,17 +604,20 @@ def box(self, direction, var, **kwargs):
"""

bins, nbins, nsector, colors, angles, kwargs = self._init_plot(direction, var,
**kwargs)
bins, nbins, nsector, colors, angles, kwargs = self._init_plot(
direction, var, **kwargs
)
kwargs.pop('facecolor', None)
edgecolor = kwargs.pop('edgecolor', None)
if edgecolor is not None:
if not isinstance(edgecolor, str):
raise ValueError('edgecolor must be a string color')
opening = np.linspace(0.0, np.pi / 16, nbins)

offs = self._calm_circle()

for j in range(nsector):
offset = 0
offset = offs
for i in range(nbins):
if i > 0:
offset += self._info['table'][i - 1, j]
Expand Down Expand Up @@ -607,9 +649,10 @@ def from_ax(ax=None, fig=None, *args, **kwargs):
fig.add_axes(ax)
return ax
else:
return(ax)
return (ax)

def pdf(self, var, bins=None, Nx=100, bar_color='b', plot_color='g', Nbins=10, *args, **kwargs):
def pdf(self, var, bins=None, Nx=100, bar_color='b', plot_color='g',
Nbins=10, *args, **kwargs):
"""
Draw probability density function and return Weibull distribution
parameters
Expand All @@ -624,7 +667,7 @@ def pdf(self, var, bins=None, Nx=100, bar_color='b', plot_color='g', Nbins=10, *
params = scipy.stats.exponweib.fit(var, floc=0, f0=1)
x = np.linspace(0, bins[-1], Nx)
self.plot(x, scipy.stats.exponweib.pdf(x, *params), color=plot_color)
return(self, params)
return (self, params)


def histogram(direction, var, bins, nsector, normed=False, blowto=False):
Expand Down Expand Up @@ -654,7 +697,7 @@ def histogram(direction, var, bins, nsector, normed=False, blowto=False):
"""

if len(var) != len(direction):
raise(ValueError("var and direction must have same length"))
raise ValueError("var and direction must have same length")

angle = 360. / nsector

Expand Down Expand Up @@ -686,8 +729,8 @@ def histogram(direction, var, bins, nsector, normed=False, blowto=False):
@_autogen_docstring(WindroseAxes.contour)
def wrcontour(direction, var, ax=None, rmax=None, **kwargs):
"""
Draw contour wiprobability density function and return Weitbull distribution
parameters
Draw contour probability density function and return Weibull
distribution parameters.
"""
ax = WindroseAxes.from_ax(ax, rmax=rmax)
ax.contour(direction, var, **kwargs)
Expand Down Expand Up @@ -720,13 +763,15 @@ def wrbar(direction, var, ax=None, rmax=None, **kwargs):


@_autogen_docstring(WindAxes.pdf)
def wrpdf(var, bins=None, Nx=100, bar_color='b', plot_color='g', Nbins=10, ax=None, rmax=None, *args, **kwargs):
def wrpdf(var, bins=None, Nx=100, bar_color='b', plot_color='g', Nbins=10,
ax=None, rmax=None, *args, **kwargs):
"""
Draw probability density function and return Weitbull distribution
parameters
"""
ax = WindAxes.from_ax(ax)
ax, params = ax.pdf(var, bins, Nx, bar_color, plot_color, Nbins, *args, **kwargs)
ax, params = ax.pdf(var, bins, Nx, bar_color, plot_color, Nbins, *args,
**kwargs)
return(ax, params)


Expand All @@ -739,6 +784,7 @@ def wrscatter(direction, var, ax=None, rmax=None, *args, **kwargs):
ax.scatter(direction, var, *args, **kwargs)
return ax


# def clean(direction, var):
# '''
# Remove masked values in the two arrays, where if a direction data is masked,
Expand All @@ -757,7 +803,7 @@ def clean_df(df, var=VAR_DEFAULT, direction=DIR_DEFAULT):
removed from DataFrame
if a direction is nan, this row is also removed from DataFrame
'''
return(df[df[var].notnull() & df[var] != 0 & df[direction].notnull()])
return (df[df[var].notnull() & df[var] != 0 & df[direction].notnull()])


def clean(direction, var, index=False):
Expand Down Expand Up @@ -790,28 +836,36 @@ def clean(direction, var, index=False):
}


def plot_windrose(direction_or_df, var=None, kind='contour', var_name=VAR_DEFAULT, direction_name=DIR_DEFAULT, by=None, rmax=None, **kwargs):
def plot_windrose(direction_or_df, var=None, kind='contour',
var_name=VAR_DEFAULT, direction_name=DIR_DEFAULT, by=None,
rmax=None, **kwargs):
if var is None:
# Assuming direction_or_df is a DataFrame
df = direction_or_df
var = df[var_name].values
direction = df[direction_name].values
else:
direction = direction_or_df
return(plot_windrose_np(direction, var, kind=kind, by=by, rmax=rmax, **kwargs))
return plot_windrose_np(direction, var, kind=kind, by=by, rmax=rmax,
**kwargs)


def plot_windrose_df(df, kind='contour', var_name=VAR_DEFAULT, direction_name=DIR_DEFAULT, by=None, rmax=None, **kwargs):
def plot_windrose_df(df, kind='contour', var_name=VAR_DEFAULT,
direction_name=DIR_DEFAULT, by=None, rmax=None,
**kwargs):
var = df[var_name].values
direction = df[direction_name].values
return(plot_windrose_np(direction, var, by=by, rmax=rmax, **kwargs))
return (plot_windrose_np(direction, var, by=by, rmax=rmax, **kwargs))


def plot_windrose_np(direction, var, kind='contour', clean_flag=True, by=None, rmax=None, **kwargs):
def plot_windrose_np(direction, var, kind='contour', clean_flag=True, by=None,
rmax=None, **kwargs):
if kind in D_KIND_PLOT.keys():
f_plot = D_KIND_PLOT[kind]
else:
raise(Exception("kind=%r but it must be in %r" % (kind, D_KIND_PLOT.keys())))
raise Exception(
"kind=%r but it must be in %r" % (kind, D_KIND_PLOT.keys())
)
# if f_clean is not None:
# df = f_clean(df)
# var = df[var_name].values
Expand All @@ -824,4 +878,7 @@ def plot_windrose_np(direction, var, kind='contour', clean_flag=True, by=None, r
ax.set_legend()
return ax
else:
raise(NotImplementedError("'by' keyword not supported for now https://github.com/scls19fr/windrose/issues/10"))
raise NotImplementedError(
"'by' keyword not supported for now "
"https://github.com/scls19fr/windrose/issues/10"
)

0 comments on commit a2607da

Please sign in to comment.