diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d20eb815..18d9e1f30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,10 @@ concurrency: # Precompute the ref if the workflow was triggered by a workflow dispatch rather than copying this logic repeatedly env: ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || null }} - use_lkg: ${{ (github.event_name == 'workflow_dispatch' && inputs.use_lkg) || github.event_name == 'pull_request' }} + # we want to use the LKG if that is explicitly requested, or if we're in a PR, but not a nightly run + # the final `|| ''` is because env vars are always converted to strings and the string 'false' is truthy (!!) + # (see https://github.com/orgs/community/discussions/25645) + use_lkg: ${{ (github.event_name == 'workflow_dispatch' && inputs.use_lkg) || github.event_name == 'pull_request' || ''}} jobs: eval: @@ -291,6 +294,7 @@ jobs: repository: testpypi # don't have access to env context here for some reason ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || null }} + # can't use env context here so need to duplicate expression, but these are true boolean values so don't need extra string logic use_lkg: ${{ (github.event_name == 'workflow_dispatch' && inputs.use_lkg) || github.event_name == 'pull_request' }} docs: @@ -303,6 +307,7 @@ jobs: environment: test # don't have access to env context here for some reason ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || null }} + # can't use env context here so need to duplicate expression, but these are true boolean values so don't need extra string logic use_lkg: ${{ (github.event_name == 'workflow_dispatch' && inputs.use_lkg) || github.event_name == 'pull_request' }} verify: diff --git a/.github/workflows/publish-documentation.yml b/.github/workflows/publish-documentation.yml index c7efad85f..68667896a 100644 --- a/.github/workflows/publish-documentation.yml +++ b/.github/workflows/publish-documentation.yml @@ -63,7 +63,7 @@ jobs: python-version: 3.8 # because of our supported TensorFlow versions, must build on 3.6-3.8 - run: python -m pip install --upgrade pip && pip install --upgrade setuptools name: Ensure latest pip and setuptools - - run: pip install -e .[all] ${{ inputs.use_lkg && '-r lkg.txt' }} + - run: pip install -e .[all] ${{ inputs.use_lkg && '-r lkg.txt' || '' }} name: Install econml[all] - run: sudo apt-get -yq install graphviz name: Install graphviz diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml index 65db3c813..e18b6f98e 100644 --- a/.github/workflows/publish-package.yml +++ b/.github/workflows/publish-package.yml @@ -70,7 +70,7 @@ jobs: - run: pip install cibuildwheel && python -m cibuildwheel --output-dir dist name: Build wheels env: - CIBW_BUILD: cp3{7,8,9,10}-* + CIBW_BUILD: cp3* CIBW_SKIP: "*musl* *win32 *i686" - uses: actions/upload-artifact@v3 name: Upload wheels as artifact @@ -91,7 +91,7 @@ jobs: python-version: 3.8 # because of our supported TensorFlow versions, must build on 3.6-3.8 - run: python -m pip install --upgrade pip && pip install --upgrade setuptools name: Ensure latest pip and setuptools - - run: pip install -e .[all] ${{ inputs.use_lkg && '-r lkg.txt' }} + - run: pip install -e .[all] ${{ inputs.use_lkg && '-r lkg.txt' || '' }} name: Install econml[all] - run: python setup.py sdist name: Build sdist diff --git a/econml/_cate_estimator.py b/econml/_cate_estimator.py index 3c453b291..f66f2f44e 100644 --- a/econml/_cate_estimator.py +++ b/econml/_cate_estimator.py @@ -11,8 +11,8 @@ from .inference import BootstrapInference from .utilities import (tensordot, ndim, reshape, shape, parse_final_model_params, get_feature_names_or_default, inverse_onehot, Summary, get_input_columns, check_input_arrays, jacify_featurizer) -from .inference import StatsModelsInference, StatsModelsInferenceDiscrete, LinearModelFinalInference,\ - LinearModelFinalInferenceDiscrete, NormalInferenceResults, GenericSingleTreatmentModelFinalInference,\ +from .inference import StatsModelsInference, StatsModelsInferenceDiscrete, LinearModelFinalInference, \ + LinearModelFinalInferenceDiscrete, NormalInferenceResults, GenericSingleTreatmentModelFinalInference, \ GenericModelFinalInferenceDiscrete from ._shap import _shap_explain_cme, _shap_explain_joint_linear_model_cate from .dowhy import DoWhyWrapper diff --git a/econml/dowhy.py b/econml/dowhy.py index 7c6489743..6e399837e 100644 --- a/econml/dowhy.py +++ b/econml/dowhy.py @@ -37,8 +37,8 @@ class DoWhyWrapper: def __init__(self, cate_estimator): from pkg_resources import parse_version - if parse_version(dowhy.__version__) >= parse_version('0.9'): - warnings.warn("econml has not been tested with dowhy versions >= 0.9") + if parse_version(dowhy.__version__) >= parse_version('0.11'): + warnings.warn("econml has not been tested with dowhy versions >= 0.11") self._cate_estimator = cate_estimator def _get_params(self): diff --git a/econml/inference/_inference.py b/econml/inference/_inference.py index 5745a6a43..23658f846 100644 --- a/econml/inference/_inference.py +++ b/econml/inference/_inference.py @@ -1067,11 +1067,11 @@ def conf_int(self, alpha=0.05): if self.stderr is None: raise AttributeError("Only point estimates are available!") if np.isscalar(self.point_estimate): - return _safe_norm_ppf(alpha / 2, loc=self.point_estimate, scale=self.stderr),\ + return _safe_norm_ppf(alpha / 2, loc=self.point_estimate, scale=self.stderr), \ _safe_norm_ppf(1 - alpha / 2, loc=self.point_estimate, scale=self.stderr) else: return np.array([_safe_norm_ppf(alpha / 2, loc=p, scale=err) - for p, err in zip(self.point_estimate, self.stderr)]),\ + for p, err in zip(self.point_estimate, self.stderr)]), \ np.array([_safe_norm_ppf(1 - alpha / 2, loc=p, scale=err) for p, err in zip(self.point_estimate, self.stderr)]) @@ -1403,7 +1403,7 @@ def conf_int_mean(self, *, alpha=None): _safe_norm_ppf(1 - alpha / 2, loc=mean_point, scale=stderr_mean)) else: return np.array([_safe_norm_ppf(alpha / 2, loc=p, scale=err) - for p, err in zip(mean_point, stderr_mean)]),\ + for p, err in zip(mean_point, stderr_mean)]), \ np.array([_safe_norm_ppf(1 - alpha / 2, loc=p, scale=err) for p, err in zip(mean_point, stderr_mean)]) diff --git a/econml/orf/_ortho_forest.py b/econml/orf/_ortho_forest.py index d97ad8b36..c23db4b69 100644 --- a/econml/orf/_ortho_forest.py +++ b/econml/orf/_ortho_forest.py @@ -352,12 +352,12 @@ def _pw_effect_inputs(self, X_single, stderr=False): slice_weights_one, slice_weights_two = self._get_weights(X_single, tree_slice=slice_it) slice_weights_list.append((slice_weights_one[mask_w1], slice_weights_two[mask_w2])) W_none = self.W_one is None - return np.concatenate((self.Y_one[mask_w1], self.Y_two[mask_w2])),\ - np.concatenate((self.T_one[mask_w1], self.T_two[mask_w2])),\ - np.concatenate((self.X_one[mask_w1], self.X_two[mask_w2])),\ + return np.concatenate((self.Y_one[mask_w1], self.Y_two[mask_w2])), \ + np.concatenate((self.T_one[mask_w1], self.T_two[mask_w2])), \ + np.concatenate((self.X_one[mask_w1], self.X_two[mask_w2])), \ np.concatenate((self.W_one[mask_w1], self.W_two[mask_w2]) - ) if not W_none else None,\ - w_nonzero,\ + ) if not W_none else None, \ + w_nonzero, \ split_inds, slice_weights_list def _get_inference_options(self): @@ -1255,7 +1255,7 @@ def const_marginal_effect_interval(self, X=None, *, alpha=0.05): param_upper = [param + np.apply_along_axis(lambda s: norm.ppf(upper, scale=s), 0, np.sqrt(np.diag(cov_mat))) for (param, cov_mat) in params_and_cov] param_lower, param_upper = np.asarray(param_lower), np.asarray(param_upper) - return param_lower.reshape((-1,) + self._estimator._d_y + self._estimator._d_t),\ + return param_lower.reshape((-1,) + self._estimator._d_y + self._estimator._d_t), \ param_upper.reshape((-1,) + self._estimator._d_y + self._estimator._d_t) def const_marginal_effect_inference(self, X=None): diff --git a/econml/sklearn_extensions/linear_model.py b/econml/sklearn_extensions/linear_model.py index a7f5d029c..b533246d2 100644 --- a/econml/sklearn_extensions/linear_model.py +++ b/econml/sklearn_extensions/linear_model.py @@ -1657,7 +1657,7 @@ def coef__interval(self, alpha=0.05): The lower and upper bounds of the confidence interval of the coefficients """ return np.array([_safe_norm_ppf(alpha / 2, loc=p, scale=err) - for p, err in zip(self.coef_, self.coef_stderr_)]),\ + for p, err in zip(self.coef_, self.coef_stderr_)]), \ np.array([_safe_norm_ppf(1 - alpha / 2, loc=p, scale=err) for p, err in zip(self.coef_, self.coef_stderr_)]) @@ -1677,15 +1677,15 @@ def intercept__interval(self, alpha=0.05): The lower and upper bounds of the confidence interval of the intercept(s) """ if not self.fit_intercept: - return (0 if self._n_out == 0 else np.zeros(self._n_out)),\ + return (0 if self._n_out == 0 else np.zeros(self._n_out)), \ (0 if self._n_out == 0 else np.zeros(self._n_out)) if self._n_out == 0: - return _safe_norm_ppf(alpha / 2, loc=self.intercept_, scale=self.intercept_stderr_),\ + return _safe_norm_ppf(alpha / 2, loc=self.intercept_, scale=self.intercept_stderr_), \ _safe_norm_ppf(1 - alpha / 2, loc=self.intercept_, scale=self.intercept_stderr_) else: return np.array([_safe_norm_ppf(alpha / 2, loc=p, scale=err) - for p, err in zip(self.intercept_, self.intercept_stderr_)]),\ + for p, err in zip(self.intercept_, self.intercept_stderr_)]), \ np.array([_safe_norm_ppf(1 - alpha / 2, loc=p, scale=err) for p, err in zip(self.intercept_, self.intercept_stderr_)]) @@ -1707,7 +1707,7 @@ def predict_interval(self, X, alpha=0.05): The lower and upper bounds of the confidence intervals of the predicted mean outcomes """ return np.array([_safe_norm_ppf(alpha / 2, loc=p, scale=err) - for p, err in zip(self.predict(X), self.prediction_stderr(X))]),\ + for p, err in zip(self.predict(X), self.prediction_stderr(X))]), \ np.array([_safe_norm_ppf(1 - alpha / 2, loc=p, scale=err) for p, err in zip(self.predict(X), self.prediction_stderr(X))]) diff --git a/econml/tests/test_statsmodels.py b/econml/tests/test_statsmodels.py index de55740fb..42f987cb5 100644 --- a/econml/tests/test_statsmodels.py +++ b/econml/tests/test_statsmodels.py @@ -148,16 +148,16 @@ def intercept__interval(self, alpha): def _compare_classes(est, lr, X_test, alpha=.05, tol=1e-12): assert np.all(np.abs(est.coef_ - lr.coef_) < tol), "{}, {}".format(est.coef_, lr.coef_) assert np.all(np.abs(np.array(est.coef__interval(alpha=alpha)) - - np.array(lr.coef__interval(alpha=alpha))) < tol),\ + np.array(lr.coef__interval(alpha=alpha))) < tol), \ "{}, {}".format(est.coef__interval(alpha=alpha), np.array(lr.coef__interval(alpha=alpha))) assert np.all(np.abs(est.intercept_ - lr.intercept_) < tol), "{}, {}".format(est.intercept_, lr.intercept_) assert np.all(np.abs(np.array(est.intercept__interval(alpha=alpha)) - - np.array(lr.intercept__interval(alpha=alpha))) < tol),\ + np.array(lr.intercept__interval(alpha=alpha))) < tol), \ "{}, {}".format(est.intercept__interval(alpha=alpha), lr.intercept__interval(alpha=alpha)) assert np.all(np.abs(est.predict(X_test) - lr.predict(X_test)) < tol), "{}, {}".format(est.predict(X_test), lr.predict(X_test)) assert np.all(np.abs(np.array(est.predict_interval(X_test, alpha=alpha)) - - np.array(lr.predict_interval(X_test, alpha=alpha))) < tol),\ + np.array(lr.predict_interval(X_test, alpha=alpha))) < tol), \ "{}, {}".format(est.predict_interval(X_test, alpha=alpha), lr.predict_interval(X_test, alpha=alpha)) @@ -244,24 +244,25 @@ def _summarize(X, y, w=None): def _compare_dml_classes(est, lr, X_test, alpha=.05, tol=1e-10): assert np.all(np.abs(est.coef_ - lr.coef_) < tol), "{}, {}".format(est.coef_, lr.coef_) - assert np.all(np.abs(np.array(est.coef__interval(alpha=alpha)) - np.array(lr.coef__interval(alpha=alpha))) < tol),\ + assert np.all(np.abs(np.array(est.coef__interval(alpha=alpha)) - np.array(lr.coef__interval(alpha=alpha))) < + tol), \ "{}, {}".format(np.array(est.coef__interval(alpha=alpha)), np.array(lr.coef__interval(alpha=alpha))) assert np.all(np.abs(est.effect(X_test) - lr.effect(X_test)) < tol), "{}, {}".format(est.effect(X_test), lr.effect(X_test)) assert np.all(np.abs(np.array(est.effect_interval(X_test, alpha=alpha)) - - np.array(lr.effect_interval(X_test, alpha=alpha))) < tol),\ + np.array(lr.effect_interval(X_test, alpha=alpha))) < tol), \ "{}, {}".format(est.effect_interval(X_test, alpha=alpha), lr.effect_interval(X_test, alpha=alpha)) def _compare_dr_classes(est, lr, X_test, alpha=.05, tol=1e-10): assert np.all(np.abs(est.coef_(T=1) - lr.coef_(T=1)) < tol), "{}, {}".format(est.coef_(T=1), lr.coef_(T=1)) assert np.all(np.abs(np.array(est.coef__interval(T=1, alpha=alpha)) - - np.array(lr.coef__interval(T=1, alpha=alpha))) < tol),\ + np.array(lr.coef__interval(T=1, alpha=alpha))) < tol), \ "{}, {}".format(np.array(est.coef__interval(T=1, alpha=alpha)), np.array(lr.coef__interval(T=1, alpha=alpha))) assert np.all(np.abs(est.effect(X_test) - lr.effect(X_test)) < tol), "{}, {}".format(est.effect(X_test), lr.effect(X_test)) assert np.all(np.abs(np.array(est.effect_interval(X_test, alpha=alpha)) - - np.array(lr.effect_interval(X_test, alpha=alpha))) < tol),\ + np.array(lr.effect_interval(X_test, alpha=alpha))) < tol), \ "{}, {}".format(est.effect_interval(X_test, alpha=alpha), lr.effect_interval(X_test, alpha=alpha)) @@ -355,7 +356,7 @@ def test_inference(self): y = X[:, 0] est = OLS(fit_intercept=False, cov_type="nonrobust").fit(X, y) assert np.all(np.abs(est.coef_ - [1, 0, 0]) <= 1e-12), "{}, {}".format(est.coef_, [1, 0, 0]) - assert np.all(np.abs(est.coef__interval() - np.array([[1, 0, 0], [1, 0, 0]])) <= 1e-12),\ + assert np.all(np.abs(est.coef__interval() - np.array([[1, 0, 0], [1, 0, 0]])) <= 1e-12), \ "{}, {}".format(est.coef__interval(), np.array([[1, 0, 0], [1, 0, 0]])) assert np.all(est.coef_stderr_ <= 1e-12) assert np.all(est._param_var <= 1e-12) @@ -366,7 +367,7 @@ def test_inference(self): est = OLS(fit_intercept=True, cov_type="nonrobust").fit(X, y) assert np.all(np.abs(est.coef_ - np.array([1] + [0] * (d - 1))) <= 1e-12), "{}, {}".format(est.coef_, [1] + [0] * (d - 1)) - assert np.all(np.abs(est.coef__interval() - np.array([[1] + [0] * (d - 1), [1] + [0] * (d - 1)])) <= 1e-12),\ + assert np.all(np.abs(est.coef__interval() - np.array([[1] + [0] * (d - 1), [1] + [0] * (d - 1)])) <= 1e-12), \ "{}, {}".format(est.coef__interval(), np.array([[1] + [0] * (d - 1), [1] + [0] * (d - 1)])) assert np.all(est.coef_stderr_ <= 1e-12) assert np.all(est._param_var <= 1e-12) @@ -383,12 +384,12 @@ def test_inference(self): assert np.all(np.abs(est.coef_stderr_ - np.array([1] * d)) <= 1e-12) assert np.all(np.abs(est.coef__interval()[0] - np.array([scipy.stats.norm.ppf(.025, loc=1, scale=1)] + - [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) <= 1e-12), \ "{}, {}".format(est.coef__interval()[0], np.array([scipy.stats.norm.ppf(.025, loc=1, scale=1)] + [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) assert np.all(np.abs(est.coef__interval()[1] - np.array([scipy.stats.norm.ppf(.975, loc=1, scale=1)] + - [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) <= 1e-12), \ "{}, {}".format(est.coef__interval()[1], np.array([scipy.stats.norm.ppf(.975, loc=1, scale=1)] + [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) @@ -405,13 +406,13 @@ def test_inference(self): assert np.all(np.abs(est.coef_stderr_[t] - np.array([1] * d)) <= 1e-12), "{}".format(est.coef_stderr_[t]) assert np.all(np.abs(est.coef__interval()[0][t] - np.array([scipy.stats.norm.ppf(.025, loc=1, scale=1)] + - [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) <= 1e-12), \ "{}, {}".format(est.coef__interval()[0][t], np.array([scipy.stats.norm.ppf(.025, loc=1, scale=1)] + [scipy.stats.norm.ppf(.025, loc=0, scale=1)] * (d - 1))) assert np.all(np.abs(est.coef__interval()[1][t] - np.array([scipy.stats.norm.ppf(.975, loc=1, scale=1)] + - [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) <= 1e-12), \ "{}, {}".format(est.coef__interval()[1][t], np.array([scipy.stats.norm.ppf(.975, loc=1, scale=1)] + [scipy.stats.norm.ppf(.975, loc=0, scale=1)] * (d - 1))) @@ -433,20 +434,22 @@ def test_inference(self): 1e-12), "{}".format(est.coef_stderr_[t]) assert np.all(np.abs(est.coef__interval()[0][t] - np.array([scipy.stats.norm.ppf(.025, loc=1, scale=np.sqrt(2))] + - [scipy.stats.norm.ppf(.025, loc=0, scale=np.sqrt(2))] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.025, loc=0, scale=np.sqrt(2))] * (d - 1))) <= + 1e-12), \ "{}, {}".format(est.coef__interval()[0][t], np.array([scipy.stats.norm.ppf(.025, loc=1, scale=np.sqrt(2))] + [scipy.stats.norm.ppf(.025, loc=0, scale=np.sqrt(2))] * (d - 1))) assert np.all(np.abs(est.coef__interval()[1][t] - np.array([scipy.stats.norm.ppf(.975, loc=1, scale=np.sqrt(2))] + - [scipy.stats.norm.ppf(.975, loc=0, scale=np.sqrt(2))] * (d - 1))) <= 1e-12),\ + [scipy.stats.norm.ppf(.975, loc=0, scale=np.sqrt(2))] * (d - 1))) <= + 1e-12), \ "{}, {}".format(est.coef__interval()[1][t], np.array([scipy.stats.norm.ppf(.975, loc=1, scale=np.sqrt(2))] + [scipy.stats.norm.ppf(.975, loc=0, scale=np.sqrt(2))] * (d - 1))) assert np.all(np.abs(est.intercept_[t]) <= 1e-12), "{}, {}".format(est.intercept_[t]) assert np.all(np.abs(est.intercept_stderr_[t] - 1) <= 1e-12), "{}".format(est.intercept_stderr_[t]) assert np.all(np.abs(est.intercept__interval()[0][t] - - scipy.stats.norm.ppf(.025, loc=0, scale=1)) <= 1e-12),\ + scipy.stats.norm.ppf(.025, loc=0, scale=1)) <= 1e-12), \ "{}, {}".format(est.intercept__interval()[0][t], scipy.stats.norm.ppf(.025, loc=0, scale=1)) def test_comp_with_statsmodels(self): @@ -551,26 +554,26 @@ def true_effect(x): sample_weight=weight) for t in range(p)] for t in range(p): - assert np.all(np.abs(est.coef_[t] - lr[t].coef_) < 1e-12),\ + assert np.all(np.abs(est.coef_[t] - lr[t].coef_) < 1e-12), \ "{}, {}, {}: {}, {}".format(cov_type, fit_intercept, t, est.coef_[t], lr[t].coef_) assert np.all(np.abs(np.array(est.coef__interval(alpha=alpha))[:, t] - - lr[t].coef__interval(alpha=alpha)) < 1e-12),\ + lr[t].coef__interval(alpha=alpha)) < 1e-12), \ "{}, {}, {}: {} vs {}".format(cov_type, fit_intercept, t, np.array(est.coef__interval(alpha=alpha))[:, t], lr[t].coef__interval(alpha=alpha)) - assert np.all(np.abs(est.intercept_[t] - lr[t].intercept_) < 1e-12),\ + assert np.all(np.abs(est.intercept_[t] - lr[t].intercept_) < 1e-12), \ "{}, {}, {}: {} vs {}".format(cov_type, fit_intercept, t, est.intercept_[t], lr[t].intercept_) assert np.all(np.abs(np.array(est.intercept__interval(alpha=alpha))[:, t] - - lr[t].intercept__interval(alpha=alpha)) < 1e-12),\ + lr[t].intercept__interval(alpha=alpha)) < 1e-12), \ "{}, {}, {}: {} vs {}".format(cov_type, fit_intercept, t, np.array(est.intercept__interval(alpha=alpha))[:, t], lr[t].intercept__interval(alpha=alpha)) - assert np.all(np.abs(est.predict(X_test)[:, t] - lr[t].predict(X_test)) < 1e-12),\ + assert np.all(np.abs(est.predict(X_test)[:, t] - lr[t].predict(X_test)) < 1e-12), \ "{}, {}, {}: {} vs {}".format(cov_type, fit_intercept, t, est.predict(X_test)[ :, t], lr[t].predict(X_test)) assert np.all(np.abs(np.array(est.predict_interval(X_test, alpha=alpha))[:, :, t] - - lr[t].predict_interval(X_test, alpha=alpha)) < 1e-12),\ + lr[t].predict_interval(X_test, alpha=alpha)) < 1e-12), \ "{}, {}, {}: {} vs {}".format(cov_type, fit_intercept, t, np.array(est.predict_interval(X_test, alpha=alpha))[:, :, t], diff --git a/pyproject.toml b/pyproject.toml index 16795f376..929ebe1db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,83 @@ +[project] +name = "econml" +requires-python = ">=3.7" +authors = [{ name = "PyWhy contributors" }] +description = "This package contains several methods for calculating Conditional Average Treatment Effects" +readme = "README.md" +keywords = ["treatment-effect"] +license = { text = "MIT" } +dynamic = ["version"] + +classifiers = [ + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux" +] +dependencies = [ + "numpy", + "scipy > 1.4.0", + "scikit-learn > 0.22.0, < 1.3", + "sparse", + "joblib >= 0.13.0; python_version > '3.7' or platform_system != 'Windows'", + # upper limit due to https://github.com/joblib/loky/issues/411 + "joblib >= 0.13.0, < 1.3.0; python_version <= '3.7' and platform_system == 'Windows'", + "statsmodels >= 0.10", + "pandas", + "shap >= 0.38.1, < 0.42.0", + "lightgbm" +] + +[project.urls] +"Homepage" = "https://github.com/py-why/EconML" +"Bug Tracker" = "https://github.com/py-why/EconML/Issues" +"Source Code" = "https://github.com/py-why/EconML" +"Documentation" = "https://econml.azurewebsites.net/" + +[project.optional-dependencies] +automl = [ + # Disabled due to incompatibility with scikit-learn + # azureml-sdk[explain,automl] == 1.0.83 + "azure-cli" +] +tf = [ + # This extra is not currently compatible with python 3.9 or above because of tensorflow breaking changes + "keras < 2.4; python_version < '3.9'", + "tensorflow > 1.10, < 2.3; python_version < '3.9'", + # Version capped due to tensorflow incompatibility + "protobuf < 4", + # Version capped due to tensorflow incompatibility + "numpy < 1.24" +] +plt = [ + "graphviz", + # Version capped due to shap incompatibility + "matplotlib < 3.6.0" +] +dowhy = [ + "dowhy < 0.11" +] +all = [ + # Disabled due to incompatibility with scikit-learn + # azureml-sdk[explain,automl] == 1.0.83 + "azure-cli", + # This extra is not currently compatible with python 3.9 or above because of tensorflow breaking changes + "keras < 2.4; python_version < '3.9'", + "tensorflow > 1.10, < 2.3; python_version < '3.9'", + # Version capped due to tensorflow incompatibility + "protobuf < 4", + # Version capped due to tensorflow incompatibility + "numpy < 1.24", + "graphviz", + # Version capped due to shap incompatibility + "matplotlib < 3.6.0", + "dowhy < 0.11" +] + [build-system] requires = [ "setuptools", diff --git a/setup.cfg b/setup.cfg index d10f1e3d5..aa2d69311 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,78 +6,13 @@ max-line-length=119 ; Use numpy style convention=numpy -[metadata] -name = econml -author = PyWhy contributors -description = This package contains several methods for calculating Conditional Average Treatment Effects -long_description = file: README.md -long_description_content_type = text/markdown -license = MIT -keywords = treatment-effect -url = https://github.com/py-why/EconML -project_urls = - Bug Tracker=https://github.com/py-why/EconML/Issues - Source Code=https://github.com/py-why/EconML - Documentation=https://econml.azurewebsites.net/ -classifiers = - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - License :: OSI Approved :: MIT License - Operating System :: MacOS - Operating System :: Microsoft :: Windows - Operating System :: POSIX :: Linux - [options] packages = find_namespace: -install_requires = - numpy - scipy > 1.4.0 - scikit-learn > 0.22.0, < 1.3 - sparse - joblib >= 0.13.0 - statsmodels >= 0.10 - pandas - shap >= 0.38.1, < 0.42.0 - lightgbm test_suite = econml.tests tests_require = pytest pytest-xdist pytest-cov - -[options.extras_require] -automl = - ; Disabled due to incompatibility with scikit-learn - ; azureml-sdk[explain,automl] == 1.0.83 - azure-cli -tf = - ; This extra is not currently compatible with python 3.9 or above because of tensorflow breaking changes - keras < 2.4;python_version < '3.9' - tensorflow > 1.10, < 2.3;python_version < '3.9' - ; Version capped due to tensorflow incompatibility - protobuf < 4 - ; Version capped due to tensorflow incompatibility - numpy < 1.24 -plt = - graphviz - ; Version capped due to shap incompatibility - matplotlib < 3.6.0 -dowhy = - dowhy < 0.9 -all = - azure-cli - ; The tf components are not currently compatible with python 3.9 or above because of tensorflow breaking changes - keras < 2.4;python_version < '3.9' - tensorflow > 1.10, < 2.3;python_version < '3.9' - ; Version capped due to tensorflow incompatibility - protobuf < 4 - ; Version capped due to tensorflow incompatibility - numpy < 1.24 - ; Version capped due to shap incompatibility - matplotlib < 3.6.0 - dowhy < 0.9 [options.packages.find] include =