diff --git a/.gitignore b/.gitignore index 0cbc3b52..bd81fa46 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/_autosummary/ # PyBuilder .pybuilder/ diff --git a/docs/Makefile b/docs/Makefile index eaa3c0ee..8ea765a7 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -23,3 +23,4 @@ clean: sphinx-build -M clean "$(SOURCEDIR)" "$(BUILDDIR)" sphinx-build -M clean "$(SOURCEDIR)" "imgs" sphinx-build -M clean "$(SOURCEDIR)" "pages/modules/generated/" + rm -rf "$(SOURCEDIR)/_autosummary" diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000..a19a78d4 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,143 @@ +/* Улучшенные цвета и стили */ +.wy-nav-content { + max-width: 1200px; +} + +/* Красивые заголовки */ +h1 { + color: #2980B9; + border-bottom: 2px solid #2980B9; + padding-bottom: 10px; +} + +h2 { + color: #34495e; + margin-top: 30px; +} + +/* Стилизация блоков кода */ +.highlight { + background: #f8f8f8 !important; + border: 1px solid #e1e4e8; + border-radius: 6px; + margin: 1em 0; +} + +/* Красивые примечания */ +.admonition { + border-radius: 6px; + padding: 12px; + border-left: 4px solid; +} + +.admonition.note { + background-color: #e3f2fd; + border-left-color: #2196f3; +} + +.admonition.warning { + background-color: #fff3cd; + border-left-color: #ffc107; +} + +.admonition.tip { + background-color: #e8f5e9; + border-left-color: #4caf50; +} + +/* Улучшенные таблицы */ +.wy-table-responsive table td, .wy-table-responsive table th { + white-space: normal; +} + +table.docutils { + border: 1px solid #e1e4e8; + border-collapse: collapse; + border-spacing: 0; + width: 100%; +} + +table.docutils tr:nth-child(2n) { + background-color: #f6f8fa; +} + +/* Красивые кнопки */ +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + border: 1px solid transparent; + border-radius: 4px; + background-color: #2980B9; + color: white; + text-decoration: none; +} + +.btn:hover { + background-color: #21618C; + color: white; +} + +/* API Reference styling */ +.class dt { + font-size: 1.1em; + font-weight: bold; + margin-top: 20px; + padding: 10px; + background-color: #f0f0f0; + border-left: 3px solid #2980B9; +} + +/* Parameters styling */ +.field-list { + margin: 20px 0; +} + +.field-name { + font-weight: bold; + min-width: 100px; + padding-right: 20px; +} + +/* Навигация */ +.wy-menu-vertical li.current > a { + background: #fcfcfc; + border-right: solid 3px #2980B9; +} + +.wy-menu-vertical li.current a { + color: #404040; +} + +/* Badges */ +.badge { + display: inline-block; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 10px; +} + +.badge-primary { + background-color: #2980B9; +} + +.badge-success { + background-color: #27ae60; +} + +.badge-warning { + background-color: #f39c12; +} \ No newline at end of file diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst index 014cfb8a..fcb258d3 100644 --- a/docs/_templates/autosummary/module.rst +++ b/docs/_templates/autosummary/module.rst @@ -1,48 +1,53 @@ .. role:: hidden - :class: hidden-section + :class: hidden-section {{ name | underline }} .. automodule:: {{ fullname }} - {% block classes %} - {% if classes %} - .. rubric:: {{ _('Classes') }} - - .. autosummary:: - :toctree: generated - :nosignatures: - :template: classtemplate.rst - {% for item in classes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block functions %} - {% if functions %} - .. rubric:: {{ _('Functions') }} - - .. autosummary:: - :toctree: generated - :nosignatures: - :template: functiontemplate.rst - {% for item in functions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} +{% block classes %} +{% if classes %} +.. rubric:: {{ _('Classes') }} + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: classtemplate.rst + + {% for item in classes %} + {{ item }} + {% endfor %} +{% endif %} +{% endblock %} + +{% block functions %} +{% if functions %} + +.. rubric:: {{ _('Functions') }} + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: functiontemplate.rst + + {% for item in functions %} + {{ item }} + {% endfor %} +{% endif %} +{% endblock %} {% block modules %} {% if modules %} + .. rubric:: {{ _('Modules') }} .. autosummary:: - :toctree: - :recursive: -{% for item in modules %} - {{ item }} -{%- endfor %} + :toctree: + :recursive: + + {% for item in modules %} + {{ item }} + {% endfor %} {% endif %} {% endblock %} diff --git a/docs/api_reference.rst b/docs/api_reference.rst new file mode 100644 index 00000000..848d801d --- /dev/null +++ b/docs/api_reference.rst @@ -0,0 +1,40 @@ +API Reference +============= + +.. currentmodule:: hypex + +Main Classes +------------ + +.. autosummary:: + :toctree: _autosummary + :nosignatures: + :template: autosummary/class.rst + + AATest + ABTest + HomogeneityTest + matching.Matching + +Dataset Module +-------------- + +.. autosummary:: + :toctree: _autosummary + :nosignatures: + :template: autosummary/class.rst + + dataset.Dataset + dataset.ExperimentData + +Roles +~~~~~ + +.. autosummary:: + :toctree: _autosummary + :nosignatures: + + dataset.TargetRole + dataset.TreatmentRole + dataset.FeatureRole + dataset.InfoRole diff --git a/docs/conf.py b/docs/conf.py index 8a808df8..796426a5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -74,11 +74,41 @@ # a list of builtin themes. html_theme = "sphinx_rtd_theme" +html_theme_options = { + 'logo_only': False, + 'prev_next_buttons_location': 'bottom', + 'style_external_links': True, + 'vcs_pageview_mode': 'blob', + 'style_nav_header_background': '#2980B9', + # Toc options + 'collapse_navigation': True, + 'sticky_navigation': True, + 'navigation_depth': 4, + 'includehidden': True, + 'titles_only': False, + 'globaltoc_collapse': True, + 'globaltoc_maxdepth': 3, +} + # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] +html_css_files = [ + 'custom.css', +] + +html_show_sourcelink = False +html_sidebars = { + '**': [ + 'globaltoc.html', + 'relations.html', + 'sourcelink.html', + 'searchbox.html', + ] +} + # code style pygments_style = "default" @@ -99,6 +129,7 @@ "ignore-module-all": True, "show-inheritance": True, "exclude-members": EXCLUDED_MEMBERS, + 'inherited-members': False, } # order of members in docs, usefully for methods in class diff --git a/docs/index.rst b/docs/index.rst index 22fe6103..b8c5f24c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,36 @@ Welcome to HypEx's documentation! ================================== +HypEx is a fast and customizable framework for Causal Inference. + .. toctree:: :maxdepth: 2 :caption: Contents: + + installation + quickstart + api_reference + +Installation +------------ + +.. code-block:: bash + + pip install hypex + +Quick Start +----------- + +.. code-block:: python + + from hypex import ABTest, AATest, Matching + from hypex.dataset import Dataset, TargetRole, TreatmentRole + + # Your code here + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..da4345a3 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,48 @@ +Installation +============ + +Requirements +------------ + +* Python 3.8 or higher +* NumPy +* Pandas +* SciPy +* Scikit-learn +* Statsmodels + +Basic Installation +------------------ + +Install HypEx using pip: + +.. code-block:: bash + + pip install hypex + +Development Installation +------------------------ + +For development, clone the repository and install in editable mode: + +.. code-block:: bash + + git clone https://github.com/sb-ai-lab/HypEx.git + cd HypEx + pip install -e . + +Optional Dependencies +--------------------- + +For additional functionality, install with extras: + +.. code-block:: bash + + # For CatBoost support + pip install hypex[cat] + + # For LightGBM support + pip install hypex[lgbm] + + # All extras + pip install hypex[all] diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 00000000..65be3ec6 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,71 @@ +Quick Start Guide +================= + +This guide will help you get started with HypEx. + +Basic Usage +----------- + +A/B Testing +~~~~~~~~~~~ + +.. code-block:: python + + from hypex import ABTest + from hypex.dataset import Dataset, TargetRole, TreatmentRole + import pandas as pd + + # Load your data + df = pd.read_csv('your_data.csv') + + # Create dataset with roles + data = Dataset( + roles={ + 'conversion': TargetRole(), + 'group': TreatmentRole(), + 'feature1': FeatureRole(), + 'feature2': FeatureRole() + }, + data=df + ) + + # Run A/B test + ab_test = ABTest() + results = ab_test.execute(data) + + # View results + print(results.resume) + +A/A Testing +~~~~~~~~~~~ + +.. code-block:: python + + from hypex import AATest + + # Run A/A test to check for sample ratio mismatch + aa_test = AATest( + n_iterations=100, + stratification=True + ) + results = aa_test.execute(data) + + # Check if splits are good + print(results.resume) + +Matching +~~~~~~~~ + +.. code-block:: python + + from hypex import Matching + + # Perform matching analysis + matching = Matching( + distance="mahalanobis", + metric="att" + ) + results = matching.execute(data) + + # View matched pairs and treatment effects + print(results.resume) diff --git a/hypex/aa.py b/hypex/aa.py index a38b14e1..4b60eb6b 100644 --- a/hypex/aa.py +++ b/hypex/aa.py @@ -100,7 +100,7 @@ class AATest(ExperimentShell): random_states (Iterable[int], optional): Random seeds to use for each iteration. If None, uses range(n_iterations). Defaults to None. t_test_equal_var (bool, optional): If True (default), perform a standard independent 2 sample - test that assumes equal population variances. If False, perform Welch’s t-test, + test that assumes equal population variances. If False, perform Welch's t-test, which does not assume equal population variance. Examples: @@ -188,7 +188,7 @@ def __init__( sample_size: float | None = None, additional_params: dict[str, Any] | None = None, random_states: Iterable[int] | None = None, - t_test_equal_var: bool = None, + t_test_equal_var: bool | None = None, ): if n_iterations is None: if precision_mode: diff --git a/hypex/ab.py b/hypex/ab.py index 147d5c6b..476775df 100644 --- a/hypex/ab.py +++ b/hypex/ab.py @@ -24,28 +24,23 @@ class ABTest(ExperimentShell): multitest_method (str, optional): Method to use for multiple testing correction. Valid options are: "bonferroni", "sidak", "holm-sidak", "holm", "simes-hochberg", "hommel", "fdr_bh", "fdr_by", "fdr_tsbh", "fdr_tsbhy", "quantile". Defaults to "holm". - For more information refer to the statsmodels documentation: - + For more information refer to the statsmodels documentation: + https://www.statsmodels.org/dev/generated/statsmodels.stats.multitest.multipletests.html Examples: - Basic A/B test with default t-test: - >>> ab_test = ABTest() - >>> results = ab_test.execute(data) + Basic A/B test with default t-test:: + + >>> ab_test = ABTest() + >>> results = ab_test.execute(data) - A/B test with multiple statistical tests: - >>> ab_test = ABTest( - ... additional_tests=["t-test", "chi2-test"], - ... multitest_method="bonferroni" - ... ) - >>> results = ab_test.execute(data) + A/B test with multiple statistical tests:: - A/B test with single non-default test: - >>> ab_test = ABTest( - ... additional_tests="u-test", - ... multitest_method="fdr_bh" - ... ) - >>> results = ab_test.execute(data) + >>> ab_test = ABTest( + ... additional_tests=["t-test", "chi2-test"], + ... multitest_method="bonferroni" + ... ) + >>> results = ab_test.execute(data) """ @staticmethod @@ -120,7 +115,7 @@ def __init__( ] | None ) = "holm", - t_test_equal_var: bool = None, + t_test_equal_var: bool | None = None, ): super().__init__( experiment=self._make_experiment(additional_tests, multitest_method), diff --git a/hypex/utils/decorator.py b/hypex/utils/decorator.py index ab9830fd..3c9c921d 100644 --- a/hypex/utils/decorator.py +++ b/hypex/utils/decorator.py @@ -7,34 +7,39 @@ def inherit_docstring_from( - source: Callable[..., Any] | property, + source: Callable[..., Any] | property, ) -> DocstringInheritDecorator: - """ - A decorator to inherit the docstring from another function or property. + """A decorator to inherit the docstring from another function or property. + This decorator can be applied to both callable objects and properties. It copies the docstring from the source object to the decorated object if the latter does not already have a docstring. + Args: - source (Callable[..., Any] or property): The object from which the docstring will be inherited. - This should be either a callable or a property that has - a well-defined __doc__ attribute. + source: The object from which the docstring will be inherited. + This should be either a callable or a property that has + a well-defined __doc__ attribute. + Returns: - Callable[[DecoratedType], DecoratedType]: A decorator that when applied to a function or property, - sets its __doc__ attribute to that of the source. + A decorator that when applied to a function or property, + sets its __doc__ attribute to that of the source. + Raises: TypeError: If the object to be decorated is neither a callable nor a property. - Usage: - class SomeClass: - @property - @inherit_docstring_from(pd.DataFrame.iloc) - def iloc(self): - return self._data.iloc + + Example: + Using with property:: + + class SomeClass: + @property + @inherit_docstring_from(pd.DataFrame.iloc) + def iloc(self): + return self._data.iloc + + Using with method:: + @inherit_docstring_from(pd.DataFrame.mean) def mean(self): return self._data.mean() - Example: - >>> my_instance = MyClass() - >>> print(my_instance.attr1.__doc__) - >>> print(my_instance.__init__().__doc__) """ def decorator(obj: DecoratedType) -> DecoratedType: diff --git a/hypex/utils/tutorial_data_creation.py b/hypex/utils/tutorial_data_creation.py index 0f31b6ad..d821bfe4 100644 --- a/hypex/utils/tutorial_data_creation.py +++ b/hypex/utils/tutorial_data_creation.py @@ -160,12 +160,11 @@ def sigmoid(x: np.ndarray) -> np.ndarray: """Logistic sigmoid ufunc for ndarrays. The sigmoid function, also known as the logistic sigmoid function, - is defined as sigmoid(x) = $\fraq{1}{(1+\\exp{-x})}$. + is defined as sigmoid(x) = 1/(1+exp(-x)). It is the inverse of the logit function. Args: - x: - Input array. + x: Input array. Returns: Sigmoid function of x. diff --git a/tox.ini b/tox.ini index a63f26cb..8b1e778e 100644 --- a/tox.ini +++ b/tox.ini @@ -57,7 +57,7 @@ deps = IPython >= 7.0 commands = make clean html - doc8 . + doc8 . --ignore-path _autosummary --ignore-path _build --ignore-path _templates [testenv:typing] skip_install = true