In [1]:
%matplotlib inline
In [2]:
from statsmodels.compat import lmap
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import statsmodels.api as sm
where $\rho$ is a symmetric function of the residuals
In [3]:
norms = sm.robust.norms
In [4]:
def plot_weights(support, weights_func, xlabels, xticks):
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(111)
ax.plot(support, weights_func(support))
ax.set_xticks(xticks)
ax.set_xticklabels(xlabels, fontsize=16)
ax.set_ylim(-.1, 1.1)
return ax
In [5]:
help(norms.AndrewWave.weights)
In [6]:
a = 1.339
support = np.linspace(-np.pi*a, np.pi*a, 100)
andrew = norms.AndrewWave(a=a)
plot_weights(support, andrew.weights, ['$-\pi*a$', '0', '$\pi*a$'], [-np.pi*a, 0, np.pi*a]);
In [7]:
help(norms.Hampel.weights)
In [8]:
c = 8
support = np.linspace(-3*c, 3*c, 1000)
hampel = norms.Hampel(a=2., b=4., c=c)
plot_weights(support, hampel.weights, ['3*c', '0', '3*c'], [-3*c, 0, 3*c]);
In [9]:
help(norms.HuberT.weights)
In [10]:
t = 1.345
support = np.linspace(-3*t, 3*t, 1000)
huber = norms.HuberT(t=t)
plot_weights(support, huber.weights, ['-3*t', '0', '3*t'], [-3*t, 0, 3*t]);
In [11]:
help(norms.LeastSquares.weights)
In [12]:
support = np.linspace(-3, 3, 1000)
lst_sq = norms.LeastSquares()
plot_weights(support, lst_sq.weights, ['-3', '0', '3'], [-3, 0, 3]);
In [13]:
help(norms.RamsayE.weights)
In [14]:
a = .3
support = np.linspace(-3*a, 3*a, 1000)
ramsay = norms.RamsayE(a=a)
plot_weights(support, ramsay.weights, ['-3*a', '0', '3*a'], [-3*a, 0, 3*a]);
In [15]:
help(norms.TrimmedMean.weights)
In [16]:
c = 2
support = np.linspace(-3*c, 3*c, 1000)
trimmed = norms.TrimmedMean(c=c)
plot_weights(support, trimmed.weights, ['-3*c', '0', '3*c'], [-3*c, 0, 3*c]);
In [17]:
help(norms.TukeyBiweight.weights)
In [18]:
c = 4.685
support = np.linspace(-3*c, 3*c, 1000)
tukey = norms.TukeyBiweight(c=c)
plot_weights(support, tukey.weights, ['-3*c', '0', '3*c'], [-3*c, 0, 3*c]);
In [19]:
x = np.array([1, 2, 3, 4, 500])
In [20]:
x.mean()
Out[20]:
In [21]:
np.median(x)
Out[21]:
In [22]:
x.std()
Out[22]:
Median Absolute Deviation
$$ median_i |X_i - median_j(X_j)|) $$Standardized Median Absolute Deviation is a consistent estimator for $\hat{\sigma}$
$$\hat{\sigma}=K \cdot MAD$$where $K$ depends on the distribution. For the normal distribution for example,
$$K = \Phi^{-1}(.75)$$
In [23]:
stats.norm.ppf(.75)
Out[23]:
In [24]:
print(x)
In [25]:
sm.robust.scale.mad(x)
Out[25]:
In [26]:
np.array([1,2,3,4,5.]).std()
Out[26]:
In [27]:
np.random.seed(12345)
fat_tails = stats.t(6).rvs(40)
In [28]:
kde = sm.nonparametric.KDEUnivariate(fat_tails)
kde.fit()
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(111)
ax.plot(kde.support, kde.density);
In [29]:
print(fat_tails.mean(), fat_tails.std())
In [30]:
print(stats.norm.fit(fat_tails))
In [31]:
print(stats.t.fit(fat_tails, f0=6))
In [32]:
huber = sm.robust.scale.Huber()
loc, scale = huber(fat_tails)
print(loc, scale)
In [33]:
sm.robust.mad(fat_tails)
Out[33]:
In [34]:
sm.robust.mad(fat_tails, c=stats.t(6).ppf(.75))
Out[34]:
In [35]:
sm.robust.scale.mad(fat_tails)
Out[35]:
In [36]:
from statsmodels.graphics.api import abline_plot
from statsmodels.formula.api import ols, rlm
In [37]:
prestige = sm.datasets.get_rdataset("Duncan", "carData", cache=True).data
In [38]:
print(prestige.head(10))
In [39]:
fig = plt.figure(figsize=(12,12))
ax1 = fig.add_subplot(211, xlabel='Income', ylabel='Prestige')
ax1.scatter(prestige.income, prestige.prestige)
xy_outlier = prestige.loc['minister', ['income','prestige']]
ax1.annotate('Minister', xy_outlier, xy_outlier+1, fontsize=16)
ax2 = fig.add_subplot(212, xlabel='Education',
ylabel='Prestige')
ax2.scatter(prestige.education, prestige.prestige);
In [40]:
ols_model = ols('prestige ~ income + education', prestige).fit()
print(ols_model.summary())
In [41]:
infl = ols_model.get_influence()
student = infl.summary_frame()['student_resid']
print(student)
In [42]:
print(student.loc[np.abs(student) > 2])
In [43]:
print(infl.summary_frame().loc['minister'])
In [44]:
sidak = ols_model.outlier_test('sidak')
sidak.sort_values('unadj_p', inplace=True)
print(sidak)
In [45]:
fdr = ols_model.outlier_test('fdr_bh')
fdr.sort_values('unadj_p', inplace=True)
print(fdr)
In [46]:
rlm_model = rlm('prestige ~ income + education', prestige).fit()
print(rlm_model.summary())
In [47]:
print(rlm_model.weights)
In [48]:
dta = sm.datasets.get_rdataset("starsCYG", "robustbase", cache=True).data
In [49]:
from matplotlib.patches import Ellipse
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(111, xlabel='log(Temp)', ylabel='log(Light)', title='Hertzsprung-Russell Diagram of Star Cluster CYG OB1')
ax.scatter(*dta.values.T)
# highlight outliers
e = Ellipse((3.5, 6), .2, 1, alpha=.25, color='r')
ax.add_patch(e);
ax.annotate('Red giants', xy=(3.6, 6), xytext=(3.8, 6),
arrowprops=dict(facecolor='black', shrink=0.05, width=2),
horizontalalignment='left', verticalalignment='bottom',
clip_on=True, # clip to the axes bounding box
fontsize=16,
)
# annotate these with their index
for i,row in dta.loc[dta['log.Te'] < 3.8].iterrows():
ax.annotate(i, row, row + .01, fontsize=14)
xlim, ylim = ax.get_xlim(), ax.get_ylim()
In [50]:
from IPython.display import Image
Image(filename='star_diagram.png')
Out[50]:
In [51]:
y = dta['log.light']
X = sm.add_constant(dta['log.Te'], prepend=True)
ols_model = sm.OLS(y, X).fit()
abline_plot(model_results=ols_model, ax=ax)
Out[51]:
In [52]:
rlm_mod = sm.RLM(y, X, sm.robust.norms.TrimmedMean(.5)).fit()
abline_plot(model_results=rlm_mod, ax=ax, color='red')
Out[52]:
In [53]:
infl = ols_model.get_influence()
In [54]:
h_bar = 2*(ols_model.df_model + 1 )/ols_model.nobs
hat_diag = infl.summary_frame()['hat_diag']
hat_diag.loc[hat_diag > h_bar]
Out[54]:
In [55]:
sidak2 = ols_model.outlier_test('sidak')
sidak2.sort_values('unadj_p', inplace=True)
print(sidak2)
In [56]:
fdr2 = ols_model.outlier_test('fdr_bh')
fdr2.sort_values('unadj_p', inplace=True)
print(fdr2)
In [57]:
l = ax.lines[-1]
l.remove()
del l
In [58]:
weights = np.ones(len(X))
weights[X[X['log.Te'] < 3.8].index.values - 1] = 0
wls_model = sm.WLS(y, X, weights=weights).fit()
abline_plot(model_results=wls_model, ax=ax, color='green')
Out[58]:
In [59]:
yy = y.values[:,None]
xx = X['log.Te'].values[:,None]
Note: The R code and the results in this notebook has been converted to markdown so that R is not required to build the documents. The R results in the notebook were computed using R 3.5.1 and robustbase 0.93.
ipython
%load_ext rpy2.ipython
%R library(robustbase)
%Rpush yy xx
%R mod <- lmrob(yy ~ xx);
%R params <- mod$coefficients;
%Rpull params
ipython
%R print(mod)
Call:
lmrob(formula = yy ~ xx)
\--> method = "MM"
Coefficients:
(Intercept) xx
-4.969 2.253
In [60]:
params = [-4.969387980288108, 2.2531613477892365] # Computed using R
print(params[0], params[1])
In [61]:
abline_plot(intercept=params[0], slope=params[1], ax=ax, color='red')
Out[61]:
In [62]:
np.random.seed(12345)
nobs = 200
beta_true = np.array([3, 1, 2.5, 3, -4])
X = np.random.uniform(-20,20, size=(nobs, len(beta_true)-1))
# stack a constant in front
X = sm.add_constant(X, prepend=True) # np.c_[np.ones(nobs), X]
mc_iter = 500
contaminate = .25 # percentage of response variables to contaminate
In [63]:
all_betas = []
for i in range(mc_iter):
y = np.dot(X, beta_true) + np.random.normal(size=200)
random_idx = np.random.randint(0, nobs, size=int(contaminate * nobs))
y[random_idx] = np.random.uniform(-750, 750)
beta_hat = sm.RLM(y, X).fit().params
all_betas.append(beta_hat)
In [64]:
all_betas = np.asarray(all_betas)
se_loss = lambda x : np.linalg.norm(x, ord=2)**2
se_beta = lmap(se_loss, all_betas - beta_true)
In [65]:
np.array(se_beta).mean()
Out[65]:
In [66]:
all_betas.mean(0)
Out[66]:
In [67]:
beta_true
Out[67]:
In [68]:
se_loss(all_betas.mean(0) - beta_true)
Out[68]: