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

Run pylint as a required check in Travis CI #248

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run]
omit = */tests/*
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ install:
- pip install -r requirements.txt

script:
- py.test --cov=pygam
- pytest --cov=pygam --cov-config=.coveragerc --cov-report term-missing --disable-warnings pygam/tests/
- pytest --pylint --pylint-error-types=EF -m pylint --disable-warnings

after_success:
- codecov
37 changes: 25 additions & 12 deletions gen_imgs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
generate some plots for the pyGAM repo
Generate some plots for the pyGAM repo.
"""
import pandas as pd
import numpy as np
Expand All @@ -8,7 +8,10 @@
from mpl_toolkits import mplot3d

from pygam import *
from pygam.datasets import hepatitis, wage, faithful, mcycle, trees, default, cake, toy_classification, toy_interaction, chicago
from pygam.datasets import (
hepatitis, wage, faithful, mcycle, trees, default, cake,
toy_classification, toy_interaction, chicago
)

np.random.seed(420)
fontP = FontProperties()
Expand All @@ -21,13 +24,14 @@
# monotonic increasing, concave constraint on hep data
# prediction intervals on motorcycle data


def gen_basis_fns():
X, y = hepatitis()
gam = LinearGAM(lam=.6, fit_intercept=False).fit(X, y)
XX = gam.generate_X_grid(term=0, n=500)

plt.figure()
fig, ax = plt.subplots(2,1)
fig, ax = plt.subplots(2, 1)
ax[0].plot(XX, gam._modelmat(XX, term=0).A);
ax[0].set_title('b-Spline Basis Functions')

Expand All @@ -38,19 +42,21 @@ def gen_basis_fns():
fig.tight_layout()
plt.savefig('imgs/pygam_basis.png', dpi=300)


def cake_data_in_one():
X, y = cake()

gam = LinearGAM(fit_intercept=True)
gam.gridsearch(X,y)
gam.gridsearch(X, y)

XX = gam.generate_X_grid()
XX = gam.generate_X_grid(term=0)

plt.figure()
plt.plot(gam.partial_dependence(XX))
plt.title('LinearGAM')
plt.savefig('imgs/pygam_cake_data.png', dpi=300)


def faithful_data_poisson():
X, y = faithful()
gam = PoissonGAM().gridsearch(X, y)
Expand All @@ -62,6 +68,7 @@ def faithful_data_poisson():
plt.title('Best Lambda: {0:.2f}'.format(gam.lam[0][0]))
plt.savefig('imgs/pygam_poisson.png', dpi=300)


def single_data_linear():
X, y = mcycle()

Expand All @@ -75,6 +82,7 @@ def single_data_linear():
plt.title('Best Lambda: {0:.2f}'.format(gam.lam))
plt.savefig('imgs/pygam_single_pred_linear.png', dpi=300)


def mcycle_data_linear():
X, y = mcycle()

Expand All @@ -90,7 +98,6 @@ def mcycle_data_linear():

plt.savefig('imgs/pygam_mcycle_data_linear.png', dpi=300)


m = X.min()
M = X.max()
XX = np.linspace(m - 10, M + 10, 500)
Expand All @@ -106,14 +113,15 @@ def mcycle_data_linear():

plt.savefig('imgs/pygam_mcycle_data_extrapolation.png', dpi=300)


def wage_data_linear():
X, y = wage()

gam = LinearGAM(s(0) + s(1) + f(2))
gam.gridsearch(X, y, lam=np.logspace(-5,3,50))
gam.gridsearch(X, y, lam=np.logspace(-5, 3, 50))

plt.figure()
fig, axs = plt.subplots(1,3)
fig, axs = plt.subplots(1, 3)

titles = ['year', 'age', 'education']
for i, ax in enumerate(axs):
Expand All @@ -122,20 +130,21 @@ def wage_data_linear():
ax.plot(XX[:, i], gam.partial_dependence(term=i, X=XX, width=.95)[1],
c='r', ls='--')
if i == 0:
ax.set_ylim(-30,30);
ax.set_ylim(-30, 30);
ax.set_title(titles[i])

fig.tight_layout()
plt.savefig('imgs/pygam_wage_data_linear.png', dpi=300)


def default_data_logistic():
X, y = default()

gam = LogisticGAM(f(0) + s(1) + s(2))
gam.gridsearch(X, y)

plt.figure()
fig, axs = plt.subplots(1,3)
fig, axs = plt.subplots(1, 3)

titles = ['student', 'balance', 'income']
for i, ax in enumerate(axs):
Expand All @@ -149,6 +158,7 @@ def default_data_logistic():
fig.tight_layout()
plt.savefig('imgs/pygam_default_data_logistic.png', dpi=300)


def constraints():
X, y = hepatitis(return_X_y=True)

Expand All @@ -167,6 +177,7 @@ def constraints():
fig.tight_layout()
plt.savefig('imgs/pygam_constraints.png', dpi=300)


def trees_data_custom():
X, y = trees()
gam = GAM(distribution='gamma', link='log')
Expand Down Expand Up @@ -247,25 +258,27 @@ def gen_multi_data(n=5000):
plt.plot(lgam.logs_['deviance'])
plt.savefig('imgs/pygam_multi_deviance.png', dpi=300)


def gen_tensor_data():
"""
toy interaction data
"""
X, y = toy_interaction(return_X_y=True, n=10000)

gam = LinearGAM(te(0, 1,lam=0.1)).fit(X, y)
gam = LinearGAM(te(0, 1, lam=0.1)).fit(X, y)

XX = gam.generate_X_grid(term=0, meshgrid=True)
Z = gam.partial_dependence(term=0, meshgrid=True)

fig = plt.figure(figsize=(9,6))
fig = plt.figure(figsize=(9, 6))
ax = plt.axes(projection='3d')
ax.dist = 7.5
ax.plot_surface(XX[0], XX[1], Z, cmap='viridis')
ax.set_axis_off()
fig.tight_layout()
plt.savefig('imgs/pygam_tensor.png', transparent=True, dpi=300)


def chicago_tensor():
"""
chicago tensor
Expand Down
23 changes: 13 additions & 10 deletions pygam/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def method_wrapper(*args, **kwargs):

return method_wrapper


def validate_callback(callback):
"""
validates a callback's on_loop_start and on_loop_end methods
Expand All @@ -77,10 +78,10 @@ def validate_callback(callback):
-------
validated callback
"""
if not(hasattr(callback, '_validated')) or callback._validated == False:
assert hasattr(callback, 'on_loop_start') \
or hasattr(callback, 'on_loop_end'), \
'callback must have `on_loop_start` or `on_loop_end` method'
if not(hasattr(callback, '_validated')) or callback._validated is False:
assert (
hasattr(callback, 'on_loop_start') or hasattr(callback, 'on_loop_end')
), 'callback must have `on_loop_start` or `on_loop_end` method'
if hasattr(callback, 'on_loop_start'):
setattr(callback, 'on_loop_start',
validate_callback_data(callback.on_loop_start))
Expand Down Expand Up @@ -181,7 +182,7 @@ def on_loop_start(self, y, mu):
-------
accuracy : np.array of length n
"""
return np.mean(y == (mu>0.5))
return np.mean(y == (mu > 0.5))


@validate_callback
Expand Down Expand Up @@ -217,6 +218,7 @@ def on_loop_end(self, diff):
"""
return diff


@validate_callback
class Coef(CallBack):
def __init__(self):
Expand Down Expand Up @@ -250,8 +252,9 @@ def on_loop_start(self, gam):
return gam.coef_


CALLBACKS = {'deviance': Deviance,
'diffs': Diffs,
'accuracy': Accuracy,
'coef': Coef
}
CALLBACKS = {
'deviance': Deviance,
'diffs': Diffs,
'accuracy': Accuracy,
'coef': Coef
}
32 changes: 18 additions & 14 deletions pygam/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

from pygam.utils import round_to_n_decimal_places, flatten

def nice_repr(name, param_kvs, line_width=30, line_offset=5, decimals=3, args=None, flatten_attrs=True):

def nice_repr(
name, param_kvs, line_width=30, line_offset=5, decimals=3, args=None, flatten_attrs=True
):
"""
tool to do a nice repr of a class.

Expand All @@ -34,15 +37,15 @@ class name
out : str
nicely formatted repr of class instance
"""
if not param_kvs and not args :
if not param_kvs and not args:
# if the object has no params it's easy
return '{}()'.format(name)

# sort keys and values
ks = list(param_kvs.keys())
vs = list(param_kvs.values())
idxs = np.argsort(ks)
param_kvs = [(ks[i],vs[i]) for i in idxs]
param_kvs = [(ks[i], vs[i]) for i in idxs]

if args is not None:
param_kvs = [(None, arg) for arg in args] + param_kvs
Expand All @@ -54,7 +57,7 @@ class name

# flatten sub-term properties, but not `terms`
k, v = param_kvs.pop()
if flatten_attrs and k is not 'terms':
if flatten_attrs and k != 'terms':
v = flatten(v)

# round the floats first
Expand All @@ -75,12 +78,12 @@ class name
current_line += param
else:
out += current_line + '\n'
current_line = ' '*line_offset + param
current_line = ' ' * line_offset + param

if len(current_line) < line_width and len(param_kvs) > 0:
current_line += ' '

out += current_line[:-1] # remove trailing comma
out += current_line[:-1] # remove trailing comma
out += ')'
return out

Expand Down Expand Up @@ -114,7 +117,6 @@ def __init__(self, name=None, line_width=70, line_offset=3):
if not hasattr(self, '_include'):
self._include = []


def __str__(self):
"""__str__ method"""
if self._name is None:
Expand Down Expand Up @@ -148,10 +150,10 @@ def get_params(self, deep=False):

if deep is True:
return attrs
return dict([(k,v) for k,v in list(attrs.items()) \
if (k[0] != '_') \
and (k[-1] != '_') \
and (k not in self._exclude)])
return dict(
[(k, v) for k, v in list(attrs.items())
if (k[0] != '_') and (k[-1] != '_') and (k not in self._exclude)]
)

def set_params(self, deep=False, force=False, **parameters):
"""
Expand All @@ -172,8 +174,10 @@ def set_params(self, deep=False, force=False, **parameters):
"""
param_names = self.get_params(deep=deep).keys()
for parameter, value in parameters.items():
if (parameter in param_names
or force
or (hasattr(self, parameter) and parameter == parameter.strip('_'))):
if (
(parameter in param_names) or force or (
hasattr(self, parameter) and parameter == parameter.strip('_')
)
):
setattr(self, parameter, value)
return self
Loading