Add estimation via normal approximations

This commit is contained in:
Brian Quistorff 2020-05-28 08:45:58 -07:00
Родитель 1003a1ea52
Коммит 0a55d3ef30
5 изменённых файлов: 401 добавлений и 7 удалений

Просмотреть файл

@ -5,19 +5,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
<!-- Separate headings for Added/Changed/Removed/Fixed/Deprecated/Security -->
## [Unreleased]
## [Unreleased](https://github.com/Microsoft/SparseSC/compare/v0.2.0...master)
### Added
- Added additional option `Y_col_block_size` to `MTLassoCV_MatchSpace_factory` to estimate `V` on block-averages of `Y` (e.g. taking a 150 cols down to 5 by doing averages over 30 cols at a time).
- Added `se_factor` to `MTLassoCV_MatchSpace_factory` to use a different penalty than the MSE min.
- For large data, approximate the outcomes using a normal distribution (`DescrSet`), and allow for calculating estimates.
## 0.2.0 - 2020-05-06
### Added
- Added tools too to use `fit_fast()` with large datasets. This includes the `sample_frac` option to `MTLassoCV_MatchSpace_factory()` to estimate the match space on a subset of the observations. It also includes `fit_fast()` option `avoid_NxN_mats` which will avoid making large matrices (at the expense of only returning the Synthetic control `targets` and `targets_aux` and not the full weight matrix)
- Added logging in `fit_fast` via the `verbose` numerical option. This can help identify out-of-memory errors.
- Added a pseudo-Doubly robust match space maker `D_LassoCV_MatchSpace_factory`. It apporptions some of the normalized variable V weight to those variables that are good predictors of treatment. This should only be done if there are many treated units so that one can reasonably model this relationship.
- Switched using standardized Azure Batch config library
## 0.1.0 - 2019-07-25
Initial release.
<!--[Unreleased]: Get compare link for Github. For VSTS it's https://econpricingengine.visualstudio.com/_git/PricingEngine/branches?baseVersion=GTv2.1.0&targetVersion=GBmaster&_a=commits >

Просмотреть файл

@ -9,10 +9,14 @@ from SparseSC.utils.match_space import (
Fixed_V_factory, D_LassoCV_MatchSpace_factory
)
from SparseSC.utils.penalty_utils import RidgeCVSolution
from SparseSC.estimate_effects import estimate_effects
from SparseSC.fit_loo import loo_v_matrix, loo_weights, loo_score
from SparseSC.fit_ct import ct_v_matrix, ct_weights, ct_score
# ESTIMATION FUNCTIONS
from SparseSC.estimate_effects import estimate_effects
from SparseSC.utils.dist_summary import SSC_DescrStat, Estimate
from SparseSC.utils.descr_sets import DescrSet, MatchingEstimate
# Public API
from SparseSC.cross_validation import (
score_train_test,

Просмотреть файл

@ -0,0 +1,132 @@
"""Store the typical information for a match.
"""
from collections import namedtuple
from SparseSC.utils.dist_summary import SSC_DescrStat
MatchingEstimate = namedtuple(
'MatchingEstimate', 'att_est att_debiased_est atut_est atut_debiased_est ate_est ate_debiased_est aa_est naive_est')
#"""
#aa = The difference between control counterfactuals for controls and controls (Y_c_cf_c - Y_c)
#Debiased means we subtract from the estimate the aa estimate
#att = Average Treatment effect on the Treated (Y_t - Y_t_cf_c).
#atut = Average Treatment on the UnTreated (Y_c_cf_t - Y_c)
#ate = Average Treatment Effect (pooling att and atut samples)
#naive = Just comparing Y_t and Y_c (no matching). Helpful for comparison (gauging selection size)
#"""
class DescrSet:
"""Holds potential distribution summaries for the various data used for matching
"""
def __init__(self, descr_Y_t=None, descr_Y_t_cf_c=None, descr_Y_diff_t_cf_c=None,
descr_Y_c=None, descr_Y_diff_t_c=None, descr_Y_c_cf_c=None, descr_Y_diff_c_cf_c=None,
descr_Y_c_cf_t=None, descr_Y_diff_c_cf_t=None):
"""Generate the common descriptive stats from data and differences
:param descr_Y_t: SSC_DescrStat for Y_t (outcomes for treated units)
:param descr_Y_t_cf_c: SSC_DescrStat for Y_t_cf_c (outcomes for (control) counterfactuals of treated units)
:param descr_Y_diff_t_cf_c: SSC_DescrStat of Y_t - Y_t_cf_c
:param descr_Y_c: SSC_DescrStat of Y_c (outcomes for control units)
:param descr_Y_diff_t_c: SSC_DescrStat for Y_t-Y_c
:param descr_Y_c_cf_c: SSC_DescrStat for Y_c_cf_c (outcomes for (control) counterfactuals of control units)
:param descr_Y_diff_c_cf_c: SSC_DescrStat for Y_c - Y_c_cf_c
:param descr_Y_c_cf_t: SSC_DescrStat for Y_c_cf_t (outcomes for (TREATED) counterfactuals of control units)
:param descr_Y_diff_c_cf_t: SSC_DescrStat for Y_c_cf_t - Y_c
"""
self.descr_Y_t = descr_Y_t
self.descr_Y_t_cf_c = descr_Y_t_cf_c
self.descr_Y_diff_t_cf_c = descr_Y_diff_t_cf_c
self.descr_Y_c = descr_Y_c
self.descr_Y_diff_t_c = descr_Y_diff_t_c
self.descr_Y_c_cf_c = descr_Y_c_cf_c
self.descr_Y_diff_c_cf_c = descr_Y_diff_c_cf_c
self.descr_Y_c_cf_t = descr_Y_c_cf_t
self.descr_Y_diff_c_cf_t = descr_Y_diff_c_cf_t
def __repr__(self):
return ("%s(descr_Y_t=%s, descr_Y_t_cf_c=%s, descr_Y_diff_t_cf_c=%s, descr_Y_c=%s" +
"descr_Y_diff_t_c=%s, descr_Y_c_cf_c=%s, descr_Y_diff_c_cf_c=%s, descr_Y_c_cf_t=%s," +
" descr_Y_diff_c_cf_t=%s)") % (self.__class__.__name__, self.descr_Y_t,
self.descr_Y_t_cf_c, self.descr_Y_diff_t_cf_c, self.descr_Y_c,
self.descr_Y_diff_t_c, self.descr_Y_c_cf_c, self.descr_Y_diff_c_cf_c,
self.descr_Y_c_cf_t, self.descr_Y_diff_c_cf_t)
@staticmethod
def from_data(Y_t=None, Y_t_cf_c=None, Y_c=None, Y_c_cf_c=None, Y_c_cf_t=None):
"""Generate the common descriptive stats from data and differences
:param Y_t: np.array of dim=(N_t, T) for outcomes for treated units
:param Y_t_cf_c: np.array of dim=(N_t, T) for outcomes for (control) counterfactuals of treated units (used to get the average treatment effect on the treated (ATT))
:param Y_c: np.array of dim=(N_c, T) for outcomes for control units
:param Y_c_cf_c: np.array of dim=(N_c, T) for outcomes for (control) counterfactuals of control units (used for AA test)
:param Y_c_cf_t: np.array of dim=(N_c, T) for outcomes for (TREATED) counterfactuals of control units (used to calculate average treatment effect on the untreated (ATUT), or pooled with ATT to get the average treatment effect (ATE))
:returns: DescrSet
"""
# Note: While possible, there's no real use for treated matched to other treateds.
def _gen_if_valid(Y):
return SSC_DescrStat.from_data(Y) if Y is not None else None
def _gen_diff_if_valid(Y1, Y2):
return SSC_DescrStat.from_data(Y1-Y2) if (Y1 is not None and Y2 is not None) else None
descr_Y_t = _gen_if_valid(Y_t)
descr_Y_t_cf_c = _gen_if_valid(Y_t_cf_c)
descr_Y_diff_t_cf_c = _gen_diff_if_valid(Y_t, Y_t_cf_c)
descr_Y_c = _gen_if_valid(Y_c)
descr_Y_diff_t_c = _gen_diff_if_valid(Y_t, Y_c)
descr_Y_c_cf_c = _gen_if_valid(Y_c_cf_c)
descr_Y_diff_c_cf_c = _gen_diff_if_valid(Y_c_cf_c, Y_c)
descr_Y_c_cf_t = _gen_if_valid(Y_c_cf_t)
descr_Y_diff_c_cf_t = _gen_diff_if_valid(Y_c_cf_t, Y_c)
return DescrSet(descr_Y_t, descr_Y_t_cf_c, descr_Y_diff_t_cf_c,
descr_Y_c, descr_Y_diff_t_c, descr_Y_c_cf_c, descr_Y_diff_c_cf_c,
descr_Y_c_cf_t, descr_Y_diff_c_cf_t)
def __add__(self, other):
def _add_if_valid(a, b):
return a+b if (a is not None and b is not None) else None
return DescrSet(descr_Y_t=_add_if_valid(self.descr_Y_t, other.descr_Y_t),
descr_Y_t_cf_c=_add_if_valid(self.descr_Y_t_cf_c, other.descr_Y_t_cf_c),
descr_Y_diff_t_cf_c=_add_if_valid(self.descr_Y_diff_t_cf_c, other.descr_Y_diff_t_cf_c),
descr_Y_c=_add_if_valid(self.descr_Y_c, other.descr_Y_c),
descr_Y_diff_t_c=_add_if_valid(self.descr_Y_diff_t_c, other.descr_Y_diff_t_c),
descr_Y_c_cf_c=_add_if_valid(self.descr_Y_c_cf_c, other.descr_Y_c_cf_c),
descr_Y_diff_c_cf_c=_add_if_valid(self.descr_Y_diff_c_cf_c, other.descr_Y_diff_c_cf_c),
descr_Y_c_cf_t=_add_if_valid(self.descr_Y_c_cf_t, other.descr_Y_c_cf_t),
descr_Y_diff_c_cf_t=_add_if_valid(self.descr_Y_diff_c_cf_t, other.descr_Y_diff_c_cf_t))
def calc_estimates(self):
""" Takes matrices of effects for multiple events and return averaged results
"""
def _calc_estimate(descr_stat1, descr_stat2):
if descr_stat1 is None or descr_stat2 is None:
return None
return SSC_DescrStat.lcl_comp_means(descr_stat1, descr_stat2)
att_est = _calc_estimate(self.descr_Y_t, self.descr_Y_t_cf_c)
att_debiased_est = _calc_estimate(self.descr_Y_diff_t_cf_c, self.descr_Y_diff_c_cf_c)
atut_est = _calc_estimate(self.descr_Y_c_cf_t, self.descr_Y_c)
atut_debiased_est = _calc_estimate(self.descr_Y_diff_c_cf_t, self.descr_Y_diff_c_cf_c)
ate_est, ate_debiased_est = None, None
if all(d is not None for d in [self.descr_Y_t, self.descr_Y_c_cf_t, self.descr_Y_t_cf_c, self.descr_Y_c]):
ate_est = _calc_estimate(self.descr_Y_t + self.descr_Y_c_cf_t,
self.descr_Y_t_cf_c + self.descr_Y_c)
if all(d is not None for d in [self.descr_Y_diff_t_cf_c, self.descr_Y_diff_c_cf_t, self.descr_Y_diff_c_cf_c]):
ate_debiased_est = _calc_estimate(self.descr_Y_diff_t_cf_c +
self.descr_Y_diff_c_cf_t, self.descr_Y_diff_c_cf_c)
aa_est = _calc_estimate(self.descr_Y_c_cf_c, self.descr_Y_c)
# descr_Y_diff_c_cf_c #used for the double comparisons
naive_est = _calc_estimate(self.descr_Y_t, self.descr_Y_c)
# descr_Y_diff_t_c #Don't think this could be useful, but just in case.
return MatchingEstimate(att_est, att_debiased_est,
atut_est, atut_debiased_est,
ate_est, ate_debiased_est,
aa_est, naive_est)

Просмотреть файл

@ -0,0 +1,172 @@
"""This is a way to summarize (using normal approximations) the distributions of real and
synthetic controls so that all data doesn't have to be stored.
"""
import math
from collections import namedtuple
import statsmodels
import numpy as np
# TODO:
# - Allow passed in vector and return scalar
def tstat_generic(mean1, mean2, stdm, dof):
"""Vectorized version of statsmodels' _tstat_generic
:param mean1: int or np.array
:param mean2: int or np.array
:param stdm: int or np.array of the standard deviation of the pooled sample
:param dof: int
"""
from statsmodels.stats.weightstats import _tstat_generic
l = len(mean1)
if l == 1:
tstat, pval = _tstat_generic(mean1, mean2, stdm, dof, 'two-sided', diff=0)
else:
tstat, pval = zip(*[_tstat_generic(mean1[i], mean2[i], stdm[i], dof, 'two-sided', diff=0)
for i in range(l)])
#tstat = (mean1 - mean2) / stdm #
#pvalue = stats.t.sf(np.abs(tstat), dof)*2
# cohen's d: diff/samplt std dev
return tstat, pval
# def pooled_variances_scalar(sample_variances, sample_sizes):
# """Estimate pooled variance from a set of samples. Assumes same variance but allows different means."""
# return np.average(sample_variances, weights=(sample_sizes-1))
def pooled_variances(sample_variances, sample_sizes):
"""Estimate pooled variance from a set of samples. Assumes same variance but allows different means.
If inputs are nxl then return l
"""
# https://en.wikipedia.org/wiki/Pooled_variance
return np.average(sample_variances, weights=(sample_sizes-1), axis=0)
Estimate = namedtuple('Estimate', 'effect pval') # can hold scalars or vectors
class SSC_DescrStat(object):
"""Stores mean and variance for a sample in a way that can be updated
with new observations or adding together summaries. Similar to statsmodel's DescrStatW
except we don't keep the raw data, and we use 'online' algorithm's to allow for incremental approach."""
# Similar to https://github.com/grantjenks/python-runstats but uses numpy and doesn't do higher order stats and has multiple columns
# Ref: https://www.statsmodels.org/stable/generated/statsmodels.stats.weightstats.DescrStatsW.html#statsmodels.stats.weightstats.DescrStatsW
def __init__(self, nobs, mean, M2):
"""
:param nobs: scalar
:param mean: vector
:param M2: vector of same length as mean. Sum of squared deviations (sum_i (x_i-mean)^2).
Sometimes called 'S' (capital; though 's' is often sample variance)
:raises: ValueError
"""
import numbers
if not isinstance(nobs, numbers.Number):
raise ValueError('mean should be np vector')
self.nobs = nobs
if len(mean.shape)!=1:
raise ValueError('mean should be np vector')
if len(M2.shape)!=1:
raise ValueError('M2 should be np vector')
if len(M2.shape)!=len(mean.shape):
raise ValueError('M2 and mean should be the same length')
self.mean = mean
self.M2 = M2 # sometimes called S (though s is often sample variance)
def __repr__(self):
return "%s(nobs=%s, mean=%s, M2=%s)" % (self.__class__.__name__, self.nobs, self.mean, self.M2)
@staticmethod
def from_data(data):
"""
:param data: 2D np.array. We compute stats per column.
:returns: SSC_DescrStat object
"""
N = data.shape[0]
mean = np.average(data, axis=0)
M2 = np.var(data, axis=0)*N
return SSC_DescrStat(N, mean, M2)
def __add__(self, other, alt=False):
"""
Chan's parallel algorithm
:param other: Other SSC_DescrStat object
:param alt: Use when roughly similar sizes and both are large. Avoid catastrophic cancellation
:returns: new SSC_DescrStat object
"""
# See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
# TODO: could make alt auto (look at, e.g., abs(self.nobs - other.nobs)/new_n)
new_n = self.nobs + other.nobs
delta = other.mean - self.mean
if not alt:
new_mean = self.mean+delta*(other.nobs/new_n)
else:
new_mean = (self.nobs*self.mean + other.nobs*other.mean)/new_n
new_M2 = self.M2 + other.M2 + np.square(delta) * self.nobs * other.nobs / new_n
return SSC_DescrStat(new_n, new_mean, new_M2)
def update(self, obs):
"""Welford's online algorithm
:param obs: new observation vector
"""
# See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
self.nobs += 1
delta = obs - self.mean
self.mean += delta*1./self.nobs
self.M2 += delta*(obs - self.mean)
def variance(self, ddof=1):
"""
:param ddof: delta degree of difference. 1 will give sample variance (s).
:returns: Variance
"""
if self.nobs == 1:
return np.zeros(self.mean.shape)
return self.M2/(self.nobs-ddof)
@property
def var(self):
return self.variance()
def stddev(self, ddof=1):
"""Standard Deviation
:param ddof: delta degree of difference. 1 will give sample Standard Deviation
:returns: Standard Deviation
"""
return np.sqrt(self.variance(ddof))
@property
def std(self):
return self.stddev()
def std_mean(self, ddof=1):
"""Standard Deviation/Error of the mean
:param ddof: delta degree of difference. 1 will give sample variance (s).
:returns: Standard Error
"""
return self.stddev(ddof)/math.sqrt(self.nobs-1)
@property
def sumsquares(self):
return self.M2
@property
def sum(self):
return self.mean*self.nobs
@property
def sum_weights(self):
return self.sum
@staticmethod
def lcl_comp_means(descr1, descr2):
""" Calclulates the t-test of the difference in means. Local version of statsmodels.stats.weightstats import CompareMeans
:param descr1: DescrStatW-type object of sample statistics
:param descr2: DescrStatW-type object of sample statistics
"""
#from statsmodels.stats.weightstats import CompareMeans
# Do statsmodels.CompareMeans with just summary stats.
var_pooled = pooled_variances(np.array([descr1.var, descr2.var]), np.array([descr1.nobs, descr2.nobs]))
stdm = np.sqrt(var_pooled * (1. / descr1.nobs + 1. / descr2.nobs)) # ~samplt std dev/sqrt(N)
dof = descr1.nobs - 1 + descr2.nobs - 1
tstat, pval = tstat_generic(descr1.mean, descr2.mean, stdm, dof)
effect = descr1.mean - descr2.mean
return Estimate(effect, pval)

Просмотреть файл

@ -2,16 +2,21 @@ name: SparseSC_36
channels:
- defaults
dependencies:
- _r-mutex=1.0.0=anacondar_1
- _tflow_select=2.3.0=mkl
- absl-py=0.7.1=py36_0
- alabaster=0.7.12=py36_0
- asn1crypto=0.24.0=py36_0
- astor=0.7.1=py36_0
- astroid=1.6.5=py36_0
- attrs=19.1.0=py36_1
- autopep8=1.4.4=py_0
- babel=2.6.0=py36_0
- backcall=0.1.0=py36_0
- blas=1.0=mkl
- bleach=3.1.0=py36_0
- ca-certificates=2019.5.15=0
- certifi=2019.6.16=py36_0
- ca-certificates=2020.1.1=0
- certifi=2020.4.5.1=py36_0
- cffi=1.12.3=py36h7a1dbc1_0
- chardet=3.0.4=py36_1
- colorama=0.4.1=py36_0
@ -22,6 +27,10 @@ dependencies:
- docutils=0.14=py36h6012d8f_0
- entrypoints=0.3=py36_0
- freetype=2.9.1=ha9979f8_1
- gast=0.2.2=py36_0
- grpcio=1.16.1=py36h351948d_1
- h5py=2.9.0=py36h5e291fa_0
- hdf5=1.10.4=h7ebc959_0
- icc_rt=2019.0.0=h0cc432a_1
- icu=58.2=ha66f8fd_1
- idna=2.8=py36_0
@ -37,41 +46,79 @@ dependencies:
- jsonschema=3.0.1=py36_0
- jupyter_client=5.2.4=py36_0
- jupyter_core=4.4.0=py36_0
- keras=2.2.4=0
- keras-applications=1.0.8=py_0
- keras-base=2.2.4=py36_0
- keras-preprocessing=1.1.0=py_1
- kiwisolver=1.1.0=py36ha925a31_0
- lazy-object-proxy=1.4.1=py36he774522_0
- libmklml=2019.0.5=0
- libpng=1.6.37=h2a8f88b_0
- libprotobuf=3.8.0=h7bd577a_0
- libsodium=1.0.16=h9d3ae62_0
- m2w64-bwidget=1.9.10=2
- m2w64-bzip2=1.0.6=6
- m2w64-expat=2.1.1=2
- m2w64-fftw=3.3.4=6
- m2w64-flac=1.3.1=3
- m2w64-gcc-libgfortran=5.3.0=6
- m2w64-gcc-libs=5.3.0=7
- m2w64-gcc-libs-core=5.3.0=7
- m2w64-gettext=0.19.7=2
- m2w64-gmp=6.1.0=2
- m2w64-libiconv=1.14=6
- m2w64-libjpeg-turbo=1.4.2=3
- m2w64-libogg=1.3.2=3
- m2w64-libpng=1.6.21=2
- m2w64-libsndfile=1.0.26=2
- m2w64-libtiff=4.0.6=2
- m2w64-libvorbis=1.3.5=2
- m2w64-libwinpthread-git=5.0.0.4634.697f757=2
- m2w64-mpfr=3.1.4=4
- m2w64-openblas=0.2.19=1
- m2w64-pcre=8.38=2
- m2w64-speex=1.2rc2=3
- m2w64-speexdsp=1.2rc3=3
- m2w64-tcl=8.6.5=3
- m2w64-tk=8.6.5=3
- m2w64-tktable=2.10=5
- m2w64-wineditline=2.101=5
- m2w64-xz=5.2.2=2
- m2w64-zlib=1.2.8=10
- markupsafe=1.1.1=py36he774522_0
- matplotlib=3.1.0=py36hc8f65d3_0
- mccabe=0.6.1=py36_1
- mistune=0.8.4=py36he774522_0
- mkl=2018.0.3=1
- mkl_fft=1.0.6=py36hdbbee80_0
- mkl_random=1.0.1=py36h77b88f5_1
- mock=3.0.5=py36_0
- msys2-conda-epoch=20160418=1
- nbconvert=5.5.0=py_0
- nbformat=4.4.0=py36h3a5bc1b_0
- notebook=5.6.0=py36_0
- numpy=1.13.1=py36haf1bc54_2
- numpy=1.15.1=py36hc27ee41_0
- numpy-base=1.15.1=py36h8128ebf_0
- openssl=1.1.1c=he774522_1
- pandas=0.24.2=py36ha925a31_0
- pandoc=2.2.3.2=0
- pandocfilters=1.4.2=py36_1
- parso=0.4.0=py_0
- patsy=0.5.1=py36_0
- pickleshare=0.7.5=py36_0
- pip=19.1.1=py36_0
- prometheus_client=0.6.0=py36_0
- prompt_toolkit=2.0.9=py36_0
- protobuf=3.8.0=py36h33f27b4_0
- psutil=5.6.3=py36he774522_0
- pycodestyle=2.5.0=py36_0
- pycparser=2.19=py36_0
- pygments=2.4.0=py_0
- pylint=1.9.2=py36_0
- pyopenssl=19.0.0=py36_0
- pyparsing=2.4.0=py_0
- pyqt=5.9.2=py36h6538335_2
- pyreadline=2.1=py36_1
- pyrsistent=0.14.11=py36he774522_0
- pysocks=1.7.0=py36_0
- python=3.6.8=h9f7ef89_7
@ -81,7 +128,37 @@ dependencies:
- pyyaml=5.1=py36he774522_0
- pyzmq=18.0.0=py36ha925a31_0
- qt=5.9.7=vc14h73c81de_0
- r-assertthat=0.2.1=r36h6115d3f_0
- r-base=3.6.0=hf18239d_0
- r-bh=1.69.0_1=r36h6115d3f_0
- r-bit=1.1_14=r36h6115d3f_0
- r-bit64=0.9_7=r36h6115d3f_0
- r-blob=1.1.1=r36h6115d3f_0
- r-cli=1.1.0=r36h6115d3f_0
- r-crayon=1.3.4=r36h6115d3f_0
- r-dbi=1.0.0=r36h6115d3f_0
- r-dbplyr=1.4.0=r36h6115d3f_0
- r-digest=0.6.18=r36h6115d3f_0
- r-dplyr=0.8.0.1=r36h6115d3f_0
- r-fansi=0.4.0=r36h6115d3f_0
- r-glue=1.3.1=r36h6115d3f_0
- r-magrittr=1.5=r36h6115d3f_4
- r-memoise=1.1.0=r36h6115d3f_0
- r-pillar=1.3.1=r36h6115d3f_0
- r-pkgconfig=2.0.2=r36h6115d3f_0
- r-plogr=0.2.0=r36h6115d3f_0
- r-prettyunits=1.0.2=r36h6115d3f_0
- r-purrr=0.3.2=r36h6115d3f_0
- r-r6=2.4.0=r36h6115d3f_0
- r-rcpp=1.0.1=r36h6115d3f_0
- r-rlang=0.3.4=r36h6115d3f_0
- r-rsqlite=2.1.1=r36h6115d3f_0
- r-tibble=2.1.1=r36h6115d3f_0
- r-tidyselect=0.2.5=r36h6115d3f_0
- r-utf8=1.1.4=r36h6115d3f_0
- requests=2.21.0=py36_0
- rope=0.14.0=py_0
- rpy2=2.9.4=py36r36h39e3cac_0
- scikit-learn=0.19.1=py36hae9bb9f_0
- scipy=1.0.1=py36hce232c7_0
- send2trash=1.5.0=py36_0
@ -93,6 +170,14 @@ dependencies:
- sphinxcontrib=1.0=py36_1
- sphinxcontrib-websupport=1.1.0=py36_1
- sqlite=3.28.0=he774522_0
- statsmodels=0.10.1=py36h8c2d366_0
- tbb=2019.4=h74a9793_0
- tbb4py=2019.4=py36h74a9793_0
- tensorboard=1.13.1=py36h33f27b4_0
- tensorflow=1.13.1=mkl_py36hd212fbe_0
- tensorflow-base=1.13.1=mkl_py36hcaf7020_0
- tensorflow-estimator=1.13.0=py_0
- termcolor=1.1.0=py36_1
- terminado=0.8.2=py36_0
- testpath=0.4.2=py36_0
- tornado=5.1.1=py36hfa6e2cd_0
@ -103,6 +188,7 @@ dependencies:
- vs2015_runtime=14.15.26706=h3a45250_4
- wcwidth=0.1.7=py36h3d5aa90_0
- webencodings=0.5.1=py36_1
- werkzeug=0.15.4=py_0
- wheel=0.33.4=py36_0
- win_inet_pton=1.1.0=py36_0
- wincertstore=0.2=py36h7fe50ca_0