|
54 | 54 | # need import in module instead of lazily to copy `__doc__`
|
55 | 55 | from . import families
|
56 | 56 |
|
| 57 | + |
57 | 58 | __all__ = ['GLM', 'PredictionResultsMean']
|
58 | 59 |
|
59 | 60 |
|
@@ -624,6 +625,90 @@ def information(self, params, scale=None):
|
624 | 625 | scale = float_like(scale, "scale", optional=True)
|
625 | 626 | return self.hessian(params, scale=scale, observed=False)
|
626 | 627 |
|
| 628 | + def _derivative_exog(self, params, exog=None, transform="dydx", |
| 629 | + dummy_idx=None, count_idx=None, |
| 630 | + offset=None, exposure=None): |
| 631 | + """ |
| 632 | + Derivative of mean, expected endog with respect to the parameters |
| 633 | + """ |
| 634 | + if exog is None: |
| 635 | + exog = self.exog |
| 636 | + if (offset is not None) or (exposure is not None): |
| 637 | + raise NotImplementedError("offset and exposure not supported") |
| 638 | + |
| 639 | + lin_pred = self.predict(params, exog, which="linear", |
| 640 | + offset=offset, exposure=exposure) |
| 641 | + |
| 642 | + k_extra = getattr(self, 'k_extra', 0) |
| 643 | + params_exog = params if k_extra == 0 else params[:-k_extra] |
| 644 | + |
| 645 | + margeff = (self.family.link.inverse_deriv(lin_pred)[:, None] * |
| 646 | + params_exog) |
| 647 | + if 'ex' in transform: |
| 648 | + margeff *= exog |
| 649 | + if 'ey' in transform: |
| 650 | + mean = self.family.link.inverse(lin_pred) |
| 651 | + margeff /= mean[:,None] |
| 652 | + |
| 653 | + return self._derivative_exog_helper(margeff, params, exog, |
| 654 | + dummy_idx, count_idx, transform) |
| 655 | + |
| 656 | + def _derivative_exog_helper(self, margeff, params, exog, dummy_idx, |
| 657 | + count_idx, transform): |
| 658 | + """ |
| 659 | + Helper for _derivative_exog to wrap results appropriately |
| 660 | + """ |
| 661 | + from statsmodels.discrete.discrete_margins import ( |
| 662 | + _get_count_effects, |
| 663 | + _get_dummy_effects, |
| 664 | + ) |
| 665 | + |
| 666 | + if count_idx is not None: |
| 667 | + margeff = _get_count_effects(margeff, exog, count_idx, transform, |
| 668 | + self, params) |
| 669 | + if dummy_idx is not None: |
| 670 | + margeff = _get_dummy_effects(margeff, exog, dummy_idx, transform, |
| 671 | + self, params) |
| 672 | + |
| 673 | + return margeff |
| 674 | + |
| 675 | + def _derivative_predict(self, params, exog=None, transform='dydx', |
| 676 | + offset=None, exposure=None): |
| 677 | + """ |
| 678 | + Derivative of the expected endog with respect to the parameters. |
| 679 | +
|
| 680 | + Parameters |
| 681 | + ---------- |
| 682 | + params : ndarray |
| 683 | + parameter at which score is evaluated |
| 684 | + exog : ndarray or None |
| 685 | + Explanatory variables at which derivative are computed. |
| 686 | + If None, then the estimation exog is used. |
| 687 | + offset, exposure : None |
| 688 | + Not yet implemented. |
| 689 | +
|
| 690 | + Returns |
| 691 | + ------- |
| 692 | + The value of the derivative of the expected endog with respect |
| 693 | + to the parameter vector. |
| 694 | + """ |
| 695 | + # core part is same as derivative_mean_params |
| 696 | + # additionally handles exog and transform |
| 697 | + if exog is None: |
| 698 | + exog = self.exog |
| 699 | + if (offset is not None) or (exposure is not None) or ( |
| 700 | + getattr(self, 'offset', None) is not None): |
| 701 | + raise NotImplementedError("offset and exposure not supported") |
| 702 | + |
| 703 | + lin_pred = self.predict(params, exog=exog, which="linear") |
| 704 | + idl = self.family.link.inverse_deriv(lin_pred) |
| 705 | + dmat = exog * idl[:, None] |
| 706 | + if 'ey' in transform: |
| 707 | + mean = self.family.link.inverse(lin_pred) |
| 708 | + dmat /= mean[:, None] |
| 709 | + |
| 710 | + return dmat |
| 711 | + |
627 | 712 | def _deriv_mean_dparams(self, params):
|
628 | 713 | """
|
629 | 714 | Derivative of the expected endog with respect to the parameters.
|
@@ -2210,6 +2295,92 @@ def get_distribution(self, exog=None, exposure=None,
|
2210 | 2295 | mu, scale, var_weights=var_weights, **kwds)
|
2211 | 2296 | return distr
|
2212 | 2297 |
|
| 2298 | + def get_margeff(self, at='overall', method='dydx', atexog=None, |
| 2299 | + dummy=False, count=False): |
| 2300 | + """Get marginal effects of the fitted model. |
| 2301 | +
|
| 2302 | + Warning: offset, exposure and weights (var_weights and freq_weights) |
| 2303 | + are not supported by margeff. |
| 2304 | +
|
| 2305 | + Parameters |
| 2306 | + ---------- |
| 2307 | + at : str, optional |
| 2308 | + Options are: |
| 2309 | +
|
| 2310 | + - 'overall', The average of the marginal effects at each |
| 2311 | + observation. |
| 2312 | + - 'mean', The marginal effects at the mean of each regressor. |
| 2313 | + - 'median', The marginal effects at the median of each regressor. |
| 2314 | + - 'zero', The marginal effects at zero for each regressor. |
| 2315 | + - 'all', The marginal effects at each observation. If `at` is all |
| 2316 | + only margeff will be available from the returned object. |
| 2317 | +
|
| 2318 | + Note that if `exog` is specified, then marginal effects for all |
| 2319 | + variables not specified by `exog` are calculated using the `at` |
| 2320 | + option. |
| 2321 | + method : str, optional |
| 2322 | + Options are: |
| 2323 | +
|
| 2324 | + - 'dydx' - dy/dx - No transformation is made and marginal effects |
| 2325 | + are returned. This is the default. |
| 2326 | + - 'eyex' - estimate elasticities of variables in `exog` -- |
| 2327 | + d(lny)/d(lnx) |
| 2328 | + - 'dyex' - estimate semi-elasticity -- dy/d(lnx) |
| 2329 | + - 'eydx' - estimate semi-elasticity -- d(lny)/dx |
| 2330 | +
|
| 2331 | + Note that tranformations are done after each observation is |
| 2332 | + calculated. Semi-elasticities for binary variables are computed |
| 2333 | + using the midpoint method. 'dyex' and 'eyex' do not make sense |
| 2334 | + for discrete variables. For interpretations of these methods |
| 2335 | + see notes below. |
| 2336 | + atexog : array_like, optional |
| 2337 | + Optionally, you can provide the exogenous variables over which to |
| 2338 | + get the marginal effects. This should be a dictionary with the key |
| 2339 | + as the zero-indexed column number and the value of the dictionary. |
| 2340 | + Default is None for all independent variables less the constant. |
| 2341 | + dummy : bool, optional |
| 2342 | + If False, treats binary variables (if present) as continuous. This |
| 2343 | + is the default. Else if True, treats binary variables as |
| 2344 | + changing from 0 to 1. Note that any variable that is either 0 or 1 |
| 2345 | + is treated as binary. Each binary variable is treated separately |
| 2346 | + for now. |
| 2347 | + count : bool, optional |
| 2348 | + If False, treats count variables (if present) as continuous. This |
| 2349 | + is the default. Else if True, the marginal effect is the |
| 2350 | + change in probabilities when each observation is increased by one. |
| 2351 | +
|
| 2352 | + Returns |
| 2353 | + ------- |
| 2354 | + DiscreteMargins : marginal effects instance |
| 2355 | + Returns an object that holds the marginal effects, standard |
| 2356 | + errors, confidence intervals, etc. See |
| 2357 | + `statsmodels.discrete.discrete_margins.DiscreteMargins` for more |
| 2358 | + information. |
| 2359 | +
|
| 2360 | + Notes |
| 2361 | + ----- |
| 2362 | + Interpretations of methods: |
| 2363 | +
|
| 2364 | + - 'dydx' - change in `endog` for a change in `exog`. |
| 2365 | + - 'eyex' - proportional change in `endog` for a proportional change |
| 2366 | + in `exog`. |
| 2367 | + - 'dyex' - change in `endog` for a proportional change in `exog`. |
| 2368 | + - 'eydx' - proportional change in `endog` for a change in `exog`. |
| 2369 | +
|
| 2370 | + When using after Poisson, returns the expected number of events per |
| 2371 | + period, assuming that the model is loglinear. |
| 2372 | +
|
| 2373 | + Status : unsupported features offset, exposure and weights. Default |
| 2374 | + handling of freq_weights for average effect "overall" might change. |
| 2375 | +
|
| 2376 | + """ |
| 2377 | + if getattr(self.model, "offset", None) is not None: |
| 2378 | + raise NotImplementedError("Margins with offset are not available.") |
| 2379 | + if (np.any(self.model.var_weights != 1) or |
| 2380 | + np.any(self.model.freq_weights != 1)): |
| 2381 | + warnings.warn("weights are not taken into account by margeff") |
| 2382 | + from statsmodels.discrete.discrete_margins import DiscreteMargins |
| 2383 | + return DiscreteMargins(self, (at, method, atexog, dummy, count)) |
2213 | 2384 |
|
2214 | 2385 | @Appender(base.LikelihoodModelResults.remove_data.__doc__)
|
2215 | 2386 | def remove_data(self):
|
|
0 commit comments