diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 516689f2..b66fbd10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,8 @@ repos: (?x)^( .*\.txt| .*\.ipynb| - .*\.html + .*\.html| + docs/source/_build/.* )$ # Format docstrings diff --git a/acro/acro.py b/acro/acro.py index f9858a2e..8bc9e3f1 100644 --- a/acro/acro.py +++ b/acro/acro.py @@ -78,9 +78,7 @@ def __init__(self, config: str = "default", suppress: bool = False) -> None: # set globals for survival analysis acro_tables.SURVIVAL_THRESHOLD = self.config["survival_safe_threshold"] - def finalise( - self, path: str = "outputs", ext: str = "json", interactive: bool = False - ) -> Records | None: + def finalise(self, path: str = "outputs", ext="json") -> Records | None: """Create a results file for checking. Parameters @@ -89,8 +87,6 @@ def finalise( Name of a folder to save outputs. ext : str Extension of the results file. Valid extensions: {json, xlsx}. - interactive : bool - Whether to prompt the user to request exceptions for failing outputs. Returns ------- @@ -105,7 +101,7 @@ def finalise( path, ) return None - self.results.finalise(path, ext, interactive) + self.results.finalise(path, ext) config_filename: str = os.path.normpath(f"{path}/config.json") try: with open(config_filename, "w", newline="", encoding="utf-8") as file: diff --git a/acro/acro_tables.py b/acro/acro_tables.py index 03810188..320a7e28 100644 --- a/acro/acro_tables.py +++ b/acro/acro_tables.py @@ -214,7 +214,6 @@ def crosstab( # pylint: disable=too-many-arguments,too-many-locals ) # record output - self.results.add( status=status, output_type="table", @@ -226,11 +225,6 @@ def crosstab( # pylint: disable=too-many-arguments,too-many-locals output=[table], comments=comments, ) - if self.suppress: - justadded = f"output_{self.results.output_id - 1}" - self.results.add_exception( - justadded, "Suppression automatically applied where needed" - ) return table def pivot_table( # pylint: disable=too-many-arguments,too-many-locals @@ -404,11 +398,6 @@ def pivot_table( # pylint: disable=too-many-arguments,too-many-locals output=[table], comments=comments, ) - if self.suppress: - justadded = f"output_{self.results.output_id - 1}" - self.results.add_exception( - justadded, "Suppression automatically applied where needed" - ) return table def surv_func( # pylint: disable=too-many-arguments,too-many-locals @@ -1378,7 +1367,7 @@ def create_dataframe(index, columns) -> DataFrame: index_df = pd.concat(index, axis=1) elif isinstance(index, pd.Series): index_df = pd.DataFrame({index.name: index}) - except (ValueError, TypeError): + except ValueError: index_df = empty_dataframe columns_df = empty_dataframe @@ -1387,12 +1376,12 @@ def create_dataframe(index, columns) -> DataFrame: columns_df = pd.concat(columns, axis=1) elif isinstance(columns, pd.Series): columns_df = pd.DataFrame({columns.name: columns}) - except (ValueError, TypeError): + except ValueError: columns_df = empty_dataframe try: data = pd.concat([index_df, columns_df], axis=1) - except (ValueError, TypeError): + except ValueError: data = empty_dataframe return data diff --git a/acro/record.py b/acro/record.py index aa29a5de..967eb5d4 100644 --- a/acro/record.py +++ b/acro/record.py @@ -430,7 +430,7 @@ def validate_outputs(self) -> None: ) record.exception = input("") - def finalise(self, path: str, ext: str, interactive: bool = False) -> None: + def finalise(self, path: str, ext: str) -> None: """Create a results file for checking. Parameters @@ -439,12 +439,9 @@ def finalise(self, path: str, ext: str, interactive: bool = False) -> None: Name of a folder to save outputs. ext : str Extension of the results file. Valid extensions: {json, xlsx}. - interactive : Bool - Whether to prompt the user to request exceptions for failing outputs. """ logger.debug("finalise()") - if interactive: - self.validate_outputs() + self.validate_outputs() if ext == "json": self.finalise_json(path) elif ext == "xlsx": diff --git a/docs/source/_build/.buildinfo b/docs/source/_build/.buildinfo new file mode 100644 index 00000000..c6066d59 --- /dev/null +++ b/docs/source/_build/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: c6e75f07f2d08ab9c68144b63a96bffd +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/source/_build/.doctrees/acro.doctree b/docs/source/_build/.doctrees/acro.doctree new file mode 100644 index 00000000..ec52efa1 Binary files /dev/null and b/docs/source/_build/.doctrees/acro.doctree differ diff --git a/docs/source/_build/.doctrees/acro_r.doctree b/docs/source/_build/.doctrees/acro_r.doctree new file mode 100644 index 00000000..7ab825e9 Binary files /dev/null and b/docs/source/_build/.doctrees/acro_r.doctree differ diff --git a/docs/source/_build/.doctrees/api.doctree b/docs/source/_build/.doctrees/api.doctree new file mode 100644 index 00000000..edebcb69 Binary files /dev/null and b/docs/source/_build/.doctrees/api.doctree differ diff --git a/docs/source/_build/.doctrees/api/acro_class.doctree b/docs/source/_build/.doctrees/api/acro_class.doctree new file mode 100644 index 00000000..e5451859 Binary files /dev/null and b/docs/source/_build/.doctrees/api/acro_class.doctree differ diff --git a/docs/source/_build/.doctrees/api/records_class.doctree b/docs/source/_build/.doctrees/api/records_class.doctree new file mode 100644 index 00000000..f97320cd Binary files /dev/null and b/docs/source/_build/.doctrees/api/records_class.doctree differ diff --git a/docs/source/_build/.doctrees/environment.pickle b/docs/source/_build/.doctrees/environment.pickle new file mode 100644 index 00000000..43f7268a Binary files /dev/null and b/docs/source/_build/.doctrees/environment.pickle differ diff --git a/docs/source/_build/.doctrees/examples.doctree b/docs/source/_build/.doctrees/examples.doctree new file mode 100644 index 00000000..4f5d6469 Binary files /dev/null and b/docs/source/_build/.doctrees/examples.doctree differ diff --git a/docs/source/_build/.doctrees/examples/configuration.doctree b/docs/source/_build/.doctrees/examples/configuration.doctree new file mode 100644 index 00000000..ea4f891d Binary files /dev/null and b/docs/source/_build/.doctrees/examples/configuration.doctree differ diff --git a/docs/source/_build/.doctrees/examples/quickstart.doctree b/docs/source/_build/.doctrees/examples/quickstart.doctree new file mode 100644 index 00000000..c0d49052 Binary files /dev/null and b/docs/source/_build/.doctrees/examples/quickstart.doctree differ diff --git a/docs/source/_build/.doctrees/faq.doctree b/docs/source/_build/.doctrees/faq.doctree new file mode 100644 index 00000000..2a07aa74 Binary files /dev/null and b/docs/source/_build/.doctrees/faq.doctree differ diff --git a/docs/source/_build/.doctrees/index.doctree b/docs/source/_build/.doctrees/index.doctree new file mode 100644 index 00000000..98647230 Binary files /dev/null and b/docs/source/_build/.doctrees/index.doctree differ diff --git a/docs/source/_build/.doctrees/installation.doctree b/docs/source/_build/.doctrees/installation.doctree new file mode 100644 index 00000000..fc931463 Binary files /dev/null and b/docs/source/_build/.doctrees/installation.doctree differ diff --git a/docs/source/_build/.doctrees/introduction.doctree b/docs/source/_build/.doctrees/introduction.doctree new file mode 100644 index 00000000..ebd7a666 Binary files /dev/null and b/docs/source/_build/.doctrees/introduction.doctree differ diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo.ipynb b/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo.ipynb new file mode 100644 index 00000000..f80d58b9 --- /dev/null +++ b/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo.ipynb @@ -0,0 +1,642 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from acro import ACRO" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- In this version, the data can be read directly from the local machine after it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab\n", + "- This is an example of crosstab using ACRO. \n", + "- The INFO lines show the researcher what will be reported to the output checkers.\n", + "- Then the (suppressed as necessary) table is shown via the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommendation | | | |\n", + "--------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "--------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, rownames=[\"recommendation\"], colnames=[\"parents\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression\n", + "- This is an example of crosstab with suppressing the cells that violate the disclosure tests.\n", + "- Note that you need to change the value of the suppress variable in the acro object to True. Then run the crosstab command. \n", + "- If you wish to continue the research while suppressing the outputs, leave the suppress variable as it is, otherwise turn it off." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:38:29.296719\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:38:29.315826\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "\"uid: output_0\\nstatus: fail\\ntype: table\\nproperties: {'method': 'crosstab'}\\nsdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 4 cells may need suppressing; \\noutcome: parents great_pret pretentious usual\\nrecommendation \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommendation \\nnot_recom 1440 1440 1440\\npriority 858 1484 1924\\nrecommend 0 0 2\\nspec_prior 2022 1264 758\\nvery_recom 0 132 196]\\ntimestamp: 2025-03-06T19:38:29.296719\\ncomments: []\\nexception: \\n\\nuid: output_1\\nstatus: fail\\ntype: table\\nproperties: {'method': 'crosstab'}\\nsdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents)\\nsummary: fail; threshold: 4 cells suppressed; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:38:29.315826\\ncomments: []\\nexception: \\n\\n\"" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_1 renamed to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_1\", \"cross_tabulation\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.add_comments(\"cross_tabulation\", \"Please let me have this data.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: cross_tabulation\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:38:29.315826\n", + "comments: ['Please let me have this data.']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: Examples\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"Examples\", \"json\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo_v2.ipynb b/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo_v2.ipynb new file mode 100644 index 00000000..93da188f --- /dev/null +++ b/docs/source/_build/.doctrees/nbsphinx/notebooks/acro_demo_v2.ipynb @@ -0,0 +1,912 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from acro import ACRO" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- In this version, the data can be read directly from the local machine after it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab\n", + "- This is an example of crosstab using ACRO. \n", + "- The INFO lines show the researcher what will be reported to the output checkers.\n", + "- Then the (suppressed as necessary) table is shown via the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommendation | | | |\n", + "--------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "--------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, rownames=[\"recommendation\"], colnames=[\"parents\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression\n", + "- This is an example of crosstab with suppressing the cells that violate the disclosure tests.\n", + "- Note that you need to change the value of the suppress variable in the acro object to True. Then run the crosstab command. \n", + "- If you wish to continue the research while suppressing the outputs, leave the suppress variable as it is, otherwise turn it off." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## An example of a more complex table\n", + "- make the children variable numeric\n", + "- so we can report statistics like mean etc." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "df[\"children\"].replace(to_replace={\"more\": \"4\"}, inplace=True)\n", + "df[\"children\"] = pd.to_numeric(df[\"children\"])\n", + "\n", + "df[\"children\"] = df.apply(\n", + " lambda row: (\n", + " row[\"children\"] if row[\"children\"] in (1, 2, 3) else np.random.randint(4, 10)\n", + " ),\n", + " axis=1,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 2 cells may need suppressing; p-ratio: 9 cells may need suppressing; nk-rule: 9 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "|recommend | not_recom| priority recommend |spec_prior |very_recom |All|\n", + "|parents finance | | | | | |\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "|great_pret convenient| ok | ok p-ratio; nk-rule; | ok | p-ratio; nk-rule; | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | p-ratio; nk-rule; | ok|\n", + "|pretentious convenient| ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "|usual convenient| ok | ok threshold; p-ratio; nk-rule; | ok | ok | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "|All | ok | ok threshold; p-ratio; nk-rule; | ok | ok | ok|\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
recommendnot_recompriorityrecommendspec_priorvery_recomAll
parentsfinance
great_pretconvenient3.1388892.787109NaN3.324353NaN3.135185
inconv3.1319442.393064NaN3.411335NaN3.155093
pretentiousconvenient3.0875003.104167NaN3.2705482.6477273.125000
inconv3.0902783.057263NaN3.3191181.3636363.116204
usualconvenient3.1277783.1172201.03.3284882.6000003.121296
inconv3.1263893.163542NaN3.3719811.3636363.136111
All3.1171303.0168781.03.3444612.1981713.131481
\n", + "
" + ], + "text/plain": [ + "recommend not_recom priority recommend spec_prior \\\n", + "parents finance \n", + "great_pret convenient 3.138889 2.787109 NaN 3.324353 \n", + " inconv 3.131944 2.393064 NaN 3.411335 \n", + "pretentious convenient 3.087500 3.104167 NaN 3.270548 \n", + " inconv 3.090278 3.057263 NaN 3.319118 \n", + "usual convenient 3.127778 3.117220 1.0 3.328488 \n", + " inconv 3.126389 3.163542 NaN 3.371981 \n", + "All 3.117130 3.016878 1.0 3.344461 \n", + "\n", + "recommend very_recom All \n", + "parents finance \n", + "great_pret convenient NaN 3.135185 \n", + " inconv NaN 3.155093 \n", + "pretentious convenient 2.647727 3.125000 \n", + " inconv 1.363636 3.116204 \n", + "usual convenient 2.600000 3.121296 \n", + " inconv 1.363636 3.136111 \n", + "All 2.198171 3.131481 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.suppress = False\n", + "acro.crosstab(\n", + " index=[df.parents, df.finance],\n", + " columns=[df.recommend],\n", + " values=df.children,\n", + " aggfunc=\"mean\",\n", + " margins=\"total\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:36:50.672611\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:36:50.694578\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 9, 'nk-rule': 9, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[4, 2], [6, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [1, 4], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [1, 4], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2]], 'all-values-are-same': []}}\n", + "command: acro.crosstab(\n", + "summary: fail; threshold: 2 cells may need suppressing; p-ratio: 9 cells may need suppressing; nk-rule: 9 cells may need suppressing; \n", + "outcome: recommend not_recom priority recommend \\\n", + "parents finance \n", + "great_pret convenient ok ok p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "pretentious convenient ok ok p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "usual convenient ok ok threshold; p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "All ok ok threshold; p-ratio; nk-rule; \n", + "\n", + "recommend spec_prior very_recom All \n", + "parents finance \n", + "great_pret convenient ok p-ratio; nk-rule; ok \n", + " inconv ok p-ratio; nk-rule; ok \n", + "pretentious convenient ok ok ok \n", + " inconv ok ok ok \n", + "usual convenient ok ok ok \n", + " inconv ok ok ok \n", + "All ok ok ok \n", + "output: [recommend not_recom priority recommend spec_prior \\\n", + "parents finance \n", + "great_pret convenient 3.138889 2.787109 NaN 3.324353 \n", + " inconv 3.131944 2.393064 NaN 3.411335 \n", + "pretentious convenient 3.087500 3.104167 NaN 3.270548 \n", + " inconv 3.090278 3.057263 NaN 3.319118 \n", + "usual convenient 3.127778 3.117220 1.0 3.328488 \n", + " inconv 3.126389 3.163542 NaN 3.371981 \n", + "All 3.117130 3.016878 1.0 3.344461 \n", + "\n", + "recommend very_recom All \n", + "parents finance \n", + "great_pret convenient NaN 3.135185 \n", + " inconv NaN 3.155093 \n", + "pretentious convenient 2.647727 3.125000 \n", + " inconv 1.363636 3.116204 \n", + "usual convenient 2.600000 3.121296 \n", + " inconv 1.363636 3.136111 \n", + "All 2.198171 3.131481 ]\n", + "timestamp: 2025-03-06T19:36:50.838863\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + } + ], + "source": [ + "_ = acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_1 renamed to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_1\", \"cross_tabulation\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.add_comments(\n", + " \"cross_tabulation\", \"Suppression has been applied. Please let me have this data.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Request an exception\n", + "An example of providing a reason why an exception should be made" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:exception request was added to output_2\n" + ] + } + ], + "source": [ + "acro.add_exception(\"output_2\", \"This is evidence of systematic bias?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)\n", + "- If an output is flagged as potentially disclosive then the \n", + " researcher is prompted to provide a reason for release if they have not already done so." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: cross_tabulation\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:36:50.694578\n", + "comments: ['Suppression has been applied. Please let me have this data.']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: Examples\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"Examples\", \"json\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks/test-nursery.ipynb b/docs/source/_build/.doctrees/nbsphinx/notebooks/test-nursery.ipynb new file mode 100644 index 00000000..b5415c8d --- /dev/null +++ b/docs/source/_build/.doctrees/nbsphinx/notebooks/test-nursery.ipynb @@ -0,0 +1,2785 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "00cac1f9", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e33fd4fb", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c01cfe12", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc8d993a", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from acro import ACRO, add_constant, add_to_acro" + ] + }, + { + "cell_type": "markdown", + "id": "530efcfe", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4b8a77e2", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: True\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=True)" + ] + }, + { + "cell_type": "markdown", + "id": "27a2baaa", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- The dataset can be read directly from OpenML using the code commented in the next cell.\n", + "- In this version, it can be read directly from the local machine if it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ac790b2b-b02f-49f7-8237-a033abed6e87", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "ea2a0d76-ba68-4a74-93c3-bdaa88c48929", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Convert 'more than 3' children to random between 4 and 10\n", + "Change the children column from categorical to numeric in order to be able to test some of the ACRO functions that require a numeric feature" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b43810a8-4da9-4cec-a613-e2562ed95601", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " children column entries in raw file ['1' '2' '3' 'more']\n" + ] + } + ], + "source": [ + "print(f\" children column entries in raw file {df.children.unique()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "042f8e9d-b33b-4daf-851e-9f70b5d4859a", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "df[\"children\"].replace(to_replace={\"more\": \"4\"}, inplace=True)\n", + "df[\"children\"] = pd.to_numeric(df[\"children\"])\n", + "\n", + "df[\"children\"] = df.apply(\n", + " lambda row: (\n", + " row[\"children\"] if row[\"children\"] in (1, 2, 3) else np.random.randint(4, 10)\n", + " ),\n", + " axis=1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "d098c704", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "4ae844a0", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "961684cb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "id": "d642ed00", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO crosstab\n", + "This is an example of crosstab using ACRO. \n", + "The INFO lines show the researcher what will be reported to the output checkers.\n", + "Then the (suppressed as necessary) table is shown via. the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bb4b2677", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend,\n", + " df.parents,\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "b3d09450", + "metadata": {}, + "source": [ + "### ACRO crosstab with totals\n", + "This is an example of crosstab with totals columns and suppression. \n", + "Note that when margins is true any row or column where all the cells are discolsive is deleted. If you wish to see such row or column set show_suppressed to True. \n", + "show_suppressed parameter does not work with herichical tables and when the aggregation function is the standard deviation." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "42825f24", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 5 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |All |\n", + "recommend | | | | |\n", + "------------------------------------------------------------------|\n", + "not_recom | ok | ok | ok | ok|\n", + "priority | ok | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok | ok|\n", + "All | ok | ok | ok | ok|\n", + "------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "b3e48dec", + "metadata": {}, + "source": [ + "### ACRO crosstab without suppression\n", + "This is an example of crosstab without suppressing the cells that violate the disclosure tests. \n", + "Note that you need to change the value of the suppress variable in the acro object to False. Then run the crosstab command. \n", + "If you wish to continue the research while suppressing the outputs, turn on the suppress variable otherwise leave it as it is." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "27329e57", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "acro.suppress = False\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ed5134ab", + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = True" + ] + }, + { + "cell_type": "markdown", + "id": "8b603548", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO crosstab with aggregation function" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "298d2b40", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "------------------------------------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | p-ratio; nk-rule; | p-ratio; nk-rule; | threshold; p-ratio; nk-rule; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | p-ratio; nk-rule; | ok | ok|\n", + "------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, values=df.children, aggfunc=\"count\"\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b4aea046", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + " mode_aggfunc |mean |\n", + "parents great_pret pretentious usual |great_pret pretentious usual |\n", + "recommend | |\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "not_recom ok ok ok | ok ok ok|\n", + "priority ok ok ok | ok ok ok|\n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; threshold; p-ratio; nk-rule; | p-ratio; nk-rule; p-ratio; nk-rule; threshold; p-ratio; nk-rule; |\n", + "spec_prior ok ok ok | ok ok ok|\n", + "very_recom p-ratio; nk-rule; ok ok | p-ratio; nk-rule; ok ok|\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, values=df.children, aggfunc=[\"mode\", \"mean\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "d66e565b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO pivot_table\n", + "This is an example of pivot table using ACRO. \n", + "- Some researchers may prefer this to using crosstab. \n", + "- Again the call syntax is identical to the pandas \"pd.pivot_table\"\n", + "- in this case the output is non-disclosive" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "966c1a9b", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "------------------------------|\n", + " mean |std |\n", + " children |children|\n", + "parents | |\n", + "------------------------------|\n", + "great_pret ok | ok |\n", + "pretentious ok | ok |\n", + "usual ok | ok |\n", + "------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std\n", + " children children\n", + "parents \n", + "great_pret 3.140972 2.270396\n", + "pretentious 3.129630 2.250436\n", + "usual 3.110648 2.213072\n" + ] + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df, index=[\"parents\"], values=[\"children\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "id": "4adc374b", + "metadata": {}, + "source": [ + "### ACRO pivot_table with margins" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9c024b65", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------------------------------------------------------|\n", + " children |\n", + "recommend not_recom priority recommend spec_prior very_recom All|\n", + "parents |\n", + "-----------------------------------------------------------------------------------------------------------|\n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok threshold; p-ratio; nk-rule; ok|\n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "usual ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "All ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "-----------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:Disclosive cells were deleted from the dataframe before calculating the pivot table\n", + "INFO:acro:records:add(): output_6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412\n" + ] + } + ], + "source": [ + "safe_table = acro.pivot_table(\n", + " df, columns=[\"recommend\"], index=[\"parents\"], values=[\"children\"], margins=True\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "8446fa99-c073-48b8-875e-700dfa17ea0c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Regression examples using ACRO\n", + "\n", + "Again there is an industry-standard package in python, this time called **statsmodels**.\n", + "- The examples below illustrate the use of the ACRO wrapper standard statsmodel functions\n", + "- Note that statsmodels can be called using an 'R-like' format (using an 'r' suffix on the command names)\n", + "- most statsmodels functiobns return a \"results object\" which has a \"summary\" function that produces printable/saveable outputs \n", + "\n", + "### Start by manipulating the nursery data to get two numeric variables\n", + "- The 'recommend' column is converted to an integer scale" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "72aefb22", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "df[\"recommend\"].replace(\n", + " to_replace={\n", + " \"not_recom\": \"0\",\n", + " \"recommend\": \"1\",\n", + " \"very_recom\": \"2\",\n", + " \"priority\": \"3\",\n", + " \"spec_prior\": \"4\",\n", + " },\n", + " inplace=True,\n", + ")\n", + "df[\"recommend\"] = pd.to_numeric(df[\"recommend\"])\n", + "\n", + "new_df = df[[\"recommend\", \"children\"]]\n", + "new_df = new_df.dropna()" + ] + }, + { + "cell_type": "markdown", + "id": "3ef880e6-726f-4a0b-9bcd-da8861dbd5a7", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO OLS \n", + "This is an example of ordinary least square regression using ACRO. \n", + "- Above recommend column was converted form categorical to numeric. \n", + "- Now we perform a the linear regression between recommend and children. \n", + "- This version includes a constant (intercept)\n", + "- This is just to show how the regression is done using ACRO. \n", + "- **No correlation is expected to be seen by using these variables**" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "2f462e42", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:ols() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_7\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: recommend R-squared: 0.001
Model: OLS Adj. R-squared: 0.001
Method: Least Squares F-statistic: 13.83
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201
Time: 19:39:47 Log-Likelihood: -25121.
No. Observations: 12960 AIC: 5.025e+04
Df Residuals: 12958 BIC: 5.026e+04
Df Model: 1
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 2.2099 0.025 87.263 0.000 2.160 2.260
children 0.0245 0.007 3.718 0.000 0.012 0.037
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 77090.215 Durbin-Watson: 2.883
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570
Skew: -0.486 Prob(JB): 0.00
Kurtosis: 1.489 Cond. No. 6.90


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & recommend & \\textbf{ R-squared: } & 0.001 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.001 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 13.83 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.000201 \\\\\n", + "\\textbf{Time:} & 19:39:47 & \\textbf{ Log-Likelihood: } & -25121. \\\\\n", + "\\textbf{No. Observations:} & 12960 & \\textbf{ AIC: } & 5.025e+04 \\\\\n", + "\\textbf{Df Residuals:} & 12958 & \\textbf{ BIC: } & 5.026e+04 \\\\\n", + "\\textbf{Df Model:} & 1 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 2.2099 & 0.025 & 87.263 & 0.000 & 2.160 & 2.260 \\\\\n", + "\\textbf{children} & 0.0245 & 0.007 & 3.718 & 0.000 & 0.012 & 0.037 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 77090.215 & \\textbf{ Durbin-Watson: } & 2.883 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1741.570 \\\\\n", + "\\textbf{Skew:} & -0.486 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 1.489 & \\textbf{ Cond. No. } & 6.90 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.001\n", + "Model: OLS Adj. R-squared: 0.001\n", + "Method: Least Squares F-statistic: 13.83\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.026e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const 2.2099 0.025 87.263 0.000 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.000 0.012 0.037\n", + "==============================================================================\n", + "Omnibus: 77090.215 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "\"\"\"" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: recommend R-squared: 0.000
Model: OLS Adj. R-squared: 0.000
Method: Least Squares F-statistic: 6.417
Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113
Time: 21:21:09 Log-Likelihood: -25124.
No. Observations: 12960 AIC: 5.025e+04
Df Residuals: 12958 BIC: 5.027e+04
Df Model: 1
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 2.2341 0.025 87.965 0.000 2.184 2.284
children 0.0168 0.007 2.533 0.011 0.004 0.030
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 76735.931 Durbin-Watson: 2.883
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843
Skew: -0.485 Prob(JB): 0.00
Kurtosis: 1.487 Cond. No. 6.89


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.000\n", + "Model: OLS Adj. R-squared: 0.000\n", + "Method: Least Squares F-statistic: 6.417\n", + "Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113\n", + "Time: 21:21:09 Log-Likelihood: -25124.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.027e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const 2.2341 0.025 87.965 0.000 2.184 2.284\n", + "children 0.0168 0.007 2.533 0.011 0.004 0.030\n", + "==============================================================================\n", + "Omnibus: 76735.931 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843\n", + "Skew: -0.485 Prob(JB): 0.00\n", + "Kurtosis: 1.487 Cond. No. 6.89\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "\"\"\"" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y = new_df[\"recommend\"]\n", + "x = new_df[\"children\"]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.ols(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c826271", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO OLSR\n", + "This is an example of ordinary least squares regression using the 'R-like' statsmodels api, i.e. from a formula and dataframe using ACRO " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "cc90f7c9", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:olsr() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_8\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.001\n", + "Model: OLS Adj. R-squared: 0.001\n", + "Method: Least Squares F-statistic: 13.83\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.026e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "Intercept 2.2099 0.025 87.263 0.000 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.000 0.012 0.037\n", + "==============================================================================\n", + "Omnibus: 77090.215 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.000\n", + "Model: OLS Adj. R-squared: 0.000\n", + "Method: Least Squares F-statistic: 6.417\n", + "Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113\n", + "Time: 21:21:09 Log-Likelihood: -25124.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.027e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "Intercept 2.2341 0.025 87.965 0.000 2.184 2.284\n", + "children 0.0168 0.007 2.533 0.011 0.004 0.030\n", + "==============================================================================\n", + "Omnibus: 76735.931 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843\n", + "Skew: -0.485 Prob(JB): 0.00\n", + "Kurtosis: 1.487 Cond. No. 6.89\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n" + ] + } + ], + "source": [ + "results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\n", + "print(results.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "2816eac7", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO Probit\n", + "This is an example of probit regression using ACRO \n", + "We use a different combination of variables from the original dataset.\n", + "\n", + "Again, we support the use of R-like formulas - because we support R " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "5b1a1611", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:probit() outcome: pass; dof=12958.0 >= 10\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.693145\n", + " Iterations 2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add(): output_9\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Probit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Probit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06\n", + "Time: 19:39:47 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.7992\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0039 0.019 -0.207 0.836 -0.041 0.033\n", + "children 0.0012 0.005 0.254 0.799 -0.008 0.011\n", + "==============================================================================\n" + ] + } + ], + "source": [ + "new_df = df[[\"finance\", \"children\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"finance\"].astype(\"category\").cat.codes # numeric\n", + "y.name = \"finance\"\n", + "x = new_df[\"children\"]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.probit(y, x)\n", + "print(results.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "f38b4334", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO Logit\n", + "This is an example of logistic regression using ACRO using the statmodels function" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "dcf30f8f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:logit() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_10\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.693145\n", + " Iterations 3\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: finance No. Observations: 12960
Model: Logit Df Residuals: 12958
Method: MLE Df Model: 1
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06
Time: 19:39:47 Log-Likelihood: -8983.2
converged: True LL-Null: -8983.2
Covariance Type: nonrobust LLR p-value: 0.7992
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const -0.0062 0.030 -0.207 0.836 -0.065 0.053
children 0.0020 0.008 0.254 0.799 -0.013 0.017
" + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & finance & \\textbf{ No. Observations: } & 12960 \\\\\n", + "\\textbf{Model:} & Logit & \\textbf{ Df Residuals: } & 12958 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 1 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 3.602e-06 \\\\\n", + "\\textbf{Time:} & 19:39:47 & \\textbf{ Log-Likelihood: } & -8983.2 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -8983.2 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 0.7992 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & -0.0062 & 0.030 & -0.207 & 0.836 & -0.065 & 0.053 \\\\\n", + "\\textbf{children} & 0.0020 & 0.008 & 0.254 & 0.799 & -0.013 & 0.017 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Logit Regression Results}\n", + "\\end{center}" + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Logit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06\n", + "Time: 19:39:47 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.7992\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0062 0.030 -0.207 0.836 -0.065 0.053\n", + "children 0.0020 0.008 0.254 0.799 -0.013 0.017\n", + "==============================================================================\n", + "\"\"\"" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: finance No. Observations: 12960
Model: Logit Df Residuals: 12958
Method: MLE Df Model: 1
Date: Mon, 04 Mar 2024 Pseudo R-squ.: 1.186e-06
Time: 21:21:09 Log-Likelihood: -8983.2
converged: True LL-Null: -8983.2
Covariance Type: nonrobust LLR p-value: 0.8839
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const -0.0036 0.030 -0.119 0.905 -0.063 0.056
children 0.0012 0.008 0.146 0.884 -0.014 0.017
" + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Logit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Mon, 04 Mar 2024 Pseudo R-squ.: 1.186e-06\n", + "Time: 21:21:09 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.8839\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0036 0.030 -0.119 0.905 -0.063 0.056\n", + "children 0.0012 0.008 0.146 0.884 -0.014 0.017\n", + "==============================================================================\n", + "\"\"\"" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.logit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "a962ff17", + "metadata": {}, + "source": [ + "### ACRO survival analysis\n", + "This is an example of survival tables and plots using ACRO. \n", + "- A dataset from statsmodels is used for the survival analysis.\n", + "- A subset of tha dataset is used in this example to demostrate the survival analysis.\n", + "- The output parameter in the surv_func define the type of output (table or plot)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "dcf2c86d", + "metadata": {}, + "outputs": [], + "source": [ + "import statsmodels.api as sm\n", + "\n", + "data = sm.datasets.get_rdataset(\"flchain\", \"survival\").data\n", + "data = data.loc[data.sex == \"F\", :]\n", + "data = data.iloc[:20, :]\n", + "# data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "df1cbb9e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 76 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------|\n", + " Surv_prob |Surv_prob_SE |num_at_risk |num_events |\n", + "Time | | | |\n", + "-----------------------------------------------------------|\n", + "51 ok | ok | ok | ok|\n", + "69 threshold; | threshold; | threshold; | threshold; |\n", + "85 threshold; | threshold; | threshold; | threshold; |\n", + "91 threshold; | threshold; | threshold; | threshold; |\n", + "115 threshold; | threshold; | threshold; | threshold; |\n", + "372 threshold; | threshold; | threshold; | threshold; |\n", + "667 threshold; | threshold; | threshold; | threshold; |\n", + "874 threshold; | threshold; | threshold; | threshold; |\n", + "1039 threshold; | threshold; | threshold; | threshold; |\n", + "1046 threshold; | threshold; | threshold; | threshold; |\n", + "1281 threshold; | threshold; | threshold; | threshold; |\n", + "1286 threshold; | threshold; | threshold; | threshold; |\n", + "1326 threshold; | threshold; | threshold; | threshold; |\n", + "1355 threshold; | threshold; | threshold; | threshold; |\n", + "1626 threshold; | threshold; | threshold; | threshold; |\n", + "1903 threshold; | threshold; | threshold; | threshold; |\n", + "1914 threshold; | threshold; | threshold; | threshold; |\n", + "2776 threshold; | threshold; | threshold; | threshold; |\n", + "2851 threshold; | threshold; | threshold; | threshold; |\n", + "3309 threshold; | threshold; | threshold; | threshold; |\n", + "-----------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_11\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN\n" + ] + } + ], + "source": [ + "safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "8762ab29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 76 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------|\n", + " Surv_prob |Surv_prob_SE |num_at_risk |num_events |\n", + "Time | | | |\n", + "-----------------------------------------------------------|\n", + "51 ok | ok | ok | ok|\n", + "69 threshold; | threshold; | threshold; | threshold; |\n", + "85 threshold; | threshold; | threshold; | threshold; |\n", + "91 threshold; | threshold; | threshold; | threshold; |\n", + "115 threshold; | threshold; | threshold; | threshold; |\n", + "372 threshold; | threshold; | threshold; | threshold; |\n", + "667 threshold; | threshold; | threshold; | threshold; |\n", + "874 threshold; | threshold; | threshold; | threshold; |\n", + "1039 threshold; | threshold; | threshold; | threshold; |\n", + "1046 threshold; | threshold; | threshold; | threshold; |\n", + "1281 threshold; | threshold; | threshold; | threshold; |\n", + "1286 threshold; | threshold; | threshold; | threshold; |\n", + "1326 threshold; | threshold; | threshold; | threshold; |\n", + "1355 threshold; | threshold; | threshold; | threshold; |\n", + "1626 threshold; | threshold; | threshold; | threshold; |\n", + "1903 threshold; | threshold; | threshold; | threshold; |\n", + "1914 threshold; | threshold; | threshold; | threshold; |\n", + "2776 threshold; | threshold; | threshold; | threshold; |\n", + "2851 threshold; | threshold; | threshold; | threshold; |\n", + "3309 threshold; | threshold; | threshold; | threshold; |\n", + "-----------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_12\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(, 'acro_artifacts/kaplan-mier_0.png')\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGwCAYAAAB7MGXBAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAANFlJREFUeJzt3Xl4VPXd///XZJkkZBmEbCwhRK1IiWANLQSKVqoBXCn1Ngo/AUVvI6LFqBW0FaReRu0XSlHBKovSC4Wror3pZaqmVRYNqESgIEipoEFJDEklkxDIen5/hBkYsjAzmZmTZJ6P65rrIifnzLzPhxPy4rOcYzEMwxAAAIBJQswuAAAABDfCCAAAMBVhBAAAmIowAgAATEUYAQAApiKMAAAAUxFGAACAqcLMLsAdTU1NOnLkiGJjY2WxWMwuBwAAuMEwDFVVValv374KCWm7/6NLhJEjR44oJSXF7DIAAIAXDh8+rP79+7f5/S4RRmJjYyU1n0xcXJzJ1QAAAHfY7XalpKQ4f4+3pUuEEcfQTFxcHGEEAIAu5lxTLJjACgAATEUYAQAApiKMAAAAU3WJOSMA0Jk0Njaqvr7e7DIA04WHhys0NLTD70MYAQA3GYah0tJSHTt2zOxSgE6jZ8+eSk5O7tB9wAgjAOAmRxBJTExUjx49uAkjgpphGKqpqVFZWZkkqU+fPl6/F2EEANzQ2NjoDCK9e/c2uxygU4iKipIklZWVKTEx0eshGyawAoAbHHNEevToYXIlQOfi+JnoyDwqwggAeIChGcCVL34mCCMAAMBUhBEAAGAqwggAIGA2btwoi8XS4eXRAwcO1OLFi31Skz989dVXslgs2rlzp8/e85VXXlHPnj3d3v+ll15SSkqKQkJCOnVbSaymAQDA51JSUlRSUqL4+HhTPt9ut2vWrFlatGiRfvnLX8pms5lSh7u6fBh5/4vvVNfQZHYZ6IISYiN12YCeTEhE0Kmrq5PVajW7jC6rvr5e4eHh7e4TGhqq5OTkAFXUUnFxserr63Xttdd26P4fgdLlw8iv3/iXyqvrzC4DXdT6ezKVkdrL7DLQRRmGoRP1jQH/3KjwUI9C9M9+9jOlp6fLarVq9erVGjJkiBYsWKCHH35Yu3btUq9evTRt2jQ9+eSTCgtr/rUwcOBAzZ49W7Nnz3a+z6WXXqqJEydq/vz5kppXUbz88st6++239e6776pfv35auHChbrjhBucx+fn5mj17tg4fPqyRI0dq2rRpLeorLCzUnDlz9Omnnyo+Pl6/+MUvlJeXp+joaEnN97CYMWOG/vGPfyg5OVlPPvmkR+01f/58rVy5Ut9995169+6tm266SUuWLHGew1tvvaWJEyc69+/Zs6cWL16s6dOn66uvvlJaWprWrVunpUuXatu2bXrmmWc0Z84cvfXWWxo/frzzuDfffFO33XabvvvuO5WXlystLU07duzQ0KFDNWDAAP3mN79RTk6Oc//PPvtMGRkZ+vLLL3X++edr0aJFWrVqlQ4ePKhevXrp+uuv17PPPquYmBiPzveVV17R7bffLkk6//zzJUmHDh3S/PnzdezYMf31r3917jt79mzt3LlTGzdulNR8rQwdOlSRkZFavny5rFarcnJynH/n/tLlw8iw/j1VeYJnRMAz27/+XpK0v7SaMAKvnahv1A8ffzfgn7t3wTj1sHr2z/err76qe+65Rx999JHKy8uVlZWl6dOna/Xq1friiy901113KTIy0uNfOk888YSeffZZ/f73v9dzzz2nKVOm6Ouvv1avXr10+PBhTZo0STk5Obrnnnu0fft2Pfjggy7H7969W+PGjdPvfvc7rVixQkePHtWsWbM0a9YsrVq1SpI0ffp0HT58WO+//76sVqvuv/9+510/z+WNN97QH/7wB61du1ZDhgxRaWmpdu3a5dE5StIjjzyihQsXatWqVYqIiNCWLVu0Zs0alzDy2muv6cYbb1RMTIzKy8ud20NCQnTLLbdozZo1LmHktddeU2ZmpjMwhISEaMmSJRo4cKAOHTqkmTNn6te//rWWLl3qUa3Z2dlKSUnRVVddpU8++UQpKSlKSEhw+/hXX31Vubm5+vjjj7V161ZNnz5do0eP1tVXX+1RHZ7o8mFkxfQfm10CuqC5b/5Lr39yWEeras0uBQiICy+8UM8++6wkafXq1UpJSdHzzz8vi8Wiiy++WEeOHNEjjzyixx9/XCEh7q9tmD59um699VZJ0lNPPaXnnntOn3zyicaPH69ly5bp/PPP1x/+8AdZLBYNGjRIu3fv1jPPPOM8/ve//70mT57s7IH5wQ9+oCVLluiKK67QsmXLVFxcrL///e/atm2bRowYIUlasWKFBg8e7FZ9xcXFSk5O1lVXXaXw8HANGDBAP/nJT9w+P4fZs2dr0qRJzq+nTJmiqVOnqqamRj169JDdbtfbb7+t9evXt3r8lClTtGjRIn399ddKTU1VU1OT1q5dq0cffdTlMxzS0tL0u9/9Tvfcc4/HYSQqKsp5l+CEhASPh4uGDh2qefPmSWr++3j++ef1z3/+kzAC+FpCbKQkqazqpMmVoCuLCg/V3gXjTPlcTw0fPtz553379ikzM9NlqGf06NGqrq7WN998owEDBrj9vkOHDnX+OTo6WrGxsc5ei3379mnkyJEun5OZmelyfFFRkf7zn/9ozZo1zm2GYaipqUmHDh3Sv//9b4WFhbnUf/HFF7u9quR//ud/tHjxYp1//vkaP368rrnmGl1//fXO4Sh3nfn5knTttdcqLCxMGzZs0C233KL169crNjZWWVlZrR7/ox/9SBdffLFef/11zZkzR5s2bVJZWZluvvlm5z4ffPCBnnrqKe3du1d2u10NDQ06efKkjh8/7hyyCoQz/06l5mfOuNsT5S2W9iIoJcRGSBI9I+gQi8WiHtawgL+8mXR95i8zwzBavIdhGM5zkpqHDBzbHFq73ffZEzktFouamppc3rM9TU1Nuvvuu7Vz507na9euXTpw4IAuuOCCFnV5KiUlRfv379cLL7ygqKgozZw5U5dffrnzXCwWi1vneXYYsFqtuummm/Taa69Jah5yyc7ObjfkTJkyxWX/cePGOVfbfP3117rmmmuUnp6u9evXq6ioSC+88EKb9XjDF3+n/kIYQVBKPBVGyggjCEI//OEPVVhY6PKLqbCwULGxserXr5+k5u79kpIS5/ftdrsOHTrk8eds27bNZdvZX1922WX6/PPPdeGFF7Z4Wa1WDR48WA0NDdq+fbvzmP3793t0n5KoqCjdcMMNWrJkiTZu3KitW7dq9+7drZ7ngQMHVFNT49b7TpkyRe+8844+//xzffDBB5oyZUq7+0+ePFm7d+9WUVGR3njjDZf9t2/froaGBi1cuFAjR47URRddpCNHjrh9ju44+1wl+fQ+KB1BGEFQomcEwWzmzJk6fPiw7rvvPn3xxRf6v//7P82bN0+5ubnO+SJjx47Vn//8Z23ZskV79uzRtGnTPH4ia05Ojr788kvl5uZq//79eu211/TKK6+47PPII49o69atuvfee7Vz504dOHBAGzZs0H333SdJGjRokMaPH6+77rpLH3/8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R1LzEMvUqVOd++fk5Ojrr79Wbm6u9u3bp5UrV2rFihV66KGHfHcWAACgy/J4zkh2drYqKiq0YMEClZSUKD09Xfn5+UpNTZUklZSUuNxzJC0tTfn5+XrggQf0wgsvqG/fvlqyZIl++ctfuv2ZERERmjdvXqtDN8GOtmkf7dM22qZ9tE/baJu20Tbe8fg+IwAAAL7Es2kAAICpCCMAAMBUhBEAAGAqwggAADBVpw8jS5cuVVpamiIjI5WRkaEtW7aYXZLfzZ8/XxaLxeWVnJzs/L5hGJo/f7769u2rqKgo/exnP9Pnn3/u8h61tbW67777FB8fr+joaN1www365ptvAn0qPrF582Zdf/316tu3rywWi/7617+6fN9X7fH999/rtttuk81mk81m02233aZjx475+ew65lxtM3369BbX0siRI1326a5tk5eXpx//+MeKjY1VYmKiJk6cqP3797vsE6zXjjttE8zXzrJlyzR06FDnjcsyMzP197//3fn9YL1u/MroxNauXWuEh4cbL7/8srF3717jV7/6lREdHW18/fXXZpfmV/PmzTOGDBlilJSUOF9lZWXO7z/99NNGbGyssX79emP37t1Gdna20adPH8Nutzv3ycnJMfr162cUFBQYn332mXHllVcaw4YNMxoaGsw4pQ7Jz883HnvsMWP9+vWGJOOtt95y+b6v2mP8+PFGenq6UVhYaBQWFhrp6enGddddF6jT9Mq52mbatGnG+PHjXa6liooKl326a9uMGzfOWLVqlbFnzx5j586dxrXXXmsMGDDAqK6udu4TrNeOO20TzNfOhg0bjLffftvYv3+/sX//fuPRRx81wsPDjT179hiGEbzXjT916jDyk5/8xMjJyXHZdvHFFxtz5swxqaLAmDdvnjFs2LBWv9fU1GQkJycbTz/9tHPbyZMnDZvNZrz44ouGYRjGsWPHjPDwcGPt2rXOfb799lsjJCTEeOedd/xau7+d/QvXV+2xd+9eQ5Kxbds25z5bt241JBlffPGFn8/KN9oKIzfeeGObxwRL2xiGYZSVlRmSjE2bNhmGwbVzprPbxjC4ds523nnnGcuXL+e68ZNOO0xTV1enoqIiZWVluWzPyspSYWGhSVUFzoEDB9S3b1+lpaXplltu0cGDByVJhw4dUmlpqUu7RERE6IorrnC2S1FRkerr61326du3r9LT07td2/mqPbZu3SqbzaYRI0Y49xk5cqRsNluXb7ONGzcqMTFRF110ke666y6VlZU5vxdMbVNZWSlJ6tWrlySunTOd3TYOXDtSY2Oj1q5dq+PHjyszM5Prxk86bRgpLy9XY2NjiwfwJSUltXjwXnczYsQIrV69Wu+++65efvlllZaWatSoUaqoqHCee3vtUlpaKqvVqvPOO6/NfboLX7VHaWmpEhMTW7x/YmJil26zCRMmaM2aNXr//fe1cOFCffrppxo7dqxqa2slBU/bGIah3Nxc/fSnP1V6erokrh2H1tpG4trZvXu3YmJiFBERoZycHL311lv64Q9/yHXjJx7fDj7QLBaLy9eGYbTY1t1MmDDB+edLLrlEmZmZuuCCC/Tqq686J5B50y7due180R6t7d/V2yw7O9v55/T0dA0fPlypqal6++23NWnSpDaP625tM2vWLP3rX//Shx9+2OJ7wX7ttNU2wX7tDBo0SDt37tSxY8e0fv16TZs2TZs2bXJ+P9ivG1/rtD0j8fHxCg0NbZEQy8rKWiTS7i46OlqXXHKJDhw44FxV0167JCcnq66uTt9//32b+3QXvmqP5ORkfffddy3e/+jRo92qzfr06aPU1FQdOHBAUnC0zX333acNGzbogw8+UP/+/Z3buXbabpvWBNu1Y7VadeGFF2r48OHKy8vTsGHD9Mc//pHrxk86bRixWq3KyMhQQUGBy/aCggKNGjXKpKrMUVtbq3379qlPnz5KS0tTcnKyS7vU1dVp06ZNznbJyMhQeHi4yz4lJSXas2dPt2s7X7VHZmamKisr9cknnzj3+fjjj1VZWdmt2qyiokKHDx9Wnz59JHXvtjEMQ7NmzdKbb76p999/X2lpaS7fD+Zr51xt05pgunZaYxiGamtrg/q68auATpf1kGNp74oVK4y9e/cas2fPNqKjo42vvvrK7NL86sEHHzQ2btxoHDx40Ni2bZtx3XXXGbGxsc7zfvrppw2bzWa8+eabxu7du41bb7211WVl/fv3N/7xj38Yn332mTF27Nguu7S3qqrK2LFjh7Fjxw5DkrFo0SJjx44dziXevmqP8ePHG0OHDjW2bt1qbN261bjkkks6/TK79tqmqqrKePDBB43CwkLj0KFDxgcffGBkZmYa/fr1C4q2ueeeewybzWZs3LjRZXlqTU2Nc59gvXbO1TbBfu3MnTvX2Lx5s3Ho0CHjX//6l/Hoo48aISEhxnvvvWcYRvBeN/7UqcOIYRjGCy+8YKSmphpWq9W47LLLXJaedVeONevh4eFG3759jUmTJhmff/658/tNTU3GvHnzjOTkZCMiIsK4/PLLjd27d7u8x4kTJ4xZs2YZvXr1MqKioozrrrvOKC4uDvSp+MQHH3xgSGrxmjZtmmEYvmuPiooKY8qUKUZsbKwRGxtrTJkyxfj+++8DdJbeaa9tampqjKysLCMhIcEIDw83BgwYYEybNq3FeXfXtmmtXSQZq1atcu4TrNfOudom2K+dO+64w/l7JyEhwfj5z3/uDCKGEbzXjT9ZDMMwAtcPAwAA4KrTzhkBAADBgTACAABMRRgBAACmIowAAABTEUYAAICpCCMAAMBUhBEAAGAqwggAADAVYQSA382fP1+XXnqp2WUA6KS4AyuADjnX486nTZum559/XrW1terdu3eAqgLQlRBGAHTImY9SX7dunR5//HHt37/fuS0qKko2m82M0gB0EQzTAOiQ5ORk58tms8lisbTYdvYwzfTp0zVx4kQ99dRTSkpKUs+ePfXEE0+ooaFBDz/8sHr16qX+/ftr5cqVLp/17bffKjs7W+edd5569+6tG2+8UV999VVgTxiAzxFGAJji/fff15EjR7R582YtWrRI8+fP13XXXafzzjtPH3/8sXJycpSTk6PDhw9LkmpqanTllVcqJiZGmzdv1ocffqiYmBiNHz9edXV1Jp8NgI4gjAAwRa9evbRkyRINGjRId9xxhwYNGqSamho9+uij+sEPfqC5c+fKarXqo48+kiStXbtWISEhWr58uS655BINHjxYq1atUnFxsTZu3GjuyQDokDCzCwAQnIYMGaKQkNP/H0pKSlJ6errz69DQUPXu3VtlZWWSpKKiIv3nP/9RbGysy/ucPHlSX375ZWCKBuAXhBEApggPD3f52mKxtLqtqalJktTU1KSMjAytWbOmxXslJCT4r1AAfkcYAdAlXHbZZVq3bp0SExMVFxdndjkAfIg5IwC6hClTpig+Pl433nijtmzZokOHDmnTpk361a9+pW+++cbs8gB0AGEEQJfQo0cPbd68WQMGDNCkSZM0ePBg3XHHHTpx4gQ9JUAXx03PAACAqegZAQAApiKMAAAAUxFGAACAqQgjAADAVIQRAABgKsIIAAAwFWEEAACYijACAABMRRgBAACmIowAAABTEUYAAICp/n+L3AbcO2Xl5QAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGwCAYAAAB7MGXBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA27UlEQVR4nO3de3hU9Z3H8c9MksmF3IBkEqCBgCCoEEDQGKmX1VRQi6W2XYo8clmLBcGVprUQq0Rb16hdWKqibKmobaGwVdFuRVxFsF5SKRFQKiJyMVTJBSgJCZDLzG//iDNhIAkzycycXN6v55nnIWd+Z+Z7DjPkw+9yjs0YYwQAAGARu9UFAACA7o0wAgAALEUYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgqUirC/CH2+3Wl19+qYSEBNlsNqvLAQAAfjDG6Pjx4+rbt6/s9pb7PzpFGPnyyy+VkZFhdRkAAKANDh48qK997WstPt8pwkhCQoKkxoNJTEy0uBoAAOCPqqoqZWRkeH+Pt6RThBHP0ExiYiJhBACATuZcUyyYwAoAACxFGAEAAJYijAAAAEt1ijkjANCRuFwu1dfXW10GYLmoqChFRES0+3UIIwDgJ2OMSktLdezYMatLATqM5ORkpaent+s6YIQRAPCTJ4g4nU7FxcVxEUZ0a8YYnThxQuXl5ZKkPn36tPm1CCMA4AeXy+UNIr1797a6HKBDiI2NlSSVl5fL6XS2eciGCawA4AfPHJG4uDiLKwE6Fs93oj3zqAgjABAAhmYAX8H4ThBGAACApQgjAADAUoQRAEDYbN68WTabrd3LozMzM7V06dKg1BQKBw4ckM1m0/bt24P2ms8++6ySk5P9bv/rX/9aGRkZstvtHfpcSaymAQAg6DIyMnTo0CGlpKRY8v5VVVWaN2+elixZou985ztKSkqypA5/deow4nIbvf5xqdVloJMa0LuHLujDXaDR/dTV1cnhcFhdRqdVX1+vqKioVttEREQoPT09TBWdraSkRPX19brxxhvbdf2PcOnUYaTe5dbs339gdRnopOw26e0F16hfcqzVpaCTMsboZL0r7O8bGxUR0AqGq6++WsOHD1dkZKR+//vfa8SIEbr//vt19913a8eOHerVq5emT5+uBx98UJGRjb8WMjMzNX/+fM2fP9/7OqNGjdKkSZN0//33S2pcRbFixQq98soreu2119SvXz8tXrxYN910k3ef9evXa/78+Tp48KAuu+wyTZ8+/az63nnnHeXn52vr1q1KSUnRt7/9bRUWFqpHjx6SGq9hcdttt+mNN95Qenq6HnzwQb+P3RijBx54QCtXrlRZWZl69+6t7373u3rssce8x7Bu3TpNmjTJu09ycrKWLl2qGTNm6MCBAxo4cKDWrFmjJ598Uu+//74effRRLViwQC+++KKuv/56737r1q3TtGnTVFZWpvLycg0cOFDbtm1TVlaW+vfvr5/97GeaM2eOt/22bds0ZswY7d+/XwMGDNCSJUv0zDPPaN++ferVq5cmTpyoRx99VPHx8X4fr9Q4nDNz5kxJ0qBBgyRJ+/fv1/33369jx47ppZde8radP3++tm/frs2bN0tq/KxkZWUpJiZGv/nNb+RwODR79mzv33modOowYrNJYwf0tLoMdEJbP/+n3EbaW15NGEGbnax36cJFr4X9fT/++XjFOQL75/u5557TnDlz9O6776q0tFQ33HCDZsyYod/+9rf65JNPNGvWLMXExAT8S+eBBx7Qo48+ql/+8pd6/PHHNXXqVH3++efq1auXDh48qJtvvllz587V7bffrq1bt+rHP/6xz/579+7VhAkT9OCDD2rlypWqqKjQvHnzNG/ePD3zzDOSpBkzZujLL7/Upk2bFBUVpX//93/3XvXzXF544QX913/9l9asWaOLLrpIpaWl2rFjR0DHKEkLFy7U4sWLNXr0aMXExOjtt9/W6tWrfcLIqlWrNGnSpLOuRWO32zVlyhStXr3aJ4ysWrVK48aN04ABA7ztHnvsMQ0cOFD79u3THXfcoZ/+9Kd68sknA6p18uTJysjIUG5urrZs2aKMjAylpqb6vf9zzz2nvLw8vf/++yoqKtKMGTM0btw4feMb3wiojkB06jASHRmh5+dcbnUZ6IRuffp9vb3nsCqO11pdChAWQ4YM0aOPPipJ+u1vf6uMjAw98cQTstlsGjZsmL788kstWLBAixYtkt3u/9qGGTNmaMqUKZKkhx56SI899pi2bNmiCRMm6KmnntJ5552nxYsXS5KGDh2qjz76SI888oh3/8LCQk2dOtXbAzNkyBA99thjuuqqq/TUU0+ppKREr776qrZs2aJLLrlEkvT000/rggsu8Ku+kpISpaenKzc3V1FRUerfv78uvfRSv4/PY/78+br55pu9P0+dOlW33nqrTpw4obi4OFVVVemVV17RunXrmt1/6tSpWrx4sUpKStS/f3+53W6tWbNG9957r897eGRmZurBBx/U7NmzAw4jsbGx3qsEp6amBjxclJWVpYKCAkmNfx9PPPGENm7cSBgBgi01IVqSVE4YQTvERkXo45+Pt+R9AzVmzBjvn3ft2qWcnByfoZ5x48apurpa//jHP9S/f3+/XzcrK8v75x49eigxMdHba7Fr1y5lZ2f7tM/JyfH5eceOHfrwww+1atUq7zZjjNxut/bv369PP/1UkZGRPvUPGzbM71Ul3/ve97R06VINGjRIEyZM0A033KCJEyd6h6P8NXbsWJ+fb7jhBkVFRelPf/qTvv/97+uFF15QYmKicnNzm91/1KhRuuCCC7R69WotXLhQb731lsrLy/W9733P2+aNN95QYWGhPvnkE1VVVamhoUGnTp3yBp5wOf3vVGq854y/PVFtxdJedEueMELPCNrDZrMpzhEZ9kdbrnjpmX/hL7vdLmOMz7bmLvd95kROm80mt9vt9/tUV1frhz/8obZv3+597NixQ3v27NF5550XUM3NycjI0O7du/Xkk08qNjZWd9xxh6688krvsdhsNr+O88zz53A49N3vflerV6+WJK1evVqTJ09uNeRMnTrVp/2ECRO8PRgHDhzQN7/5TWVlZemFF15QcXGxli1bJqlxwnEwhOvvtE21hfTVgQ7KmRAjSSo/fsriSoDwu+CCC1RUVOTzi+ndd99VQkKCvva1r0lq7N4/dOiQ9/mqqirt378/4PfZsmWLz7a//vWvPj9ffPHF+vjjjzV48OCzHg6HQ8OGDVNDQ4OKi4u9++zevTug65TExsZq4sSJeuyxx7R582YVFRXpo48+avY49+zZoxMnTvj1ulOnTtWGDRv097//XW+++aamTp3aavtbbrlFO3fuVHFxsZ5//nmf9sXFxXK73Vq8eLEuu+wynX/++fryyy/9PkZ/nHmskoJ6HZT2IIygW6JnBN3ZHXfcoYMHD+rOO+/UJ598opdfflkFBQXKy8vzzhe55ppr9Lvf/U5vv/22PvroI02fPj3gO7LOnj1be/bs0d13363du3dr9erVevbZZ33aLFiwQO+9957mzZun7du3a8+ePXr55Zc1b948SY3zTCZMmKAf/vCHev/991VcXKwf/OAH3rvFnsuzzz6rp59+Wjt37tS+ffv0+9//XrGxsd5Jo9dcc42eeOIJbdu2TVu3btXs2bPPuWzX48orr1R6erqmTp2qgQMHnjUkdabMzExdfvnluu222+RyuXxWHQ0ePFj19fV6/PHHtW/fPv3ud7/T8uXL/arDX9dcc422bt2q3/72t9qzZ48KCgq0c+fOoL5HWxFG0C2lxhNG0H3169dP69ev15YtWzRy5EjNnj1bt912m89kyvz8fF111VX65je/qRtvvFGTJk0KeNikf//+euGFF/TSSy9p5MiRWr58uR566CGfNllZWXrrrbf06aef6oorrtDo0aO1aNEi9e3b19vmmWeeUd++fXXVVVfp5ptv1u233y6n0+lXDcnJyVqxYoXGjRunrKwsvfHGG/rf//1f7/DI4sWLlZGRoSuuuEK33HKLfvKTn/g9P8Nms2nKlCnasWPHOXtFPKZOnaodO3bo29/+tk+gGjlypJYsWaJHHnlEw4cP16pVq1RYWOjXa/pr/Pjxuu+++/TTn/5Ul1xyiY4fP65p06YF9T3aymbOHEDqgKqqqpSUlKTKykolJnKRKrTf3opqXbv4LSVER+qjB8I/ARGdz6lTp7R//34NHDhQMTExVpcDdBitfTf8/f1Nzwi6Jc8wzfHaBp2sC/9FqwAATQgj6JYSoiMVE9X48WeoBui8Vq1apfj4+GYfF110kdXlhcxFF13U4nGfvky6s+A6I+iWbDabUhOidfDoSVVUn1L/3uFbww8geG666aYWJ476OxG1M1q/fn2zy3IlKS0tLczVtB9hBN1WanxjGCmvomcE6KwSEhKUkJBgdRlh51kN1FUwTINuy3OtkYpqwgj8F+qLPwGdTTC+E/SMoNvyXhKenhH4weFwyG6368svv1RqaqocDkebroQKdBXGGNXV1amiokJ2u10Oh6PNr0UYQbfl5MJnCIDdbtfAgQN16NChoF8ZE+jM4uLi1L9//4BusHgmwgi6Le9VWBmmgZ8cDof69++vhoYGuVwsCQciIiIUGdm2+yWdjjCCbsuZ6LlzL/engf9sNpuioqK69EoNINyYwIpuKzX+qwmsDNMAgKUII+i2PMM0h6vr5HJ3+LsiAECXRRhBt9U73iGbTXK5jf55os7qcgCg2yKMoNuKirCrV1zjUjSGagDAOoQRdGvea40QRgDAMoQRdGupXGsEACxHGEG31tQzwvJeALAKYQTdmvf+NPSMAIBlCCPo1pgzAgDWI4ygW+P+NABgPcIIujXvhc8IIwBgGcIIujUnwzQAYDnCCLo1T89IdW2DTtQ1WFwNAHRPhBF0a/HRkYqJavwaMG8EAKxBGEG3ZrPZWN4LABYjjKDb4yqsAGAtwgi6PSaxAoC1CCPo9ugZAQBrEUbQ7Tm5Pw0AWIowgm6PnhEAsBZhBN0e96cBAGsRRtDtsbQXAKxFGEG35+kZOVJTJ5fbWFwNAHQ/hBF0e717OGSzSS630dGaOqvLAYBuhzCCbi8ywq7ePRySGKoBACsQRgBJKfEs7wUAqxBGAEnORCaxAoBVCCOApNSvekYqqgkjABBuhBFAkjPxq2GaKsIIAIQbYQQQPSMAYCXCCKCmnpEKekYAIOwII4DoGQEAKxFGAJ12f5oqlvYCQLgRRgA1Le2tqXOpprbB4moAoHshjACSejgiFBsVIUk6zFANAIRVm8LIsmXLlJmZqZiYGGVnZ2vLli2ttl+6dKmGDh2q2NhYZWRk6Ec/+pFOnaI7HB2HzWZrWt7Lhc8AIKwCDiNr165VXl6eCgoK9MEHH2jkyJEaP368ysvLm22/evVqLVy4UAUFBdq1a5eefvpprV27Vvfcc0+7iweCyTuJlTACAGEVcBhZsmSJZs2apZkzZ+rCCy/U8uXLFRcXp5UrVzbb/r333tO4ceN0yy23KDMzU9ddd52mTJnSam9KbW2tqqqqfB5AqDGJFQCsEVAYqaurU3FxsXJzc5tewG5Xbm6uioqKmt3n8ssvV3FxsTd87Nu3T+vXr9cNN9zQ4vsUFhYqKSnJ+8jIyAikTKBNnAks7wUAK0QG0vjw4cNyuVxKS0vz2Z6WlqZPPvmk2X1uueUWHT58WF//+tdljFFDQ4Nmz57d6jBNfn6+8vLyvD9XVVURSBBynp4RhmkAILxCvppm8+bNeuihh/Tkk0/qgw8+0IsvvqhXXnlFv/jFL1rcJzo6WomJiT4PINScCY3Le5nACgDhFVDPSEpKiiIiIlRWVuazvaysTOnp6c3uc9999+nWW2/VD37wA0nSiBEjVFNTo9tvv10/+9nPZLezuhgdAz0jAGCNgJKAw+HQmDFjtHHjRu82t9utjRs3Kicnp9l9Tpw4cVbgiIhovJ6DMSbQeoGQ8U5gJYwAQFgF1DMiSXl5eZo+fbrGjh2rSy+9VEuXLlVNTY1mzpwpSZo2bZr69eunwsJCSdLEiRO1ZMkSjR49WtnZ2frss8903333aeLEid5QAnQEngmsR6pr5XIbRdhtFlcEAN1DwGFk8uTJqqio0KJFi1RaWqpRo0Zpw4YN3kmtJSUlPj0h9957r2w2m+6991598cUXSk1N1cSJE/Uf//EfwTsKIAh69XDIZpPcRjpSU+udQwIACC2b6QRjJVVVVUpKSlJlZSWTWRFSYx98Q4era/XKv39dF/VNsrocAOjU/P39zexR4DRMYgWA8COMAKdxMokVAMKOMAKchp4RAAg/wghwGsIIAIQfYQQ4jZMwAgBhRxgBTkPPCACEH2EEOE3T/WlOWVwJAHQfhBHgNPSMAED4EUaA03jmjNTUuVRT22BxNQDQPRBGgNP0iI5UnKPxnkn0jgBAeBBGgDNw914ACC/CCHAGlvcCQHgRRoAzNE1iZUUNAIQDYQQ4Q9PyXnpGACAcCCPAGVjeCwDhRRgBzpAazwRWAAgnwghwhtREekYAIJwII8AZPD0jFdWEEQAIB8IIcAbnVz0jR6pr5XIbi6sBgK6PMAKcoXePaNltkttIR2roHQGAUCOMAGeIsNvU2zOJtYowAgChRhgBmsG8EQAIH8II0AzvtUboGQGAkCOMAM3w3p+GnhEACDnCCNAMrsIKAOFDGAGa4ekZKedmeQAQcoQRoBmpX90sj54RAAg9wgjQjNQE7k8DAOFCGAGa4WTOCACEDWEEaIanZ+REnUs1tQ0WVwMAXRthBGhGj+hI9XBESGKoBgBCjTACtIDlvQAQHoQRoAXOr1bUsLwXAEKLMAK0gJ4RAAgPwgjQApb3AkB4EEaAFtAzAgDhQRgBWkAYAYDwIIwALXAyTAMAYUEYAVpAzwgAhAdhBGiBJ4wcqalVg8ttcTUA0HURRoAW9O4RLbtNMkY6WlNndTkA0GURRoAWRNht6h3PvBEACDXCCNAK7t4LAKFHGAFawSRWAAg9wgjQiqblvdyfBgBChTACtIKeEQAIPcII0IpUJrACQMgRRoBWOBNjJNEzAgChRBgBWuEdpqkmjABAqBBGgFZ4J7BW1coYY3E1ANA1EUaAVqR8NWfkZL1LNXUui6sBgK6JMAK0okd0pHo4IiRJ5VUs7wWAUCCMAOfAJFYACC3CCHAOLO8FgNAijADnkJrIhc8AIJQII8A5eHpGWN4LAKFBGAHOwZnYtLwXABB8hBHgHOgZAYDQIowA55DqvfAZS3sBIBQII8A5OBMal/YepmcEAEKCMAKcg6dn5EhNnRpcbourAYCuhzACnEOvHg5F2G0ypjGQAACCizACnEOE3abePRySuNYIAIRCm8LIsmXLlJmZqZiYGGVnZ2vLli2ttj927Jjmzp2rPn36KDo6Wueff77Wr1/fpoIBK3iX9x5nEisABFtkoDusXbtWeXl5Wr58ubKzs7V06VKNHz9eu3fvltPpPKt9XV2dvvGNb8jpdOr5559Xv3799Pnnnys5OTkY9QNh4V3eS88IAARdwGFkyZIlmjVrlmbOnClJWr58uV555RWtXLlSCxcuPKv9ypUrdfToUb333nuKioqSJGVmZrb6HrW1taqtbfpHv6qqKtAygaBqWt5LGAGAYAtomKaurk7FxcXKzc1tegG7Xbm5uSoqKmp2nz/96U/KycnR3LlzlZaWpuHDh+uhhx6Sy+Vq8X0KCwuVlJTkfWRkZARSJhB0nuW9XPgMAIIvoDBy+PBhuVwupaWl+WxPS0tTaWlps/vs27dPzz//vFwul9avX6/77rtPixcv1oMPPtji++Tn56uystL7OHjwYCBlAkHn6RlhmAYAgi/gYZpAud1uOZ1O/frXv1ZERITGjBmjL774Qr/85S9VUFDQ7D7R0dGKjo4OdWmA35yeYRrCCAAEXUBhJCUlRRERESorK/PZXlZWpvT09Gb36dOnj6KiohQREeHddsEFF6i0tFR1dXVyOBxtKBsIL3pGACB0AhqmcTgcGjNmjDZu3Ojd5na7tXHjRuXk5DS7z7hx4/TZZ5/J7W66cuWnn36qPn36EETQaXgnsB4/JWOMxdUAQNcS8HVG8vLytGLFCj333HPatWuX5syZo5qaGu/qmmnTpik/P9/bfs6cOTp69Kjuuusuffrpp3rllVf00EMPae7cucE7CiDEPGHkVL1b1bUNFlcDAF1LwHNGJk+erIqKCi1atEilpaUaNWqUNmzY4J3UWlJSIru9KeNkZGTotdde049+9CNlZWWpX79+uuuuu7RgwYLgHQUQYnGOSMVHR6q6tkEVx2uVEBNldUkA0GXYTCfoc66qqlJSUpIqKyuVmJhodTnopq75z83ad7hGa26/TJcN6m11OQDQ4fn7+5t70wB+SmESKwCEBGEE8BPLewEgNAgjgJ9Y3gsAoUEYAfx0+vJeAEDwEEYAP3nvT0PPCAAEFWEE8BPDNAAQGoQRwE9OwggAhARhBPCTp2fk6Ik61bvc52gNAPAXYQTwU684hyLsNhkjHamus7ocAOgyCCOAn+x2m1LiG2/uyFANAAQPYQQIgHcSazXLewEgWAgjQAA8y3vLq+gZAYBgIYwAAUiNZ0UNAAQbYQQIgDOR+9MAQLARRoAAcOEzAAg+wggQAM8wDfenAYDgIYwAAfAM01RU0zMCAMFCGAECkBrfdLM8Y4zF1QBA10AYAQLgmTNyqt6t47UNFlcDAF0DYQQIQKwjQgnRkZKYxAoAwUIYAQLk6R3hwmcAEByEESBATZeEJ4wAQDAQRoAAca0RAAguwggQIO/9abjWCAAEBWEECBA9IwAQXIQRIEBOwggABBVhBAgQPSMAEFyEESBA3qW9hBEACArCCBAgzzDN0Zo61bvcFlcDAJ0fYQQIUM84hyLtNknSkeo6i6sBgM6PMAIEyG63KSXeM1TD8l4AaC/CCNAGTGIFgOAhjABtwCRWAAgewgjQBlxrBACChzACtAHDNAAQPIQRoA2cCUxgBYBgIYwAbUDPCAAED2EEaINU7517CSMA0F6EEaANTp/AaoyxuBoA6NwII0AbeIZpahvcqjrVYHE1ANC5EUaANoiJilBCTKQk5o0AQHsRRoA2YhIrAAQHYQRoI5b3AkBwEEaANvKsqKFnBADahzACtFFqPMM0ABAMhBGgjZyJhBEACAbCCNBG3p6RasIIALQHYQRoI0/PSHkVYQQA2oMwArSRd2kvPSMA0C6EEaCNnF+tpjlaU6e6BrfF1QBA50UYAdooOTZKkXabJOlIDb0jANBWhBGgjex2m1LimTcCAO1FGAHageW9ANB+hBGgHVjeCwDtRxgB2oHlvQDQfoQRoB2aeka4WR4AtBVhBGgHz7VG6BkBgLYjjADt4L1zL3NGAKDNCCNAO9AzAgDtRxgB2sF52iXhjTEWVwMAnRNhBGgHT89IXYNbVacaLK4GADonwgjQDjFREUqMiZQkVRxnRQ0AtAVhBGgn77wRrsIKAG3SpjCybNkyZWZmKiYmRtnZ2dqyZYtf+61Zs0Y2m02TJk1qy9sCHZInjHBJeABom4DDyNq1a5WXl6eCggJ98MEHGjlypMaPH6/y8vJW9ztw4IB+8pOf6IorrmhzsUBH5PQs7yWMAECbBBxGlixZolmzZmnmzJm68MILtXz5csXFxWnlypUt7uNyuTR16lQ98MADGjRoULsKBjoaekYAoH0CCiN1dXUqLi5Wbm5u0wvY7crNzVVRUVGL+/385z+X0+nUbbfd5tf71NbWqqqqyucBdFRO5owAQLsEFEYOHz4sl8ultLQ0n+1paWkqLS1tdp933nlHTz/9tFasWOH3+xQWFiopKcn7yMjICKRMIKzoGQGA9gnpaprjx4/r1ltv1YoVK5SSkuL3fvn5+aqsrPQ+Dh48GMIqgfZpWk3D0l4AaIvIQBqnpKQoIiJCZWVlPtvLysqUnp5+Vvu9e/fqwIEDmjhxoneb2+1ufOPISO3evVvnnXfeWftFR0crOjo6kNIAyzCBFQDaJ6CeEYfDoTFjxmjjxo3ebW63Wxs3blROTs5Z7YcNG6aPPvpI27dv9z5uuukm/cu//Iu2b9/O8Au6BE/PyD9P1KuuwW1xNQDQ+QTUMyJJeXl5mj59usaOHatLL71US5cuVU1NjWbOnClJmjZtmvr166fCwkLFxMRo+PDhPvsnJydL0lnbgc4qOTZKURE21buMDlfXqm9yrNUlAUCnEnAYmTx5sioqKrRo0SKVlpZq1KhR2rBhg3dSa0lJiex2LuyK7sNutyklPlqHKk+p4jhhBAACZTOd4FajVVVVSkpKUmVlpRITE60uBzjLt554Rzv+UakV08bqGxemnXsHAOgG/P39TRcGEAQs7wWAtiOMAEHA8l4AaDvCCBAEqSzvBYA2I4wAQcAwDQC0HWEECALuTwMAbUcYAYKAnhEAaDvCCBAEztPCSCdYLQ8AHQphBAiClPjGMFLncqvqZIPF1QBA50IYAYIgJipCiTGNFzRmeS8ABIYwAgSJM5HlvQDQFoQRIEhSvxqqqagmjABAIAgjQJA4E79a3ltFGAGAQBBGgCChZwQA2oYwAgSJ9/40VUxgBYBAEEaAIPEM09AzAgCBIYwAQZIaz2oaAGgLwggQJN4JrIQRAAgIYQQIEs8E1mMn6lXb4LK4GgDoPAgjQJAkx0UpKsImSTpcXWdxNQDQeRBGgCCx2WxNy3sZqgEAvxFGgCBieS8ABI4wAgRRasJXK2pY3gsAfiOMAEHk6RlhmAYA/EcYAYLImcDyXgAIFGEECCJ6RgAgcIQRIIhS6RkBgIARRoAg8gzTHCaMAIDfCCNAEJ0+TGOMsbgaAOgcCCNAEHnCSJ3LrcqT9RZXAwCdA2EECKLoyAglxUZJYhIrAPiLMAIEGct7ASAwhBEgyFjeCwCBIYwAQda0vJf70wCAPwgjQJA56RkBgIAQRoAgY5gGAAJDGAGCzPnVnXuZwAoA/iGMAEFGzwgABIYwAgQZ96cBgMAQRoAg80xgrTxZr9oGl8XVAEDHRxgBgiwpNkqOiMav1uHqOourAYCOjzACBJnNZmsaqqniWiMAcC6EESAEUpjECgB+I4wAIcD9aQDAf4QRIARY3gsA/iOMACGQGk/PCAD4izAChIAzkZ4RAPAXYQQIAU/PSEU1YQQAzoUwAoSAM7Hx/jQVLO0FgHMijAAh4J3AWl0rY4zF1QBAx0YYAUIgJd4hSap3GR07UW9xNQDQsRFGgBCIjoxQclyUJOaNAMC5EEaAEPFOYmVFDQC0ijAChIhneW/5cSaxAkBrCCNAiNAzAgD+IYwAIeJZ3lteRRgBgNYQRoAQ4cJnAOAfwggQIp5rjdAzAgCtI4wAIeJMoGcEAPxBGAFCxHsVViawAkCrCCNAiDgTGiewVp6s16l6l8XVAEDHRRgBQiQxNlKOiMav2GGGagCgRYQRIERsNlvTJFaGagCgRYQRIISYNwIA59amMLJs2TJlZmYqJiZG2dnZ2rJlS4ttV6xYoSuuuEI9e/ZUz549lZub22p7oCuhZwQAzi3gMLJ27Vrl5eWpoKBAH3zwgUaOHKnx48ervLy82fabN2/WlClTtGnTJhUVFSkjI0PXXXedvvjii3YXD3R0TnpGAOCcAg4jS5Ys0axZszRz5kxdeOGFWr58ueLi4rRy5cpm269atUp33HGHRo0apWHDhuk3v/mN3G63Nm7c2O7igY6OYRoAOLeAwkhdXZ2Ki4uVm5vb9AJ2u3Jzc1VUVOTXa5w4cUL19fXq1atXi21qa2tVVVXl8wA6I8/y3gru3AsALQoojBw+fFgul0tpaWk+29PS0lRaWurXayxYsEB9+/b1CTRnKiwsVFJSkveRkZERSJlAh0HPCACcW1hX0zz88MNas2aN1q1bp5iYmBbb5efnq7Ky0vs4ePBgGKsEgocJrABwbpGBNE5JSVFERITKysp8tpeVlSk9Pb3Vff/zP/9TDz/8sN544w1lZWW12jY6OlrR0dGBlAZ0SJ4JrIera+V2G9ntNosrAoCOJ6CeEYfDoTFjxvhMPvVMRs3JyWlxv0cffVS/+MUvtGHDBo0dO7bt1QKdTEp8YxipdxlVnqy3uBoA6JgCHqbJy8vTihUr9Nxzz2nXrl2aM2eOampqNHPmTEnStGnTlJ+f723/yCOP6L777tPKlSuVmZmp0tJSlZaWqrq6OnhHAXRQjki7esZFSWKoBgBaEtAwjSRNnjxZFRUVWrRokUpLSzVq1Cht2LDBO6m1pKREdntTxnnqqadUV1en7373uz6vU1BQoPvvv7991QOdQGpCtP55ol4Vx2s1ND3B6nIAoMMJOIxI0rx58zRv3rxmn9u8ebPPzwcOHGjLWwBdRmpCtD4tq1Y5y3sBoFncmwYIsaZrjTBMAwDNIYwAIcbyXgBoHWEECDHuTwMArSOMACHGVVgBoHWEESDEmoZpmMAKAM0hjAAhxjANALSOMAKEWGp842qaqlMNOlXvsrgaAOh4CCNAiCXGRsoR2fhVo3cEAM5GGAFCzGazKfWre9RUVBNGAOBMhBEgDJyJX01irSKMAMCZCCNAGNAzAgAtI4wAYeDpGamoYnkvAJyJMAKEgWdFDT0jAHA2wggQBt4LnzFnBADOQhgBwsB74TN6RgDgLIQRIAy4Pw0AtIwwAoSBdwLr8Vq53cbiagCgYyGMAGHQu0djGGlwGx07WW9xNQDQsRBGgDBwRNrVMy5KEnfvBYAzEUaAMHEmfLW8l3kjAOCDMAKECZNYAaB5hBEgTDzLe8sJIwDggzAChAk9IwDQPMIIECap9IwAQLMII0CYNPWMsJoGAE5HGAHChJ4RAGgeYQQIE5b2AkDzCCNAmHh6Ro6fatCpepfF1QBAx0EYAcIkMSZS0ZGNXzl6RwCgCWEECBObzca8EQBoBmEECCNW1ADA2QgjQBg5ufAZAJyFMAKEEVdhBYCzEUaAMPIs72XOCAA0IYwAYUTPCACcjTAChBF37gWAsxFGgDCiZwQAzkYYAcLIE0YOV9fK7TYWVwMAHQNhBAijlPjGMNLgNvrniTqLqwGAjoEwAoRRVIRdvXo4JEkV1QzVAIBEGAHCzjuJtYowAgASYQQIOyaxAoAvwggQZqnxLO8FgNMRRoAwS02kZwQATkcYAcLM0zPCBFYAaEQYAcLMmfjV/WmqTllcCQB0DIQRIMzoGQEAX4QRIMycnjkjLO0FAEmEESDsPEt7j9c26GSdy+JqAMB6hBEgzBKiIxUd2fjVY0UNABBGgLCz2WxNQzXVTGIFAMIIYAHvJFZ6RgCAMAJYwZnw1fJewggAEEYAK3B/GgBoQhgBLJDKnXsBwIswAljAmcCFzwDAgzACWIBhGgBoQhgBLNA0gZWlvQBAGAEs4OkZOVxdJ7fbWFwNAFiLMAJYoHe8Qzab5HIbHT1RZ3U5AGApwghggagIu3rFOSQxbwQACCOARbzLewkjALo5wghgEVbUAECjNoWRZcuWKTMzUzExMcrOztaWLVtabf/HP/5Rw4YNU0xMjEaMGKH169e3qVigKyGMAECjgMPI2rVrlZeXp4KCAn3wwQcaOXKkxo8fr/Ly8mbbv/fee5oyZYpuu+02bdu2TZMmTdKkSZO0c+fOdhcPdGYs7wWARjZjTEDrCrOzs3XJJZfoiSeekCS53W5lZGTozjvv1MKFC89qP3nyZNXU1OjPf/6zd9tll12mUaNGafny5X69Z1VVlZKSklRZWanExMRAygU6rKff2a9f/PljRUXY9PiU0VaXA6AbG+yM12BnQtBf19/f35GBvGhdXZ2Ki4uVn5/v3Wa325Wbm6uioqJm9ykqKlJeXp7PtvHjx+ull15q8X1qa2tVW9vUdV1ZWSmp8aCAriLBXid37QnVSrr96XesLgdANzb36sGa8y/nBf11Pb+3z9XvEVAYOXz4sFwul9LS0ny2p6Wl6ZNPPml2n9LS0mbbl5aWtvg+hYWFeuCBB87anpGREUi5AADADwuXSmePbQTP8ePHlZSU1OLzAYWRcMnPz/fpTTl27JgGDBigkpKSVg+mO6qqqlJGRoYOHjzIEFYzOD8t49y0jHPTOs5Pyzg3vowxOn78uPr27dtqu4DCSEpKiiIiIlRWVuazvaysTOnp6c3uk56eHlB7SYqOjlZ0dPRZ25OSkvjLbUFiYiLnphWcn5ZxblrGuWkd56dlnJsm/nQiBLSaxuFwaMyYMdq4caN3m9vt1saNG5WTk9PsPjk5OT7tJen1119vsT0AAOheAh6mycvL0/Tp0zV27FhdeumlWrp0qWpqajRz5kxJ0rRp09SvXz8VFhZKku666y5dddVVWrx4sW688UatWbNGW7du1a9//evgHgkAAOiUAg4jkydPVkVFhRYtWqTS0lKNGjVKGzZs8E5SLSkpkd3e1OFy+eWXa/Xq1br33nt1zz33aMiQIXrppZc0fPhwv98zOjpaBQUFzQ7ddHecm9ZxflrGuWkZ56Z1nJ+WcW7aJuDrjAAAAAQT96YBAACWIowAAABLEUYAAIClCCMAAMBSHT6MLFu2TJmZmYqJiVF2dra2bNlidUkhd//998tms/k8hg0b5n3+1KlTmjt3rnr37q34+Hh95zvfOevCciUlJbrxxhsVFxcnp9Opu+++Ww0NDeE+lKD4y1/+ookTJ6pv376y2Wxn3dfIGKNFixapT58+io2NVW5urvbs2ePT5ujRo5o6daoSExOVnJys2267TdXV1T5tPvzwQ11xxRWKiYlRRkaGHn300VAfWrud69zMmDHjrM/ShAkTfNp01XNTWFioSy65RAkJCXI6nZo0aZJ2797t0yZY36XNmzfr4osvVnR0tAYPHqxnn3021IfXLv6cm6uvvvqsz87s2bN92nTFcyNJTz31lLKysrwXLsvJydGrr77qfb67fm5CynRga9asMQ6Hw6xcudL8/e9/N7NmzTLJycmmrKzM6tJCqqCgwFx00UXm0KFD3kdFRYX3+dmzZ5uMjAyzceNGs3XrVnPZZZeZyy+/3Pt8Q0ODGT58uMnNzTXbtm0z69evNykpKSY/P9+Kw2m39evXm5/97GfmxRdfNJLMunXrfJ5/+OGHTVJSknnppZfMjh07zE033WQGDhxoTp486W0zYcIEM3LkSPPXv/7VvP3222bw4MFmypQp3ucrKytNWlqamTp1qtm5c6f5wx/+YGJjY81///d/h+sw2+Rc52b69OlmwoQJPp+lo0eP+rTpqudm/Pjx5plnnjE7d+4027dvNzfccIPp37+/qa6u9rYJxndp3759Ji4uzuTl5ZmPP/7YPP744yYiIsJs2LAhrMcbCH/OzVVXXWVmzZrl89mprKz0Pt9Vz40xxvzpT38yr7zyivn000/N7t27zT333GOioqLMzp07jTHd93MTSh06jFx66aVm7ty53p9dLpfp27evKSwstLCq0CsoKDAjR45s9rljx46ZqKgo88c//tG7bdeuXUaSKSoqMsY0/oKy2+2mtLTU2+app54yiYmJpra2NqS1h9qZv3DdbrdJT083v/zlL73bjh07ZqKjo80f/vAHY4wxH3/8sZFk/va3v3nbvPrqq8Zms5kvvvjCGGPMk08+aXr27OlzfhYsWGCGDh0a4iMKnpbCyLe+9a0W9+ku58YYY8rLy40k89Zbbxljgvdd+ulPf2ouuugin/eaPHmyGT9+fKgPKWjOPDfGNIaRu+66q8V9usu58ejZs6f5zW9+w+cmRDrsME1dXZ2Ki4uVm5vr3Wa325Wbm6uioiILKwuPPXv2qG/fvho0aJCmTp2qkpISSVJxcbHq6+t9zsuwYcPUv39/73kpKirSiBEjfO6WPH78eFVVVenvf/97eA8kxPbv36/S0lKf85GUlKTs7Gyf85GcnKyxY8d62+Tm5sput+v999/3trnyyivlcDi8bcaPH6/du3frn//8Z5iOJjQ2b94sp9OpoUOHas6cOTpy5Ij3ue50biorKyVJvXr1khS871JRUZHPa3jadKZ/p848Nx6rVq1SSkqKhg8frvz8fJ04ccL7XHc5Ny6XS2vWrFFNTY1ycnL43IRIh7xrryQdPnxYLpfL5y9TktLS0vTJJ59YVFV4ZGdn69lnn9XQoUN16NAhPfDAA7riiiu0c+dOlZaWyuFwKDk52WeftLQ0lZaWSpJKS0ubPW+e57oSz/E0d7ynnw+n0+nzfGRkpHr16uXTZuDAgWe9hue5nj17hqT+UJswYYJuvvlmDRw4UHv37tU999yj66+/XkVFRYqIiOg258btdmv+/PkaN26c9+rPwfoutdSmqqpKJ0+eVGxsbCgOKWiaOzeSdMstt2jAgAHq27evPvzwQy1YsEC7d+/Wiy++KKnrn5uPPvpIOTk5OnXqlOLj47Vu3TpdeOGF2r59O5+bEOiwYaQ7u/76671/zsrKUnZ2tgYMGKD/+Z//6XYfULTP97//fe+fR4wYoaysLJ133nnavHmzrr32WgsrC6+5c+dq586deuedd6wupcNp6dzcfvvt3j+PGDFCffr00bXXXqu9e/fqvPPOC3eZYTd06FBt375dlZWVev755zV9+nS99dZbVpfVZXXYYZqUlBRFREScNUO5rKxM6enpFlVljeTkZJ1//vn67LPPlJ6errq6Oh07dsynzennJT09vdnz5nmuK/EcT2ufk/T0dJWXl/s839DQoKNHj3a7czZo0CClpKTos88+k9Q9zs28efP05z//WZs2bdLXvvY17/ZgfZdaapOYmNjh//PQ0rlpTnZ2tiT5fHa68rlxOBwaPHiwxowZo8LCQo0cOVK/+tWv+NyESIcNIw6HQ2PGjNHGjRu929xutzZu3KicnBwLKwu/6upq7d27V3369NGYMWMUFRXlc152796tkpIS73nJycnRRx995PNL5vXXX1diYqIuvPDCsNcfSgMHDlR6errP+aiqqtL777/vcz6OHTum4uJib5s333xTbrfb+w9sTk6O/vKXv6i+vt7b5vXXX9fQoUM7xTCEv/7xj3/oyJEj6tOnj6SufW6MMZo3b57WrVunN99886yhpmB9l3Jycnxew9OmI/87da5z05zt27dLks9npyuem5a43W7V1tZ2689NSFk9g7Y1a9asMdHR0ebZZ581H3/8sbn99ttNcnKyzwzlrujHP/6x2bx5s9m/f7959913TW5urklJSTHl5eXGmMZlZf379zdvvvmm2bp1q8nJyTE5OTne/T3Lyq677jqzfft2s2HDBpOamtppl/YeP37cbNu2zWzbts1IMkuWLDHbtm0zn3/+uTGmcWlvcnKyefnll82HH35ovvWtbzW7tHf06NHm/fffN++8844ZMmSIz/LVY8eOmbS0NHPrrbeanTt3mjVr1pi4uLgOv3y1tXNz/Phx85Of/MQUFRWZ/fv3mzfeeMNcfPHFZsiQIebUqVPe1+iq52bOnDkmKSnJbN682Wd56okTJ7xtgvFd8izRvPvuu82uXbvMsmXLOvwSzXOdm88++8z8/Oc/N1u3bjX79+83L7/8shk0aJC58sorva/RVc+NMcYsXLjQvPXWW2b//v3mww8/NAsXLjQ2m8383//9nzGm+35uQqlDhxFjjHn88cdN//79jcPhMJdeeqn561//anVJITd58mTTp08f43A4TL9+/czkyZPNZ5995n3+5MmT5o477jA9e/Y0cXFx5tvf/rY5dOiQz2scOHDAXH/99SY2NtakpKSYH//4x6a+vj7chxIUmzZtMpLOekyfPt0Y07i897777jNpaWkmOjraXHvttWb37t0+r3HkyBEzZcoUEx8fbxITE83MmTPN8ePHfdrs2LHDfP3rXzfR0dGmX79+5uGHHw7XIbZZa+fmxIkT5rrrrjOpqakmKirKDBgwwMyaNeusMN9Vz01z50WSeeaZZ7xtgvVd2rRpkxk1apRxOBxm0KBBPu/REZ3r3JSUlJgrr7zS9OrVy0RHR5vBgwebu+++2+c6I8Z0zXNjjDH/9m//ZgYMGGAcDodJTU011157rTeIGNN9PzehZDPGmPD1wwAAAPjqsHNGAABA90AYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgKcIIAACwFGEEAABYijACIORmzJihSZMmWV0GgA4q0uoCAHRuNput1ecLCgr0q1/9SlzsGUBLCCMA2uXQoUPeP69du1aLFi3S7t27vdvi4+MVHx9vRWkAOgmGaQC0S3p6uveRlJQkm83msy0+Pv6sYZqrr75ad955p+bPn6+ePXsqLS1NK1asUE1NjWbOnKmEhAQNHjxYr776qs977dy5U9dff73i4+OVlpamW2+9VYcPHw7zEQMINsIIAEs899xzSklJ0ZYtW3TnnXdqzpw5+t73vqfLL79cH3zwga677jrdeuutOnHihCTp2LFjuuaaazR69Ght3bpVGzZsUFlZmf71X//V4iMB0F6EEQCWGDlypO69914NGTJE+fn5iomJUUpKimbNmqUhQ4Zo0aJFOnLkiD788ENJ0hNPPKHRo0froYce0rBhwzR69GitXLlSmzZt0qeffmrx0QBoD+aMALBEVlaW988RERHq3bu3RowY4d2WlpYmSSovL5ck7dixQ5s2bWp2/snevXt1/vnnh7hiAKFCGAFgiaioKJ+fbTabzzbPKh232y1Jqq6u1sSJE/XII4+c9Vp9+vQJYaUAQo0wAqBTuPjii/XCCy8oMzNTkZH80wV0JcwZAdApzJ07V0ePHtWUKVP0t7/9TXv37tVrr72mmTNnyuVyWV0egHYgjADoFPr27at3331XLpdL1113nUaMGKH58+crOTlZdjv/lAGdmc1wWUQAAGAh/jsBAAAsRRgBAACWIowAAABLEUYAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJYijAAAAEv9P6VlgXOlX8h0AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "safe_plot = acro.surv_func(\n", + " data.futime, data.death, output=\"plot\", filename=\"kaplan-mier.png\"\n", + ")\n", + "print(safe_plot)" + ] + }, + { + "cell_type": "markdown", + "id": "9e554eea", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "ec960039", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:46.897407\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "summary: fail; threshold: 5 cells suppressed; \n", + "outcome: parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom ok ok ok ok\n", + "priority ok ok ok ok\n", + "recommend threshold; threshold; threshold; threshold; \n", + "spec_prior ok ok ok ok\n", + "very_recom threshold; ok ok ok\n", + "All ok ok ok ok\n", + "output: [parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958]\n", + "timestamp: 2025-03-06T19:39:46.961631\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:39:46.980090\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 1, 'p-ratio': 4, 'nk-rule': 4, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [4, 0]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [4, 0]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious \\\n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + "parents usual \n", + "recommend \n", + "not_recom ok \n", + "priority ok \n", + "recommend threshold; p-ratio; nk-rule; \n", + "spec_prior ok \n", + "very_recom ok \n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:47.019919\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 8, 'nk-rule': 8, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2], [2, 5]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "outcome: mode_aggfunc \\\n", + "parents great_pret pretentious \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + " mean \\\n", + "parents usual great_pret \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok p-ratio; nk-rule; \n", + "\n", + " \n", + "parents pretentious usual \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok ok \n", + "output: [ mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\n", + "timestamp: 2025-03-06T19:39:47.068066\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_5\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " children children\n", + "parents \n", + "great_pret ok ok\n", + "pretentious ok ok\n", + "usual ok ok\n", + "output: [ mean std\n", + " children children\n", + "parents \n", + "great_pret 3.140972 2.270396\n", + "pretentious 3.129630 2.250436\n", + "usual 3.110648 2.213072]\n", + "timestamp: 2025-03-06T19:39:47.105651\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 5, 'nk-rule': 5, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.pivot_table(\n", + "summary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "outcome: children \\\n", + "recommend not_recom priority recommend spec_prior \n", + "parents \n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok \n", + "usual ok ok threshold; p-ratio; nk-rule; ok \n", + "All ok ok threshold; p-ratio; nk-rule; ok \n", + "\n", + " \n", + "recommend very_recom All \n", + "parents \n", + "great_pret threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok \n", + "usual ok ok \n", + "All ok ok \n", + "output: [ children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412]\n", + "timestamp: 2025-03-06T19:39:47.231513\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_7\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'ols', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.ols(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ recommend R-squared: 0.001\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.001000\n", + "Method: Least Squares F-statistic: 13.830000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.000000\n", + "No. Observations: 12960 AIC: 50250.000000\n", + "Df Residuals: 12958 BIC: 50260.000000\n", + "Df Model: 1 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "const 2.2099 0.025 87.263 0.0 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90]\n", + "timestamp: 2025-03-06T19:39:47.388052\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_8\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'olsr', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ recommend R-squared: 0.001\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.001000\n", + "Method: Least Squares F-statistic: 13.830000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.000000\n", + "No. Observations: 12960 AIC: 50250.000000\n", + "Df Residuals: 12958 BIC: 50260.000000\n", + "Df Model: 1 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "Intercept 2.2099 0.025 87.263 0.0 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90]\n", + "timestamp: 2025-03-06T19:39:47.414293\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_9\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'probit', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.probit(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ finance No. Observations: 12960\n", + "Dep. Variable: \n", + "Model: Probit Df Residuals: 12958.000000\n", + "Method: MLE Df Model: 1.000000\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\n", + "Time: 19:39:47 Log-Likelihood: -8983.200000\n", + "converged: True LL-Null: -8983.200000\n", + "Covariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\n", + "const -0.0039 0.019 -0.207 0.836 -0.041 0.033\n", + "children 0.0012 0.005 0.254 0.799 -0.008 0.011]\n", + "timestamp: 2025-03-06T19:39:47.439598\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_10\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'logit', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.logit(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ finance No. Observations: 12960\n", + "Dep. Variable: \n", + "Model: Logit Df Residuals: 12958.000000\n", + "Method: MLE Df Model: 1.000000\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\n", + "Time: 19:39:47 Log-Likelihood: -8983.200000\n", + "converged: True LL-Null: -8983.200000\n", + "Covariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\n", + "const -0.0062 0.030 -0.207 0.836 -0.065 0.053\n", + "children 0.0020 0.008 0.254 0.799 -0.013 0.017]\n", + "timestamp: 2025-03-06T19:39:47.457696\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_11\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Surv_prob Surv_prob_SE num_at_risk num_events\n", + "Time \n", + "51 ok ok ok ok\n", + "69 threshold; threshold; threshold; threshold; \n", + "85 threshold; threshold; threshold; threshold; \n", + "91 threshold; threshold; threshold; threshold; \n", + "115 threshold; threshold; threshold; threshold; \n", + "372 threshold; threshold; threshold; threshold; \n", + "667 threshold; threshold; threshold; threshold; \n", + "874 threshold; threshold; threshold; threshold; \n", + "1039 threshold; threshold; threshold; threshold; \n", + "1046 threshold; threshold; threshold; threshold; \n", + "1281 threshold; threshold; threshold; threshold; \n", + "1286 threshold; threshold; threshold; threshold; \n", + "1326 threshold; threshold; threshold; threshold; \n", + "1355 threshold; threshold; threshold; threshold; \n", + "1626 threshold; threshold; threshold; threshold; \n", + "1903 threshold; threshold; threshold; threshold; \n", + "1914 threshold; threshold; threshold; threshold; \n", + "2776 threshold; threshold; threshold; threshold; \n", + "2851 threshold; threshold; threshold; threshold; \n", + "3309 threshold; threshold; threshold; threshold; \n", + "output: [ Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN]\n", + "timestamp: 2025-03-06T19:39:48.298262\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_12\n", + "status: fail\n", + "type: survival plot\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_plot = acro.surv_func(\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/kaplan-mier_0.png']\n", + "timestamp: 2025-03-06T19:39:48.450221\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "'uid: output_0\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 4, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 4 cells suppressed; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:39:46.897407\\ncomments: []\\nexception: \\n\\nuid: output_1\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 5, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\\nsummary: fail; threshold: 5 cells suppressed; \\noutcome: parents great_pret pretentious usual All\\nrecommend \\nnot_recom ok ok ok ok\\npriority ok ok ok ok\\nrecommend threshold; threshold; threshold; threshold; \\nspec_prior ok ok ok ok\\nvery_recom threshold; ok ok ok\\nAll ok ok ok ok\\noutput: [parents great_pret pretentious usual All\\nrecommend \\nnot_recom 1440.0 1440 1440 4320\\npriority 858.0 1484 1924 4266\\nspec_prior 2022.0 1264 758 4044\\nvery_recom NaN 132 196 328\\nAll 4320.0 4320 4318 12958]\\ntimestamp: 2025-03-06T19:39:46.961631\\ncomments: []\\nexception: \\n\\nuid: output_2\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': False, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 4, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents)\\nsummary: fail; threshold: 4 cells may need suppressing; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440 1440 1440\\npriority 858 1484 1924\\nrecommend 0 0 2\\nspec_prior 2022 1264 758\\nvery_recom 0 132 196]\\ntimestamp: 2025-03-06T19:39:46.980090\\ncomments: []\\nexception: \\n\\nuid: output_3\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 1, \\'p-ratio\\': 4, \\'nk-rule\\': 4, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 2]], \\'p-ratio\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'nk-rule\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \\noutcome: parents great_pret pretentious \\\\\\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom p-ratio; nk-rule; ok \\n\\nparents usual \\nrecommend \\nnot_recom ok \\npriority ok \\nrecommend threshold; p-ratio; nk-rule; \\nspec_prior ok \\nvery_recom ok \\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:39:47.019919\\ncomments: []\\nexception: \\n\\nuid: output_4\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 2, \\'p-ratio\\': 8, \\'nk-rule\\': 8, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 2], [2, 5]], \\'p-ratio\\': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], \\'nk-rule\\': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \\noutcome: mode_aggfunc \\\\\\nparents great_pret pretentious \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom p-ratio; nk-rule; ok \\n\\n mean \\\\\\nparents usual great_pret \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom ok p-ratio; nk-rule; \\n\\n \\nparents pretentious usual \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom ok ok \\noutput: [ mode_aggfunc mean \\nparents great_pret pretentious usual great_pret pretentious usual\\nrecommend \\nnot_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\\npriority 1.0 1.0 1.0 2.665501 3.030323 3.116944\\nrecommend NaN NaN NaN NaN NaN NaN\\nspec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\\nvery_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\\ntimestamp: 2025-03-06T19:39:47.068066\\ncomments: []\\nexception: \\n\\nuid: output_5\\nstatus: pass\\ntype: table\\nproperties: {\\'method\\': \\'pivot_table\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 0, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: table = acro.pivot_table(\\nsummary: pass\\noutcome: mean std\\n children children\\nparents \\ngreat_pret ok ok\\npretentious ok ok\\nusual ok ok\\noutput: [ mean std\\n children children\\nparents \\ngreat_pret 3.140972 2.270396\\npretentious 3.129630 2.250436\\nusual 3.110648 2.213072]\\ntimestamp: 2025-03-06T19:39:47.105651\\ncomments: []\\nexception: \\n\\nuid: output_6\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'pivot_table\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 5, \\'p-ratio\\': 5, \\'nk-rule\\': 5, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'p-ratio\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'nk-rule\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.pivot_table(\\nsummary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \\noutcome: children \\\\\\nrecommend not_recom priority recommend spec_prior \\nparents \\ngreat_pret ok ok threshold; p-ratio; nk-rule; ok \\npretentious ok ok threshold; p-ratio; nk-rule; ok \\nusual ok ok threshold; p-ratio; nk-rule; ok \\nAll ok ok threshold; p-ratio; nk-rule; ok \\n\\n \\nrecommend very_recom All \\nparents \\ngreat_pret threshold; p-ratio; nk-rule; ok \\npretentious ok ok \\nusual ok ok \\nAll ok ok \\noutput: [ children \\nrecommend not_recom priority spec_prior very_recom All\\nparents \\ngreat_pret 3.125694 2.665501 3.353610 NaN 3.140972\\npretentious 3.105556 3.030323 3.370253 2.204545 3.129630\\nusual 3.074306 3.116944 3.393140 2.244898 3.111626\\nAll 3.101852 2.996015 3.366222 2.228659 3.127412]\\ntimestamp: 2025-03-06T19:39:47.231513\\ncomments: []\\nexception: \\n\\nuid: output_7\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'ols\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.ols(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ recommend R-squared: 0.001\\nDep. Variable: \\nModel: OLS Adj. R-squared: 0.001000\\nMethod: Least Squares F-statistic: 13.830000\\nDate: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\\nTime: 19:39:47 Log-Likelihood: -25121.000000\\nNo. Observations: 12960 AIC: 50250.000000\\nDf Residuals: 12958 BIC: 50260.000000\\nDf Model: 1 NaN NaN\\nCovariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\\nconst 2.2099 0.025 87.263 0.0 2.160 2.260\\nchildren 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\\nOmnibus: \\nProb(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\\nSkew: -0.486 Prob(JB): 0.00\\nKurtosis: 1.489 Cond. No. 6.90]\\ntimestamp: 2025-03-06T19:39:47.388052\\ncomments: []\\nexception: \\n\\nuid: output_8\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'olsr\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ recommend R-squared: 0.001\\nDep. Variable: \\nModel: OLS Adj. R-squared: 0.001000\\nMethod: Least Squares F-statistic: 13.830000\\nDate: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\\nTime: 19:39:47 Log-Likelihood: -25121.000000\\nNo. Observations: 12960 AIC: 50250.000000\\nDf Residuals: 12958 BIC: 50260.000000\\nDf Model: 1 NaN NaN\\nCovariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\\nIntercept 2.2099 0.025 87.263 0.0 2.160 2.260\\nchildren 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\\nOmnibus: \\nProb(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\\nSkew: -0.486 Prob(JB): 0.00\\nKurtosis: 1.489 Cond. No. 6.90]\\ntimestamp: 2025-03-06T19:39:47.414293\\ncomments: []\\nexception: \\n\\nuid: output_9\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'probit\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.probit(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ finance No. Observations: 12960\\nDep. Variable: \\nModel: Probit Df Residuals: 12958.000000\\nMethod: MLE Df Model: 1.000000\\nDate: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\\nTime: 19:39:47 Log-Likelihood: -8983.200000\\nconverged: True LL-Null: -8983.200000\\nCovariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\\nconst -0.0039 0.019 -0.207 0.836 -0.041 0.033\\nchildren 0.0012 0.005 0.254 0.799 -0.008 0.011]\\ntimestamp: 2025-03-06T19:39:47.439598\\ncomments: []\\nexception: \\n\\nuid: output_10\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'logit\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.logit(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ finance No. Observations: 12960\\nDep. Variable: \\nModel: Logit Df Residuals: 12958.000000\\nMethod: MLE Df Model: 1.000000\\nDate: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\\nTime: 19:39:47 Log-Likelihood: -8983.200000\\nconverged: True LL-Null: -8983.200000\\nCovariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\\nconst -0.0062 0.030 -0.207 0.836 -0.065 0.053\\nchildren 0.0020 0.008 0.254 0.799 -0.013 0.017]\\ntimestamp: 2025-03-06T19:39:47.457696\\ncomments: []\\nexception: \\n\\nuid: output_11\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'surv_func\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 76, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\\nsummary: fail; threshold: 76 cells suppressed; \\noutcome: Surv_prob Surv_prob_SE num_at_risk num_events\\nTime \\n51 ok ok ok ok\\n69 threshold; threshold; threshold; threshold; \\n85 threshold; threshold; threshold; threshold; \\n91 threshold; threshold; threshold; threshold; \\n115 threshold; threshold; threshold; threshold; \\n372 threshold; threshold; threshold; threshold; \\n667 threshold; threshold; threshold; threshold; \\n874 threshold; threshold; threshold; threshold; \\n1039 threshold; threshold; threshold; threshold; \\n1046 threshold; threshold; threshold; threshold; \\n1281 threshold; threshold; threshold; threshold; \\n1286 threshold; threshold; threshold; threshold; \\n1326 threshold; threshold; threshold; threshold; \\n1355 threshold; threshold; threshold; threshold; \\n1626 threshold; threshold; threshold; threshold; \\n1903 threshold; threshold; threshold; threshold; \\n1914 threshold; threshold; threshold; threshold; \\n2776 threshold; threshold; threshold; threshold; \\n2851 threshold; threshold; threshold; threshold; \\n3309 threshold; threshold; threshold; threshold; \\noutput: [ Surv prob Surv prob SE num at risk num events\\nTime \\n51 0.95 0.048734 20.0 1.0\\n69 NaN NaN NaN NaN\\n85 NaN NaN NaN NaN\\n91 NaN NaN NaN NaN\\n115 NaN NaN NaN NaN\\n372 NaN NaN NaN NaN\\n667 NaN NaN NaN NaN\\n874 NaN NaN NaN NaN\\n1039 NaN NaN NaN NaN\\n1046 NaN NaN NaN NaN\\n1281 NaN NaN NaN NaN\\n1286 NaN NaN NaN NaN\\n1326 NaN NaN NaN NaN\\n1355 NaN NaN NaN NaN\\n1626 NaN NaN NaN NaN\\n1903 NaN NaN NaN NaN\\n1914 NaN NaN NaN NaN\\n2776 NaN NaN NaN NaN\\n2851 NaN NaN NaN NaN\\n3309 NaN NaN NaN NaN]\\ntimestamp: 2025-03-06T19:39:48.298262\\ncomments: []\\nexception: \\n\\nuid: output_12\\nstatus: fail\\ntype: survival plot\\nproperties: {\\'method\\': \\'surv_func\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 76, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_plot = acro.surv_func(\\nsummary: fail; threshold: 76 cells suppressed; \\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [\\'acro_artifacts/kaplan-mier_0.png\\']\\ntimestamp: 2025-03-06T19:39:48.450221\\ncomments: []\\nexception: \\n\\n'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "id": "3136bc78", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- Currently, all outputs names contain timestamp; that is the time when the output was created. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "e4ee985e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "id": "df2a02e0", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name. \n", + "The timestamp associated with the output name will not get overwritten" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "b9d0b9ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_2 renamed to pivot_table\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_2\", \"pivot_table\")" + ] + }, + { + "cell_type": "markdown", + "id": "56d2b6a1", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "8e21f7b0", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to output_1\n", + "INFO:acro:records:a comment was added to output_1\n" + ] + } + ], + "source": [ + "acro.add_comments(\"output_1\", \"Please let me have this data.\")\n", + "acro.add_comments(\"output_1\", \"6 cells were suppressed in this table\")" + ] + }, + { + "cell_type": "markdown", + "id": "8496fed4", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 5: Add an unsupported output to the list of outputs\n", + "This is an example to add an unsupported outputs (such as images) to the list of outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "1e8000a1", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add_custom(): output_13\n" + ] + } + ], + "source": [ + "acro.custom_output(\n", + " \"XandY.jpeg\", \"This output is an image showing the relationship between X and Y\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5a586694", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## 6: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "f941aca2", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "summary: fail; threshold: 5 cells suppressed; \n", + "outcome: parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom ok ok ok ok\n", + "priority ok ok ok ok\n", + "recommend threshold; threshold; threshold; threshold; \n", + "spec_prior ok ok ok ok\n", + "very_recom threshold; ok ok ok\n", + "All ok ok ok ok\n", + "output: [parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958]\n", + "timestamp: 2025-03-06T19:39:46.961631\n", + "comments: ['Please let me have this data.', '6 cells were suppressed in this table']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 1, 'p-ratio': 4, 'nk-rule': 4, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [4, 0]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [4, 0]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious \\\n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + "parents usual \n", + "recommend \n", + "not_recom ok \n", + "priority ok \n", + "recommend threshold; p-ratio; nk-rule; \n", + "spec_prior ok \n", + "very_recom ok \n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:47.019919\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 8, 'nk-rule': 8, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2], [2, 5]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "outcome: mode_aggfunc \\\n", + "parents great_pret pretentious \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + " mean \\\n", + "parents usual great_pret \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok p-ratio; nk-rule; \n", + "\n", + " \n", + "parents pretentious usual \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok ok \n", + "output: [ mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\n", + "timestamp: 2025-03-06T19:39:47.068066\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 5, 'nk-rule': 5, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.pivot_table(\n", + "summary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "outcome: children \\\n", + "recommend not_recom priority recommend spec_prior \n", + "parents \n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok \n", + "usual ok ok threshold; p-ratio; nk-rule; ok \n", + "All ok ok threshold; p-ratio; nk-rule; ok \n", + "\n", + " \n", + "recommend very_recom All \n", + "parents \n", + "great_pret threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok \n", + "usual ok ok \n", + "All ok ok \n", + "output: [ children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412]\n", + "timestamp: 2025-03-06T19:39:47.231513\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " some reason\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_11\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Surv_prob Surv_prob_SE num_at_risk num_events\n", + "Time \n", + "51 ok ok ok ok\n", + "69 threshold; threshold; threshold; threshold; \n", + "85 threshold; threshold; threshold; threshold; \n", + "91 threshold; threshold; threshold; threshold; \n", + "115 threshold; threshold; threshold; threshold; \n", + "372 threshold; threshold; threshold; threshold; \n", + "667 threshold; threshold; threshold; threshold; \n", + "874 threshold; threshold; threshold; threshold; \n", + "1039 threshold; threshold; threshold; threshold; \n", + "1046 threshold; threshold; threshold; threshold; \n", + "1281 threshold; threshold; threshold; threshold; \n", + "1286 threshold; threshold; threshold; threshold; \n", + "1326 threshold; threshold; threshold; threshold; \n", + "1355 threshold; threshold; threshold; threshold; \n", + "1626 threshold; threshold; threshold; threshold; \n", + "1903 threshold; threshold; threshold; threshold; \n", + "1914 threshold; threshold; threshold; threshold; \n", + "2776 threshold; threshold; threshold; threshold; \n", + "2851 threshold; threshold; threshold; threshold; \n", + "3309 threshold; threshold; threshold; threshold; \n", + "output: [ Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN]\n", + "timestamp: 2025-03-06T19:39:48.298262\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " some other reason\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_12\n", + "status: fail\n", + "type: survival plot\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_plot = acro.surv_func(\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/kaplan-mier_0.png']\n", + "timestamp: 2025-03-06T19:39:48.450221\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: pivot_table\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:39:46.980090\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason is provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_13\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['XandY.jpeg']\n", + "timestamp: 2025-03-06T19:39:48.518030\n", + "comments: ['This output is an image showing the relationship between X and Y']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " image is not disclosive\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: NURSERY\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"NURSERY\", \"json\")" + ] + }, + { + "cell_type": "markdown", + "id": "113d84ec", + "metadata": {}, + "source": [ + "### 7: Add a directory of outputs to an acro object \n", + "This is an example of adding a list of files (produced by the researcher without using ACRO) to an acro object and creates a results file for checking." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "fdea993a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n", + "INFO:acro:records:add_custom(): output_0\n", + "INFO:acro:records:rename_output(): output_0 renamed to crosstab.pkl\n", + "INFO:acro:records:\n", + "uid: crosstab.pkl\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['test_add_to_acro/crosstab.pkl']\n", + "timestamp: 2025-03-06T19:41:22.128464\n", + "comments: ['']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " pickle file need some explanation \n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: SDC_results\n" + ] + } + ], + "source": [ + "import shutil\n", + "\n", + "table = pd.crosstab(df.recommend, df.parents)\n", + "# save the output table to a file and add this file to a directory\n", + "src_path = \"test_add_to_acro\"\n", + "file_path = \"crosstab.pkl\"\n", + "dest_path = \"SDC_results\"\n", + "if not os.path.exists(src_path):\n", + " table.to_pickle(file_path)\n", + " os.mkdir(src_path)\n", + " shutil.move(file_path, src_path, copy_function=shutil.copytree)\n", + "\n", + "# add the output to acro\n", + "add_to_acro(src_path, dest_path)" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks/test.ipynb b/docs/source/_build/.doctrees/nbsphinx/notebooks/test.ipynb new file mode 100644 index 00000000..1cf9ee87 --- /dev/null +++ b/docs/source/_build/.doctrees/nbsphinx/notebooks/test.ipynb @@ -0,0 +1,4393 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "00cac1f9", + "metadata": {}, + "source": [ + "# ACRO Tests" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e33fd4fb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c01cfe12", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc8d993a", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [], + "source": [ + "from acro import ACRO, add_constant, utils" + ] + }, + { + "cell_type": "markdown", + "id": "530efcfe", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4b8a77e2", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "id": "27a2baaa", + "metadata": {}, + "source": [ + "### Load test data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8722735f", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
charitygrant_typeindexyearinc_activityinc_grantsinc_donationsinc_otherinc_totaltotal_costs...sh_staff_grants_givensh_assets_grants_givensh_income_balancesh_staff_balancesh_assets_balancesh_income_assetssh_staff_assetssh_income_staff_costssh_assets_staff_costswgt
04ChildrenR1.020112880902.09603182.091404.0310947.012886435.012127472.0...NaNNaN0.0726360.1359710.7678090.0946020.1770900.5342035.6468431.0
14ChildrenR1.020146810520.018768904.058002.0401879.026039304.025493796.0...NaNNaN0.0576410.0891501.0013960.0575600.0890260.64656111.2327291.0
24ChildrenR1.020157199403.021638036.0132191.0512654.029482284.032290108.0...NaNNaN-0.049619-0.079828-0.6202100.0800040.1287110.6215837.7693651.0
34ChildrenR1.020135573013.015194731.0228844.0267156.021263744.020989048.0...NaNNaN0.0457400.0682511.0082590.0453650.0676920.67016614.7727491.0
44ChildrenR1.020102056816.07335103.0110256.0424628.09926803.09769816.0...NaNNaN0.0576960.1225320.5675390.1016600.2159010.4708624.6317491.0
\n", + "

5 rows × 44 columns

\n", + "
" + ], + "text/plain": [ + " charity grant_type index year inc_activity inc_grants inc_donations \\\n", + "0 4Children R 1.0 2011 2880902.0 9603182.0 91404.0 \n", + "1 4Children R 1.0 2014 6810520.0 18768904.0 58002.0 \n", + "2 4Children R 1.0 2015 7199403.0 21638036.0 132191.0 \n", + "3 4Children R 1.0 2013 5573013.0 15194731.0 228844.0 \n", + "4 4Children R 1.0 2010 2056816.0 7335103.0 110256.0 \n", + "\n", + " inc_other inc_total total_costs ... sh_staff_grants_given \\\n", + "0 310947.0 12886435.0 12127472.0 ... NaN \n", + "1 401879.0 26039304.0 25493796.0 ... NaN \n", + "2 512654.0 29482284.0 32290108.0 ... NaN \n", + "3 267156.0 21263744.0 20989048.0 ... NaN \n", + "4 424628.0 9926803.0 9769816.0 ... NaN \n", + "\n", + " sh_assets_grants_given sh_income_balance sh_staff_balance \\\n", + "0 NaN 0.072636 0.135971 \n", + "1 NaN 0.057641 0.089150 \n", + "2 NaN -0.049619 -0.079828 \n", + "3 NaN 0.045740 0.068251 \n", + "4 NaN 0.057696 0.122532 \n", + "\n", + " sh_assets_balance sh_income_assets sh_staff_assets sh_income_staff_costs \\\n", + "0 0.767809 0.094602 0.177090 0.534203 \n", + "1 1.001396 0.057560 0.089026 0.646561 \n", + "2 -0.620210 0.080004 0.128711 0.621583 \n", + "3 1.008259 0.045365 0.067692 0.670166 \n", + "4 0.567539 0.101660 0.215901 0.470862 \n", + "\n", + " sh_assets_staff_costs wgt \n", + "0 5.646843 1.0 \n", + "1 11.232729 1.0 \n", + "2 7.769365 1.0 \n", + "3 14.772749 1.0 \n", + "4 4.631749 1.0 \n", + "\n", + "[5 rows x 44 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "path = os.path.join(\"../data\", \"test_data.dta\")\n", + "df = pd.read_stata(path)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "4ae844a0", + "metadata": {}, + "source": [ + "### Pandas crosstab" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "961684cb", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
grant_typeGRGNRR/G
year
20103471259248
20113471259248
20123471259248
20133471259248
20143471259248
20153471259248
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = pd.crosstab(df.year, [df.survivor, df.grant_type])\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "d642ed00", + "metadata": {}, + "source": [ + "### ACRO crosstab" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "bb4b2677", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 12 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015 |\n", + "grant_type |G R |G N R R/G |\n", + "year | | |\n", + "----------------------------------------------------------------|\n", + "2010 | threshold; ok | ok ok ok threshold; |\n", + "2011 | threshold; ok | ok ok ok threshold; |\n", + "2012 | threshold; ok | ok ok ok threshold; |\n", + "2013 | threshold; ok | ok ok ok threshold; |\n", + "2014 | threshold; ok | ok ok ok threshold; |\n", + "2015 | threshold; ok | ok ok ok threshold; |\n", + "----------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
grant_typeGRGNRR/G
year
20103471259248
20113471259248
20123471259248
20133471259248
20143471259248
20153471259248
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, [df.survivor, df.grant_type])\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "59b223fb-7b88-4f51-9bdf-7dbb797849d1", + "metadata": { + "tags": [] + }, + "source": [ + "### same table with column hierarchy reversed to make sure spaces in variable name. dealt with properly" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d01f7437-ceee-41b3-84ad-07976e0d58c3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 12 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |\n", + "survivor |Dead_in_2015 Alive_in_2015 |Alive_in_2015 |Dead_in_2015 Alive_in_2015 |Alive_in_2015|\n", + "year | | | | |\n", + "------------------------------------------------------------------------------------------------|\n", + "2010 | threshold; ok | ok | ok ok | threshold; |\n", + "2011 | threshold; ok | ok | ok ok | threshold; |\n", + "2012 | threshold; ok | ok | ok ok | threshold; |\n", + "2013 | threshold; ok | ok | ok ok | threshold; |\n", + "2014 | threshold; ok | ok | ok ok | threshold; |\n", + "2015 | threshold; ok | ok | ok ok | threshold; |\n", + "------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
survivorDead in 2015Alive in 2015Alive in 2015Dead in 2015Alive in 2015Alive in 2015
year
20103125947248
20113125947248
20123125947248
20133125947248
20143125947248
20153125947248
\n", + "
" + ], + "text/plain": [ + "grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 3 12 59 47 \n", + "2011 3 12 59 47 \n", + "2012 3 12 59 47 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 47 \n", + "2015 3 12 59 47 \n", + "\n", + "grant_type R/G \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 24 8 \n", + "2011 24 8 \n", + "2012 24 8 \n", + "2013 24 8 \n", + "2014 24 8 \n", + "2015 24 8 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, [df.grant_type, df.survivor])\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "1c34d5ba-8200-4181-9440-ca02f4bfd2f4", + "metadata": {}, + "source": [ + "### checking for testing purposes" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e4382b14-cfcf-4d01-a25a-97106852bd65", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "-------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015|\n", + "year | | |\n", + "-------------------------------------|\n", + "2010 | threshold; | threshold; |\n", + "2011 | threshold; | threshold; |\n", + "-------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
year
201022
201122
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mydata = df[(df[\"charity\"].str[0] == \"W\")]\n", + "mydata = mydata[mydata[\"year\"] < 2012]\n", + "acro.crosstab(mydata.year, mydata.survivor)" + ] + }, + { + "cell_type": "markdown", + "id": "6d4730c4", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "37ddb939", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells suppressed; p-ratio: 2 cells suppressed; nk-rule: 1 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "---------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |\n", + "year | | | | |\n", + "---------------------------------------------------------------------------|\n", + "2010 | ok | threshold; p-ratio; | ok | threshold; p-ratio; nk-rule; |\n", + "2011 | ok | ok | ok | threshold; |\n", + "2012 | ok | ok | ok | threshold; |\n", + "2013 | ok | ok | ok | threshold; |\n", + "2014 | ok | ok | ok | threshold; |\n", + "2015 | ok | ok | ok | threshold; |\n", + "---------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_3\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
year
20109921906.0NaN8402284.0NaN
20118502246.0124013.8593757716880.0NaN
201211458580.0131859.0625006958050.5NaN
201313557147.0147937.7968757202273.5NaN
201413748147.0133198.2500008277525.0NaN
201511133433.0146572.18750010812888.0NaN
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 NaN 8402284.0 NaN\n", + "2011 8502246.0 124013.859375 7716880.0 NaN\n", + "2012 11458580.0 131859.062500 6958050.5 NaN\n", + "2013 13557147.0 147937.796875 7202273.5 NaN\n", + "2014 13748147.0 133198.250000 8277525.0 NaN\n", + "2015 11133433.0 146572.187500 10812888.0 NaN" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.year, df.grant_type, values=df.inc_grants, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "0c695e09", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression and totals" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ef42beb6", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:Empty columns: ('N', 'Dead in 2015'), ('R/G', 'Dead in 2015') were deleted.\n", + "INFO:acro:get_summary(): fail; threshold: 14 cells may need suppressing; p-ratio: 8 cells may need suppressing; nk-rule: 7 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |All|\n", + "survivor |Dead_in_2015 Alive_in_2015 |Alive_in_2015 |Dead_in_2015 Alive_in_2015 |Alive_in_2015 | |\n", + "year | | | | | |\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "2010 | threshold; p-ratio; nk-rule; ok | threshold; p-ratio; | ok ok | threshold; p-ratio; nk-rule; | ok|\n", + "2011 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2012 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2013 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2014 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2015 | threshold; p-ratio; nk-rule; threshold; | ok | ok ok | threshold; | ok|\n", + "All | ok ok | ok | ok ok | ok | ok|\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 2 12 5 40 \n", + "2011 3 12 58 45 \n", + "2012 3 12 59 45 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 43 \n", + "2015 3 9 58 28 \n", + "All 17 69 298 248 \n", + "\n", + "grant_type R/G All \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 20 4 83 \n", + "2011 24 8 150 \n", + "2012 24 8 151 \n", + "2013 24 8 153 \n", + "2014 24 8 149 \n", + "2015 23 8 129 \n", + "All 139 44 815 \n" + ] + } + ], + "source": [ + "acro.suppress = False\n", + "table = acro.crosstab(\n", + " df.year,\n", + " [df.grant_type, df.survivor],\n", + " values=df.inc_grants,\n", + " aggfunc=\"count\",\n", + " margins=True,\n", + ")\n", + "print(table)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "506135e0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "id": "8b603548", + "metadata": {}, + "source": [ + "### ACRO crosstab with aggregation function" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "83718cb1", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "-------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015|\n", + "year | | |\n", + "-------------------------------------|\n", + "2010 | ok | ok |\n", + "2011 | ok | ok |\n", + "2012 | ok | ok |\n", + "2013 | ok | ok |\n", + "2014 | ok | ok |\n", + "2015 | ok | ok |\n", + "-------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_5\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
year
20101320337.75015466672.0
20111295468.0007190086.5
20121270522.1257119017.5
20131325315.5007682584.0
20141282249.6258276287.5
20151608412.2508060488.5
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 1320337.750 15466672.0\n", + "2011 1295468.000 7190086.5\n", + "2012 1270522.125 7119017.5\n", + "2013 1325315.500 7682584.0\n", + "2014 1282249.625 8276287.5\n", + "2015 1608412.250 8060488.5" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, df.survivor, values=df.inc_grants, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "de4266cd-b4d4-417b-ae44-5d972e8bfdde", + "metadata": {}, + "source": [ + "### ACRO crosstab with multiple aggregation functions and totals" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "fb7abfc9-e428-4b71-9066-01ac9a08d655", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 14 cells may need suppressing; p-ratio: 4 cells may need suppressing; nk-rule: 2 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + " mean |std |\n", + "grant_type G N R R/G All |G N R R/G All|\n", + "year | |\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok | ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok|\n", + "2011 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2012 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2013 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2014 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2015 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "All ok ok ok ok ok | ok ok ok ok ok|\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_6\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
grant_typeGNRR/GAllGNRR/GAll
year
20109921906.00.0000008402284.011636000.08308286.51.855055e+070.0000003.059557e+071.701088e+072.727398e+07
20118502246.0124013.8593757716880.016047500.05303808.51.688595e+07205959.4929032.954322e+071.561638e+072.137658e+07
201211458580.0131859.0625006958050.516810000.05259893.52.061090e+07210476.5391752.721184e+071.646449e+072.026400e+07
201313557147.0147937.7968757202273.516765625.05605045.52.486844e+07203747.4170172.989833e+071.671112e+072.251787e+07
201413748147.0133198.2500008277525.017845750.06117054.53.134559e+07181865.9255803.546348e+071.741251e+072.641722e+07
201511133433.0146572.18750010812888.018278624.06509989.52.553919e+07201602.8008324.130935e+071.730471e+072.784636e+07
All11412787.0134431.8906258098502.016648273.05997796.52.283220e+07198873.7266563.204495e+071.583532e+072.405324e+07
\n", + "
" + ], + "text/plain": [ + " mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 9921906.0 0.000000 8402284.0 11636000.0 8308286.5 \n", + "2011 8502246.0 124013.859375 7716880.0 16047500.0 5303808.5 \n", + "2012 11458580.0 131859.062500 6958050.5 16810000.0 5259893.5 \n", + "2013 13557147.0 147937.796875 7202273.5 16765625.0 5605045.5 \n", + "2014 13748147.0 133198.250000 8277525.0 17845750.0 6117054.5 \n", + "2015 11133433.0 146572.187500 10812888.0 18278624.0 6509989.5 \n", + "All 11412787.0 134431.890625 8098502.0 16648273.0 5997796.5 \n", + "\n", + " std \\\n", + "grant_type G N R R/G \n", + "year \n", + "2010 1.855055e+07 0.000000 3.059557e+07 1.701088e+07 \n", + "2011 1.688595e+07 205959.492903 2.954322e+07 1.561638e+07 \n", + "2012 2.061090e+07 210476.539175 2.721184e+07 1.646449e+07 \n", + "2013 2.486844e+07 203747.417017 2.989833e+07 1.671112e+07 \n", + "2014 3.134559e+07 181865.925580 3.546348e+07 1.741251e+07 \n", + "2015 2.553919e+07 201602.800832 4.130935e+07 1.730471e+07 \n", + "All 2.283220e+07 198873.726656 3.204495e+07 1.583532e+07 \n", + "\n", + " \n", + "grant_type All \n", + "year \n", + "2010 2.727398e+07 \n", + "2011 2.137658e+07 \n", + "2012 2.026400e+07 \n", + "2013 2.251787e+07 \n", + "2014 2.641722e+07 \n", + "2015 2.784636e+07 \n", + "All 2.405324e+07 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.year, df.grant_type, values=df.inc_grants, aggfunc=[\"mean\", \"std\"], margins=True\n", + ")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "0aa99fbf", + "metadata": {}, + "source": [ + "### ACRO crosstab with missing values\n", + "- This is an example of a crosstab where there are missing values that have not been filled or dealt with in the data.\n", + "- Note that you need to change the value of the CHECK_MISSING_VALUES variable in the acro object to True. Then run the crosstab command.\n", + "- In this example, ten values in the column inc_grants were set to nan to represent missing data.\n", + "- In this version of acro checking the disclosiveness of an output with missing values is not supported.\n", + "- The status of the command will be \"review\" to indicate that the output needs to be checked by the output checker to review if the output is disclosive or not.\n", + "- In the outcome_df each cell with missing value/values will be shown as missing.\n", + "- The output hist will not be suppressed even if the suppress=True." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "bf132239", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |All|\n", + "year | | | | | |\n", + "--------------------------------------------------------------------------------|\n", + "2010 | ok | threshold; p-ratio; | ok | threshold; p-ratio; nk-rule; | ok|\n", + "2011 | ok | ok | ok | threshold; | ok|\n", + "2012 | ok | ok | ok | threshold; | ok|\n", + "2013 | ok | ok | ok | threshold; | ok|\n", + "2014 | ok | ok | ok | threshold; | ok|\n", + "2015 | ok | ok | ok | threshold; | ok|\n", + "All | ok | ok | ok | ok | ok|\n", + "--------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_7\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/GAll
year
20109921906.00.0000008420373.011636000.08320154.5
20118502246.0125663.2265627689140.016047500.05310392.0
201211458580.0131859.0625006896304.016810000.05220580.5
201313557147.0150488.4531257088095.516765625.05578657.0
201413748147.0135494.7812508118565.517845750.06072600.0
201511133433.0149143.62500010596385.018278624.06442131.0
All11412787.0136158.8593758006361.016648273.05968295.5
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.CHECK_MISSING_VALUES = True\n", + "\n", + "missing = df.inc_grants.copy()\n", + "missing[0:10] = np.nan\n", + "\n", + "safe_table = acro.crosstab(\n", + " df.year, df.grant_type, values=missing, aggfunc=\"mean\", margins=True\n", + ")\n", + "safe_table" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "7cc417a0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "utils.CHECK_MISSING_VALUES = False" + ] + }, + { + "cell_type": "markdown", + "id": "fcc81e98", + "metadata": {}, + "source": [ + "### ACRO crosstab with negative values" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "15bcdc7c", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): review; negative values found\n", + "INFO:acro:outcome_df:\n", + "----------------------------------------|\n", + "grant_type |G |N |R |R/G|\n", + "year | | | | |\n", + "----------------------------------------|\n", + "2010 | | | negative | |\n", + "2011 | | negative | negative | |\n", + "2012 | | | negative | |\n", + "2013 | | negative | negative | |\n", + "2014 | | negative | negative | |\n", + "2015 | | negative | negative | |\n", + "----------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_8\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
year
20109921906.00.0000008280032.511636000.0
20118502246.0123496.4453127577703.516047500.0
201211458580.0131859.0625006796357.516810000.0
201313557147.0147937.6250006988263.016765625.0
201413748147.0133198.0781257997392.017845750.0
201511133433.0146572.01562510388612.018278624.0
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "negative = df.inc_grants.copy()\n", + "negative[0:10] = -10\n", + "\n", + "safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "d66e565b", + "metadata": {}, + "source": [ + "### ACRO pivot_table" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "b13b5f7e", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------------------------------------------------------------|\n", + " inc_grants |\n", + "year 2010 2011 2012 2013 2014 2015 All|\n", + "grant_type |\n", + "--------------------------------------------------------------------------------------------------------------|\n", + "G ok ok ok ok ok ok ok|\n", + "N threshold; p-ratio; ok ok ok ok ok ok|\n", + "R ok ok ok ok ok ok ok|\n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; threshold; threshold; threshold; ok|\n", + "All ok ok ok ok ok ok ok|\n", + "--------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_9\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inc_grants
year201020112012201320142015All
grant_type
G138906688.0127533696.0171878704.0203357200.0206222208.0133601200.09.814997e+08
N0.07192804.07779685.08728330.07858697.08501187.04.006070e+07
R504137056.0532464704.0480105472.0511361408.0554594176.0551457280.03.134120e+09
R/G46544000.0128380000.0134480000.0134125000.0142766000.0146228992.07.325240e+08
All689587776.0795571264.0794243904.0857571968.0911441088.0839788672.04.888204e+09
\n", + "
" + ], + "text/plain": [ + " inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df,\n", + " index=[\"grant_type\"],\n", + " columns=[\"year\"],\n", + " values=[\"inc_grants\"],\n", + " margins=True,\n", + " aggfunc=\"sum\",\n", + ")\n", + "table" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f72162c8", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G ok | ok |\n", + "N ok | ok |\n", + "R ok | ok |\n", + "R/G ok | ok |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_10\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.344319e+051.988737e+05
R8.098502e+063.204495e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.344319e+05 1.988737e+05\n", + "R 8.098502e+06 3.204495e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "dc99fa71", + "metadata": {}, + "source": [ + "### ACRO pivot_table with missing values" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f3a87c20", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G ok | ok |\n", + "N ok | ok |\n", + "R ok | ok |\n", + "R/G ok | ok |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_11\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.364700e+051.999335e+05
R8.006361e+063.228216e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.364700e+05 1.999335e+05\n", + "R 8.006361e+06 3.228216e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.CHECK_MISSING_VALUES = True\n", + "\n", + "df.loc[0:10, \"inc_grants\"] = np.nan\n", + "\n", + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "8cabd236", + "metadata": {}, + "outputs": [], + "source": [ + "utils.CHECK_MISSING_VALUES = False" + ] + }, + { + "cell_type": "markdown", + "id": "b1f77749", + "metadata": {}, + "source": [ + "### ACRO pivot_table with negative values" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "864d39f4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): review; negative values found\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G | |\n", + "N negative | negative |\n", + "R negative | negative |\n", + "R/G | |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_12\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.341800e+051.990196e+05
R7.882230e+063.204558e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.loc[0:10, \"inc_grants\"] = -10\n", + "\n", + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "45ec04ef", + "metadata": {}, + "source": [ + "### ACRO OLS" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "d0724d08-6969-4f0a-8a32-e00d253f3597", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:ols() outcome: pass; dof=807.0 >= 10\n", + "INFO:acro:records:add(): output_13\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: inc_activity R-squared: 0.894
Model: OLS Adj. R-squared: 0.893
Method: Least Squares F-statistic: 2261.
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00
Time: 19:43:34 Log-Likelihood: -14495.
No. Observations: 811 AIC: 2.900e+04
Df Residuals: 807 BIC: 2.902e+04
Df Model: 3
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06
inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836
inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633
total_costs 0.8313 0.011 78.674 0.000 0.811 0.852
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 1339.956 Durbin-Watson: 1.414
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706
Skew: 9.899 Prob(JB): 0.00
Kurtosis: 194.566 Cond. No. 1.05e+08


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.05e+08. This might indicate that there are
strong multicollinearity or other numerical problems." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & inc\\_activity & \\textbf{ R-squared: } & 0.894 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.893 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 2261. \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.00 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -14495. \\\\\n", + "\\textbf{No. Observations:} & 811 & \\textbf{ AIC: } & 2.900e+04 \\\\\n", + "\\textbf{Df Residuals:} & 807 & \\textbf{ BIC: } & 2.902e+04 \\\\\n", + "\\textbf{Df Model:} & 3 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 3.01e+05 & 5.33e+05 & 0.565 & 0.572 & -7.45e+05 & 1.35e+06 \\\\\n", + "\\textbf{inc\\_grants} & -0.8846 & 0.025 & -35.956 & 0.000 & -0.933 & -0.836 \\\\\n", + "\\textbf{inc\\_donations} & -0.6647 & 0.016 & -40.721 & 0.000 & -0.697 & -0.633 \\\\\n", + "\\textbf{total\\_costs} & 0.8313 & 0.011 & 78.674 & 0.000 & 0.811 & 0.852 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 1339.956 & \\textbf{ Durbin-Watson: } & 1.414 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1253317.706 \\\\\n", + "\\textbf{Skew:} & 9.899 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 194.566 & \\textbf{ Cond. No. } & 1.05e+08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified. \\newline\n", + " [2] The condition number is large, 1.05e+08. This might indicate that there are \\newline\n", + " strong multicollinearity or other numerical problems." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: inc_activity R-squared: 0.894\n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00\n", + "Time: 19:43:34 Log-Likelihood: -14495.\n", + "No. Observations: 811 AIC: 2.900e+04\n", + "Df Residuals: 807 BIC: 2.902e+04\n", + "Df Model: 3 \n", + "Covariance Type: nonrobust \n", + "=================================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852\n", + "==============================================================================\n", + "Omnibus: 1339.956 Durbin-Watson: 1.414\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706\n", + "Skew: 9.899 Prob(JB): 0.00\n", + "Kurtosis: 194.566 Cond. No. 1.05e+08\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "[2] The condition number is large, 1.05e+08. This might indicate that there are\n", + "strong multicollinearity or other numerical problems.\n", + "\"\"\"" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df = df[[\"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"inc_activity\"]\n", + "x = new_df[[\"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.ols(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c826271", + "metadata": {}, + "source": [ + "### ACRO OLSR" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "cc90f7c9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:olsr() outcome: pass; dof=807.0 >= 10\n", + "INFO:acro:records:add(): output_14\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: inc_activity R-squared: 0.894
Model: OLS Adj. R-squared: 0.893
Method: Least Squares F-statistic: 2261.
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00
Time: 19:43:34 Log-Likelihood: -14495.
No. Observations: 811 AIC: 2.900e+04
Df Residuals: 807 BIC: 2.902e+04
Df Model: 3
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
Intercept 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06
inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836
inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633
total_costs 0.8313 0.011 78.674 0.000 0.811 0.852
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 1339.956 Durbin-Watson: 1.414
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706
Skew: 9.899 Prob(JB): 0.00
Kurtosis: 194.566 Cond. No. 1.05e+08


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.05e+08. This might indicate that there are
strong multicollinearity or other numerical problems." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & inc\\_activity & \\textbf{ R-squared: } & 0.894 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.893 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 2261. \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.00 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -14495. \\\\\n", + "\\textbf{No. Observations:} & 811 & \\textbf{ AIC: } & 2.900e+04 \\\\\n", + "\\textbf{Df Residuals:} & 807 & \\textbf{ BIC: } & 2.902e+04 \\\\\n", + "\\textbf{Df Model:} & 3 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{Intercept} & 3.01e+05 & 5.33e+05 & 0.565 & 0.572 & -7.45e+05 & 1.35e+06 \\\\\n", + "\\textbf{inc\\_grants} & -0.8846 & 0.025 & -35.956 & 0.000 & -0.933 & -0.836 \\\\\n", + "\\textbf{inc\\_donations} & -0.6647 & 0.016 & -40.721 & 0.000 & -0.697 & -0.633 \\\\\n", + "\\textbf{total\\_costs} & 0.8313 & 0.011 & 78.674 & 0.000 & 0.811 & 0.852 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 1339.956 & \\textbf{ Durbin-Watson: } & 1.414 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1253317.706 \\\\\n", + "\\textbf{Skew:} & 9.899 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 194.566 & \\textbf{ Cond. No. } & 1.05e+08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified. \\newline\n", + " [2] The condition number is large, 1.05e+08. This might indicate that there are \\newline\n", + " strong multicollinearity or other numerical problems." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: inc_activity R-squared: 0.894\n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00\n", + "Time: 19:43:34 Log-Likelihood: -14495.\n", + "No. Observations: 811 AIC: 2.900e+04\n", + "Df Residuals: 807 BIC: 2.902e+04\n", + "Df Model: 3 \n", + "Covariance Type: nonrobust \n", + "=================================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "Intercept 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852\n", + "==============================================================================\n", + "Omnibus: 1339.956 Durbin-Watson: 1.414\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706\n", + "Skew: 9.899 Prob(JB): 0.00\n", + "Kurtosis: 194.566 Cond. No. 1.05e+08\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "[2] The condition number is large, 1.05e+08. This might indicate that there are\n", + "strong multicollinearity or other numerical problems.\n", + "\"\"\"" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.olsr(\n", + " formula=\"inc_activity ~ inc_grants + inc_donations + total_costs\", data=new_df\n", + ")\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c489203", + "metadata": {}, + "source": [ + "### ACRO Probit" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "5b1a1611", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:probit() outcome: pass; dof=806.0 >= 10\n", + "INFO:acro:records:add(): output_15\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.493791\n", + " Iterations 10\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Probit Regression Results
Dep. Variable: survivor No. Observations: 811
Model: Probit Df Residuals: 806
Method: MLE Df Model: 4
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2140
Time: 19:43:34 Log-Likelihood: -400.46
converged: True LL-Null: -509.50
Covariance Type: nonrobust LLR p-value: 4.875e-46
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const 0.0474 0.057 0.838 0.402 -0.063 0.158
inc_activity 1.836e-07 5.16e-08 3.559 0.000 8.25e-08 2.85e-07
inc_grants 8.576e-08 3.9e-08 2.197 0.028 9.25e-09 1.62e-07
inc_donations 2.406e-07 4.54e-08 5.297 0.000 1.52e-07 3.3e-07
total_costs -8.644e-08 3.68e-08 -2.351 0.019 -1.59e-07 -1.44e-08


Possibly complete quasi-separation: A fraction 0.18 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & survivor & \\textbf{ No. Observations: } & 811 \\\\\n", + "\\textbf{Model:} & Probit & \\textbf{ Df Residuals: } & 806 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 4 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 0.2140 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -400.46 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -509.50 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 4.875e-46 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 0.0474 & 0.057 & 0.838 & 0.402 & -0.063 & 0.158 \\\\\n", + "\\textbf{inc\\_activity} & 1.836e-07 & 5.16e-08 & 3.559 & 0.000 & 8.25e-08 & 2.85e-07 \\\\\n", + "\\textbf{inc\\_grants} & 8.576e-08 & 3.9e-08 & 2.197 & 0.028 & 9.25e-09 & 1.62e-07 \\\\\n", + "\\textbf{inc\\_donations} & 2.406e-07 & 4.54e-08 & 5.297 & 0.000 & 1.52e-07 & 3.3e-07 \\\\\n", + "\\textbf{total\\_costs} & -8.644e-08 & 3.68e-08 & -2.351 & 0.019 & -1.59e-07 & -1.44e-08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Probit Regression Results}\n", + "\\end{center}\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be \\newline\n", + " perfectly predicted. This might indicate that there is complete \\newline\n", + " quasi-separation. In this case some parameters will not be identified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Probit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: survivor No. Observations: 811\n", + "Model: Probit Df Residuals: 806\n", + "Method: MLE Df Model: 4\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2140\n", + "Time: 19:43:34 Log-Likelihood: -400.46\n", + "converged: True LL-Null: -509.50\n", + "Covariance Type: nonrobust LLR p-value: 4.875e-46\n", + "=================================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 0.0474 0.057 0.838 0.402 -0.063 0.158\n", + "inc_activity 1.836e-07 5.16e-08 3.559 0.000 8.25e-08 2.85e-07\n", + "inc_grants 8.576e-08 3.9e-08 2.197 0.028 9.25e-09 1.62e-07\n", + "inc_donations 2.406e-07 4.54e-08 5.297 0.000 1.52e-07 3.3e-07\n", + "total_costs -8.644e-08 3.68e-08 -2.351 0.019 -1.59e-07 -1.44e-08\n", + "=================================================================================\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be\n", + "perfectly predicted. This might indicate that there is complete\n", + "quasi-separation. In this case some parameters will not be identified.\n", + "\"\"\"" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df = df[[\"survivor\", \"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"survivor\"].astype(\"category\").cat.codes # numeric\n", + "y.name = \"survivor\"\n", + "x = new_df[[\"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.probit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "22efa3df", + "metadata": {}, + "source": [ + "### ACRO Logit" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "dcf30f8f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:logit() outcome: pass; dof=806.0 >= 10\n", + "INFO:acro:records:add(): output_16\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.490836\n", + " Iterations 12\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: survivor No. Observations: 811
Model: Logit Df Residuals: 806
Method: MLE Df Model: 4
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2187
Time: 19:43:35 Log-Likelihood: -398.07
converged: True LL-Null: -509.50
Covariance Type: nonrobust LLR p-value: 4.532e-47
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const 0.0512 0.091 0.561 0.575 -0.128 0.230
inc_activity 2.981e-07 8.95e-08 3.330 0.001 1.23e-07 4.74e-07
inc_grants 1.351e-07 6.67e-08 2.026 0.043 4.39e-09 2.66e-07
inc_donations 5.123e-07 1.04e-07 4.927 0.000 3.08e-07 7.16e-07
total_costs -1.442e-07 6.26e-08 -2.304 0.021 -2.67e-07 -2.15e-08


Possibly complete quasi-separation: A fraction 0.18 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & survivor & \\textbf{ No. Observations: } & 811 \\\\\n", + "\\textbf{Model:} & Logit & \\textbf{ Df Residuals: } & 806 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 4 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 0.2187 \\\\\n", + "\\textbf{Time:} & 19:43:35 & \\textbf{ Log-Likelihood: } & -398.07 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -509.50 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 4.532e-47 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 0.0512 & 0.091 & 0.561 & 0.575 & -0.128 & 0.230 \\\\\n", + "\\textbf{inc\\_activity} & 2.981e-07 & 8.95e-08 & 3.330 & 0.001 & 1.23e-07 & 4.74e-07 \\\\\n", + "\\textbf{inc\\_grants} & 1.351e-07 & 6.67e-08 & 2.026 & 0.043 & 4.39e-09 & 2.66e-07 \\\\\n", + "\\textbf{inc\\_donations} & 5.123e-07 & 1.04e-07 & 4.927 & 0.000 & 3.08e-07 & 7.16e-07 \\\\\n", + "\\textbf{total\\_costs} & -1.442e-07 & 6.26e-08 & -2.304 & 0.021 & -2.67e-07 & -2.15e-08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Logit Regression Results}\n", + "\\end{center}\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be \\newline\n", + " perfectly predicted. This might indicate that there is complete \\newline\n", + " quasi-separation. In this case some parameters will not be identified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: survivor No. Observations: 811\n", + "Model: Logit Df Residuals: 806\n", + "Method: MLE Df Model: 4\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2187\n", + "Time: 19:43:35 Log-Likelihood: -398.07\n", + "converged: True LL-Null: -509.50\n", + "Covariance Type: nonrobust LLR p-value: 4.532e-47\n", + "=================================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 0.0512 0.091 0.561 0.575 -0.128 0.230\n", + "inc_activity 2.981e-07 8.95e-08 3.330 0.001 1.23e-07 4.74e-07\n", + "inc_grants 1.351e-07 6.67e-08 2.026 0.043 4.39e-09 2.66e-07\n", + "inc_donations 5.123e-07 1.04e-07 4.927 0.000 3.08e-07 7.16e-07\n", + "total_costs -1.442e-07 6.26e-08 -2.304 0.021 -2.67e-07 -2.15e-08\n", + "=================================================================================\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be\n", + "perfectly predicted. This might indicate that there is complete\n", + "quasi-separation. In this case some parameters will not be identified.\n", + "\"\"\"" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.logit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "3631a59d", + "metadata": {}, + "source": [ + "### ACRO Histogram without suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "af2f4313", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:status: fail\n", + "INFO:acro:records:add(): output_17\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "acro_artifacts/histogram_0.png\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAHDCAYAAAAOZuFZAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAANSRJREFUeJzt3X9UVPed//HXCMMgBFAxzkhDDHEx1kisxQSlSaRFsDZGrd2aLWljG7O1NbGl6FqNzWZ0E4xsF+lioptdjlJdSrZNyLZn01SskdQaW/zVRNOatBoTslCSiICBDiPc7x/5crsjqAzC8EGej3PmnNzPfd97P/fNJby888thWZYlAAAAgwwb6AkAAABciIACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIMMdu3b5fD4dBbb7010FMxygsvvCCv1zvQ0wDw/zn4qHtgaHnvvff0pz/9SVOnTpXL5Rro6RjjoYce0pNPPin+lwiYIXygJwAgtK699lpde+21Az2NXvH7/XI4HAoP539dwNWOp3iAIebCp3gyMjI0efJkVVdX64477lBUVJRuvPFGPfHEE+ro6AjY9uzZs1qxYoVuvPFGuVwujRkzRp/73Of0hz/8ocfHtyxL+fn5GjdunCIjIzVt2jRVVlYqIyNDGRkZdt3evXvlcDi0Y8cOrVixQh/72Mfkcrn0xz/+Ue+9956WLVumSZMm6ZprrtGYMWP0mc98Rr/61a8CjvXWW2/J4XDo+9//vgoLC5WUlKRrrrlGM2bM0IEDB+y6r371q3ryySclSQ6Hw3509ujHP/6x0tLSFBcXZ/fn/vvvD6LrAILFP0MAqK6uTvfee69WrFihRx99VBUVFVqzZo0SEhJ03333SZKam5t1++2366233tJ3v/tdpaWl6dy5c3r55ZdVW1uriRMn9uhYa9eu1YYNG/T1r39dCxcu1DvvvKMHHnhAfr9fEyZM6FK/Zs0azZgxQ1u3btWwYcM0ZswYvffee5KkRx99VB6PR+fOnVNFRYUyMjL0y1/+MiDoSNKTTz6piRMnqqioSJL0yCOP6HOf+5xOnTqluLg4PfLII/rwww/1k5/8RK+88oq93dixY/XKK6/onnvu0T333COv16vIyEidPn1ae/bs6UWnAfSYBWBI2bZtmyXJOnXqlGVZljVz5kxLkvWb3/wmoG7SpEnW7Nmz7eX169dbkqzKyspeH/vMmTOWy+Wy7rnnnoDxV155xZJkzZw50x576aWXLEnWnXfeedn9nj9/3vL7/VZmZqb1+c9/3h4/deqUJclKSUmxzp8/b4//9re/tSRZP/rRj+yxBx980Oruf4nf//73LUnW2bNngzlVAFeIp3gAyOPx6LbbbgsYu+WWW3T69Gl7+ec//7kmTJigWbNm9fo4Bw4ckM/n06JFiwLGp0+frhtuuKHbbb7whS90O75161Z98pOfVGRkpMLDw+V0OvXLX/5Sv//977vU3nXXXQoLC7OXb7nlFkkKOL+LufXWWyVJixYt0n/913/p3Xffvew2AK4cAQWA4uPju4y5XC61trbay++9956uu+66KzrOBx98IElyu91d1nU3Jn30NMuFCgsL9c1vflNpaWl69tlndeDAAVVXV+uzn/1swJw7XXh+ne9e6q72Qnfeeaeef/55nT9/Xvfdd5+uu+46TZ48WT/60Y8uuy2A3uM1KAB65Nprr1VNTc0V7aMzKPz5z3/usq6urq7buygOh6PL2M6dO5WRkaEtW7YEjDc3N1/R/C5m/vz5mj9/vnw+nw4cOKANGzYoJydHN9xwg2bMmNEvxwSGOu6gAOiROXPm6I033riiF4empaXJ5XLpmWeeCRg/cOBAj55u6eRwOLp8hsurr74a8ALXYPXkrorL5dLMmTO1ceNGSdKRI0d6fTwAl8YdFAA9kpubq2eeeUbz58/X6tWrddttt6m1tVVVVVWaO3euPv3pT192H6NGjVJeXp42bNigkSNH6vOf/7xqamq0bt06jR07VsOG9ezfTHPnztU//dM/6dFHH9XMmTN14sQJrV+/XklJSTp//nyvzi8lJUWStHHjRs2ZM0dhYWG65ZZb9Nhjj6mmpkaZmZm67rrrdPbsWf3gBz+Q0+nUzJkze3UsAJdHQAHQIzExMdq3b5+8Xq+efvpprVu3TiNHjtStt96qr3/96z3ez+OPP67o6Ght3bpV27Zt08SJE7VlyxatXbtWI0aM6NE+1q5dq5aWFpWUlKigoECTJk3S1q1bVVFRob179/bq/HJycvTrX/9aTz31lNavXy/LsnTq1CmlpaXp4MGD+u53v6v33ntPI0aM0LRp07Rnzx7dfPPNvToWgMvjo+4BDLhTp05p4sSJevTRR/Xwww8P9HQAGICAAiCkfve73+lHP/qR0tPTFRsbqxMnTqigoEBNTU06duzYRd/NA2Bo4SkeAH2ivb39kl+053A4FBYWpujoaB08eFAlJSU6e/as4uLilJGRoccff5xwAsDGHRQAfSIjI0NVVVUXXT9u3Dj7u20A4HIIKAD6xIkTJy75OSQul8t+pwwAXA4BBQAAGCeoD2o7f/68vve97ykpKUnDhw/XjTfeqPXr1wd8JbtlWfJ6vUpISNDw4cOVkZGh48ePB+zH5/Np+fLlGj16tKKjozVv3rwr/oRKAABw9QjqRbIbN27U1q1bVVpaqptvvlkHDx7U1772NcXFxenb3/62JKmgoECFhYXavn27JkyYoMcee0xZWVk6ceKEYmJiJH30gU8/+9nPVF5ervj4eK1YsUJz587VoUOHAr7Q62I6Ojr0v//7v4qJien2Y7ABAIB5LMtSc3OzEhISLv/BjMF89fFdd91l3X///QFjCxcutL785S9blmVZHR0dlsfjsZ544gl7/V/+8hcrLi7O2rp1q2VZlnX27FnL6XRa5eXlds27775rDRs2zHrxxRe7Pe5f/vIXq7Gx0X68/vrrliQePHjw4MGDxyB8vPPOO5fNHEHdQbn99tu1detWvfHGG5owYYJ+97vfad++fSoqKpL00Yct1dXVKTs7296m87sr9u/fr6VLl+rQoUPy+/0BNQkJCZo8ebL279+v2bNndznuhg0btG7dui7j//Ef/6GoqKhgTgEAAAyQlpYWPfDAA/YzKpcSVED57ne/q8bGRk2cOFFhYWFqb2/X448/ri996UuSPvo2Uqnr16a73W77i8Dq6uoUERGhkSNHdqnp3P5Ca9asUV5enr3c1NSkxMRELViwQLGxscGcwmX5/X5VVlYqKytLTqezT/eNv6LPoUOvQ4M+hw69Do3+6HNTU5MeeOCBHr08I6iA8swzz2jnzp0qKyvTzTffrKNHjyo3N1cJCQlavHixXXfhgS3LuuxkLlXjcrm6fHOpJDmdzn67OPtz3/gr+hw69Do06HPo0OvQ6Ms+B7OfoALKP/zDP2j16tX6u7/7O0kfffvn6dOntWHDBi1evFgej0fSR3dJxo4da29XX19v31XxeDxqa2tTQ0NDwF2U+vp6paenBzMdAABwlQrqbcYtLS1dXnUbFhZmv804KSlJHo9HlZWV9vq2tjZVVVXZ4SM1NVVOpzOgpra2VseOHSOgAAAASUHeQbn77rv1+OOP6/rrr9fNN9+sI0eOqLCwUPfff7+kj57ayc3NVX5+vpKTk5WcnKz8/HxFRUUpJydHkhQXF6clS5ZoxYoVio+P16hRo7Ry5UqlpKRo1qxZfX+GAABg0AkqoBQXF+uRRx7RsmXLVF9fr4SEBC1dulT/+I//aNesWrVKra2tWrZsmRoaGpSWlqZdu3YFvGJ306ZNCg8P16JFi9Ta2qrMzExt3769R5+BAgAArn5BBZSYmBgVFRXZbyvujsPhkNfrldfrvWhNZGSkiouLVVxcHMzhAQDAEBHUa1AAAABCgYACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADBOUB/UNpRM9v5CvvbLfx20Kd564q6BngIAAH2GOygAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGCcoALKDTfcIIfD0eXx4IMPSpIsy5LX61VCQoKGDx+ujIwMHT9+PGAfPp9Py5cv1+jRoxUdHa158+appqam784IAAAMekEFlOrqatXW1tqPyspKSdIXv/hFSVJBQYEKCwu1efNmVVdXy+PxKCsrS83NzfY+cnNzVVFRofLycu3bt0/nzp3T3Llz1d7e3oenBQAABrPwYIqvvfbagOUnnnhC48eP18yZM2VZloqKirR27VotXLhQklRaWiq3262ysjItXbpUjY2NKikp0Y4dOzRr1ixJ0s6dO5WYmKjdu3dr9uzZ3R7X5/PJ5/PZy01NTZIkv98vv98fzClcVuf+XMOsPt1vf+vrPvS3zvkOtnkPRvQ6NOhz6NDr0OiPPgezL4dlWb36S9zW1qaEhATl5eXp4Ycf1smTJzV+/HgdPnxYU6dOtevmz5+vESNGqLS0VHv27FFmZqbOnDmjkSNH2jVTpkzRggULtG7dum6P5fV6u11XVlamqKio3kwfAACEWEtLi3JyctTY2KjY2NhL1gZ1B+X/ev7553X27Fl99atflSTV1dVJktxud0Cd2+3W6dOn7ZqIiIiAcNJZ07l9d9asWaO8vDx7uampSYmJicrOzr7sCQbL7/ersrJSjxwcJl+Ho0/33Z+Oebu/+2Sqzj5nZWXJ6XQO9HSuavQ6NOhz6NDr0OiPPnc+A9ITvQ4oJSUlmjNnjhISEgLGHY7AP+qWZXUZu9Dlalwul1wuV5dxp9PZbxenr8MhX/vgCSiD9Ze0P3+GCESvQ4M+hw69Do2+7HMw++nV24xPnz6t3bt364EHHrDHPB6PJHW5E1JfX2/fVfF4PGpra1NDQ8NFawAAAHoVULZt26YxY8borrvusseSkpLk8Xjsd/ZIH71OpaqqSunp6ZKk1NRUOZ3OgJra2lodO3bMrgEAAAj6KZ6Ojg5t27ZNixcvVnj4Xzd3OBzKzc1Vfn6+kpOTlZycrPz8fEVFRSknJ0eSFBcXpyVLlmjFihWKj4/XqFGjtHLlSqWkpNjv6gEAAAg6oOzevVtvv/227r///i7rVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY9ds2rRJ4eHhWrRokVpbW5WZmant27crLCzsys4EAABcNYIOKNnZ2brYO5MdDoe8Xq+8Xu9Ft4+MjFRxcbGKi4uDPTQAABgi+C4eAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABgn6IDy7rvv6stf/rLi4+MVFRWlT3ziEzp06JC93rIseb1eJSQkaPjw4crIyNDx48cD9uHz+bR8+XKNHj1a0dHRmjdvnmpqaq78bAAAwFUhqIDS0NCgT33qU3I6nfr5z3+u119/Xf/yL/+iESNG2DUFBQUqLCzU5s2bVV1dLY/Ho6ysLDU3N9s1ubm5qqioUHl5ufbt26dz585p7ty5am9v77MTAwAAg1d4MMUbN25UYmKitm3bZo/dcMMN9n9blqWioiKtXbtWCxculCSVlpbK7XarrKxMS5cuVWNjo0pKSrRjxw7NmjVLkrRz504lJiZq9+7dmj17dpfj+nw++Xw+e7mpqUmS5Pf75ff7gzmFy+rcn2uY1af77W993Yf+1jnfwTbvwYhehwZ9Dh16HRr90edg9uWwLKvHf4knTZqk2bNnq6amRlVVVfrYxz6mZcuW6e///u8lSSdPntT48eN1+PBhTZ061d5u/vz5GjFihEpLS7Vnzx5lZmbqzJkzGjlypF0zZcoULViwQOvWretyXK/X2+14WVmZoqKienyyAABg4LS0tCgnJ0eNjY2KjY29ZG1Qd1BOnjypLVu2KC8vTw8//LB++9vf6lvf+pZcLpfuu+8+1dXVSZLcbnfAdm63W6dPn5Yk1dXVKSIiIiCcdNZ0bn+hNWvWKC8vz15uampSYmKisrOzL3uCwfL7/aqsrNQjB4fJ1+Ho0333p2PerneeTNbZ56ysLDmdzoGezlWNXocGfQ4deh0a/dHnzmdAeiKogNLR0aFp06YpPz9fkjR16lQdP35cW7Zs0X333WfXORyBf9gty+oydqFL1bhcLrlcri7jTqez3y5OX4dDvvbBE1AG6y9pf/4MEYhehwZ9Dh16HRp92edg9hPUi2THjh2rSZMmBYx9/OMf19tvvy1J8ng8ktTlTkh9fb19V8Xj8aitrU0NDQ0XrQEAAENbUAHlU5/6lE6cOBEw9sYbb2jcuHGSpKSkJHk8HlVWVtrr29raVFVVpfT0dElSamqqnE5nQE1tba2OHTtm1wAAgKEtqKd4vvOd7yg9PV35+flatGiRfvvb3+rpp5/W008/Lemjp3Zyc3OVn5+v5ORkJScnKz8/X1FRUcrJyZEkxcXFacmSJVqxYoXi4+M1atQorVy5UikpKfa7egAAwNAWVEC59dZbVVFRoTVr1mj9+vVKSkpSUVGR7r33Xrtm1apVam1t1bJly9TQ0KC0tDTt2rVLMTExds2mTZsUHh6uRYsWqbW1VZmZmdq+fbvCwsL67swAAMCgFVRAkaS5c+dq7ty5F13vcDjk9Xrl9XovWhMZGani4mIVFxcHe3gAADAE8F08AADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADBOUAHF6/XK4XAEPDwej73esix5vV4lJCRo+PDhysjI0PHjxwP24fP5tHz5co0ePVrR0dGaN2+eampq+uZsAADAVSHoOyg333yzamtr7cdrr71mrysoKFBhYaE2b96s6upqeTweZWVlqbm52a7Jzc1VRUWFysvLtW/fPp07d05z585Ve3t735wRAAAY9MKD3iA8POCuSSfLslRUVKS1a9dq4cKFkqTS0lK53W6VlZVp6dKlamxsVElJiXbs2KFZs2ZJknbu3KnExETt3r1bs2fP7vaYPp9PPp/PXm5qapIk+f1++f3+YE/hkjr35xpm9el++1tf96G/dc53sM17MKLXoUGfQ4deh0Z/9DmYfTksy+rxX2Kv16t//ud/VlxcnFwul9LS0pSfn68bb7xRJ0+e1Pjx43X48GFNnTrV3mb+/PkaMWKESktLtWfPHmVmZurMmTMaOXKkXTNlyhQtWLBA69atu+hxu1tXVlamqKioHp8sAAAYOC0tLcrJyVFjY6NiY2MvWRvUHZS0tDT98Ic/1IQJE/TnP/9Zjz32mNLT03X8+HHV1dVJktxud8A2brdbp0+fliTV1dUpIiIiIJx01nRu3501a9YoLy/PXm5qalJiYqKys7Mve4LB8vv9qqys1CMHh8nX4ejTffenY97u7z6ZqrPPWVlZcjqdAz2dqxq9Dg36HDr0OjT6o8+dz4D0RFABZc6cOfZ/p6SkaMaMGRo/frxKS0s1ffp0SZLDEfhH3bKsLmMXulyNy+WSy+XqMu50Ovvt4vR1OORrHzwBZbD+kvbnzxCB6HVo0OfQodeh0Zd9DmY/V/Q24+joaKWkpOjNN9+0X5dy4Z2Q+vp6+66Kx+NRW1ubGhoaLloDAABwRQHF5/Pp97//vcaOHaukpCR5PB5VVlba69va2lRVVaX09HRJUmpqqpxOZ0BNbW2tjh07ZtcAAAAE9RTPypUrdffdd+v6669XfX29HnvsMTU1NWnx4sVyOBzKzc1Vfn6+kpOTlZycrPz8fEVFRSknJ0eSFBcXpyVLlmjFihWKj4/XqFGjtHLlSqWkpNjv6gEAAAgqoNTU1OhLX/qS3n//fV177bWaPn26Dhw4oHHjxkmSVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY+9j06ZNCg8P16JFi9Ta2qrMzExt375dYWFhfXtmAABg0AoqoJSXl19yvcPhkNfrldfrvWhNZGSkiouLVVxcHMyhAQDAEMJ38QAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOFcUUDZs2CCHw6Hc3Fx7zLIseb1eJSQkaPjw4crIyNDx48cDtvP5fFq+fLlGjx6t6OhozZs3TzU1NVcyFQAAcBXpdUCprq7W008/rVtuuSVgvKCgQIWFhdq8ebOqq6vl8XiUlZWl5uZmuyY3N1cVFRUqLy/Xvn37dO7cOc2dO1ft7e29PxMAAHDVCO/NRufOndO9996rf//3f9djjz1mj1uWpaKiIq1du1YLFy6UJJWWlsrtdqusrExLly5VY2OjSkpKtGPHDs2aNUuStHPnTiUmJmr37t2aPXt2l+P5fD75fD57uampSZLk9/vl9/t7cwoX1bk/1zCrT/fb3/q6D/2tc76Dbd6DEb0ODfocOvQ6NPqjz8Hsy2FZVtB/iRcvXqxRo0Zp06ZNysjI0Cc+8QkVFRXp5MmTGj9+vA4fPqypU6fa9fPnz9eIESNUWlqqPXv2KDMzU2fOnNHIkSPtmilTpmjBggVat25dl+N5vd5ux8vKyhQVFRXs9AEAwABoaWlRTk6OGhsbFRsbe8naoO+glJeX6/Dhw6quru6yrq6uTpLkdrsDxt1ut06fPm3XREREBISTzprO7S+0Zs0a5eXl2ctNTU1KTExUdnb2ZU8wWH6/X5WVlXrk4DD5Ohx9uu/+dMzb9c6TyTr7nJWVJafTOdDTuarR69Cgz6FDr0OjP/rc+QxITwQVUN555x19+9vf1q5duxQZGXnROocj8A+7ZVldxi50qRqXyyWXy9Vl3Ol09tvF6etwyNc+eALKYP0l7c+fIQLR69Cgz6FDr0OjL/sczH6CepHsoUOHVF9fr9TUVIWHhys8PFxVVVX613/9V4WHh9t3Ti68E1JfX2+v83g8amtrU0NDw0VrAADA0BZUQMnMzNRrr72mo0eP2o9p06bp3nvv1dGjR3XjjTfK4/GosrLS3qatrU1VVVVKT0+XJKWmpsrpdAbU1NbW6tixY3YNAAAY2oJ6iicmJkaTJ08OGIuOjlZ8fLw9npubq/z8fCUnJys5OVn5+fmKiopSTk6OJCkuLk5LlizRihUrFB8fr1GjRmnlypVKSUmx39UDAACGtl69zfhSVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY9ds2rRJ4eHhWrRokVpbW5WZmant27crLCysr6cDAAAGoSsOKHv37g1Ydjgc8nq98nq9F90mMjJSxcXFKi4uvtLDAwCAqxDfxQMAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4wQVULZs2aJbbrlFsbGxio2N1YwZM/Tzn//cXm9ZlrxerxISEjR8+HBlZGTo+PHjAfvw+Xxavny5Ro8erejoaM2bN081NTV9czYAAOCqEFRAue666/TEE0/o4MGDOnjwoD7zmc9o/vz5dggpKChQYWGhNm/erOrqank8HmVlZam5udneR25urioqKlReXq59+/bp3Llzmjt3rtrb2/v2zAAAwKAVHkzx3XffHbD8+OOPa8uWLTpw4IAmTZqkoqIirV27VgsXLpQklZaWyu12q6ysTEuXLlVjY6NKSkq0Y8cOzZo1S5K0c+dOJSYmavfu3Zo9e3a3x/X5fPL5fPZyU1OTJMnv98vv9wdzCpfVuT/XMKtP99vf+roP/a1zvoNt3oMRvQ4N+hw69Do0+qPPwezLYVlWr/4St7e368c//rEWL16sI0eOKDIyUuPHj9fhw4c1depUu27+/PkaMWKESktLtWfPHmVmZurMmTMaOXKkXTNlyhQtWLBA69at6/ZYXq+323VlZWWKiorqzfQBAECItbS0KCcnR42NjYqNjb1kbVB3UCTptdde04wZM/SXv/xF11xzjSoqKjRp0iTt379fkuR2uwPq3W63Tp8+LUmqq6tTREREQDjprKmrq7voMdesWaO8vDx7uampSYmJicrOzr7sCQbL7/ersrJSjxwcJl+Ho0/33Z+Oebu/+2Sqzj5nZWXJ6XQO9HSuavQ6NOhz6NDr0OiPPnc+A9ITQQeUm266SUePHtXZs2f17LPPavHixaqqqrLXOxyBf9Qty+oydqHL1bhcLrlcri7jTqez3y5OX4dDvvbBE1AG6y9pf/4MEYhehwZ9Dh16HRp92edg9hP024wjIiL0N3/zN5o2bZo2bNigKVOm6Ac/+IE8Ho8kdbkTUl9fb99V8Xg8amtrU0NDw0VrAAAArvhzUCzLks/nU1JSkjwejyorK+11bW1tqqqqUnp6uiQpNTVVTqczoKa2tlbHjh2zawAAAIJ6iufhhx/WnDlzlJiYqObmZpWXl2vv3r168cUX5XA4lJubq/z8fCUnJys5OVn5+fmKiopSTk6OJCkuLk5LlizRihUrFB8fr1GjRmnlypVKSUmx39UDAAAQVED585//rK985Suqra1VXFycbrnlFr344ovKysqSJK1atUqtra1atmyZGhoalJaWpl27dikmJsbex6ZNmxQeHq5FixaptbVVmZmZ2r59u8LCwvr2zAAAwKAVVEApKSm55HqHwyGv1yuv13vRmsjISBUXF6u4uDiYQwMAgCGE7+IBAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHGCCigbNmzQrbfeqpiYGI0ZM0YLFizQiRMnAmosy5LX61VCQoKGDx+ujIwMHT9+PKDG5/Np+fLlGj16tKKjozVv3jzV1NRc+dkAAICrQlABpaqqSg8++KAOHDigyspKnT9/XtnZ2frwww/tmoKCAhUWFmrz5s2qrq6Wx+NRVlaWmpub7Zrc3FxVVFSovLxc+/bt07lz5zR37ly1t7f33ZkBAIBBKzyY4hdffDFgedu2bRozZowOHTqkO++8U5ZlqaioSGvXrtXChQslSaWlpXK73SorK9PSpUvV2NiokpIS7dixQ7NmzZIk7dy5U4mJidq9e7dmz57d5bg+n08+n89ebmpqkiT5/X75/f7gzvgyOvfnGmb16X77W1/3ob91znewzXswotehQZ9Dh16HRn/0OZh9OSzL6vVf4j/+8Y9KTk7Wa6+9psmTJ+vkyZMaP368Dh8+rKlTp9p18+fP14gRI1RaWqo9e/YoMzNTZ86c0ciRI+2aKVOmaMGCBVq3bl2X43i93m7Hy8rKFBUV1dvpAwCAEGppaVFOTo4aGxsVGxt7ydqg7qD8X5ZlKS8vT7fffrsmT54sSaqrq5Mkud3ugFq3263Tp0/bNREREQHhpLOmc/sLrVmzRnl5efZyU1OTEhMTlZ2dfdkTDJbf71dlZaUeOThMvg5Hn+67Px3zdr3zZLLOPmdlZcnpdA70dK5q9Do06HPo0OvQ6I8+dz4D0hO9DigPPfSQXn31Ve3bt6/LOocj8A+7ZVldxi50qRqXyyWXy9Vl3Ol09tvF6etwyNc+eALKYP0l7c+fIQLR69Cgz6FDr0OjL/sczH569Tbj5cuX66c//aleeuklXXfddfa4x+ORpC53Qurr6+27Kh6PR21tbWpoaLhoDQAAGNqCCiiWZemhhx7Sc889pz179igpKSlgfVJSkjwejyorK+2xtrY2VVVVKT09XZKUmpoqp9MZUFNbW6tjx47ZNQAAYGgL6imeBx98UGVlZfrv//5vxcTE2HdK4uLiNHz4cDkcDuXm5io/P1/JyclKTk5Wfn6+oqKilJOTY9cuWbJEK1asUHx8vEaNGqWVK1cqJSXFflcPAAAY2oIKKFu2bJEkZWRkBIxv27ZNX/3qVyVJq1atUmtrq5YtW6aGhgalpaVp165diomJses3bdqk8PBwLVq0SK2trcrMzNT27dsVFhZ2ZWcDAACuCkEFlJ68I9nhcMjr9crr9V60JjIyUsXFxSouLg7m8AAAYIjgu3gAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDhBB5SXX35Zd999txISEuRwOPT8888HrLcsS16vVwkJCRo+fLgyMjJ0/PjxgBqfz6fly5dr9OjRio6O1rx581RTU3NFJwIAAK4eQQeUDz/8UFOmTNHmzZu7XV9QUKDCwkJt3rxZ1dXV8ng8ysrKUnNzs12Tm5uriooKlZeXa9++fTp37pzmzp2r9vb23p8JAAC4aoQHu8GcOXM0Z86cbtdZlqWioiKtXbtWCxculCSVlpbK7XarrKxMS5cuVWNjo0pKSrRjxw7NmjVLkrRz504lJiZq9+7dmj179hWcDgAAuBoEHVAu5dSpU6qrq1N2drY95nK5NHPmTO3fv19Lly7VoUOH5Pf7A2oSEhI0efJk7d+/v9uA4vP55PP57OWmpiZJkt/vl9/v78tTsPfnGmb16X77W1/3ob91znewzXswotehQZ9Dh16HRn/0OZh99WlAqaurkyS53e6AcbfbrdOnT9s1ERERGjlyZJeazu0vtGHDBq1bt67L+K5duxQVFdUXU+/in6Z19Mt++8sLL7ww0FPolcrKyoGewpBBr0ODPocOvQ6NvuxzS0tLj2v7NKB0cjgcAcuWZXUZu9ClatasWaO8vDx7uampSYmJicrOzlZsbOyVT/j/8Pv9qqys1CMHh8nXcek5m+SYd3A9NdbZ56ysLDmdzoGezlWNXocGfQ4deh0a/dHnzmdAeqJPA4rH45H00V2SsWPH2uP19fX2XRWPx6O2tjY1NDQE3EWpr69Xenp6t/t1uVxyuVxdxp1OZ79dnL4Oh3ztgyegDNZf0v78GSIQvQ4N+hw69Do0+rLPweynTz8HJSkpSR6PJ+B2UFtbm6qqquzwkZqaKqfTGVBTW1urY8eOXTSgAACAoSXoOyjnzp3TH//4R3v51KlTOnr0qEaNGqXrr79eubm5ys/PV3JyspKTk5Wfn6+oqCjl5ORIkuLi4rRkyRKtWLFC8fHxGjVqlFauXKmUlBT7XT0AAGBoCzqgHDx4UJ/+9Kft5c7XhixevFjbt2/XqlWr1NraqmXLlqmhoUFpaWnatWuXYmJi7G02bdqk8PBwLVq0SK2trcrMzNT27dsVFhbWB6cEAAAGu6ADSkZGhizr4m/BdTgc8nq98nq9F62JjIxUcXGxiouLgz08AAAYAvguHgAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAME74QE8AfeOG1f8z0FMIiivMUsFtAz0LAICpuIMCAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABhnQAPKU089paSkJEVGRio1NVW/+tWvBnI6AADAEAP2SbLPPPOMcnNz9dRTT+lTn/qU/u3f/k1z5szR66+/ruuvv36gpoUQm+z9hXztjoGeRo+99cRdAz0FGGqy9xcquI1rGugrA3YHpbCwUEuWLNEDDzygj3/84yoqKlJiYqK2bNkyUFMCAACGGJA7KG1tbTp06JBWr14dMJ6dna39+/d3qff5fPL5fPZyY2OjJOnMmTPy+/19Oje/36+WlhaF+4epvWPw/CtosAnvsNTS0jHo+vw3K/9roKcQtH0r71RLS4s++OADOZ3OgZ7OVSvc/yHXdIgMxms6bcMvB3oKQeuPPjc3N0uSLMu6bO2ABJT3339f7e3tcrvdAeNut1t1dXVd6jds2KB169Z1GU9KSuq3OaL/5Qz0BIaIsf8y0DMYOrimQ4NrOjT6s8/Nzc2Ki4u7ZM2AfpuxwxH4rwzLsrqMSdKaNWuUl5dnL3d0dOjMmTOKj4/vtv5KNDU1KTExUe+8845iY2P7dN/4K/ocOvQ6NOhz6NDr0OiPPluWpebmZiUkJFy2dkACyujRoxUWFtblbkl9fX2XuyqS5HK55HK5AsZGjBjRn1NUbGwsF34I0OfQodehQZ9Dh16HRl/3+XJ3TjoNyItkIyIilJqaqsrKyoDxyspKpaenD8SUAACAQQbsKZ68vDx95Stf0bRp0zRjxgw9/fTTevvtt/WNb3xjoKYEAAAMMWAB5Z577tEHH3yg9evXq7a2VpMnT9YLL7ygcePGDdSUJH30dNKjjz7a5Skl9C36HDr0OjToc+jQ69AY6D47rJ681wcAACCE+C4eAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGGZIB5amnnlJSUpIiIyOVmpqqX/3qV5esr6qqUmpqqiIjI3XjjTdq69atIZrp4BZMn/fu3SuHw9Hl8Yc//CGEMx58Xn75Zd19991KSEiQw+HQ888/f9ltuJ57J9hec033zoYNG3TrrbcqJiZGY8aM0YIFC3TixInLbsd1HZze9DnU1/SQCyjPPPOMcnNztXbtWh05ckR33HGH5syZo7fffrvb+lOnTulzn/uc7rjjDh05ckQPP/ywvvWtb+nZZ58N8cwHl2D73OnEiROqra21H8nJySGa8eD04YcfasqUKdq8eXOP6rmeey/YXnfimg5OVVWVHnzwQR04cECVlZU6f/68srOz9eGHH150G67r4PWmz51Cdk1bQ8xtt91mfeMb3wgYmzhxorV69epu61etWmVNnDgxYGzp0qXW9OnT+22OV4Ng+/zSSy9ZkqyGhoYQzO7qJMmqqKi4ZA3Xc9/oSa+5pvtGfX29Jcmqqqq6aA3X9ZXrSZ9DfU0PqTsobW1tOnTokLKzswPGs7OztX///m63eeWVV7rUz549WwcPHpTf7++3uQ5mvelzp6lTp2rs2LHKzMzUSy+91J/THJK4nkOPa/rKNDY2SpJGjRp10Rqu6yvXkz53CtU1PaQCyvvvv6/29vYu35jsdru7fLNyp7q6um7rz58/r/fff7/f5jqY9abPY8eO1dNPP61nn31Wzz33nG666SZlZmbq5ZdfDsWUhwyu59Dhmr5ylmUpLy9Pt99+uyZPnnzROq7rK9PTPof6mh6w7+IZSA6HI2DZsqwuY5er724cgYLp80033aSbbrrJXp4xY4beeecdff/739edd97Zr/McarieQ4Nr+so99NBDevXVV7Vv377L1nJd915P+xzqa3pI3UEZPXq0wsLCuvwrvr6+vkv67uTxeLqtDw8PV3x8fL/NdTDrTZ+7M336dL355pt9Pb0hjet5YHFN99zy5cv105/+VC+99JKuu+66S9ZyXfdeMH3uTn9e00MqoERERCg1NVWVlZUB45WVlUpPT+92mxkzZnSp37Vrl6ZNmyan09lvcx3MetPn7hw5ckRjx47t6+kNaVzPA4tr+vIsy9JDDz2k5557Tnv27FFSUtJlt+G6Dl5v+tydfr2mQ/JSXIOUl5dbTqfTKikpsV5//XUrNzfXio6Ott566y3Lsixr9erV1le+8hW7/uTJk1ZUVJT1ne98x3r99detkpISy+l0Wj/5yU8G6hQGhWD7vGnTJquiosJ64403rGPHjlmrV6+2JFnPPvvsQJ3CoNDc3GwdOXLEOnLkiCXJKiwstI4cOWKdPn3asiyu574UbK+5pnvnm9/8phUXF2ft3bvXqq2ttR8tLS12Ddf1letNn0N9TQ+5gGJZlvXkk09a48aNsyIiIqxPfvKTAW+rWrx4sTVz5syA+r1791pTp061IiIirBtuuMHasmVLiGc8OAXT540bN1rjx4+3IiMjrZEjR1q333679T//8z8DMOvBpfNtfxc+Fi9ebFkW13NfCrbXXNO9012PJVnbtm2za7iur1xv+hzqa9rx/ycKAABgjCH1GhQAADA4EFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAALaXX35Zd999txISEuRwOPT8888HvY9f/OIXmj59umJiYnTttdfqC1/4gk6dOhXUPggoAADA9uGHH2rKlCnavHlzr7Y/efKk5s+fr8985jM6evSofvGLX+j999/XwoULg9oPnyQLAAC65XA4VFFRoQULFthjbW1t+t73vqf//M//1NmzZzV58mRt3LhRGRkZkqSf/OQn+tKXviSfz6dhwz66D/Kzn/1M8+fPl8/n6/EXOHIHBQAA9NjXvvY1/frXv1Z5ebleffVVffGLX9RnP/tZvfnmm5KkadOmKSwsTNu2bVN7e7saGxu1Y8cOZWdnB/Xt0txBAQAA3brwDsqf/vQnJScnq6amRgkJCXbdrFmzdNtttyk/P1/SR69j+eIXv6gPPvhA7e3tmjFjhl544QWNGDGix8fmDgoAAOiRw4cPy7IsTZgwQddcc439qKqq0p/+9CdJUl1dnR544AEtXrxY1dXVqqqqUkREhP72b/9WwdwTCe+vkwAAAFeXjo4OhYWF6dChQwoLCwtYd80110iSnnzyScXGxqqgoMBet3PnTiUmJuo3v/mNpk+f3qNjEVAAAECPTJ06Ve3t7aqvr9cdd9zRbU1LS0uX8NK53NHR0eNj8RQPAACwnTt3TkePHtXRo0clSadOndLRo0f19ttva8KECbr33nt133336bnnntOpU6dUXV2tjRs36oUXXpAk3XXXXaqurtb69ev15ptv6vDhw/ra176mcePGaerUqT2eBy+SBQAAtr179+rTn/50l/HFixdr+/bt8vv9euyxx/TDH/5Q7777ruLj4zVjxgytW7dOKSkpkqTy8nIVFBTojTfeUFRUlGbMmKGNGzdq4sSJPZ4HAQUAABiHp3gAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYJz/B3neUANYkvVvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "hist = acro.hist(df, \"inc_grants\")\n", + "print(hist)" + ] + }, + { + "cell_type": "markdown", + "id": "5faf9a98", + "metadata": {}, + "source": [ + "### ACRO Histogram with suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "349d8a29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:acro:Histogram will not be shown as the inc_grants column is disclosive.\n", + "INFO:acro:status: fail\n", + "INFO:acro:records:add(): output_18\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "acro.suppress = True\n", + "hist = acro.hist(df, \"inc_grants\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "ab0fe892", + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "id": "589fedc6", + "metadata": {}, + "source": [ + "### List current ACRO outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "ec960039", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 12, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 5]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, [df.survivor, df.grant_type])\n", + "summary: fail; threshold: 12 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 threshold; ok ok ok ok threshold; \n", + "2011 threshold; ok ok ok ok threshold; \n", + "2012 threshold; ok ok ok ok threshold; \n", + "2013 threshold; ok ok ok ok threshold; \n", + "2014 threshold; ok ok ok ok threshold; \n", + "2015 threshold; ok ok ok ok threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8]\n", + "timestamp: 2025-03-06T19:43:34.383332\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 12, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 5]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, [df.grant_type, df.survivor])\n", + "summary: fail; threshold: 12 cells may need suppressing; \n", + "outcome: grant_type G N R \\\n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 Dead_in_2015 \n", + "year \n", + "2010 threshold; ok ok ok \n", + "2011 threshold; ok ok ok \n", + "2012 threshold; ok ok ok \n", + "2013 threshold; ok ok ok \n", + "2014 threshold; ok ok ok \n", + "2015 threshold; ok ok ok \n", + "\n", + "grant_type R/G \n", + "survivor Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 ok threshold; \n", + "2011 ok threshold; \n", + "2012 ok threshold; \n", + "2013 ok threshold; \n", + "2014 ok threshold; \n", + "2015 ok threshold; \n", + "output: [grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 3 12 59 47 \n", + "2011 3 12 59 47 \n", + "2012 3 12 59 47 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 47 \n", + "2015 3 12 59 47 \n", + "\n", + "grant_type R/G \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 24 8 \n", + "2011 24 8 \n", + "2012 24 8 \n", + "2013 24 8 \n", + "2014 24 8 \n", + "2015 24 8 ]\n", + "timestamp: 2025-03-06T19:43:34.405157\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 1], [1, 0], [1, 1]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: acro.crosstab(mydata.year, mydata.survivor)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 threshold; threshold; \n", + "2011 threshold; threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2]\n", + "timestamp: 2025-03-06T19:43:34.423743\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=df.inc_grants, aggfunc=\"mean\")\n", + "summary: fail; threshold: 7 cells suppressed; p-ratio: 2 cells suppressed; nk-rule: 1 cells suppressed; \n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; \n", + "2011 ok ok ok threshold; \n", + "2012 ok ok ok threshold; \n", + "2013 ok ok ok threshold; \n", + "2014 ok ok ok threshold; \n", + "2015 ok ok ok threshold; \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 NaN 8402284.0 NaN\n", + "2011 8502246.0 124013.859375 7716880.0 NaN\n", + "2012 11458580.0 131859.062500 6958050.5 NaN\n", + "2013 13557147.0 147937.796875 7202273.5 NaN\n", + "2014 13748147.0 133198.250000 8277525.0 NaN\n", + "2015 11133433.0 146572.187500 10812888.0 NaN]\n", + "timestamp: 2025-03-06T19:43:34.454789\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 14, 'p-ratio': 8, 'nk-rule': 7, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 2], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 1], [5, 5]], 'p-ratio': [[0, 0], [0, 2], [0, 5], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]], 'nk-rule': [[0, 0], [0, 5], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.crosstab(\n", + "summary: fail; threshold: 14 cells may need suppressing; p-ratio: 8 cells may need suppressing; nk-rule: 7 cells may need suppressing; \n", + "outcome: grant_type G N \\\n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 threshold; p-ratio; nk-rule; ok threshold; p-ratio; \n", + "2011 threshold; p-ratio; nk-rule; ok ok \n", + "2012 threshold; p-ratio; nk-rule; ok ok \n", + "2013 threshold; p-ratio; nk-rule; ok ok \n", + "2014 threshold; p-ratio; nk-rule; ok ok \n", + "2015 threshold; p-ratio; nk-rule; threshold; ok \n", + "All ok ok ok \n", + "\n", + "grant_type R R/G All \n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 ok ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok threshold; ok \n", + "2012 ok ok threshold; ok \n", + "2013 ok ok threshold; ok \n", + "2014 ok ok threshold; ok \n", + "2015 ok ok threshold; ok \n", + "All ok ok ok ok \n", + "output: [grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 2 12 5 40 \n", + "2011 3 12 58 45 \n", + "2012 3 12 59 45 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 43 \n", + "2015 3 9 58 28 \n", + "All 17 69 298 248 \n", + "\n", + "grant_type R/G All \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 20 4 83 \n", + "2011 24 8 150 \n", + "2012 24 8 151 \n", + "2013 24 8 153 \n", + "2014 24 8 149 \n", + "2015 23 8 129 \n", + "All 139 44 815 ]\n", + "timestamp: 2025-03-06T19:43:34.523720\n", + "comments: [\"Empty columns: ('N', 'Dead in 2015'), ('R/G', 'Dead in 2015') were deleted.\"]\n", + "exception: \n", + "\n", + "uid: output_5\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.survivor, values=df.inc_grants, aggfunc=\"mean\")\n", + "summary: pass\n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 ok ok\n", + "2011 ok ok\n", + "2012 ok ok\n", + "2013 ok ok\n", + "2014 ok ok\n", + "2015 ok ok\n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 1320337.750 15466672.0\n", + "2011 1295468.000 7190086.5\n", + "2012 1270522.125 7119017.5\n", + "2013 1325315.500 7682584.0\n", + "2014 1282249.625 8276287.5\n", + "2015 1608412.250 8060488.5]\n", + "timestamp: 2025-03-06T19:43:34.555960\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 14, 'p-ratio': 4, 'nk-rule': 2, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [0, 6], [0, 8], [1, 3], [1, 8], [2, 3], [2, 8], [3, 3], [3, 8], [4, 3], [4, 8], [5, 3], [5, 8]], 'p-ratio': [[0, 1], [0, 3], [0, 6], [0, 8]], 'nk-rule': [[0, 3], [0, 8]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 14 cells may need suppressing; p-ratio: 4 cells may need suppressing; nk-rule: 2 cells may need suppressing; \n", + "outcome: mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok ok threshold; ok \n", + "2012 ok ok ok threshold; ok \n", + "2013 ok ok ok threshold; ok \n", + "2014 ok ok ok threshold; ok \n", + "2015 ok ok ok threshold; ok \n", + "All ok ok ok ok ok \n", + "\n", + " std \n", + "grant_type G N R R/G All \n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok ok threshold; ok \n", + "2012 ok ok ok threshold; ok \n", + "2013 ok ok ok threshold; ok \n", + "2014 ok ok ok threshold; ok \n", + "2015 ok ok ok threshold; ok \n", + "All ok ok ok ok ok \n", + "output: [ mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 9921906.0 0.000000 8402284.0 11636000.0 8308286.5 \n", + "2011 8502246.0 124013.859375 7716880.0 16047500.0 5303808.5 \n", + "2012 11458580.0 131859.062500 6958050.5 16810000.0 5259893.5 \n", + "2013 13557147.0 147937.796875 7202273.5 16765625.0 5605045.5 \n", + "2014 13748147.0 133198.250000 8277525.0 17845750.0 6117054.5 \n", + "2015 11133433.0 146572.187500 10812888.0 18278624.0 6509989.5 \n", + "All 11412787.0 134431.890625 8098502.0 16648273.0 5997796.5 \n", + "\n", + " std \\\n", + "grant_type G N R R/G \n", + "year \n", + "2010 1.855055e+07 0.000000 3.059557e+07 1.701088e+07 \n", + "2011 1.688595e+07 205959.492903 2.954322e+07 1.561638e+07 \n", + "2012 2.061090e+07 210476.539175 2.721184e+07 1.646449e+07 \n", + "2013 2.486844e+07 203747.417017 2.989833e+07 1.671112e+07 \n", + "2014 3.134559e+07 181865.925580 3.546348e+07 1.741251e+07 \n", + "2015 2.553919e+07 201602.800832 4.130935e+07 1.730471e+07 \n", + "All 2.283220e+07 198873.726656 3.204495e+07 1.583532e+07 \n", + "\n", + " \n", + "grant_type All \n", + "year \n", + "2010 2.727398e+07 \n", + "2011 2.137658e+07 \n", + "2012 2.026400e+07 \n", + "2013 2.251787e+07 \n", + "2014 2.641722e+07 \n", + "2015 2.784636e+07 \n", + "All 2.405324e+07 ]\n", + "timestamp: 2025-03-06T19:43:34.639249\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_7\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: grant_type G N R R/G All\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok\n", + "2011 ok ok ok threshold; ok\n", + "2012 ok ok ok threshold; ok\n", + "2013 ok ok ok threshold; ok\n", + "2014 ok ok ok threshold; ok\n", + "2015 ok ok ok threshold; ok\n", + "All ok ok ok ok ok\n", + "output: [grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5]\n", + "timestamp: 2025-03-06T19:43:34.695759\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_8\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 10, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [[0, 2], [1, 1], [1, 2], [2, 2], [3, 1], [3, 2], [4, 1], [4, 2], [5, 1], [5, 2]], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "summary: review; negative values found\n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 negative \n", + "2011 negative negative \n", + "2012 negative \n", + "2013 negative negative \n", + "2014 negative negative \n", + "2015 negative negative \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0]\n", + "timestamp: 2025-03-06T19:43:34.727227\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_9\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]], 'p-ratio': [[1, 0], [3, 0]], 'nk-rule': [[3, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: inc_grants \\\n", + "year 2010 2011 2012 \n", + "grant_type \n", + "G ok ok ok \n", + "N threshold; p-ratio; ok ok \n", + "R ok ok ok \n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; \n", + "All ok ok ok \n", + "\n", + " \n", + "year 2013 2014 2015 All \n", + "grant_type \n", + "G ok ok ok ok \n", + "N ok ok ok ok \n", + "R ok ok ok ok \n", + "R/G threshold; threshold; threshold; ok \n", + "All ok ok ok ok \n", + "output: [ inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 ]\n", + "timestamp: 2025-03-06T19:43:34.782873\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_10\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G ok ok\n", + "N ok ok\n", + "R ok ok\n", + "R/G ok ok\n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.344319e+05 1.988737e+05\n", + "R 8.098502e+06 3.204495e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.814054\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_11\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G ok ok\n", + "N ok ok\n", + "R ok ok\n", + "R/G ok ok\n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.364700e+05 1.999335e+05\n", + "R 8.006361e+06 3.228216e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.844023\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_12\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 4, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [[1, 0], [1, 1], [2, 0], [2, 1]], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: review; negative values found\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G \n", + "N negative negative\n", + "R negative negative\n", + "R/G \n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.872024\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_13\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'ols', 'dof': 807.0}\n", + "sdc: {}\n", + "command: results = acro.ols(y, x)\n", + "summary: pass; dof=807.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ inc_activity R-squared: 0.894\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000\n", + "Time: 19:43:34 Log-Likelihood: -14495.000\n", + "No. Observations: 811 AIC: 29000.000\n", + "Df Residuals: 807 BIC: 29020.000\n", + "Df Model: 3 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "const 301000.0000 533000.000 0.565 0.572 -745000.000 1350000.000\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852, 1339.956 Durbin-Watson: 1.414\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1.253318e+06\n", + "Skew: 9.899 Prob(JB): 0.000000e+00\n", + "Kurtosis: 194.566 Cond. No. 1.050000e+08]\n", + "timestamp: 2025-03-06T19:43:34.926753\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_14\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'olsr', 'dof': 807.0}\n", + "sdc: {}\n", + "command: results = acro.olsr(\n", + "summary: pass; dof=807.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ inc_activity R-squared: 0.894\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000\n", + "Time: 19:43:34 Log-Likelihood: -14495.000\n", + "No. Observations: 811 AIC: 29000.000\n", + "Df Residuals: 807 BIC: 29020.000\n", + "Df Model: 3 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "Intercept 301000.0000 533000.000 0.565 0.572 -745000.000 1350000.000\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852, 1339.956 Durbin-Watson: 1.414\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1.253318e+06\n", + "Skew: 9.899 Prob(JB): 0.000000e+00\n", + "Kurtosis: 194.566 Cond. No. 1.050000e+08]\n", + "timestamp: 2025-03-06T19:43:34.954407\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_15\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'probit', 'dof': 806.0}\n", + "sdc: {}\n", + "command: results = acro.probit(y, x)\n", + "summary: pass; dof=806.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ survivor No. Observations: 811\n", + "Dep. Variable: \n", + "Model: Probit Df Residuals: 8.060000e+02\n", + "Method: MLE Df Model: 4.000000e+00\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 2.140000e-01\n", + "Time: 19:43:34 Log-Likelihood: -4.004600e+02\n", + "converged: True LL-Null: -5.095000e+02\n", + "Covariance Type: nonrobust LLR p-value: 4.875000e-46, coef std err z P>|z| [0.025 \\\n", + "const 4.740000e-02 5.700000e-02 0.838 0.402 -6.300000e-02 \n", + "inc_activity 1.836000e-07 5.160000e-08 3.559 0.000 8.250000e-08 \n", + "inc_grants 8.576000e-08 3.900000e-08 2.197 0.028 9.250000e-09 \n", + "inc_donations 2.406000e-07 4.540000e-08 5.297 0.000 1.520000e-07 \n", + "total_costs -8.644000e-08 3.680000e-08 -2.351 0.019 -1.590000e-07 \n", + "\n", + " 0.975] \n", + "const 1.580000e-01 \n", + "inc_activity 2.850000e-07 \n", + "inc_grants 1.620000e-07 \n", + "inc_donations 3.300000e-07 \n", + "total_costs -1.440000e-08 ]\n", + "timestamp: 2025-03-06T19:43:34.982272\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_16\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'logit', 'dof': 806.0}\n", + "sdc: {}\n", + "command: results = acro.logit(y, x)\n", + "summary: pass; dof=806.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ survivor No. Observations: 811\n", + "Dep. Variable: \n", + "Model: Logit Df Residuals: 8.060000e+02\n", + "Method: MLE Df Model: 4.000000e+00\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 2.187000e-01\n", + "Time: 19:43:35 Log-Likelihood: -3.980700e+02\n", + "converged: True LL-Null: -5.095000e+02\n", + "Covariance Type: nonrobust LLR p-value: 4.532000e-47, coef std err z P>|z| [0.025 \\\n", + "const 5.120000e-02 9.100000e-02 0.561 0.575 -1.280000e-01 \n", + "inc_activity 2.981000e-07 8.950000e-08 3.330 0.001 1.230000e-07 \n", + "inc_grants 1.351000e-07 6.670000e-08 2.026 0.043 4.390000e-09 \n", + "inc_donations 5.123000e-07 1.040000e-07 4.927 0.000 3.080000e-07 \n", + "total_costs -1.442000e-07 6.260000e-08 -2.304 0.021 -2.670000e-07 \n", + "\n", + " 0.975] \n", + "const 2.300000e-01 \n", + "inc_activity 4.740000e-07 \n", + "inc_grants 2.660000e-07 \n", + "inc_donations 7.160000e-07 \n", + "total_costs -2.150000e-08 ]\n", + "timestamp: 2025-03-06T19:43:35.004433\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_17\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_0.png']\n", + "timestamp: 2025-03-06T19:43:35.149884\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_18\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_1.png']\n", + "timestamp: 2025-03-06T19:43:35.204307\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + } + ], + "source": [ + "results_str = acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "id": "f78b5a08", + "metadata": {}, + "source": [ + "### Remove some ACRO outputs before finalising" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "6211a9cf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_1 removed\n", + "INFO:acro:records:remove(): output_4 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_1\")\n", + "acro.remove_output(\"output_4\")" + ] + }, + { + "cell_type": "markdown", + "id": "df2a02e0", + "metadata": {}, + "source": [ + "### Rename ACRO outputs before finalising" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "c9864a29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_2 renamed to pivot_table\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_2\", \"pivot_table\")" + ] + }, + { + "cell_type": "markdown", + "id": "56d2b6a1", + "metadata": {}, + "source": [ + "### Add a comment to output" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "b392be9f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to output_0\n", + "INFO:acro:records:a comment was added to output_0\n" + ] + } + ], + "source": [ + "acro.add_comments(\"output_0\", \"This is a cross table between year and grant_type\")\n", + "acro.add_comments(\"output_0\", \"6 cells were suppressed in this table\")" + ] + }, + { + "cell_type": "markdown", + "id": "8496fed4", + "metadata": {}, + "source": [ + "### Add an unsupported output to the list of outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "2816eac7", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add_custom(): output_19\n" + ] + } + ], + "source": [ + "acro.custom_output(\n", + " \"XandY.jpeg\", \"This output is an image showing the relationship between X and Y\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6efde761", + "metadata": {}, + "source": [ + "### Request an exception for some of the outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "f38b4334", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:exception request was added to output_0\n", + "INFO:acro:records:exception request was added to output_3\n", + "INFO:acro:records:exception request was added to output_5\n", + "INFO:acro:records:exception request was added to output_6\n" + ] + } + ], + "source": [ + "acro.add_exception(\"output_0\", \"I really need this.\")\n", + "acro.add_exception(\"output_3\", \"This one is safe. Trust me, I'm a professor.\")\n", + "acro.add_exception(\"output_5\", \"It's not disclosive, I promise.\")\n", + "acro.add_exception(\"output_6\", \"I need this one too\")" + ] + }, + { + "cell_type": "markdown", + "id": "5a586694", + "metadata": {}, + "source": [ + "### Finalise ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "9e554eea", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_7\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: grant_type G N R R/G All\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok\n", + "2011 ok ok ok threshold; ok\n", + "2012 ok ok ok threshold; ok\n", + "2013 ok ok ok threshold; ok\n", + "2014 ok ok ok threshold; ok\n", + "2015 ok ok ok threshold; ok\n", + "All ok ok ok ok ok\n", + "output: [grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5]\n", + "timestamp: 2025-03-06T19:43:34.695759\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason should be provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_8\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 10, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [[0, 2], [1, 1], [1, 2], [2, 2], [3, 1], [3, 2], [4, 1], [4, 2], [5, 1], [5, 2]], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "summary: review; negative values found\n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 negative \n", + "2011 negative negative \n", + "2012 negative \n", + "2013 negative negative \n", + "2014 negative negative \n", + "2015 negative negative \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0]\n", + "timestamp: 2025-03-06T19:43:34.727227\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " negative values are valid financial losses\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_9\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]], 'p-ratio': [[1, 0], [3, 0]], 'nk-rule': [[3, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: inc_grants \\\n", + "year 2010 2011 2012 \n", + "grant_type \n", + "G ok ok ok \n", + "N threshold; p-ratio; ok ok \n", + "R ok ok ok \n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; \n", + "All ok ok ok \n", + "\n", + " \n", + "year 2013 2014 2015 All \n", + "grant_type \n", + "G ok ok ok ok \n", + "N ok ok ok ok \n", + "R ok ok ok ok \n", + "R/G threshold; threshold; threshold; ok \n", + "All ok ok ok ok \n", + "output: [ inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 ]\n", + "timestamp: 2025-03-06T19:43:34.782873\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason should be provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_12\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 4, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [[1, 0], [1, 1], [2, 0], [2, 1]], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: review; negative values found\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G \n", + "N negative negative\n", + "R negative negative\n", + "R/G \n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.872024\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " negative values are valid financial losses\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_17\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_0.png']\n", + "timestamp: 2025-03-06T19:43:35.149884\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_18\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_1.png']\n", + "timestamp: 2025-03-06T19:43:35.204307\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: pivot_table\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 1], [1, 0], [1, 1]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: acro.crosstab(mydata.year, mydata.survivor)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 threshold; threshold; \n", + "2011 threshold; threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2]\n", + "timestamp: 2025-03-06T19:43:34.423743\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_19\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['XandY.jpeg']\n", + "timestamp: 2025-03-06T19:43:35.258648\n", + "comments: ['This output is an image showing the relationship between X and Y']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " please review- this image is not disclosive\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: ACRO_RES\n" + ] + } + ], + "source": [ + "SAVE_PATH = \"ACRO_RES\"\n", + "\n", + "# output = acro.finalise(SAVE_PATH, \"xlsx\")\n", + "output = acro.finalise(SAVE_PATH, \"json\")" + ] + }, + { + "cell_type": "markdown", + "id": "64e00920", + "metadata": {}, + "source": [ + "### List files generated" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "96b72072", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XandY.jpeg\n", + "config.json\n", + "histogram_0.png\n", + "histogram_1.png\n", + "output_0_0.csv\n", + "output_10_0.csv\n", + "output_11_0.csv\n", + "output_12_0.csv\n", + "output_13_0.csv\n", + "output_13_1.csv\n", + "output_13_2.csv\n", + "output_14_0.csv\n", + "output_14_1.csv\n", + "output_14_2.csv\n", + "output_15_0.csv\n", + "output_15_1.csv\n", + "output_16_0.csv\n", + "output_16_1.csv\n", + "output_3_0.csv\n", + "output_5_0.csv\n", + "output_6_0.csv\n", + "output_7_0.csv\n", + "output_8_0.csv\n", + "output_9_0.csv\n", + "pivot_table_0.csv\n", + "results.json\n" + ] + } + ], + "source": [ + "files = []\n", + "for name in os.listdir(SAVE_PATH):\n", + " if os.path.isfile(os.path.join(SAVE_PATH, name)):\n", + " files.append(name)\n", + "files.sort()\n", + "for f in files:\n", + " print(f)" + ] + }, + { + "cell_type": "markdown", + "id": "a0e77cbe", + "metadata": {}, + "source": [ + "### Checksums" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "f5f6364e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XandY.jpeg.txt\n", + "histogram_0.png.txt\n", + "histogram_1.png.txt\n", + "output_0_0.csv.txt\n", + "output_10_0.csv.txt\n", + "output_11_0.csv.txt\n", + "output_12_0.csv.txt\n", + "output_13_0.csv.txt\n", + "output_13_1.csv.txt\n", + "output_13_2.csv.txt\n", + "output_14_0.csv.txt\n", + "output_14_1.csv.txt\n", + "output_14_2.csv.txt\n", + "output_15_0.csv.txt\n", + "output_15_1.csv.txt\n", + "output_16_0.csv.txt\n", + "output_16_1.csv.txt\n", + "output_3_0.csv.txt\n", + "output_5_0.csv.txt\n", + "output_6_0.csv.txt\n", + "output_7_0.csv.txt\n", + "output_8_0.csv.txt\n", + "output_9_0.csv.txt\n", + "pivot_table_0.csv.txt\n", + "results.json.txt\n" + ] + } + ], + "source": [ + "files = []\n", + "checksum_dir = os.path.join(SAVE_PATH, \"checksums\")\n", + "for name in os.listdir(checksum_dir):\n", + " if os.path.isfile(os.path.join(checksum_dir, name)):\n", + " files.append(name)\n", + "files.sort()\n", + "for f in files:\n", + " print(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f241054a-c91e-4a91-bdc0-0395bbe084dd", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_2.png b/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_2.png new file mode 100644 index 00000000..e0090c1f Binary files /dev/null and b/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_2.png differ diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_3.png b/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_3.png new file mode 100644 index 00000000..1999823c Binary files /dev/null and b/docs/source/_build/.doctrees/nbsphinx/notebooks_test-nursery_41_3.png differ diff --git a/docs/source/_build/.doctrees/nbsphinx/notebooks_test_47_2.png b/docs/source/_build/.doctrees/nbsphinx/notebooks_test_47_2.png new file mode 100644 index 00000000..62cd7570 Binary files /dev/null and b/docs/source/_build/.doctrees/nbsphinx/notebooks_test_47_2.png differ diff --git a/docs/source/_build/.doctrees/notebook_examples.doctree b/docs/source/_build/.doctrees/notebook_examples.doctree new file mode 100644 index 00000000..1ad1a7f4 Binary files /dev/null and b/docs/source/_build/.doctrees/notebook_examples.doctree differ diff --git a/docs/source/_build/.doctrees/notebooks/acro_demo.doctree b/docs/source/_build/.doctrees/notebooks/acro_demo.doctree new file mode 100644 index 00000000..07dffb21 Binary files /dev/null and b/docs/source/_build/.doctrees/notebooks/acro_demo.doctree differ diff --git a/docs/source/_build/.doctrees/notebooks/acro_demo_v2.doctree b/docs/source/_build/.doctrees/notebooks/acro_demo_v2.doctree new file mode 100644 index 00000000..c39672b4 Binary files /dev/null and b/docs/source/_build/.doctrees/notebooks/acro_demo_v2.doctree differ diff --git a/docs/source/_build/.doctrees/notebooks/test-nursery.doctree b/docs/source/_build/.doctrees/notebooks/test-nursery.doctree new file mode 100644 index 00000000..83e23b17 Binary files /dev/null and b/docs/source/_build/.doctrees/notebooks/test-nursery.doctree differ diff --git a/docs/source/_build/.doctrees/notebooks/test.doctree b/docs/source/_build/.doctrees/notebooks/test.doctree new file mode 100644 index 00000000..6b1b416c Binary files /dev/null and b/docs/source/_build/.doctrees/notebooks/test.doctree differ diff --git a/docs/source/_build/.doctrees/record.doctree b/docs/source/_build/.doctrees/record.doctree new file mode 100644 index 00000000..7906e714 Binary files /dev/null and b/docs/source/_build/.doctrees/record.doctree differ diff --git a/docs/source/_build/.doctrees/sacro_ml.doctree b/docs/source/_build/.doctrees/sacro_ml.doctree new file mode 100644 index 00000000..77b8ace1 Binary files /dev/null and b/docs/source/_build/.doctrees/sacro_ml.doctree differ diff --git a/docs/source/_build/.doctrees/sacro_viewer.doctree b/docs/source/_build/.doctrees/sacro_viewer.doctree new file mode 100644 index 00000000..910b7a98 Binary files /dev/null and b/docs/source/_build/.doctrees/sacro_viewer.doctree differ diff --git a/docs/source/_build/.doctrees/support.doctree b/docs/source/_build/.doctrees/support.doctree new file mode 100644 index 00000000..939e520f Binary files /dev/null and b/docs/source/_build/.doctrees/support.doctree differ diff --git a/docs/source/_build/.doctrees/user_guide.doctree b/docs/source/_build/.doctrees/user_guide.doctree new file mode 100644 index 00000000..d6d1bdcf Binary files /dev/null and b/docs/source/_build/.doctrees/user_guide.doctree differ diff --git a/docs/source/_build/.doctrees/user_guide/configuration.doctree b/docs/source/_build/.doctrees/user_guide/configuration.doctree new file mode 100644 index 00000000..f76670f5 Binary files /dev/null and b/docs/source/_build/.doctrees/user_guide/configuration.doctree differ diff --git a/docs/source/_build/.doctrees/user_guide/core_concepts.doctree b/docs/source/_build/.doctrees/user_guide/core_concepts.doctree new file mode 100644 index 00000000..0f7e8957 Binary files /dev/null and b/docs/source/_build/.doctrees/user_guide/core_concepts.doctree differ diff --git a/docs/source/_build/.doctrees/user_guide/getting_started.doctree b/docs/source/_build/.doctrees/user_guide/getting_started.doctree new file mode 100644 index 00000000..0bf7c961 Binary files /dev/null and b/docs/source/_build/.doctrees/user_guide/getting_started.doctree differ diff --git a/docs/source/_build/_images/notebooks_test-nursery_41_2.png b/docs/source/_build/_images/notebooks_test-nursery_41_2.png new file mode 100644 index 00000000..e0090c1f Binary files /dev/null and b/docs/source/_build/_images/notebooks_test-nursery_41_2.png differ diff --git a/docs/source/_build/_images/notebooks_test-nursery_41_3.png b/docs/source/_build/_images/notebooks_test-nursery_41_3.png new file mode 100644 index 00000000..1999823c Binary files /dev/null and b/docs/source/_build/_images/notebooks_test-nursery_41_3.png differ diff --git a/docs/source/_build/_images/notebooks_test_47_2.png b/docs/source/_build/_images/notebooks_test_47_2.png new file mode 100644 index 00000000..62cd7570 Binary files /dev/null and b/docs/source/_build/_images/notebooks_test_47_2.png differ diff --git a/docs/source/_build/_images/schematic.png b/docs/source/_build/_images/schematic.png new file mode 100644 index 00000000..bc6ff5ba Binary files /dev/null and b/docs/source/_build/_images/schematic.png differ diff --git a/docs/source/_build/_modules/acro/acro.html b/docs/source/_build/_modules/acro/acro.html new file mode 100644 index 00000000..8e8f13f9 --- /dev/null +++ b/docs/source/_build/_modules/acro/acro.html @@ -0,0 +1,672 @@ + + + + + + + + + + acro.acro — ACRO 0.4.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + + + +
+ + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for acro.acro

+"""ACRO: Automatic Checking of Research Outputs."""
+
+from __future__ import annotations
+
+import json
+import logging
+import os
+import pathlib
+import warnings
+
+import yaml
+
+from . import acro_tables
+from .acro_regression import Regression
+from .acro_tables import Tables
+from .record import Records
+from .version import __version__
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger("acro")
+warnings.simplefilter(action="ignore", category=FutureWarning)
+
+
+
+[docs] +class ACRO(Tables, Regression): + """ACRO: Automatic Checking of Research Outputs. + + Attributes + ---------- + config : dict + Safe parameters and their values. + results : Records + The current outputs including the results of checks. + suppress : bool + Whether to automatically apply suppression + + Examples + -------- + >>> acro = ACRO() + >>> results = acro.ols( + ... y, x + ... ) + >>> results.summary() + >>> acro.finalise( + ... "MYFOLDER", + ... "json", + ... ) + """ + +
+[docs] + def __init__(self, config: str = "default", suppress: bool = False) -> None: + """Construct a new ACRO object and reads parameters from config. + + Parameters + ---------- + config : str + Name of a yaml configuration file with safe parameters. + suppress : bool, default False + Whether to automatically apply suppression. + """ + Tables.__init__(self, suppress) + Regression.__init__(self, config) + self.config: dict = {} + self.results: Records = Records() + self.suppress: bool = suppress + path = pathlib.Path(__file__).with_name(config + ".yaml") + logger.debug("path: %s", path) + with open(path, encoding="utf-8") as handle: + self.config = yaml.load(handle, Loader=yaml.loader.SafeLoader) + logger.info("version: %s", __version__) + logger.info("config: %s", self.config) + logger.info("automatic suppression: %s", self.suppress) + # set globals needed for aggregation functions + acro_tables.THRESHOLD = self.config["safe_threshold"] + acro_tables.SAFE_PRATIO_P = self.config["safe_pratio_p"] + acro_tables.SAFE_NK_N = self.config["safe_nk_n"] + acro_tables.SAFE_NK_K = self.config["safe_nk_k"] + acro_tables.CHECK_MISSING_VALUES = self.config["check_missing_values"] + acro_tables.ZEROS_ARE_DISCLOSIVE = self.config["zeros_are_disclosive"] + # set globals for survival analysis + acro_tables.SURVIVAL_THRESHOLD = self.config["survival_safe_threshold"]
+ + +
+[docs] + def finalise(self, path: str = "outputs", ext="json") -> Records | None: + """Create a results file for checking. + + Parameters + ---------- + path : str + Name of a folder to save outputs. + ext : str + Extension of the results file. Valid extensions: {json, xlsx}. + + Returns + ------- + Records + Object storing the outputs. + """ + # check if the path exists + if os.path.exists(path): + logger.warning( + "Results file can not be created. " + "Directory %s already exists. Please choose a different directory name.", + path, + ) + return None + self.results.finalise(path, ext) + config_filename: str = os.path.normpath(f"{path}/config.json") + try: + with open(config_filename, "w", newline="", encoding="utf-8") as file: + json.dump(self.config, file, indent=4, sort_keys=False) + except FileNotFoundError: # pragma: no cover + logger.debug( + "The config file will not be created because the " + "output folder was not created as the acro object was empty." + ) + return self.results
+ + +
+[docs] + def remove_output(self, key: str) -> None: + """Remove an output from the results. + + Parameters + ---------- + key : str + Key specifying which output to remove, e.g., 'output_0'. + """ + self.results.remove(key)
+ + +
+[docs] + def print_outputs(self) -> str: + """Print the current results dictionary. + + Returns + ------- + str + String representation of all outputs. + """ + return self.results.print()
+ + +
+[docs] + def custom_output(self, filename: str, comment: str = "") -> None: + """Add an unsupported output to the results dictionary. + + Parameters + ---------- + filename : str + The name of the file that will be added to the list of the outputs. + comment : str + An optional comment. + """ + self.results.add_custom(filename, comment)
+ + +
+[docs] + def rename_output(self, old: str, new: str) -> None: + """Rename an output. + + Parameters + ---------- + old : str + The old name of the output. + new : str + The new name of the output. + """ + self.results.rename(old, new)
+ + +
+[docs] + def add_comments(self, output: str, comment: str) -> None: + """Add a comment to an output. + + Parameters + ---------- + output : str + The name of the output. + comment : str + The comment. + """ + self.results.add_comments(output, comment)
+ + +
+[docs] + def add_exception(self, output: str, reason: str) -> None: + """Add an exception request to an output. + + Parameters + ---------- + output : str + The name of the output. + reason : str + The comment. + """ + self.results.add_exception(output, reason)
+
+ + + +def add_to_acro(src_path: str, dest_path: str = "sdc_results") -> None: + """Add outputs to an acro object and creates a results file for checking. + + Parameters + ---------- + src_path : str + Name of the folder with outputs produced without using acro. + dest_path : str + Name of the folder to save outputs. + """ + acro = ACRO() + output_id = 0 + # add the files from the folder to an acro obj + for file in os.listdir(src_path): + filename = os.path.join(src_path, file) + acro.custom_output(filename) + acro.rename_output(f"output_{output_id}", file) + output_id += 1 + acro.finalise(dest_path, "json") +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + diff --git a/docs/source/_build/_modules/acro/record.html b/docs/source/_build/_modules/acro/record.html new file mode 100644 index 00000000..8407c0ad --- /dev/null +++ b/docs/source/_build/_modules/acro/record.html @@ -0,0 +1,1122 @@ + + + + + + + + + + acro.record — ACRO 0.4.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + + + +
+ + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for acro.record

+"""ACRO: Output storage and serialization."""
+
+from __future__ import annotations
+
+import datetime
+import hashlib
+import json
+import logging
+import os
+import shutil
+from pathlib import Path
+from typing import Any
+
+import pandas as pd
+from pandas import DataFrame
+
+from .version import __version__
+
+logger = logging.getLogger("acro:records")
+
+
+
+[docs] +def load_outcome(outcome: dict) -> DataFrame: + """Return a DataFrame from an outcome dictionary. + + Parameters + ---------- + outcome : dict + The outcome to load as a DataFrame. + """ + return pd.DataFrame.from_dict(outcome)
+ + + +
+[docs] +def load_output(path: str, output: list[str]) -> list[str] | list[DataFrame]: + """Return a loaded output. + + Parameters + ---------- + path : str + The path to the output folder (with results.json). + output : list[str] + The output to load. + + Returns + ------- + list[str] | list[DataFrame] + The loaded output field. + """ + if len(output) < 1: + raise ValueError("error loading output") + loaded: list[DataFrame] = [] + for filename in output: + _, ext = os.path.splitext(filename) + if ext == ".csv": + filename = os.path.normpath(f"{path}/{filename}") + loaded.append(pd.read_csv(filename)) + if len(loaded) < 1: # output is path(s) to custom file(s) + return output + return loaded
+ + + +
+[docs] +class Record: # pylint: disable=too-many-instance-attributes + """Stores data related to a single output record. + + Attributes + ---------- + uid : str + Unique identifier. + status : str + SDC status: {"pass", "fail", "review"} + output_type : str + Type of output, e.g., "regression" + properties : dict + Dictionary containing structured output data. + sdc : dict + Dictionary containing SDC results. + command : str + String representation of the operation performed. + summary : str + String summarising the ACRO checks. + outcome : DataFrame + DataFrame describing the details of ACRO checks. + output : Any + List of output DataFrames. + comments : list[str] + List of strings entered by the user to add comments to the output. + exception : str + Description of why an exception to fail/review should be granted. + timestamp : str + Time the record was created in ISO format. + """ + +
+[docs] + def __init__( # pylint: disable=too-many-arguments + self, + uid: str, + status: str, + output_type: str, + properties: dict, + sdc: dict, + command: str, + summary: str, + outcome: DataFrame, + output: list[str] | list[DataFrame], + comments: list[str] | None = None, + ) -> None: + """Construct a new output record. + + Parameters + ---------- + uid : str + Unique identifier. + status : str + SDC status: {"pass", "fail", "review"} + output_type : str + Type of output, e.g., "regression" + properties : dict + Dictionary containing structured output data. + sdc : dict + Dictionary containing SDC results. + command : str + String representation of the operation performed. + summary : str + String summarising the ACRO checks. + outcome : DataFrame + DataFrame describing the details of ACRO checks. + output : list[str] | list[DataFrame] + List of output DataFrames. + comments : list[str] | None, default None + List of strings entered by the user to add comments to the output. + """ + self.uid: str = uid + self.status: str = status + self.output_type: str = output_type + self.properties: dict = properties + self.sdc: dict = sdc + self.command: str = command + self.summary: str = summary + self.outcome: DataFrame = outcome + self.output: Any = output + self.comments: list[str] = [] if comments is None else comments + self.exception: str = "" + now = datetime.datetime.now() + self.timestamp: str = now.isoformat()
+ + +
+[docs] + def serialize_output(self, path: str = "outputs") -> list[str]: + """Serialize outputs. + + Parameters + ---------- + path : str, default 'outputs' + Name of the folder that outputs are to be written. + + Returns + ------- + list[str] + List of filepaths of the written outputs. + """ + output: list[str] = [] + # check if the outputs directory was already created + try: # pragma: no cover + os.makedirs(path) + logger.debug("Directory %s created successfully", path) + except FileExistsError: + logger.debug("Directory %s already exists", path) + # save each output DataFrame to a different csv + if all(isinstance(obj, DataFrame) for obj in self.output): + for i, data in enumerate(self.output): + filename = f"{self.uid}_{i}.csv" + output.append(filename) + filename = os.path.normpath(f"{path}/{filename}") + with open(filename, mode="w", newline="", encoding="utf-8") as file: + file.write(data.to_csv()) + # move custom files to the output folder + if self.output_type == "custom": + for filename in self.output: + if os.path.exists(filename): + shutil.copy(filename, path) + output.append(Path(filename).name) + if self.output_type in ["survival plot", "histogram"]: + for filename in self.output: + if os.path.exists(filename): + output.append(Path(filename).name) + shutil.copy(filename, path) + return output
+ + + def __str__(self) -> str: + """Return a string representation of a record. + + Returns + ------- + str + The record. + """ + return ( + f"uid: {self.uid}\n" + f"status: {self.status}\n" + f"type: {self.output_type}\n" + f"properties: {self.properties}\n" + f"sdc: {self.sdc}\n" + f"command: {self.command}\n" + f"summary: {self.summary}\n" + f"outcome: {self.outcome}\n" + f"output: {self.output}\n" + f"timestamp: {self.timestamp}\n" + f"comments: {self.comments}\n" + f"exception: {self.exception}\n" + )
+ + + +
+[docs] +class Records: + """Stores data related to a collection of output records.""" + +
+[docs] + def __init__(self) -> None: + """Construct a new object for storing multiple records.""" + self.results: dict[str, Record] = {} + self.output_id: int = 0
+ + +
+[docs] + def add( # pylint: disable=too-many-arguments + self, + status: str, + output_type: str, + properties: dict, + sdc: dict, + command: str, + summary: str, + outcome: DataFrame, + output: list[str] | list[DataFrame], + comments: list[str] | None = None, + ) -> None: + """Add an output to the results. + + Parameters + ---------- + status : str + SDC status: {"pass", "fail", "review"} + output_type : str + Type of output, e.g., "regression" + properties : dict + Dictionary containing structured output data. + sdc : dict + Dictionary containing SDC results. + command : str + String representation of the operation performed. + summary : str + String summarising the ACRO checks. + outcome : DataFrame + DataFrame describing the details of ACRO checks. + output : list[str | list[DataFrame] + List of output DataFrames. + comments : list[str] | None, default None + List of strings entered by the user to add comments to the output. + """ + new = Record( + uid=f"output_{self.output_id}", + status=status, + output_type=output_type, + properties=properties, + sdc=sdc, + command=command, + summary=summary, + outcome=outcome, + output=output, + comments=comments, + ) + self.results[new.uid] = new + self.output_id += 1 + logger.info("add(): %s", new.uid)
+ + +
+[docs] + def remove(self, key: str) -> None: + """Remove an output from the results. + + Parameters + ---------- + key : str + Key specifying which output to remove, e.g., 'output_0'. + """ + if key not in self.results: + raise ValueError(f"unable to remove {key}, key not found") + del self.results[key] + logger.info("remove(): %s removed", key)
+ + +
+[docs] + def get(self, key: str) -> Record: + """Return a specified output from the results. + + Parameters + ---------- + key : str + Key specifying which output to return, e.g., 'output_0'. + + Returns + ------- + Record + The requested output. + """ + logger.debug("get(): %s ", key) + return self.results[key]
+ + +
+[docs] + def get_keys(self) -> list[str]: + """Return the list of available output keys. + + Returns + ------- + list[str] + List of output names. + """ + logger.debug("get_keys()") + return list(self.results.keys())
+ + +
+[docs] + def get_index(self, index: int) -> Record: + """Return the output at the specified position. + + Parameters + ---------- + index : int + Position of the output to return. + + Returns + ------- + Record + The requested output. + """ + logger.debug("get_index(): %s", index) + key = list(self.results.keys())[index] + return self.results[key]
+ + +
+[docs] + def add_custom(self, filename: str, comment: str | None = None) -> None: + """Add an unsupported output to the results dictionary. + + Parameters + ---------- + filename : str + The name of the file that will be added to the list of the outputs. + comment : str | None, default None + An optional comment. + """ + if os.path.exists(filename): + output = Record( + uid=f"output_{self.output_id}", + status="review", + output_type="custom", + properties={}, + sdc={}, + command="custom", + summary="review", + outcome=DataFrame(), + output=[os.path.normpath(filename)], + comments=None if comment is None else [comment], + ) + self.results[output.uid] = output + self.output_id += 1 + logger.info("add_custom(): %s", output.uid) + else: + logger.info( + "WARNING: Unable to add %s because the file does not exist", filename + ) # pragma: no cover
+ + +
+[docs] + def rename(self, old: str, new: str) -> None: + """Rename an output. + + Parameters + ---------- + old : str + The old name of the output. + new : str + The new name of the output. + """ + if old not in self.results: + raise ValueError(f"unable to rename {old}, key not found") + if new in self.results: + raise ValueError(f"unable to rename, {new} already exists") + self.results[new] = self.results[old] + self.results[new].uid = new + del self.results[old] + logger.info("rename_output(): %s renamed to %s", old, new)
+ + +
+[docs] + def add_comments(self, output: str, comment: str) -> None: + """Add a comment to an output. + + Parameters + ---------- + output : str + The name of the output. + comment : str + The comment. + """ + if output not in self.results: + raise ValueError(f"unable to find {output}, key not found") + self.results[output].comments.append(comment) + logger.info("a comment was added to %s", output)
+ + +
+[docs] + def add_exception(self, output: str, reason: str) -> None: + """Add an exception request to an output. + + Parameters + ---------- + output : str + The name of the output. + reason : str + The reason the output should be released. + """ + if output not in self.results: + raise ValueError(f"unable to add exception: {output} not found") + self.results[output].exception = reason + logger.info("exception request was added to %s", output)
+ + +
+[docs] + def print(self) -> str: + """Print the current results. + + Returns + ------- + str + String representation of all outputs. + """ + logger.debug("print()") + outputs: str = "" + for _, record in self.results.items(): + outputs += str(record) + "\n" + print(outputs) + return outputs
+ + +
+[docs] + def validate_outputs(self) -> None: + """Prompt researcher to complete any required fields.""" + for _, record in self.results.items(): + if record.status != "pass" and record.exception == "": + logger.info( + "\n%s\n" + "The status of the record above is: %s.\n" + "Please explain why an exception should be granted.\n", + str(record), + record.status, + ) + record.exception = input("")
+ + +
+[docs] + def finalise(self, path: str, ext: str) -> None: + """Create a results file for checking. + + Parameters + ---------- + path : str + Name of a folder to save outputs. + ext : str + Extension of the results file. Valid extensions: {json, xlsx}. + """ + logger.debug("finalise()") + self.validate_outputs() + if ext == "json": + self.finalise_json(path) + elif ext == "xlsx": + self.finalise_excel(path) + else: + raise ValueError("Invalid file extension. Options: {json, xlsx}") + self.write_checksums(path) + # check if the directory acro_artifacts exists and delete it + if os.path.exists("acro_artifacts"): + shutil.rmtree("acro_artifacts") + logger.info("outputs written to: %s", path)
+ + +
+[docs] + def finalise_json(self, path: str) -> None: + """Write outputs to a JSON file. + + Parameters + ---------- + path : str + Name of a folder to save outputs. + """ + outputs: dict = {} + for key, val in self.results.items(): + outputs[key] = { + "uid": val.uid, + "status": val.status, + "type": val.output_type, + "properties": val.properties, + "files": [], + "outcome": json.loads(val.outcome.to_json()), + "command": val.command, + "summary": val.summary, + "timestamp": val.timestamp, + "comments": val.comments, + "exception": val.exception, + } + files: list[str] = val.serialize_output(path) + for file in files: + outputs[key]["files"].append({"name": file, "sdc": val.sdc}) + + results: dict = {"version": __version__, "results": outputs} + filename: str = os.path.normpath(f"{path}/results.json") + try: + with open(filename, "w", newline="", encoding="utf-8") as handle: + json.dump(results, handle, indent=4, sort_keys=False) + except FileNotFoundError: # pragma: no cover + logger.info( + "You don't have any output in the acro object. " + "Directory %s will not be created.", + path, + )
+ + +
+[docs] + def finalise_excel(self, path: str) -> None: + """Write outputs to an excel spreadsheet. + + Parameters + ---------- + path : str + Name of a folder to save outputs. + """ + filename: str = os.path.normpath(f"{path}/results.xlsx") + try: # check if the directory was already created + os.makedirs(path, exist_ok=True) + logger.debug("Directory %s created successfully", path) + except FileExistsError: # pragma: no cover + logger.debug("Directory %s already exists", path) + with pd.ExcelWriter( # pylint: disable=abstract-class-instantiated + filename, engine="openpyxl" + ) as writer: + # description sheet + sheet = [] + summary = [] + command = [] + for output_id, output in self.results.items(): + if output.output_type == "custom": + continue # avoid writing custom outputs + sheet.append(output_id) + command.append(output.command) + summary.append(output.summary) + tmp_df = pd.DataFrame( + {"Sheet": sheet, "Command": command, "Summary": summary} + ) + tmp_df.to_excel(writer, sheet_name="description", index=False, startrow=0) + # individual sheets + for output_id, output in self.results.items(): + if output.output_type == "custom": + continue # avoid writing custom outputs + # command and summary + start = 0 + tmp_df = pd.DataFrame( + [output.command, output.summary], index=["Command", "Summary"] + ) + tmp_df.to_excel(writer, sheet_name=output_id, startrow=start) + # outcome + if output.outcome is not None: + output.outcome.to_excel(writer, sheet_name=output_id, startrow=4) + # output + for table in output.output: + start = 1 + writer.sheets[output_id].max_row + table.to_excel(writer, sheet_name=output_id, startrow=start)
+ + +
+[docs] + def write_checksums(self, path: str) -> None: + """Write checksums for each file to checksums folder. + + Parameters + ---------- + path : str + Name of a folder to save outputs. + """ + if os.path.exists(path): + checksums: dict[str, str] = {} + for name in os.listdir(path): + filename = os.path.join(path, name) + if os.path.isfile(filename): + with open(filename, "rb") as file: + read = file.read() + checksums[name] = hashlib.sha256(read).hexdigest() + checksums_dir: str = os.path.normpath(f"{path}/checksums") + os.makedirs(checksums_dir, exist_ok=True) + for name, sha256 in checksums.items(): + filename = os.path.join(checksums_dir, name + ".txt") + with open(filename, "w", encoding="utf-8") as file: + file.write(sha256) + else: + logger.debug("There is no file to do the checksums") # pragma: no cover
+
+ + + +
+[docs] +def load_records(path: str) -> Records: + """Load outputs from a JSON file. + + Parameters + ---------- + path : str + Name of an output folder containing results.json. + + Returns + ------- + Records + The loaded records. + """ + records = Records() + filename = os.path.normpath(f"{path}/results.json") + with open(filename, newline="", encoding="utf-8") as handle: + data = json.load(handle) + if data["version"] != __version__: # pragma: no cover + raise ValueError("error loading output") + for key, val in data["results"].items(): + files: list[dict] = val["files"] + filenames: list = [] + sdcs: list = [] + for file in files: + filenames.append(file["name"]) + sdcs.append(file["sdc"]) + records.results[key] = Record( + uid=val["uid"], + status=val["status"], + output_type=val["type"], + properties=val["properties"], + sdc=sdcs[0], + command=val["command"], + summary=val["summary"], + outcome=load_outcome(val["outcome"]), + output=load_output(path, filenames), + comments=val["comments"], + ) + records.results[key].exception = val["exception"] + records.results[key].timestamp = val["timestamp"] + return records
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + diff --git a/docs/source/_build/_modules/acro/utils.html b/docs/source/_build/_modules/acro/utils.html new file mode 100644 index 00000000..b56cb483 --- /dev/null +++ b/docs/source/_build/_modules/acro/utils.html @@ -0,0 +1,527 @@ + + + + + + + + + + acro.utils — ACRO 0.4.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + + + +
+ + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for acro.utils

+"""ACRO: Utility Functions."""
+
+from __future__ import annotations
+
+import logging
+from inspect import FrameInfo, getframeinfo
+
+import pandas as pd
+
+logger = logging.getLogger("acro")
+
+
+
+[docs] +def get_command(default: str, stack_list: list[FrameInfo]) -> str: + """Return the calling source line as a string. + + Parameters + ---------- + default : str + Default string to return if unable to extract the stack. + stack_list : list[tuple] + A list of frame records for the caller's stack. The first entry in the + returned list represents the caller; the last entry represents the + outermost call on the stack. + + Returns + ------- + str + The calling source line. + """ + command: str = default + if len(stack_list) > 1: + code = getframeinfo(stack_list[1][0]).code_context + if code is not None: + command = "\n".join(code).strip() + logger.debug("command: %s", command) + return command
+ + + +
+[docs] +def prettify_table_string(table: pd.DataFrame, separator: str | None = None) -> str: + """ + Add delimiters to table.to_string() to improve readability for onscreen display. + + Splits fields on whitespace unless an optional separator is provided e.g. ',' for csv. + """ + hdelim = "-" + vdelim = "|" + + table.rename(columns=lambda x: str(x).replace(" ", "_"), inplace=True) + output = table.to_string(justify="left") + as_strings = output.split("\n") + nheaders = len(as_strings) - table.shape[0] + rowlen = len(as_strings[0]) + + # get top level column labels and their positions + if separator is not None: + rowone_strings = as_strings[0].split(separator) + else: + rowone_strings = as_strings[0].split() + + vals = rowone_strings[1:] + positions = [] + for val in vals: + positions.append(as_strings[0].find(val)) + + for row, _ in enumerate(as_strings): + for pos in positions[::-1]: + as_strings[row] = as_strings[row][0:pos] + vdelim + as_strings[row][pos:] + + rowlen += len(positions) # add on space for v delimiters + + outstr = "" + outstr += hdelim * rowlen + vdelim + "\n" + for row in range(nheaders): + outstr += as_strings[row] + vdelim + "\n" + outstr += hdelim * rowlen + vdelim + "\n" + for row in range(nheaders, len(as_strings)): + outstr += as_strings[row] + vdelim + "\n" + outstr += hdelim * rowlen + vdelim + "\n" + return outstr
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + diff --git a/docs/source/_build/_modules/index.html b/docs/source/_build/_modules/index.html new file mode 100644 index 00000000..c784abc9 --- /dev/null +++ b/docs/source/_build/_modules/index.html @@ -0,0 +1,442 @@ + + + + + + + + + + Overview: module code — ACRO 0.4.11 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ +
+ + + + + +
+
+ + + + + + +
+ + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+
+ + + + + + + + + + + +
+ +
+
+
+ +
+ + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + diff --git a/docs/source/_build/_sources/acro.rst.txt b/docs/source/_build/_sources/acro.rst.txt new file mode 100644 index 00000000..9bc82da6 --- /dev/null +++ b/docs/source/_build/_sources/acro.rst.txt @@ -0,0 +1,7 @@ +:orphan: + +==== +ACRO +==== + +This page contains ACRO-specific documentation. diff --git a/docs/source/_build/_sources/acro_r.rst.txt b/docs/source/_build/_sources/acro_r.rst.txt new file mode 100644 index 00000000..96819ed6 --- /dev/null +++ b/docs/source/_build/_sources/acro_r.rst.txt @@ -0,0 +1,7 @@ +:orphan: + +======= +ACRO-R +======= + +This page contains ACRO-R documentation. diff --git a/docs/source/_build/_sources/api.rst.txt b/docs/source/_build/_sources/api.rst.txt new file mode 100644 index 00000000..0dcb0d05 --- /dev/null +++ b/docs/source/_build/_sources/api.rst.txt @@ -0,0 +1,228 @@ +============= +API Reference +============= + +This section provides detailed documentation for all ACRO classes, functions, and modules. + +.. currentmodule:: acro + +Core Classes +============ + +.. toctree:: + :maxdepth: 2 + :caption: ACRO Classes + + api/acro_class + api/records_class + +ACRO Class +---------- + +.. autoclass:: acro.ACRO + :members: + :inherited-members: + :show-inheritance: + :no-index: + +Record Management +================= + +Record Classes +-------------- + +.. autoclass:: acro.record.Records + :members: + :show-inheritance: + :no-index: + +Record Module +------------- + +.. include:: record.rst + +Utilities +========= + +Helper Functions +---------------- + +.. automodule:: acro.utils + :members: + :show-inheritance: + :no-index: + +Function Reference by Category +============================== + +Output Management +----------------- + +* ``finalise()`` - Prepare outputs for review +* ``remove_output()`` - Remove specific output +* ``print_outputs()`` - Display current outputs +* ``custom_output()`` - Add custom output +* ``rename_output()`` - Rename an output +* ``add_comments()`` - Add comments to output +* ``add_exception()`` - Add exception request + +Method Parameters +================= + +Common Parameters +----------------- + +Many ACRO methods share common parameters: + +.. list-table:: + :header-rows: 1 + :widths: 20 15 65 + + * - Parameter + - Type + - Description + * - ``suppress`` + - bool + - Whether to suppress potentially disclosive outputs + * - ``show_suppressed`` + - bool + - Whether to display suppressed values in output + * - ``safe_threshold`` + - int + - Minimum cell count threshold for safety + * - ``safe_dof_threshold`` + - int + - Minimum degrees of freedom for statistical models + * - ``safe_nk_n`` + - int + - Minimum number of observations for nk-dominance rule + * - ``safe_nk_k`` + - float + - Threshold for nk-dominance rule (0-1) + * - ``safe_p_threshold`` + - float + - P-value threshold for statistical significance + +Return Types +============ + +Output Objects +-------------- + +Most ACRO functions return specialized output objects that contain: + +* **Original Result**: The unmodified analysis result +* **Safe Result**: The disclosure-controlled version +* **Disclosure Checks**: Details of applied safety checks +* **Metadata**: Information about the analysis and safety measures + +.. code-block:: python + + # Example return object structure + result = acro.crosstab(df.col1, df.col2) + + # Access components + print(result.output) # Safe output for display + print(result.disclosure_checks) # Applied safety checks + print(result.metadata) # Analysis metadata + +Return Types +============ + +Output Objects +-------------- + +ACRO functions return results that are automatically checked for disclosure risks: + +.. code-block:: python + + import acro + + # Initialize ACRO + session = acro.ACRO(suppress=True) + + # Results are automatically checked + result = session.crosstab(df.col1, df.col2) + + # View outputs + session.print_outputs() + + # Finalize for review + session.finalise("outputs/") + +Version Information +=================== + +.. code-block:: python + + import acro + from acro.version import __version__ + print(__version__) + +Compatibility +============= + +Python Version Support +---------------------- + +ACRO supports Python 3.9 and later versions. + +Dependency Requirements +----------------------- + +.. list-table:: + :header-rows: 1 + :widths: 30 20 50 + + * - Package + - Minimum Version + - Purpose + * - pandas + - 1.5.0 + - Data manipulation and analysis + * - numpy + - 1.21.0 + - Numerical computing + * - statsmodels + - 0.13.0 + - Statistical modeling + * - openpyxl + - 3.0.0 + - Excel file support + * - pyyaml + - 5.4.0 + - Configuration file handling + +Configuration +============= + +ACRO uses YAML configuration files to set safety parameters: + +.. code-block:: python + + # Initialize with default config + session = acro.ACRO(config="default", suppress=True) + + # Configuration is loaded from default.yaml + print(session.config) + +Custom Configuration +-------------------- + +Create custom YAML files for different environments: + +.. code-block:: yaml + + # custom.yaml + safe_threshold: 10 + safe_nk_n: 2 + safe_nk_k: 0.9 + check_missing_values: true + zeros_are_disclosive: false + +See Also +======== + +* :doc:`examples` - Usage examples and tutorials +* :doc:`installation` - Installation instructions +* :doc:`introduction` - Getting started guide diff --git a/docs/source/_build/_sources/api/acro_class.rst.txt b/docs/source/_build/_sources/api/acro_class.rst.txt new file mode 100644 index 00000000..b99efe42 --- /dev/null +++ b/docs/source/_build/_sources/api/acro_class.rst.txt @@ -0,0 +1,39 @@ +=========== +ACRO Class +=========== + +.. currentmodule:: acro + +.. autoclass:: ACRO + :members: + :inherited-members: + :show-inheritance: + +Key Methods +=========== + +Data Analysis +------------- + +.. automethod:: ACRO.crosstab + :no-index: +.. automethod:: ACRO.pivot_table + :no-index: +.. automethod:: ACRO.ols + :no-index: +.. automethod:: ACRO.logit + :no-index: +.. automethod:: ACRO.probit + :no-index: + +Output Management +----------------- + +.. automethod:: ACRO.finalise + :no-index: +.. automethod:: ACRO.print_outputs + :no-index: +.. automethod:: ACRO.remove_output + :no-index: +.. automethod:: ACRO.custom_output + :no-index: diff --git a/docs/source/_build/_sources/api/records_class.rst.txt b/docs/source/_build/_sources/api/records_class.rst.txt new file mode 100644 index 00000000..5336f43a --- /dev/null +++ b/docs/source/_build/_sources/api/records_class.rst.txt @@ -0,0 +1,10 @@ +============= +Records Class +============= + +.. currentmodule:: acro.record + +.. autoclass:: Records + :members: + :show-inheritance: + :no-index: diff --git a/docs/source/_build/_sources/examples.rst.txt b/docs/source/_build/_sources/examples.rst.txt new file mode 100644 index 00000000..96ad7bd8 --- /dev/null +++ b/docs/source/_build/_sources/examples.rst.txt @@ -0,0 +1,181 @@ +======== +Examples +======== + +This section provides comprehensive examples and tutorials for using the ACRO family of tools. + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + examples/quickstart + examples/configuration + notebook_examples + + + + + +Interactive Notebooks +===================== + +Python Jupyter Notebooks +------------------------- + +* :doc:`../notebooks/test` - Introduction to ACRO with the charities dataset +* :doc:`../notebooks/test-nursery` - Complex analysis with the nursery dataset + +R Notebooks +----------- + +* `ACRO-R Quickstart `_ - Getting started with ACRO in R +* `Statistical Modeling in R `_ - Advanced R workflows + +Video Tutorials +=============== + +* `ACRO Installation and Setup `_ +* `Basic Data Analysis Workflow `_ +* `Output Checking with SACRO-Viewer `_ + +Example Datasets +================ + +The following datasets are available for testing and learning: + +Charities Dataset +----------------- + +A synthetic dataset containing information about charitable organizations, including: + +* Organization details (name, type, region) +* Financial information (income, expenditure) +* Activity classifications + +**Usage**: Ideal for learning cross-tabulation and basic statistical analysis + +Nursery Dataset +--------------- + +A classification dataset for nursery school applications, featuring: + +* Categorical variables (parents, has_nurs, form, children, housing, finance, social, health) +* Target variable (spec_prior, priority, not_recom) + +**Usage**: Perfect for statistical modeling and regression examples + +Sample Code Repository +====================== + +All example code is available in the `ACRO Examples Repository `_. + +.. code-block:: bash + + # Clone examples repository + git clone https://github.com/AI-SDC/ACRO-Examples.git + cd ACRO-Examples + + # Install requirements + pip install -r requirements.txt + + # Run Jupyter notebooks + jupyter notebook + +Common Use Cases +================ + +Research Workflow Examples +-------------------------- + +1. **Exploratory Data Analysis** + + * Safe data exploration and summarization + * Identifying patterns while protecting privacy + * Generating publication-ready tables + +2. **Statistical Modeling** + + * Regression analysis with disclosure control + * Model comparison and selection + * Coefficient interpretation and reporting + +3. **Advanced Statistical Analysis** + + * Complex regression modeling + * Survival analysis with disclosure control + * Custom statistical procedures + +TRE Integration +--------------- + +ACRO integrates with Trusted Research Environments (TREs) to provide: + +* **Automated output submission** to approval workflows +* **Integration with existing TRE systems** and security protocols +* **Multi-user support** with shared configuration management +* **Audit trails** for compliance and tracking + +Best Practices Examples +----------------------- + +1. **Configuration Management** + + * Environment-specific settings + * Threshold customization + * Policy compliance + +2. **Quality Assurance** + + * Reproducible analysis workflows + * Version control integration + * Documentation standards + +Getting Help +============ + +If you need help with any examples: + +* Check the :doc:`api` for detailed function documentation +* Visit our `GitHub Issues `_ page +* Join our community discussions +* Contact support: acro-support@ai-sdc.org + +Contributing Examples +===================== + +We welcome contributions of new examples and tutorials: + +1. Fork the `ACRO Examples Repository `_ +2. Create a new example following our template +3. Test your example thoroughly +4. Submit a pull request with documentation + +Example Template +---------------- + +.. code-block:: python + + """ + Example Title: Brief Description + + This example demonstrates [specific functionality]. + + Requirements: + - acro >= 0.4.8 + - pandas >= 1.5.0 + + Dataset: [dataset name and source] + Difficulty: [Beginner/Intermediate/Advanced] + """ + + import acro + import pandas as pd + + # Example code here... + +See Also +======== + +* :doc:`installation` - Installation instructions +* :doc:`api` - Complete API reference +* :doc:`introduction` - Getting started guide diff --git a/docs/source/_build/_sources/examples/configuration.rst.txt b/docs/source/_build/_sources/examples/configuration.rst.txt new file mode 100644 index 00000000..ac8c445d --- /dev/null +++ b/docs/source/_build/_sources/examples/configuration.rst.txt @@ -0,0 +1,68 @@ +============= +Configuration +============= + +Guide to configuring ACRO for your specific needs. + +Basic Configuration +=================== + +ACRO expects configuration parameters to be supplied via a YAML file. +You can specify the name of your custom YAML configuration file when +initializing the ACRO session. + +For example, if you have a file named `my_config.yaml` with the following content: + +.. code-block:: yaml + + # my_config.yaml + safe_threshold: 10 + safe_dof_threshold: 10 + safe_nk_n: 2 + safe_nk_k: 0.9 + # ... other parameters ... + +You would then initialize your ACRO session like this: + +.. code-block:: python + + import acro + + # Initialize with a custom configuration file + session = acro.ACRO(suppress=True, config='my_config.yaml') + +.. note:: + The `config` parameter in `acro.ACRO()` expects the *name of a YAML file* (e.g., 'my_config.yaml'), not a Python dictionary directly. If you wish to use a custom configuration, please save your parameters in a YAML file and provide its name. + +Environment-Specific Settings +============================= + +You can create different YAML files for various research environments. + +For example, to define a 'high_security_config.yaml': + +.. code-block:: yaml + + # high_security_config.yaml + safe_threshold: 20 + safe_p_threshold: 0.01 + +And a 'standard_config.yaml': + +.. code-block:: yaml + + # standard_config.yaml + safe_threshold: 10 + safe_p_threshold: 0.1 + +Then, initialize your sessions accordingly: + +.. code-block:: python + + import acro + + # For a high-security environment + high_security_session = acro.ACRO(suppress=True, config='high_security_config.yaml') + + # For a standard research environment + standard_session = acro.ACRO(suppress=True, config='standard_config.yaml') diff --git a/docs/source/_build/_sources/examples/quickstart.rst.txt b/docs/source/_build/_sources/examples/quickstart.rst.txt new file mode 100644 index 00000000..1f6bc709 --- /dev/null +++ b/docs/source/_build/_sources/examples/quickstart.rst.txt @@ -0,0 +1,57 @@ +=========== +Quick Start +=========== + +This guide will get you up and running with ACRO in just a few minutes. + +.. currentmodule:: acro + +Installation +============ + +.. code-block:: bash + + pip install acro + +Core ACRO Class +=============== + +.. autoclass:: ACRO + :noindex: + +Essential Methods +================= + +Data Analysis +------------- + +.. automethod:: ACRO.crosstab + :noindex: + +.. automethod:: ACRO.olsr + :noindex: + +Output Management +----------------- + +.. automethod:: ACRO.finalise + :noindex: + +.. automethod:: ACRO.print_outputs + :noindex: + +Quick Workflow +============== + +1. **Install**: ``pip install acro`` +2. **Initialize**: Create ACRO session with ``suppress=True`` +3. **Analyze**: Use ACRO methods for statistical analysis +4. **Review**: Check outputs with ``print_outputs()`` +5. **Finalize**: Export results with ``finalise()`` + +Next Steps +========== + +* :doc:`configuration` - Learn configuration options +* :doc:`../api` - Explore the full API reference +* :doc:`../notebook_examples` - Interactive Jupyter notebook examples diff --git a/docs/source/_build/_sources/faq.rst.txt b/docs/source/_build/_sources/faq.rst.txt new file mode 100644 index 00000000..ab1e03fa --- /dev/null +++ b/docs/source/_build/_sources/faq.rst.txt @@ -0,0 +1,55 @@ +:orphan: + +======== +FAQ +======== + +Frequently Asked Questions (FAQ) +================================ + +This section provides answers to common questions about the AI-SDC project, including risk explainers for SVM and agreements required from researchers at the design phase. + +--------------------------- +SVM Risk Explainer +--------------------------- + +**Q: What is the SVM risk explainer in AI-SDC?** + +A: The SVM (Support Vector Machine) risk explainer in AI-SDC provides insights into how SVM models may introduce disclosure risks. It helps researchers understand which features or data points are most influential in model predictions, and assesses the potential for sensitive information leakage through model outputs. + +**Q: Why is SVM risk assessment important for statistical disclosure control?** + +A: SVMs can inadvertently reveal patterns or individual-level information if not carefully controlled. The risk explainer assists in identifying and mitigating these risks, ensuring that outputs comply with disclosure control policies. + +--------------------------- +ML Risk Agreements +--------------------------- + +**Q: What agreements do researchers need to make regarding machine learning risks at the design phase?** + +A: Before using AI-SDC for machine learning tasks, researchers must agree to specific terms regarding risk management. These agreements typically include: + +- Acknowledging the risks of data leakage and potential re-identification in ML models. +- Committing to follow recommended disclosure control practices for training, testing, and reporting results. +- Agreeing to use risk assessment tools provided by AI-SDC (such as the SVM risk explainer). +- Documenting the intended use, data sources, and mitigation strategies at the project design phase. + +**Q: Why are these agreements necessary?** + +A: These agreements ensure that all research conducted with AI-SDC aligns with ethical standards, regulatory requirements, and organizational policies on data privacy and disclosure risk. + +--------------------------- +General +--------------------------- + +**Q: Where can I find more information about configuring and using AI-SDC?** + +A: Please refer to the :doc:`user_guide/configuration` and :doc:`examples` guides in this documentation, or visit the `AI-SDC GitHub repository `_ for further resources. + +**Q: Who should I contact for support or to report a security concern?** + +A: Contact the project maintainers via the GitHub Issues page or the email address listed in the repository's README. + +--------------------------- + +*If you have additional questions, please submit an issue or consult the community discussions on GitHub.* diff --git a/docs/source/_build/_sources/index.rst.txt b/docs/source/_build/_sources/index.rst.txt new file mode 100644 index 00000000..3cbfd31e --- /dev/null +++ b/docs/source/_build/_sources/index.rst.txt @@ -0,0 +1,188 @@ +.. raw:: html + +
+ SACRO Logo +
+ +======================================== +Welcome to the AI-SDC family of tools +======================================== + +Our tools are designed to help researchers assess the privacy disclosure risks of their outputs, including tables, plots, statistical models, and trained machine learning models + + +.. toctree:: + :maxdepth: 2 + :hidden: + :caption: Documentation + + introduction + support + installation + user_guide + examples + api + + +.. grid:: 2 + + .. grid-item-card:: ACRO (Python) + :link: introduction + :link-type: doc + :shadow: md + :class-header: bg-primary + + **Statistical Disclosure Control for Python** + + Tools for the Semi-Automatic Checking of Research Outputs. Drop-in replacements for common analysis commands with built-in privacy protection. + + +++ + + :bdg-primary:`Current Documentation Focus` :doc:`Get Started → ` + + .. grid-item-card:: SACRO-ML + :link: http://sacro-ml.sacro-tools.org + :link-type: url + :shadow: md + :class-header: bg-info + + **Machine Learning Privacy Tools** + + Collection of tools and resources for managing the statistical disclosure control of trained machine learning models. + + +++ + + :bdg-info:`ML Privacy` :doc:`Learn More → ` + +.. grid:: 2 + + .. grid-item-card:: ACRO-R + :link: http://acro-r.sacro-tools.org + :link-type: url + :shadow: md + :class-header: bg-success + + **R Package Integration** + + R-language interface for the Python ACRO library, providing familiar R syntax for statistical disclosure control. + + +++ + + :bdg-success:`R Integration` :doc:`Explore → ` + + .. grid-item-card:: SACRO-Viewer + :link: http://sacro-viewer.sacro-tools.org + :link-type: url + :shadow: md + :class-header: bg-info + + + **Graphical User Interface** + + A graphical user interface for fast, secure and effective output checking, which can work in any TRE (Trusted Research Environment). + + +++ + + :bdg-warning:`GUI Tool` :doc:`View Docs → ` + +ACRO: Statistical Disclosure Control +==================================== + +ACRO is a free and open source tool that supports the semi-automated checking of research outputs (SACRO) for privacy disclosure within secure data environments. SACRO is a framework that applies best-practice principles-based statistical disclosure control (SDC) techniques on-the-fly as researchers conduct their analysis. SACRO is designed to assist human checkers rather than seeking to replace them as with current automated rules-based approaches. + +.. note:: + **New in v0.4.8:** Enhanced support for complex statistical models and improved R integration. + +What is ACRO? +============= + +ACRO implements a principles-based statistical disclosure control (SDC) methodology that: + +* Automatically identifies potentially disclosive outputs +* Applies optional disclosure mitigation strategies +* Reports reasons for applying SDC +* Produces summary documents for output checkers + + +Core Features +============= + +Semi-Automated Disclosure Checking +---------------------------------- + +* **Drop-in replacements** for common Python analysis commands (pandas, statsmodels, etc.) with configurable disclosure checks +* **Automated sensitivity tests**: frequency thresholds, dominance (p%, NK rules, etc.), residual degrees-of-freedom checks +* **Optional mitigations**: suppression, rounding, and more to come +* **Session management**: track, rename, comment, remove, add exceptions, and finalise reports +* **Configurable risk parameters** via YAML files +* **Generates auditable reports** in JSON or Excel + +Design Principles +----------------- + +* **Free and open source** under MIT (ACRO) / GPLv3 (SACRO Viewer) +* **Easy to install** via PyPI, CRAN, or GitHub; cross-platform (Linux, macOS, Windows) +* **Familiar APIs** - same function signatures as native commands: acro.crosstab mirrors pandas.crosstab, etc. +* **Comprehensive coverage** - tables, regressions, histograms, survival plots, etc. +* **Transparent & auditable** - clear reports, stored queries, designed for human-checkers +* **Configurable & extensible** - organisation-defined disclosure rules, multi-language support +* **Scalable** - lightweight, session-based, local execution + +Getting Started +=============== + +.. grid:: 3 + + .. grid-item-card:: Install + :link: installation + :link-type: doc + :class-header: bg-light + + Get ACRO installed and configured in your environment + + .. grid-item-card:: Learn + :link: examples + :link-type: doc + :class-header: bg-light + + Explore tutorials and examples for common use cases + + .. grid-item-card:: Reference + :link: api + :link-type: doc + :class-header: bg-light + + Complete API documentation and function reference + +Key Methods +----------- + +* **Making tables** - e.g. :py:meth:`~acro.ACRO.crosstab` +* **Regression analysis** - e.g. :py:meth:`~acro.ACRO.ols` +* **Making plots** - e.g. :py:meth:`~acro.ACRO.hist` +* **Managing a research session** - e.g. :py:meth:`~acro.ACRO.finalise` + +Community and Support +===================== + +.. grid:: 2 + + .. grid-item-card:: Get Help + :class-header: bg-light + + * `GitHub Issues `_ + * `Discussion Forum `_ + * Email: sacro.contact@uwe.ac.uk + + .. grid-item-card:: Contribute + :class-header: bg-light + + * `Contributing Guide `_ + * `Source Code `_ + * `Report Issues `_ + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/docs/source/_build/_sources/installation.rst.txt b/docs/source/_build/_sources/installation.rst.txt new file mode 100644 index 00000000..3058f7d4 --- /dev/null +++ b/docs/source/_build/_sources/installation.rst.txt @@ -0,0 +1,71 @@ +============ +Installation +============ + +ACRO can be installed through multiple package managers and methods. + +Requirements +============ + +* **Python**: 3.10 or higher +* **Operating System**: Windows, macOS, or Linux + +Quick Installation +================== + +PyPI Installation (Recommended) +-------------------------------- + +You may need admin privileges to install this way. + +.. code-block:: bash + + pip install acro + +Conda Installation +------------------ + +You may need admin privileges to install this way. + +.. code-block:: bash + + conda install -c conda-forge acro + +Installing within a Virtual Environment +======================================== + +This isolates ACRO from changes in the rest of your system, and may be necessary if you don't have admin privileges on your machine. + +.. code-block:: bash + + # Create virtual environment (creates 'acro-env' folder in current directory) + python -m venv acro-env + + # Activate (Windows) + acro-env\Scripts\activate + + # Activate (Linux/macOS) + source acro-env/bin/activate + + # Install ACRO + pip install acro + +Verification +============ + +.. code-block:: python + + import acro + from acro.version import __version__ + print(f"ACRO version: {__version__}") + + # Test basic functionality + session = acro.ACRO() + print("ACRO initialized successfully!") + +Next Steps +========== + +* :doc:`introduction` - Welcome to ACRO +* :doc:`examples` - Usage examples and tutorials +* :doc:`api` - Complete API reference diff --git a/docs/source/_build/_sources/introduction.rst.txt b/docs/source/_build/_sources/introduction.rst.txt new file mode 100644 index 00000000..6bb36ea2 --- /dev/null +++ b/docs/source/_build/_sources/introduction.rst.txt @@ -0,0 +1,130 @@ +Welcome to ACRO +============================== + +ACRO is a free and open source tool that supports the semi-automated checking of research outputs (SACRO) for privacy disclosure within secure data environments. This package acts as a lightweight Python tool that sits over well-known analysis tools to provide statistical disclosure control. + +.. note:: + **New in v0.4.8:** Enhanced support for complex statistical models and improved R integration. + +.. image:: ../schematic.png + :alt: ACRO workflow and architecture schematic + :align: center + :width: 600px + + +What is ACRO? +============= + +ACRO implements a principles-based statistical disclosure control (SDC) methodology that: + +* Automatically identifies potentially disclosive outputs +* Applies optional disclosure mitigation strategies +* Reports reasons for applying SDC +* Produces summary documents for output checkers + +Example +============= + +.. code-block:: python + + import acro + import pandas as pd + import numpy as np + + # Create synthetic data + np.random.seed(42) + df = pd.DataFrame({ + 'region': np.random.choice(['North', 'South', 'East', 'West'], 1000), + 'income': np.random.choice(['Low', 'Medium', 'High'], 1000), + 'age_group': np.random.choice(['18-30', '31-50', '51+'], 1000) + }) + + # Initialize ACRO + session = acro.ACRO(suppress=True) + + # Create a cross-tabulation with automatic disclosure checking + safe_table = session.crosstab( + df.region, + df.income, + show_suppressed=True + ) + + # Finalize outputs for review + session.finalise(output_folder="outputs") + +Core Features +============= + +Automated Disclosure Checking +----------------------------- + +ACRO automatically runs disclosure tests on your outputs, checking for: + +* Group sizes below TRE's threshold +* Class disclosure +* Saturated statistical models +* Dominance of one record in a group + +Integration with Popular Libraries +---------------------------------- + +Works seamlessly with: + +* **Pandas** - for data manipulation and table creation +* **Statsmodels** - for statistical modeling +* **R and Stata** - through wrapper packages + +API Overview +============ + +The main ACRO class provides the interface for all disclosure checking functionality. See the :doc:`api` documentation for complete details. + +Key Parameters +-------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - Parameter + - Type + - Description + * - suppress + - bool + - Whether to suppress potentially disclosive outputs + * - config + - dict, optional + - Configuration options for disclosure checking + +Key Methods +----------- + +* :py:meth:`~ACRO.crosstab` - Create cross-tabulations with disclosure checking +* :py:meth:`~ACRO.pivot_table` - Create pivot tables with disclosure checking +* :py:meth:`~ACRO.ols` - Ordinary least squares regression with disclosure checking +* :py:meth:`~ACRO.finalise` - Prepare outputs for review by data controllers + +Installation +============ + +Install ACRO using pip: + +.. code-block:: bash + + pip install acro + +Quick Start +=========== + +1. Import ACRO and initialize +2. Load your data +3. Run analysis with automatic disclosure checking +4. Finalize outputs for review + +Next Steps +========== + +* :doc:`installation` - Install ACRO and set up your environment +* :doc:`user_guide` - Follow the comprehensive user guide +* :doc:`examples` - Explore example notebooks and tutorials +* :doc:`api` - Check the complete API reference diff --git a/docs/source/_build/_sources/notebook_examples.rst.txt b/docs/source/_build/_sources/notebook_examples.rst.txt new file mode 100644 index 00000000..061b3620 --- /dev/null +++ b/docs/source/_build/_sources/notebook_examples.rst.txt @@ -0,0 +1,13 @@ +================== +Notebook Examples +================== + +Interactive Jupyter notebook examples demonstrating ACRO functionality. + +.. toctree:: + :maxdepth: 2 + :caption: ACRO Notebooks + + notebooks/acro_demo.ipynb + notebooks/test.ipynb + notebooks/test-nursery.ipynb diff --git a/docs/source/_build/_sources/notebooks/acro_demo.ipynb.txt b/docs/source/_build/_sources/notebooks/acro_demo.ipynb.txt new file mode 100644 index 00000000..f80d58b9 --- /dev/null +++ b/docs/source/_build/_sources/notebooks/acro_demo.ipynb.txt @@ -0,0 +1,642 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from acro import ACRO" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- In this version, the data can be read directly from the local machine after it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab\n", + "- This is an example of crosstab using ACRO. \n", + "- The INFO lines show the researcher what will be reported to the output checkers.\n", + "- Then the (suppressed as necessary) table is shown via the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommendation | | | |\n", + "--------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "--------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, rownames=[\"recommendation\"], colnames=[\"parents\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression\n", + "- This is an example of crosstab with suppressing the cells that violate the disclosure tests.\n", + "- Note that you need to change the value of the suppress variable in the acro object to True. Then run the crosstab command. \n", + "- If you wish to continue the research while suppressing the outputs, leave the suppress variable as it is, otherwise turn it off." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:38:29.296719\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:38:29.315826\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "\"uid: output_0\\nstatus: fail\\ntype: table\\nproperties: {'method': 'crosstab'}\\nsdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 4 cells may need suppressing; \\noutcome: parents great_pret pretentious usual\\nrecommendation \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommendation \\nnot_recom 1440 1440 1440\\npriority 858 1484 1924\\nrecommend 0 0 2\\nspec_prior 2022 1264 758\\nvery_recom 0 132 196]\\ntimestamp: 2025-03-06T19:38:29.296719\\ncomments: []\\nexception: \\n\\nuid: output_1\\nstatus: fail\\ntype: table\\nproperties: {'method': 'crosstab'}\\nsdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents)\\nsummary: fail; threshold: 4 cells suppressed; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:38:29.315826\\ncomments: []\\nexception: \\n\\n\"" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_1 renamed to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_1\", \"cross_tabulation\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.add_comments(\"cross_tabulation\", \"Please let me have this data.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: cross_tabulation\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:38:29.315826\n", + "comments: ['Please let me have this data.']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: Examples\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"Examples\", \"json\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/_build/_sources/notebooks/acro_demo_v2.ipynb.txt b/docs/source/_build/_sources/notebooks/acro_demo_v2.ipynb.txt new file mode 100644 index 00000000..93da188f --- /dev/null +++ b/docs/source/_build/_sources/notebooks/acro_demo_v2.ipynb.txt @@ -0,0 +1,912 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from acro import ACRO" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- In this version, the data can be read directly from the local machine after it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab\n", + "- This is an example of crosstab using ACRO. \n", + "- The INFO lines show the researcher what will be reported to the output checkers.\n", + "- Then the (suppressed as necessary) table is shown via the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommendation | | | |\n", + "--------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "--------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, rownames=[\"recommendation\"], colnames=[\"parents\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression\n", + "- This is an example of crosstab with suppressing the cells that violate the disclosure tests.\n", + "- Note that you need to change the value of the suppress variable in the acro object to True. Then run the crosstab command. \n", + "- If you wish to continue the research while suppressing the outputs, leave the suppress variable as it is, otherwise turn it off." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## An example of a more complex table\n", + "- make the children variable numeric\n", + "- so we can report statistics like mean etc." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "df[\"children\"].replace(to_replace={\"more\": \"4\"}, inplace=True)\n", + "df[\"children\"] = pd.to_numeric(df[\"children\"])\n", + "\n", + "df[\"children\"] = df.apply(\n", + " lambda row: (\n", + " row[\"children\"] if row[\"children\"] in (1, 2, 3) else np.random.randint(4, 10)\n", + " ),\n", + " axis=1,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 2 cells may need suppressing; p-ratio: 9 cells may need suppressing; nk-rule: 9 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "|recommend | not_recom| priority recommend |spec_prior |very_recom |All|\n", + "|parents finance | | | | | |\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "|great_pret convenient| ok | ok p-ratio; nk-rule; | ok | p-ratio; nk-rule; | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | p-ratio; nk-rule; | ok|\n", + "|pretentious convenient| ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "|usual convenient| ok | ok threshold; p-ratio; nk-rule; | ok | ok | ok|\n", + "| inconv | ok | ok p-ratio; nk-rule; | ok | ok | ok|\n", + "|All | ok | ok threshold; p-ratio; nk-rule; | ok | ok | ok|\n", + "-----------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
recommendnot_recompriorityrecommendspec_priorvery_recomAll
parentsfinance
great_pretconvenient3.1388892.787109NaN3.324353NaN3.135185
inconv3.1319442.393064NaN3.411335NaN3.155093
pretentiousconvenient3.0875003.104167NaN3.2705482.6477273.125000
inconv3.0902783.057263NaN3.3191181.3636363.116204
usualconvenient3.1277783.1172201.03.3284882.6000003.121296
inconv3.1263893.163542NaN3.3719811.3636363.136111
All3.1171303.0168781.03.3444612.1981713.131481
\n", + "
" + ], + "text/plain": [ + "recommend not_recom priority recommend spec_prior \\\n", + "parents finance \n", + "great_pret convenient 3.138889 2.787109 NaN 3.324353 \n", + " inconv 3.131944 2.393064 NaN 3.411335 \n", + "pretentious convenient 3.087500 3.104167 NaN 3.270548 \n", + " inconv 3.090278 3.057263 NaN 3.319118 \n", + "usual convenient 3.127778 3.117220 1.0 3.328488 \n", + " inconv 3.126389 3.163542 NaN 3.371981 \n", + "All 3.117130 3.016878 1.0 3.344461 \n", + "\n", + "recommend very_recom All \n", + "parents finance \n", + "great_pret convenient NaN 3.135185 \n", + " inconv NaN 3.155093 \n", + "pretentious convenient 2.647727 3.125000 \n", + " inconv 1.363636 3.116204 \n", + "usual convenient 2.600000 3.121296 \n", + " inconv 1.363636 3.136111 \n", + "All 2.198171 3.131481 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.suppress = False\n", + "acro.crosstab(\n", + " index=[df.parents, df.finance],\n", + " columns=[df.recommend],\n", + " values=df.children,\n", + " aggfunc=\"mean\",\n", + " margins=\"total\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommendation \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:36:50.672611\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:36:50.694578\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 9, 'nk-rule': 9, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[4, 2], [6, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [1, 4], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [1, 4], [2, 2], [3, 2], [4, 2], [5, 2], [6, 2]], 'all-values-are-same': []}}\n", + "command: acro.crosstab(\n", + "summary: fail; threshold: 2 cells may need suppressing; p-ratio: 9 cells may need suppressing; nk-rule: 9 cells may need suppressing; \n", + "outcome: recommend not_recom priority recommend \\\n", + "parents finance \n", + "great_pret convenient ok ok p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "pretentious convenient ok ok p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "usual convenient ok ok threshold; p-ratio; nk-rule; \n", + " inconv ok ok p-ratio; nk-rule; \n", + "All ok ok threshold; p-ratio; nk-rule; \n", + "\n", + "recommend spec_prior very_recom All \n", + "parents finance \n", + "great_pret convenient ok p-ratio; nk-rule; ok \n", + " inconv ok p-ratio; nk-rule; ok \n", + "pretentious convenient ok ok ok \n", + " inconv ok ok ok \n", + "usual convenient ok ok ok \n", + " inconv ok ok ok \n", + "All ok ok ok \n", + "output: [recommend not_recom priority recommend spec_prior \\\n", + "parents finance \n", + "great_pret convenient 3.138889 2.787109 NaN 3.324353 \n", + " inconv 3.131944 2.393064 NaN 3.411335 \n", + "pretentious convenient 3.087500 3.104167 NaN 3.270548 \n", + " inconv 3.090278 3.057263 NaN 3.319118 \n", + "usual convenient 3.127778 3.117220 1.0 3.328488 \n", + " inconv 3.126389 3.163542 NaN 3.371981 \n", + "All 3.117130 3.016878 1.0 3.344461 \n", + "\n", + "recommend very_recom All \n", + "parents finance \n", + "great_pret convenient NaN 3.135185 \n", + " inconv NaN 3.155093 \n", + "pretentious convenient 2.647727 3.125000 \n", + " inconv 1.363636 3.116204 \n", + "usual convenient 2.600000 3.121296 \n", + " inconv 1.363636 3.136111 \n", + "All 2.198171 3.131481 ]\n", + "timestamp: 2025-03-06T19:36:50.838863\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + } + ], + "source": [ + "_ = acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_1 renamed to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_1\", \"cross_tabulation\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to cross_tabulation\n" + ] + } + ], + "source": [ + "acro.add_comments(\n", + " \"cross_tabulation\", \"Suppression has been applied. Please let me have this data.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Request an exception\n", + "An example of providing a reason why an exception should be made" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:exception request was added to output_2\n" + ] + } + ], + "source": [ + "acro.add_exception(\"output_2\", \"This is evidence of systematic bias?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)\n", + "- If an output is flagged as potentially disclosive then the \n", + " researcher is prompted to provide a reason for release if they have not already done so." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: cross_tabulation\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:36:50.694578\n", + "comments: ['Suppression has been applied. Please let me have this data.']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: Examples\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"Examples\", \"json\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/_build/_sources/notebooks/test-nursery.ipynb.txt b/docs/source/_build/_sources/notebooks/test-nursery.ipynb.txt new file mode 100644 index 00000000..b5415c8d --- /dev/null +++ b/docs/source/_build/_sources/notebooks/test-nursery.ipynb.txt @@ -0,0 +1,2785 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "00cac1f9", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# ACRO Demonstration" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e33fd4fb", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c01cfe12", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc8d993a", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from acro import ACRO, add_constant, add_to_acro" + ] + }, + { + "cell_type": "markdown", + "id": "530efcfe", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4b8a77e2", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: True\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=True)" + ] + }, + { + "cell_type": "markdown", + "id": "27a2baaa", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Load test data\n", + "The dataset used in this notebook is the nursery dataset from OpenML. \n", + "- The dataset can be read directly from OpenML using the code commented in the next cell.\n", + "- In this version, it can be read directly from the local machine if it has been downloaded. \n", + "- The code below reads the data from a folder called \"data\" which we assume is at the same level as the folder where you are working.\n", + "- The path might need to be changed if the data has been downloaded and stored elsewhere.\n", + " - for example use: \n", + " path = os.path.join(\"data\", \"nursery.arff\") \n", + " if the data is in a sub-folder of your work folder" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ac790b2b-b02f-49f7-8237-a033abed6e87", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
parentshas_nursformchildrenhousingfinancesocialhealthrecommend
0usualpropercomplete1convenientconvenientnonprobrecommendedrecommend
1usualpropercomplete1convenientconvenientnonprobprioritypriority
2usualpropercomplete1convenientconvenientnonprobnot_recomnot_recom
3usualpropercomplete1convenientconvenientslightly_probrecommendedrecommend
4usualpropercomplete1convenientconvenientslightly_probprioritypriority
\n", + "
" + ], + "text/plain": [ + " parents has_nurs form children housing finance social \\\n", + "0 usual proper complete 1 convenient convenient nonprob \n", + "1 usual proper complete 1 convenient convenient nonprob \n", + "2 usual proper complete 1 convenient convenient nonprob \n", + "3 usual proper complete 1 convenient convenient slightly_prob \n", + "4 usual proper complete 1 convenient convenient slightly_prob \n", + "\n", + " health recommend \n", + "0 recommended recommend \n", + "1 priority priority \n", + "2 not_recom not_recom \n", + "3 recommended recommend \n", + "4 priority priority " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from scipy.io.arff import loadarff\n", + "\n", + "path = os.path.join(\"../data\", \"nursery.arff\")\n", + "data = loadarff(path)\n", + "df = pd.DataFrame(data[0])\n", + "df = df.select_dtypes([object])\n", + "df = df.stack().str.decode(\"utf-8\").unstack()\n", + "df.rename(columns={\"class\": \"recommend\"}, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "ea2a0d76-ba68-4a74-93c3-bdaa88c48929", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Convert 'more than 3' children to random between 4 and 10\n", + "Change the children column from categorical to numeric in order to be able to test some of the ACRO functions that require a numeric feature" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b43810a8-4da9-4cec-a613-e2562ed95601", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " children column entries in raw file ['1' '2' '3' 'more']\n" + ] + } + ], + "source": [ + "print(f\" children column entries in raw file {df.children.unique()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "042f8e9d-b33b-4daf-851e-9f70b5d4859a", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "df[\"children\"].replace(to_replace={\"more\": \"4\"}, inplace=True)\n", + "df[\"children\"] = pd.to_numeric(df[\"children\"])\n", + "\n", + "df[\"children\"] = df.apply(\n", + " lambda row: (\n", + " row[\"children\"] if row[\"children\"] in (1, 2, 3) else np.random.randint(4, 10)\n", + " ),\n", + " axis=1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "d098c704", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Examples of producing tabular output\n", + "We rely on the industry-standard package **pandas** for tabulating data. \n", + "In the next few examples we show:\n", + "- first, how a researcher would normally make a call in pandas, saving the results in a variable that they can view on screen (or save to file?)\n", + "- then how the call is identical in SACRO, except that:\n", + " - \"pd\" is replaced by \"acro\"\n", + " - the researcher immediately sees a copy of what the TRE output checker will see.\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "4ae844a0", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Pandas crosstab\n", + "This is an example of crosstab using pandas. \n", + "We first make the call, then the second line print the outputs to screen." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "961684cb", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "table = pd.crosstab(df.recommend, df.parents)\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "id": "d642ed00", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO crosstab\n", + "This is an example of crosstab using ACRO. \n", + "The INFO lines show the researcher what will be reported to the output checkers.\n", + "Then the (suppressed as necessary) table is shown via. the print command as before." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bb4b2677", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend,\n", + " df.parents,\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "b3d09450", + "metadata": {}, + "source": [ + "### ACRO crosstab with totals\n", + "This is an example of crosstab with totals columns and suppression. \n", + "Note that when margins is true any row or column where all the cells are discolsive is deleted. If you wish to see such row or column set show_suppressed to True. \n", + "show_suppressed parameter does not work with herichical tables and when the aggregation function is the standard deviation." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "42825f24", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 5 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |All |\n", + "recommend | | | | |\n", + "------------------------------------------------------------------|\n", + "not_recom | ok | ok | ok | ok|\n", + "priority | ok | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok | ok|\n", + "All | ok | ok | ok | ok|\n", + "------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "b3e48dec", + "metadata": {}, + "source": [ + "### ACRO crosstab without suppression\n", + "This is an example of crosstab without suppressing the cells that violate the disclosure tests. \n", + "Note that you need to change the value of the suppress variable in the acro object to False. Then run the crosstab command. \n", + "If you wish to continue the research while suppressing the outputs, turn on the suppress variable otherwise leave it as it is." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "27329e57", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "----------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | threshold; | threshold; | threshold; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | threshold; | ok | ok|\n", + "----------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196\n" + ] + } + ], + "source": [ + "acro.suppress = False\n", + "\n", + "safe_table = acro.crosstab(df.recommend, df.parents)\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ed5134ab", + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = True" + ] + }, + { + "cell_type": "markdown", + "id": "8b603548", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO crosstab with aggregation function" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "298d2b40", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------|\n", + "parents |great_pret |pretentious |usual |\n", + "recommend | | | |\n", + "------------------------------------------------------------------------------------|\n", + "not_recom | ok | ok | ok|\n", + "priority | ok | ok | ok|\n", + "recommend | p-ratio; nk-rule; | p-ratio; nk-rule; | threshold; p-ratio; nk-rule; |\n", + "spec_prior | ok | ok | ok|\n", + "very_recom | p-ratio; nk-rule; | ok | ok|\n", + "------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, values=df.children, aggfunc=\"count\"\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b4aea046", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + " mode_aggfunc |mean |\n", + "parents great_pret pretentious usual |great_pret pretentious usual |\n", + "recommend | |\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "not_recom ok ok ok | ok ok ok|\n", + "priority ok ok ok | ok ok ok|\n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; threshold; p-ratio; nk-rule; | p-ratio; nk-rule; p-ratio; nk-rule; threshold; p-ratio; nk-rule; |\n", + "spec_prior ok ok ok | ok ok ok|\n", + "very_recom p-ratio; nk-rule; ok ok | p-ratio; nk-rule; ok ok|\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898\n" + ] + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.recommend, df.parents, values=df.children, aggfunc=[\"mode\", \"mean\"]\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "d66e565b", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO pivot_table\n", + "This is an example of pivot table using ACRO. \n", + "- Some researchers may prefer this to using crosstab. \n", + "- Again the call syntax is identical to the pandas \"pd.pivot_table\"\n", + "- in this case the output is non-disclosive" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "966c1a9b", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "------------------------------|\n", + " mean |std |\n", + " children |children|\n", + "parents | |\n", + "------------------------------|\n", + "great_pret ok | ok |\n", + "pretentious ok | ok |\n", + "usual ok | ok |\n", + "------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mean std\n", + " children children\n", + "parents \n", + "great_pret 3.140972 2.270396\n", + "pretentious 3.129630 2.250436\n", + "usual 3.110648 2.213072\n" + ] + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df, index=[\"parents\"], values=[\"children\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "print(table)" + ] + }, + { + "cell_type": "markdown", + "id": "4adc374b", + "metadata": {}, + "source": [ + "### ACRO pivot_table with margins" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9c024b65", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------------------------------------------------------|\n", + " children |\n", + "recommend not_recom priority recommend spec_prior very_recom All|\n", + "parents |\n", + "-----------------------------------------------------------------------------------------------------------|\n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok threshold; p-ratio; nk-rule; ok|\n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "usual ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "All ok ok threshold; p-ratio; nk-rule; ok ok ok|\n", + "-----------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:Disclosive cells were deleted from the dataframe before calculating the pivot table\n", + "INFO:acro:records:add(): output_6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412\n" + ] + } + ], + "source": [ + "safe_table = acro.pivot_table(\n", + " df, columns=[\"recommend\"], index=[\"parents\"], values=[\"children\"], margins=True\n", + ")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "markdown", + "id": "8446fa99-c073-48b8-875e-700dfa17ea0c", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Regression examples using ACRO\n", + "\n", + "Again there is an industry-standard package in python, this time called **statsmodels**.\n", + "- The examples below illustrate the use of the ACRO wrapper standard statsmodel functions\n", + "- Note that statsmodels can be called using an 'R-like' format (using an 'r' suffix on the command names)\n", + "- most statsmodels functiobns return a \"results object\" which has a \"summary\" function that produces printable/saveable outputs \n", + "\n", + "### Start by manipulating the nursery data to get two numeric variables\n", + "- The 'recommend' column is converted to an integer scale" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "72aefb22", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "df[\"recommend\"].replace(\n", + " to_replace={\n", + " \"not_recom\": \"0\",\n", + " \"recommend\": \"1\",\n", + " \"very_recom\": \"2\",\n", + " \"priority\": \"3\",\n", + " \"spec_prior\": \"4\",\n", + " },\n", + " inplace=True,\n", + ")\n", + "df[\"recommend\"] = pd.to_numeric(df[\"recommend\"])\n", + "\n", + "new_df = df[[\"recommend\", \"children\"]]\n", + "new_df = new_df.dropna()" + ] + }, + { + "cell_type": "markdown", + "id": "3ef880e6-726f-4a0b-9bcd-da8861dbd5a7", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO OLS \n", + "This is an example of ordinary least square regression using ACRO. \n", + "- Above recommend column was converted form categorical to numeric. \n", + "- Now we perform a the linear regression between recommend and children. \n", + "- This version includes a constant (intercept)\n", + "- This is just to show how the regression is done using ACRO. \n", + "- **No correlation is expected to be seen by using these variables**" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "2f462e42", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:ols() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_7\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: recommend R-squared: 0.001
Model: OLS Adj. R-squared: 0.001
Method: Least Squares F-statistic: 13.83
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201
Time: 19:39:47 Log-Likelihood: -25121.
No. Observations: 12960 AIC: 5.025e+04
Df Residuals: 12958 BIC: 5.026e+04
Df Model: 1
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 2.2099 0.025 87.263 0.000 2.160 2.260
children 0.0245 0.007 3.718 0.000 0.012 0.037
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 77090.215 Durbin-Watson: 2.883
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570
Skew: -0.486 Prob(JB): 0.00
Kurtosis: 1.489 Cond. No. 6.90


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & recommend & \\textbf{ R-squared: } & 0.001 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.001 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 13.83 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.000201 \\\\\n", + "\\textbf{Time:} & 19:39:47 & \\textbf{ Log-Likelihood: } & -25121. \\\\\n", + "\\textbf{No. Observations:} & 12960 & \\textbf{ AIC: } & 5.025e+04 \\\\\n", + "\\textbf{Df Residuals:} & 12958 & \\textbf{ BIC: } & 5.026e+04 \\\\\n", + "\\textbf{Df Model:} & 1 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 2.2099 & 0.025 & 87.263 & 0.000 & 2.160 & 2.260 \\\\\n", + "\\textbf{children} & 0.0245 & 0.007 & 3.718 & 0.000 & 0.012 & 0.037 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 77090.215 & \\textbf{ Durbin-Watson: } & 2.883 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1741.570 \\\\\n", + "\\textbf{Skew:} & -0.486 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 1.489 & \\textbf{ Cond. No. } & 6.90 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.001\n", + "Model: OLS Adj. R-squared: 0.001\n", + "Method: Least Squares F-statistic: 13.83\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.026e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const 2.2099 0.025 87.263 0.000 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.000 0.012 0.037\n", + "==============================================================================\n", + "Omnibus: 77090.215 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "\"\"\"" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: recommend R-squared: 0.000
Model: OLS Adj. R-squared: 0.000
Method: Least Squares F-statistic: 6.417
Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113
Time: 21:21:09 Log-Likelihood: -25124.
No. Observations: 12960 AIC: 5.025e+04
Df Residuals: 12958 BIC: 5.027e+04
Df Model: 1
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 2.2341 0.025 87.965 0.000 2.184 2.284
children 0.0168 0.007 2.533 0.011 0.004 0.030
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 76735.931 Durbin-Watson: 2.883
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843
Skew: -0.485 Prob(JB): 0.00
Kurtosis: 1.487 Cond. No. 6.89


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.000\n", + "Model: OLS Adj. R-squared: 0.000\n", + "Method: Least Squares F-statistic: 6.417\n", + "Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113\n", + "Time: 21:21:09 Log-Likelihood: -25124.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.027e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const 2.2341 0.025 87.965 0.000 2.184 2.284\n", + "children 0.0168 0.007 2.533 0.011 0.004 0.030\n", + "==============================================================================\n", + "Omnibus: 76735.931 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843\n", + "Skew: -0.485 Prob(JB): 0.00\n", + "Kurtosis: 1.487 Cond. No. 6.89\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "\"\"\"" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y = new_df[\"recommend\"]\n", + "x = new_df[\"children\"]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.ols(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c826271", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO OLSR\n", + "This is an example of ordinary least squares regression using the 'R-like' statsmodels api, i.e. from a formula and dataframe using ACRO " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "cc90f7c9", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:olsr() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_8\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.001\n", + "Model: OLS Adj. R-squared: 0.001\n", + "Method: Least Squares F-statistic: 13.83\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.026e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "Intercept 2.2099 0.025 87.263 0.000 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.000 0.012 0.037\n", + "==============================================================================\n", + "Omnibus: 77090.215 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.570\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: recommend R-squared: 0.000\n", + "Model: OLS Adj. R-squared: 0.000\n", + "Method: Least Squares F-statistic: 6.417\n", + "Date: Mon, 04 Mar 2024 Prob (F-statistic): 0.0113\n", + "Time: 21:21:09 Log-Likelihood: -25124.\n", + "No. Observations: 12960 AIC: 5.025e+04\n", + "Df Residuals: 12958 BIC: 5.027e+04\n", + "Df Model: 1 \n", + "Covariance Type: nonrobust \n", + "==============================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "Intercept 2.2341 0.025 87.965 0.000 2.184 2.284\n", + "children 0.0168 0.007 2.533 0.011 0.004 0.030\n", + "==============================================================================\n", + "Omnibus: 76735.931 Durbin-Watson: 2.883\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1742.843\n", + "Skew: -0.485 Prob(JB): 0.00\n", + "Kurtosis: 1.487 Cond. No. 6.89\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n" + ] + } + ], + "source": [ + "results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\n", + "print(results.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "2816eac7", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO Probit\n", + "This is an example of probit regression using ACRO \n", + "We use a different combination of variables from the original dataset.\n", + "\n", + "Again, we support the use of R-like formulas - because we support R " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "5b1a1611", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:probit() outcome: pass; dof=12958.0 >= 10\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.693145\n", + " Iterations 2\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add(): output_9\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Probit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Probit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06\n", + "Time: 19:39:47 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.7992\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0039 0.019 -0.207 0.836 -0.041 0.033\n", + "children 0.0012 0.005 0.254 0.799 -0.008 0.011\n", + "==============================================================================\n" + ] + } + ], + "source": [ + "new_df = df[[\"finance\", \"children\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"finance\"].astype(\"category\").cat.codes # numeric\n", + "y.name = \"finance\"\n", + "x = new_df[\"children\"]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.probit(y, x)\n", + "print(results.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "f38b4334", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ACRO Logit\n", + "This is an example of logistic regression using ACRO using the statmodels function" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "dcf30f8f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:logit() outcome: pass; dof=12958.0 >= 10\n", + "INFO:acro:records:add(): output_10\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.693145\n", + " Iterations 3\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: finance No. Observations: 12960
Model: Logit Df Residuals: 12958
Method: MLE Df Model: 1
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06
Time: 19:39:47 Log-Likelihood: -8983.2
converged: True LL-Null: -8983.2
Covariance Type: nonrobust LLR p-value: 0.7992
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const -0.0062 0.030 -0.207 0.836 -0.065 0.053
children 0.0020 0.008 0.254 0.799 -0.013 0.017
" + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & finance & \\textbf{ No. Observations: } & 12960 \\\\\n", + "\\textbf{Model:} & Logit & \\textbf{ Df Residuals: } & 12958 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 1 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 3.602e-06 \\\\\n", + "\\textbf{Time:} & 19:39:47 & \\textbf{ Log-Likelihood: } & -8983.2 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -8983.2 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 0.7992 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & -0.0062 & 0.030 & -0.207 & 0.836 & -0.065 & 0.053 \\\\\n", + "\\textbf{children} & 0.0020 & 0.008 & 0.254 & 0.799 & -0.013 & 0.017 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Logit Regression Results}\n", + "\\end{center}" + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Logit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 3.602e-06\n", + "Time: 19:39:47 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.7992\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0062 0.030 -0.207 0.836 -0.065 0.053\n", + "children 0.0020 0.008 0.254 0.799 -0.013 0.017\n", + "==============================================================================\n", + "\"\"\"" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: finance No. Observations: 12960
Model: Logit Df Residuals: 12958
Method: MLE Df Model: 1
Date: Mon, 04 Mar 2024 Pseudo R-squ.: 1.186e-06
Time: 21:21:09 Log-Likelihood: -8983.2
converged: True LL-Null: -8983.2
Covariance Type: nonrobust LLR p-value: 0.8839
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const -0.0036 0.030 -0.119 0.905 -0.063 0.056
children 0.0012 0.008 0.146 0.884 -0.014 0.017
" + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: finance No. Observations: 12960\n", + "Model: Logit Df Residuals: 12958\n", + "Method: MLE Df Model: 1\n", + "Date: Mon, 04 Mar 2024 Pseudo R-squ.: 1.186e-06\n", + "Time: 21:21:09 Log-Likelihood: -8983.2\n", + "converged: True LL-Null: -8983.2\n", + "Covariance Type: nonrobust LLR p-value: 0.8839\n", + "==============================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "------------------------------------------------------------------------------\n", + "const -0.0036 0.030 -0.119 0.905 -0.063 0.056\n", + "children 0.0012 0.008 0.146 0.884 -0.014 0.017\n", + "==============================================================================\n", + "\"\"\"" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.logit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "a962ff17", + "metadata": {}, + "source": [ + "### ACRO survival analysis\n", + "This is an example of survival tables and plots using ACRO. \n", + "- A dataset from statsmodels is used for the survival analysis.\n", + "- A subset of tha dataset is used in this example to demostrate the survival analysis.\n", + "- The output parameter in the surv_func define the type of output (table or plot)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "dcf2c86d", + "metadata": {}, + "outputs": [], + "source": [ + "import statsmodels.api as sm\n", + "\n", + "data = sm.datasets.get_rdataset(\"flchain\", \"survival\").data\n", + "data = data.loc[data.sex == \"F\", :]\n", + "data = data.iloc[:20, :]\n", + "# data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "df1cbb9e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 76 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------|\n", + " Surv_prob |Surv_prob_SE |num_at_risk |num_events |\n", + "Time | | | |\n", + "-----------------------------------------------------------|\n", + "51 ok | ok | ok | ok|\n", + "69 threshold; | threshold; | threshold; | threshold; |\n", + "85 threshold; | threshold; | threshold; | threshold; |\n", + "91 threshold; | threshold; | threshold; | threshold; |\n", + "115 threshold; | threshold; | threshold; | threshold; |\n", + "372 threshold; | threshold; | threshold; | threshold; |\n", + "667 threshold; | threshold; | threshold; | threshold; |\n", + "874 threshold; | threshold; | threshold; | threshold; |\n", + "1039 threshold; | threshold; | threshold; | threshold; |\n", + "1046 threshold; | threshold; | threshold; | threshold; |\n", + "1281 threshold; | threshold; | threshold; | threshold; |\n", + "1286 threshold; | threshold; | threshold; | threshold; |\n", + "1326 threshold; | threshold; | threshold; | threshold; |\n", + "1355 threshold; | threshold; | threshold; | threshold; |\n", + "1626 threshold; | threshold; | threshold; | threshold; |\n", + "1903 threshold; | threshold; | threshold; | threshold; |\n", + "1914 threshold; | threshold; | threshold; | threshold; |\n", + "2776 threshold; | threshold; | threshold; | threshold; |\n", + "2851 threshold; | threshold; | threshold; | threshold; |\n", + "3309 threshold; | threshold; | threshold; | threshold; |\n", + "-----------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_11\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN\n" + ] + } + ], + "source": [ + "safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "print(safe_table)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "8762ab29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 76 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "-----------------------------------------------------------|\n", + " Surv_prob |Surv_prob_SE |num_at_risk |num_events |\n", + "Time | | | |\n", + "-----------------------------------------------------------|\n", + "51 ok | ok | ok | ok|\n", + "69 threshold; | threshold; | threshold; | threshold; |\n", + "85 threshold; | threshold; | threshold; | threshold; |\n", + "91 threshold; | threshold; | threshold; | threshold; |\n", + "115 threshold; | threshold; | threshold; | threshold; |\n", + "372 threshold; | threshold; | threshold; | threshold; |\n", + "667 threshold; | threshold; | threshold; | threshold; |\n", + "874 threshold; | threshold; | threshold; | threshold; |\n", + "1039 threshold; | threshold; | threshold; | threshold; |\n", + "1046 threshold; | threshold; | threshold; | threshold; |\n", + "1281 threshold; | threshold; | threshold; | threshold; |\n", + "1286 threshold; | threshold; | threshold; | threshold; |\n", + "1326 threshold; | threshold; | threshold; | threshold; |\n", + "1355 threshold; | threshold; | threshold; | threshold; |\n", + "1626 threshold; | threshold; | threshold; | threshold; |\n", + "1903 threshold; | threshold; | threshold; | threshold; |\n", + "1914 threshold; | threshold; | threshold; | threshold; |\n", + "2776 threshold; | threshold; | threshold; | threshold; |\n", + "2851 threshold; | threshold; | threshold; | threshold; |\n", + "3309 threshold; | threshold; | threshold; | threshold; |\n", + "-----------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_12\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(, 'acro_artifacts/kaplan-mier_0.png')\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGwCAYAAAB7MGXBAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAANFlJREFUeJzt3Xl4VPXd///XZJkkZBmEbCwhRK1IiWANLQSKVqoBXCn1Ngo/AUVvI6LFqBW0FaReRu0XSlHBKovSC4Wror3pZaqmVRYNqESgIEipoEFJDEklkxDIen5/hBkYsjAzmZmTZJ6P65rrIifnzLzPhxPy4rOcYzEMwxAAAIBJQswuAAAABDfCCAAAMBVhBAAAmIowAgAATEUYAQAApiKMAAAAUxFGAACAqcLMLsAdTU1NOnLkiGJjY2WxWMwuBwAAuMEwDFVVValv374KCWm7/6NLhJEjR44oJSXF7DIAAIAXDh8+rP79+7f5/S4RRmJjYyU1n0xcXJzJ1QAAAHfY7XalpKQ4f4+3pUuEEcfQTFxcHGEEAIAu5lxTLJjACgAATEUYAQAApiKMAAAAU3WJOSMA0Jk0Njaqvr7e7DIA04WHhys0NLTD70MYAQA3GYah0tJSHTt2zOxSgE6jZ8+eSk5O7tB9wAgjAOAmRxBJTExUjx49uAkjgpphGKqpqVFZWZkkqU+fPl6/F2EEANzQ2NjoDCK9e/c2uxygU4iKipIklZWVKTEx0eshGyawAoAbHHNEevToYXIlQOfi+JnoyDwqwggAeIChGcCVL34mCCMAAMBUhBEAAGAqwggAIGA2btwoi8XS4eXRAwcO1OLFi31Skz989dVXslgs2rlzp8/e85VXXlHPnj3d3v+ll15SSkqKQkJCOnVbSaymAQDA51JSUlRSUqL4+HhTPt9ut2vWrFlatGiRfvnLX8pms5lSh7u6fBh5/4vvVNfQZHYZ6IISYiN12YCeTEhE0Kmrq5PVajW7jC6rvr5e4eHh7e4TGhqq5OTkAFXUUnFxserr63Xttdd26P4fgdLlw8iv3/iXyqvrzC4DXdT6ezKVkdrL7DLQRRmGoRP1jQH/3KjwUI9C9M9+9jOlp6fLarVq9erVGjJkiBYsWKCHH35Yu3btUq9evTRt2jQ9+eSTCgtr/rUwcOBAzZ49W7Nnz3a+z6WXXqqJEydq/vz5kppXUbz88st6++239e6776pfv35auHChbrjhBucx+fn5mj17tg4fPqyRI0dq2rRpLeorLCzUnDlz9Omnnyo+Pl6/+MUvlJeXp+joaEnN97CYMWOG/vGPfyg5OVlPPvmkR+01f/58rVy5Ut9995169+6tm266SUuWLHGew1tvvaWJEyc69+/Zs6cWL16s6dOn66uvvlJaWprWrVunpUuXatu2bXrmmWc0Z84cvfXWWxo/frzzuDfffFO33XabvvvuO5WXlystLU07duzQ0KFDNWDAAP3mN79RTk6Oc//PPvtMGRkZ+vLLL3X++edr0aJFWrVqlQ4ePKhevXrp+uuv17PPPquYmBiPzveVV17R7bffLkk6//zzJUmHDh3S/PnzdezYMf31r3917jt79mzt3LlTGzdulNR8rQwdOlSRkZFavny5rFarcnJynH/n/tLlw8iw/j1VeYJnRMAz27/+XpK0v7SaMAKvnahv1A8ffzfgn7t3wTj1sHr2z/err76qe+65Rx999JHKy8uVlZWl6dOna/Xq1friiy901113KTIy0uNfOk888YSeffZZ/f73v9dzzz2nKVOm6Ouvv1avXr10+PBhTZo0STk5Obrnnnu0fft2Pfjggy7H7969W+PGjdPvfvc7rVixQkePHtWsWbM0a9YsrVq1SpI0ffp0HT58WO+//76sVqvuv/9+510/z+WNN97QH/7wB61du1ZDhgxRaWmpdu3a5dE5StIjjzyihQsXatWqVYqIiNCWLVu0Zs0alzDy2muv6cYbb1RMTIzKy8ud20NCQnTLLbdozZo1LmHktddeU2ZmpjMwhISEaMmSJRo4cKAOHTqkmTNn6te//rWWLl3qUa3Z2dlKSUnRVVddpU8++UQpKSlKSEhw+/hXX31Vubm5+vjjj7V161ZNnz5do0eP1tVXX+1RHZ7o8mFkxfQfm10CuqC5b/5Lr39yWEeras0uBQiICy+8UM8++6wkafXq1UpJSdHzzz8vi8Wiiy++WEeOHNEjjzyixx9/XCEh7q9tmD59um699VZJ0lNPPaXnnntOn3zyicaPH69ly5bp/PPP1x/+8AdZLBYNGjRIu3fv1jPPPOM8/ve//70mT57s7IH5wQ9+oCVLluiKK67QsmXLVFxcrL///e/atm2bRowYIUlasWKFBg8e7FZ9xcXFSk5O1lVXXaXw8HANGDBAP/nJT9w+P4fZs2dr0qRJzq+nTJmiqVOnqqamRj169JDdbtfbb7+t9evXt3r8lClTtGjRIn399ddKTU1VU1OT1q5dq0cffdTlMxzS0tL0u9/9Tvfcc4/HYSQqKsp5l+CEhASPh4uGDh2qefPmSWr++3j++ef1z3/+kzAC+FpCbKQkqazqpMmVoCuLCg/V3gXjTPlcTw0fPtz553379ikzM9NlqGf06NGqrq7WN998owEDBrj9vkOHDnX+OTo6WrGxsc5ei3379mnkyJEun5OZmelyfFFRkf7zn/9ozZo1zm2GYaipqUmHDh3Sv//9b4WFhbnUf/HFF7u9quR//ud/tHjxYp1//vkaP368rrnmGl1//fXO4Sh3nfn5knTttdcqLCxMGzZs0C233KL169crNjZWWVlZrR7/ox/9SBdffLFef/11zZkzR5s2bVJZWZluvvlm5z4ffPCBnnrqKe3du1d2u10NDQ06efKkjh8/7hyyCoQz/06l5mfOuNsT5S2W9iIoJcRGSBI9I+gQi8WiHtawgL+8mXR95i8zwzBavIdhGM5zkpqHDBzbHFq73ffZEzktFouamppc3rM9TU1Nuvvuu7Vz507na9euXTpw4IAuuOCCFnV5KiUlRfv379cLL7ygqKgozZw5U5dffrnzXCwWi1vneXYYsFqtuummm/Taa69Jah5yyc7ObjfkTJkyxWX/cePGOVfbfP3117rmmmuUnp6u9evXq6ioSC+88EKb9XjDF3+n/kIYQVBKPBVGyggjCEI//OEPVVhY6PKLqbCwULGxserXr5+k5u79kpIS5/ftdrsOHTrk8eds27bNZdvZX1922WX6/PPPdeGFF7Z4Wa1WDR48WA0NDdq+fbvzmP3793t0n5KoqCjdcMMNWrJkiTZu3KitW7dq9+7drZ7ngQMHVFNT49b7TpkyRe+8844+//xzffDBB5oyZUq7+0+ePFm7d+9WUVGR3njjDZf9t2/froaGBi1cuFAjR47URRddpCNHjrh9ju44+1wl+fQ+KB1BGEFQomcEwWzmzJk6fPiw7rvvPn3xxRf6v//7P82bN0+5ubnO+SJjx47Vn//8Z23ZskV79uzRtGnTPH4ia05Ojr788kvl5uZq//79eu211/TKK6+47PPII49o69atuvfee7Vz504dOHBAGzZs0H333SdJGjRokMaPH6+77rpLH3/8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R1LzEMvUqVOd++fk5Ojrr79Wbm6u9u3bp5UrV2rFihV66KGHfHcWAACgy/J4zkh2drYqKiq0YMEClZSUKD09Xfn5+UpNTZUklZSUuNxzJC0tTfn5+XrggQf0wgsvqG/fvlqyZIl++ctfuv2ZERERmjdvXqtDN8GOtmkf7dM22qZ9tE/baJu20Tbe8fg+IwAAAL7Es2kAAICpCCMAAMBUhBEAAGAqwggAADBVpw8jS5cuVVpamiIjI5WRkaEtW7aYXZLfzZ8/XxaLxeWVnJzs/L5hGJo/f7769u2rqKgo/exnP9Pnn3/u8h61tbW67777FB8fr+joaN1www365ptvAn0qPrF582Zdf/316tu3rywWi/7617+6fN9X7fH999/rtttuk81mk81m02233aZjx475+ew65lxtM3369BbX0siRI1326a5tk5eXpx//+MeKjY1VYmKiJk6cqP3797vsE6zXjjttE8zXzrJlyzR06FDnjcsyMzP197//3fn9YL1u/MroxNauXWuEh4cbL7/8srF3717jV7/6lREdHW18/fXXZpfmV/PmzTOGDBlilJSUOF9lZWXO7z/99NNGbGyssX79emP37t1Gdna20adPH8Nutzv3ycnJMfr162cUFBQYn332mXHllVcaw4YNMxoaGsw4pQ7Jz883HnvsMWP9+vWGJOOtt95y+b6v2mP8+PFGenq6UVhYaBQWFhrp6enGddddF6jT9Mq52mbatGnG+PHjXa6liooKl326a9uMGzfOWLVqlbFnzx5j586dxrXXXmsMGDDAqK6udu4TrNeOO20TzNfOhg0bjLffftvYv3+/sX//fuPRRx81wsPDjT179hiGEbzXjT916jDyk5/8xMjJyXHZdvHFFxtz5swxqaLAmDdvnjFs2LBWv9fU1GQkJycbTz/9tHPbyZMnDZvNZrz44ouGYRjGsWPHjPDwcGPt2rXOfb799lsjJCTEeOedd/xau7+d/QvXV+2xd+9eQ5Kxbds25z5bt241JBlffPGFn8/KN9oKIzfeeGObxwRL2xiGYZSVlRmSjE2bNhmGwbVzprPbxjC4ds523nnnGcuXL+e68ZNOO0xTV1enoqIiZWVluWzPyspSYWGhSVUFzoEDB9S3b1+lpaXplltu0cGDByVJhw4dUmlpqUu7RERE6IorrnC2S1FRkerr61326du3r9LT07td2/mqPbZu3SqbzaYRI0Y49xk5cqRsNluXb7ONGzcqMTFRF110ke666y6VlZU5vxdMbVNZWSlJ6tWrlySunTOd3TYOXDtSY2Oj1q5dq+PHjyszM5Prxk86bRgpLy9XY2NjiwfwJSUltXjwXnczYsQIrV69Wu+++65efvlllZaWatSoUaqoqHCee3vtUlpaKqvVqvPOO6/NfboLX7VHaWmpEhMTW7x/YmJil26zCRMmaM2aNXr//fe1cOFCffrppxo7dqxqa2slBU/bGIah3Nxc/fSnP1V6erokrh2H1tpG4trZvXu3YmJiFBERoZycHL311lv64Q9/yHXjJx7fDj7QLBaLy9eGYbTY1t1MmDDB+edLLrlEmZmZuuCCC/Tqq686J5B50y7due180R6t7d/V2yw7O9v55/T0dA0fPlypqal6++23NWnSpDaP625tM2vWLP3rX//Shx9+2OJ7wX7ttNU2wX7tDBo0SDt37tSxY8e0fv16TZs2TZs2bXJ+P9ivG1/rtD0j8fHxCg0NbZEQy8rKWiTS7i46OlqXXHKJDhw44FxV0167JCcnq66uTt9//32b+3QXvmqP5ORkfffddy3e/+jRo92qzfr06aPU1FQdOHBAUnC0zX333acNGzbogw8+UP/+/Z3buXbabpvWBNu1Y7VadeGFF2r48OHKy8vTsGHD9Mc//pHrxk86bRixWq3KyMhQQUGBy/aCggKNGjXKpKrMUVtbq3379qlPnz5KS0tTcnKyS7vU1dVp06ZNznbJyMhQeHi4yz4lJSXas2dPt2s7X7VHZmamKisr9cknnzj3+fjjj1VZWdmt2qyiokKHDx9Wnz59JHXvtjEMQ7NmzdKbb76p999/X2lpaS7fD+Zr51xt05pgunZaYxiGamtrg/q68auATpf1kGNp74oVK4y9e/cas2fPNqKjo42vvvrK7NL86sEHHzQ2btxoHDx40Ni2bZtx3XXXGbGxsc7zfvrppw2bzWa8+eabxu7du41bb7211WVl/fv3N/7xj38Yn332mTF27Nguu7S3qqrK2LFjh7Fjxw5DkrFo0SJjx44dziXevmqP8ePHG0OHDjW2bt1qbN261bjkkks6/TK79tqmqqrKePDBB43CwkLj0KFDxgcffGBkZmYa/fr1C4q2ueeeewybzWZs3LjRZXlqTU2Nc59gvXbO1TbBfu3MnTvX2Lx5s3Ho0CHjX//6l/Hoo48aISEhxnvvvWcYRvBeN/7UqcOIYRjGCy+8YKSmphpWq9W47LLLXJaedVeONevh4eFG3759jUmTJhmff/658/tNTU3GvHnzjOTkZCMiIsK4/PLLjd27d7u8x4kTJ4xZs2YZvXr1MqKioozrrrvOKC4uDvSp+MQHH3xgSGrxmjZtmmEYvmuPiooKY8qUKUZsbKwRGxtrTJkyxfj+++8DdJbeaa9tampqjKysLCMhIcEIDw83BgwYYEybNq3FeXfXtmmtXSQZq1atcu4TrNfOudom2K+dO+64w/l7JyEhwfj5z3/uDCKGEbzXjT9ZDMMwAtcPAwAA4KrTzhkBAADBgTACAABMRRgBAACmIowAAABTEUYAAICpCCMAAMBUhBEAAGAqwggAADAVYQSA382fP1+XXnqp2WUA6KS4AyuADjnX486nTZum559/XrW1terdu3eAqgLQlRBGAHTImY9SX7dunR5//HHt37/fuS0qKko2m82M0gB0EQzTAOiQ5ORk58tms8lisbTYdvYwzfTp0zVx4kQ99dRTSkpKUs+ePfXEE0+ooaFBDz/8sHr16qX+/ftr5cqVLp/17bffKjs7W+edd5569+6tG2+8UV999VVgTxiAzxFGAJji/fff15EjR7R582YtWrRI8+fP13XXXafzzjtPH3/8sXJycpSTk6PDhw9LkmpqanTllVcqJiZGmzdv1ocffqiYmBiNHz9edXV1Jp8NgI4gjAAwRa9evbRkyRINGjRId9xxhwYNGqSamho9+uij+sEPfqC5c+fKarXqo48+kiStXbtWISEhWr58uS655BINHjxYq1atUnFxsTZu3GjuyQDokDCzCwAQnIYMGaKQkNP/H0pKSlJ6errz69DQUPXu3VtlZWWSpKKiIv3nP/9RbGysy/ucPHlSX375ZWCKBuAXhBEApggPD3f52mKxtLqtqalJktTU1KSMjAytWbOmxXslJCT4r1AAfkcYAdAlXHbZZVq3bp0SExMVFxdndjkAfIg5IwC6hClTpig+Pl433nijtmzZokOHDmnTpk361a9+pW+++cbs8gB0AGEEQJfQo0cPbd68WQMGDNCkSZM0ePBg3XHHHTpx4gQ9JUAXx03PAACAqegZAQAApiKMAAAAUxFGAACAqQgjAADAVIQRAABgKsIIAAAwFWEEAACYijACAABMRRgBAACmIowAAABTEUYAAICp/n+L3AbcO2Xl5QAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGwCAYAAAB7MGXBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA27UlEQVR4nO3de3hU9Z3H8c9MksmF3IBkEqCBgCCoEEDQGKmX1VRQi6W2XYo8clmLBcGVprUQq0Rb16hdWKqibKmobaGwVdFuRVxFsF5SKRFQKiJyMVTJBSgJCZDLzG//iDNhIAkzycycXN6v55nnIWd+Z+Z7DjPkw+9yjs0YYwQAAGARu9UFAACA7o0wAgAALEUYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgqUirC/CH2+3Wl19+qYSEBNlsNqvLAQAAfjDG6Pjx4+rbt6/s9pb7PzpFGPnyyy+VkZFhdRkAAKANDh48qK997WstPt8pwkhCQoKkxoNJTEy0uBoAAOCPqqoqZWRkeH+Pt6RThBHP0ExiYiJhBACATuZcUyyYwAoAACxFGAEAAJYijAAAAEt1ijkjANCRuFwu1dfXW10GYLmoqChFRES0+3UIIwDgJ2OMSktLdezYMatLATqM5ORkpaent+s6YIQRAPCTJ4g4nU7FxcVxEUZ0a8YYnThxQuXl5ZKkPn36tPm1CCMA4AeXy+UNIr1797a6HKBDiI2NlSSVl5fL6XS2eciGCawA4AfPHJG4uDiLKwE6Fs93oj3zqAgjABAAhmYAX8H4ThBGAACApQgjAADAUoQRAEDYbN68WTabrd3LozMzM7V06dKg1BQKBw4ckM1m0/bt24P2ms8++6ySk5P9bv/rX/9aGRkZstvtHfpcSaymAQAg6DIyMnTo0CGlpKRY8v5VVVWaN2+elixZou985ztKSkqypA5/deow4nIbvf5xqdVloJMa0LuHLujDXaDR/dTV1cnhcFhdRqdVX1+vqKioVttEREQoPT09TBWdraSkRPX19brxxhvbdf2PcOnUYaTe5dbs339gdRnopOw26e0F16hfcqzVpaCTMsboZL0r7O8bGxUR0AqGq6++WsOHD1dkZKR+//vfa8SIEbr//vt19913a8eOHerVq5emT5+uBx98UJGRjb8WMjMzNX/+fM2fP9/7OqNGjdKkSZN0//33S2pcRbFixQq98soreu2119SvXz8tXrxYN910k3ef9evXa/78+Tp48KAuu+wyTZ8+/az63nnnHeXn52vr1q1KSUnRt7/9bRUWFqpHjx6SGq9hcdttt+mNN95Qenq6HnzwQb+P3RijBx54QCtXrlRZWZl69+6t7373u3rssce8x7Bu3TpNmjTJu09ycrKWLl2qGTNm6MCBAxo4cKDWrFmjJ598Uu+//74effRRLViwQC+++KKuv/56737r1q3TtGnTVFZWpvLycg0cOFDbtm1TVlaW+vfvr5/97GeaM2eOt/22bds0ZswY7d+/XwMGDNCSJUv0zDPPaN++ferVq5cmTpyoRx99VPHx8X4fr9Q4nDNz5kxJ0qBBgyRJ+/fv1/33369jx47ppZde8radP3++tm/frs2bN0tq/KxkZWUpJiZGv/nNb+RwODR79mzv33modOowYrNJYwf0tLoMdEJbP/+n3EbaW15NGEGbnax36cJFr4X9fT/++XjFOQL75/u5557TnDlz9O6776q0tFQ33HCDZsyYod/+9rf65JNPNGvWLMXExAT8S+eBBx7Qo48+ql/+8pd6/PHHNXXqVH3++efq1auXDh48qJtvvllz587V7bffrq1bt+rHP/6xz/579+7VhAkT9OCDD2rlypWqqKjQvHnzNG/ePD3zzDOSpBkzZujLL7/Upk2bFBUVpX//93/3XvXzXF544QX913/9l9asWaOLLrpIpaWl2rFjR0DHKEkLFy7U4sWLNXr0aMXExOjtt9/W6tWrfcLIqlWrNGnSpLOuRWO32zVlyhStXr3aJ4ysWrVK48aN04ABA7ztHnvsMQ0cOFD79u3THXfcoZ/+9Kd68sknA6p18uTJysjIUG5urrZs2aKMjAylpqb6vf9zzz2nvLw8vf/++yoqKtKMGTM0btw4feMb3wiojkB06jASHRmh5+dcbnUZ6IRuffp9vb3nsCqO11pdChAWQ4YM0aOPPipJ+u1vf6uMjAw98cQTstlsGjZsmL788kstWLBAixYtkt3u/9qGGTNmaMqUKZKkhx56SI899pi2bNmiCRMm6KmnntJ5552nxYsXS5KGDh2qjz76SI888oh3/8LCQk2dOtXbAzNkyBA99thjuuqqq/TUU0+ppKREr776qrZs2aJLLrlEkvT000/rggsu8Ku+kpISpaenKzc3V1FRUerfv78uvfRSv4/PY/78+br55pu9P0+dOlW33nqrTpw4obi4OFVVVemVV17RunXrmt1/6tSpWrx4sUpKStS/f3+53W6tWbNG9957r897eGRmZurBBx/U7NmzAw4jsbGx3qsEp6amBjxclJWVpYKCAkmNfx9PPPGENm7cSBgBgi01IVqSVE4YQTvERkXo45+Pt+R9AzVmzBjvn3ft2qWcnByfoZ5x48apurpa//jHP9S/f3+/XzcrK8v75x49eigxMdHba7Fr1y5lZ2f7tM/JyfH5eceOHfrwww+1atUq7zZjjNxut/bv369PP/1UkZGRPvUPGzbM71Ul3/ve97R06VINGjRIEyZM0A033KCJEyd6h6P8NXbsWJ+fb7jhBkVFRelPf/qTvv/97+uFF15QYmKicnNzm91/1KhRuuCCC7R69WotXLhQb731lsrLy/W9733P2+aNN95QYWGhPvnkE1VVVamhoUGnTp3yBp5wOf3vVGq854y/PVFtxdJedEueMELPCNrDZrMpzhEZ9kdbrnjpmX/hL7vdLmOMz7bmLvd95kROm80mt9vt9/tUV1frhz/8obZv3+597NixQ3v27NF5550XUM3NycjI0O7du/Xkk08qNjZWd9xxh6688krvsdhsNr+O88zz53A49N3vflerV6+WJK1evVqTJ09uNeRMnTrVp/2ECRO8PRgHDhzQN7/5TWVlZemFF15QcXGxli1bJqlxwnEwhOvvtE21hfTVgQ7KmRAjSSo/fsriSoDwu+CCC1RUVOTzi+ndd99VQkKCvva1r0lq7N4/dOiQ9/mqqirt378/4PfZsmWLz7a//vWvPj9ffPHF+vjjjzV48OCzHg6HQ8OGDVNDQ4OKi4u9++zevTug65TExsZq4sSJeuyxx7R582YVFRXpo48+avY49+zZoxMnTvj1ulOnTtWGDRv097//XW+++aamTp3aavtbbrlFO3fuVHFxsZ5//nmf9sXFxXK73Vq8eLEuu+wynX/++fryyy/9PkZ/nHmskoJ6HZT2IIygW6JnBN3ZHXfcoYMHD+rOO+/UJ598opdfflkFBQXKy8vzzhe55ppr9Lvf/U5vv/22PvroI02fPj3gO7LOnj1be/bs0d13363du3dr9erVevbZZ33aLFiwQO+9957mzZun7du3a8+ePXr55Zc1b948SY3zTCZMmKAf/vCHev/991VcXKwf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kd3e1OFy+eWXa/Xq1br33nt1zz33aMiQIXrppZc0fPhwv98zOjpaBQUFzQ7ddHecm9ZxflrGuWkZ56Z1nJ+WcW7aJuDrjAAAAAQT96YBAACWIowAAABLEUYAAIClCCMAAMBSHT6MLFu2TJmZmYqJiVF2dra2bNlidUkhd//998tms/k8hg0b5n3+1KlTmjt3rnr37q34+Hh95zvfOevCciUlJbrxxhsVFxcnp9Opu+++Ww0NDeE+lKD4y1/+ookTJ6pv376y2Wxn3dfIGKNFixapT58+io2NVW5urvbs2ePT5ujRo5o6daoSExOVnJys2267TdXV1T5tPvzwQ11xxRWKiYlRRkaGHn300VAfWrud69zMmDHjrM/ShAkTfNp01XNTWFioSy65RAkJCXI6nZo0aZJ2797t0yZY36XNmzfr4osvVnR0tAYPHqxnn3021IfXLv6cm6uvvvqsz87s2bN92nTFcyNJTz31lLKysrwXLsvJydGrr77qfb67fm5CynRga9asMQ6Hw6xcudL8/e9/N7NmzTLJycmmrKzM6tJCqqCgwFx00UXm0KFD3kdFRYX3+dmzZ5uMjAyzceNGs3XrVnPZZZeZyy+/3Pt8Q0ODGT58uMnNzTXbtm0z69evNykpKSY/P9+Kw2m39evXm5/97GfmxRdfNJLMunXrfJ5/+OGHTVJSknnppZfMjh07zE033WQGDhxoTp486W0zYcIEM3LkSPPXv/7VvP3222bw4MFmypQp3ucrKytNWlqamTp1qtm5c6f5wx/+YGJjY81///d/h+sw2+Rc52b69OlmwoQJPp+lo0eP+rTpqudm/Pjx5plnnjE7d+4027dvNzfccIPp37+/qa6u9rYJxndp3759Ji4uzuTl5ZmPP/7YPP744yYiIsJs2LAhrMcbCH/OzVVXXWVmzZrl89mprKz0Pt9Vz40xxvzpT38yr7zyivn000/N7t27zT333GOioqLMzp07jTHd93MTSh06jFx66aVm7ty53p9dLpfp27evKSwstLCq0CsoKDAjR45s9rljx46ZqKgo88c//tG7bdeuXUaSKSoqMsY0/oKy2+2mtLTU2+app54yiYmJpra2NqS1h9qZv3DdbrdJT083v/zlL73bjh07ZqKjo80f/vAHY4wxH3/8sZFk/va3v3nbvPrqq8Zms5kvvvjCGGPMk08+aXr27OlzfhYsWGCGDh0a4iMKnpbCyLe+9a0W9+ku58YYY8rLy40k89Zbbxljgvdd+ulPf2ouuugin/eaPHmyGT9+fKgPKWjOPDfGNIaRu+66q8V9usu58ejZs6f5zW9+w+cmRDrsME1dXZ2Ki4uVm5vr3Wa325Wbm6uioiILKwuPPXv2qG/fvho0aJCmTp2qkpISSVJxcbHq6+t9zsuwYcPUv39/73kpKirSiBEjfO6WPH78eFVVVenvf/97eA8kxPbv36/S0lKf85GUlKTs7Gyf85GcnKyxY8d62+Tm5sput+v999/3trnyyivlcDi8bcaPH6/du3frn//8Z5iOJjQ2b94sp9OpoUOHas6cOTpy5Ij3ue50biorKyVJvXr1khS871JRUZHPa3jadKZ/p848Nx6rVq1SSkqKhg8frvz8fJ04ccL7XHc5Ny6XS2vWrFFNTY1ycnL43IRIh7xrryQdPnxYLpfL5y9TktLS0vTJJ59YVFV4ZGdn69lnn9XQoUN16NAhPfDAA7riiiu0c+dOlZaWyuFwKDk52WeftLQ0lZaWSpJKS0ubPW+e57oSz/E0d7ynnw+n0+nzfGRkpHr16uXTZuDAgWe9hue5nj17hqT+UJswYYJuvvlmDRw4UHv37tU999yj66+/XkVFRYqIiOg258btdmv+/PkaN26c9+rPwfoutdSmqqpKJ0+eVGxsbCgOKWiaOzeSdMstt2jAgAHq27evPvzwQy1YsEC7d+/Wiy++KKnrn5uPPvpIOTk5OnXqlOLj47Vu3TpdeOGF2r59O5+bEOiwYaQ7u/76671/zsrKUnZ2tgYMGKD/+Z//6XYfULTP97//fe+fR4wYoaysLJ133nnavHmzrr32WgsrC6+5c+dq586deuedd6wupcNp6dzcfvvt3j+PGDFCffr00bXXXqu9e/fqvPPOC3eZYTd06FBt375dlZWVev755zV9+nS99dZbVpfVZXXYYZqUlBRFREScNUO5rKxM6enpFlVljeTkZJ1//vn67LPPlJ6errq6Oh07dsynzennJT09vdnz5nmuK/EcT2ufk/T0dJWXl/s839DQoKNHj3a7czZo0CClpKTos88+k9Q9zs28efP05z//WZs2bdLXvvY17/ZgfZdaapOYmNjh//PQ0rlpTnZ2tiT5fHa68rlxOBwaPHiwxowZo8LCQo0cOVK/+tWv+NyESIcNIw6HQ2PGjNHGjRu929xutzZu3KicnBwLKwu/6upq7d27V3369NGYMWMUFRXlc152796tkpIS73nJycnRRx995PNL5vXXX1diYqIuvPDCsNcfSgMHDlR6errP+aiqqtL777/vcz6OHTum4uJib5s333xTbrfb+w9sTk6O/vKXv6i+vt7b5vXXX9fQoUM7xTCEv/7xj3/oyJEj6tOnj6SufW6MMZo3b57WrVunN99886yhpmB9l3Jycnxew9OmI/87da5z05zt27dLks9npyuem5a43W7V1tZ2689NSFk9g7Y1a9asMdHR0ebZZ581H3/8sbn99ttNcnKyzwzlrujHP/6x2bx5s9m/f7959913TW5urklJSTHl5eXGmMZlZf379zdvvvmm2bp1q8nJyTE5OTne/T3Lyq677jqzfft2s2HDBpOamtppl/YeP37cbNu2zWzbts1IMkuWLDHbtm0zn3/+uTGmcWlvcnKyefnll82HH35ovvWtbzW7tHf06NHm/fffN++8844ZMmSIz/LVY8eOmbS0NHPrrbeanTt3mjVr1pi4uLgOv3y1tXNz/Phx85Of/MQUFRWZ/fv3mzfeeMNcfPHFZsiQIebUqVPe1+iq52bOnDkmKSnJbN682Wd56okTJ7xtgvFd8izRvPvuu82uXbvMsmXLOvwSzXOdm88++8z8/Oc/N1u3bjX79+83L7/8shk0aJC58sorva/RVc+NMcYsXLjQvPXWW2b//v3mww8/NAsXLjQ2m8383//9nzGm+35uQqlDhxFjjHn88cdN//79jcPhMJdeeqn561//anVJITd58mTTp08f43A4TL9+/czkyZPNZ5995n3+5MmT5o477jA9e/Y0cXFx5tvf/rY5dOiQz2scOHDAXH/99SY2NtakpKSYH//4x6a+vj7chxIUmzZtMpLOekyfPt0Y07i897777jNpaWkmOjraXHvttWb37t0+r3HkyBEzZcoUEx8fbxITE83MmTPN8ePHfdrs2LHDfP3rXzfR0dGmX79+5uGHHw7XIbZZa+fmxIkT5rrrrjOpqakmKirKDBgwwMyaNeusMN9Vz01z50WSeeaZZ7xtgvVd2rRpkxk1apRxOBxm0KBBPu/REZ3r3JSUlJgrr7zS9OrVy0RHR5vBgwebu+++2+c6I8Z0zXNjjDH/9m//ZgYMGGAcDodJTU011157rTeIGNN9PzehZDPGmPD1wwAAAPjqsHNGAABA90AYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgKcIIAACwFGEEAABYijACIORmzJihSZMmWV0GgA4q0uoCAHRuNput1ecLCgr0q1/9SlzsGUBLCCMA2uXQoUPeP69du1aLFi3S7t27vdvi4+MVHx9vRWkAOgmGaQC0S3p6uveRlJQkm83msy0+Pv6sYZqrr75ad955p+bPn6+ePXsqLS1NK1asUE1NjWbOnKmEhAQNHjxYr776qs977dy5U9dff73i4+OVlpamW2+9VYcPHw7zEQMINsIIAEs899xzSklJ0ZYtW3TnnXdqzpw5+t73vqfLL79cH3zwga677jrdeuutOnHihCTp2LFjuuaaazR69Ght3bpVGzZsUFlZmf71X//V4iMB0F6EEQCWGDlypO69914NGTJE+fn5iomJUUpKimbNmqUhQ4Zo0aJFOnLkiD788ENJ0hNPPKHRo0froYce0rBhwzR69GitXLlSmzZt0qeffmrx0QBoD+aMALBEVlaW988RERHq3bu3RowY4d2WlpYmSSovL5ck7dixQ5s2bWp2/snevXt1/vnnh7hiAKFCGAFgiaioKJ+fbTabzzbPKh232y1Jqq6u1sSJE/XII4+c9Vp9+vQJYaUAQo0wAqBTuPjii/XCCy8oMzNTkZH80wV0JcwZAdApzJ07V0ePHtWUKVP0t7/9TXv37tVrr72mmTNnyuVyWV0egHYgjADoFPr27at3331XLpdL1113nUaMGKH58+crOTlZdjv/lAGdmc1wWUQAAGAh/jsBAAAsRRgBAACWIowAAABLEUYAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJYijAAAAEv9P6VlgXOlX8h0AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "safe_plot = acro.surv_func(\n", + " data.futime, data.death, output=\"plot\", filename=\"kaplan-mier.png\"\n", + ")\n", + "print(safe_plot)" + ] + }, + { + "cell_type": "markdown", + "id": "9e554eea", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# ACRO functionality to let users manage their outputs\n", + "\n", + "### 1: List current ACRO outputs\n", + "This is an example of using the print_output function to list all the outputs created so far" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "ec960039", + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:46.897407\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "summary: fail; threshold: 5 cells suppressed; \n", + "outcome: parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom ok ok ok ok\n", + "priority ok ok ok ok\n", + "recommend threshold; threshold; threshold; threshold; \n", + "spec_prior ok ok ok ok\n", + "very_recom threshold; ok ok ok\n", + "All ok ok ok ok\n", + "output: [parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958]\n", + "timestamp: 2025-03-06T19:39:46.961631\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:39:46.980090\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 1, 'p-ratio': 4, 'nk-rule': 4, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [4, 0]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [4, 0]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious \\\n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + "parents usual \n", + "recommend \n", + "not_recom ok \n", + "priority ok \n", + "recommend threshold; p-ratio; nk-rule; \n", + "spec_prior ok \n", + "very_recom ok \n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:47.019919\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 8, 'nk-rule': 8, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2], [2, 5]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "outcome: mode_aggfunc \\\n", + "parents great_pret pretentious \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + " mean \\\n", + "parents usual great_pret \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok p-ratio; nk-rule; \n", + "\n", + " \n", + "parents pretentious usual \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok ok \n", + "output: [ mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\n", + "timestamp: 2025-03-06T19:39:47.068066\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_5\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " children children\n", + "parents \n", + "great_pret ok ok\n", + "pretentious ok ok\n", + "usual ok ok\n", + "output: [ mean std\n", + " children children\n", + "parents \n", + "great_pret 3.140972 2.270396\n", + "pretentious 3.129630 2.250436\n", + "usual 3.110648 2.213072]\n", + "timestamp: 2025-03-06T19:39:47.105651\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 5, 'nk-rule': 5, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.pivot_table(\n", + "summary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "outcome: children \\\n", + "recommend not_recom priority recommend spec_prior \n", + "parents \n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok \n", + "usual ok ok threshold; p-ratio; nk-rule; ok \n", + "All ok ok threshold; p-ratio; nk-rule; ok \n", + "\n", + " \n", + "recommend very_recom All \n", + "parents \n", + "great_pret threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok \n", + "usual ok ok \n", + "All ok ok \n", + "output: [ children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412]\n", + "timestamp: 2025-03-06T19:39:47.231513\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_7\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'ols', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.ols(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ recommend R-squared: 0.001\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.001000\n", + "Method: Least Squares F-statistic: 13.830000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.000000\n", + "No. Observations: 12960 AIC: 50250.000000\n", + "Df Residuals: 12958 BIC: 50260.000000\n", + "Df Model: 1 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "const 2.2099 0.025 87.263 0.0 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90]\n", + "timestamp: 2025-03-06T19:39:47.388052\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_8\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'olsr', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ recommend R-squared: 0.001\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.001000\n", + "Method: Least Squares F-statistic: 13.830000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\n", + "Time: 19:39:47 Log-Likelihood: -25121.000000\n", + "No. Observations: 12960 AIC: 50250.000000\n", + "Df Residuals: 12958 BIC: 50260.000000\n", + "Df Model: 1 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "Intercept 2.2099 0.025 87.263 0.0 2.160 2.260\n", + "children 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\n", + "Skew: -0.486 Prob(JB): 0.00\n", + "Kurtosis: 1.489 Cond. No. 6.90]\n", + "timestamp: 2025-03-06T19:39:47.414293\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_9\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'probit', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.probit(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ finance No. Observations: 12960\n", + "Dep. Variable: \n", + "Model: Probit Df Residuals: 12958.000000\n", + "Method: MLE Df Model: 1.000000\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\n", + "Time: 19:39:47 Log-Likelihood: -8983.200000\n", + "converged: True LL-Null: -8983.200000\n", + "Covariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\n", + "const -0.0039 0.019 -0.207 0.836 -0.041 0.033\n", + "children 0.0012 0.005 0.254 0.799 -0.008 0.011]\n", + "timestamp: 2025-03-06T19:39:47.439598\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_10\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'logit', 'dof': 12958.0}\n", + "sdc: {}\n", + "command: results = acro.logit(y, x)\n", + "summary: pass; dof=12958.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ finance No. Observations: 12960\n", + "Dep. Variable: \n", + "Model: Logit Df Residuals: 12958.000000\n", + "Method: MLE Df Model: 1.000000\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\n", + "Time: 19:39:47 Log-Likelihood: -8983.200000\n", + "converged: True LL-Null: -8983.200000\n", + "Covariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\n", + "const -0.0062 0.030 -0.207 0.836 -0.065 0.053\n", + "children 0.0020 0.008 0.254 0.799 -0.013 0.017]\n", + "timestamp: 2025-03-06T19:39:47.457696\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_11\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Surv_prob Surv_prob_SE num_at_risk num_events\n", + "Time \n", + "51 ok ok ok ok\n", + "69 threshold; threshold; threshold; threshold; \n", + "85 threshold; threshold; threshold; threshold; \n", + "91 threshold; threshold; threshold; threshold; \n", + "115 threshold; threshold; threshold; threshold; \n", + "372 threshold; threshold; threshold; threshold; \n", + "667 threshold; threshold; threshold; threshold; \n", + "874 threshold; threshold; threshold; threshold; \n", + "1039 threshold; threshold; threshold; threshold; \n", + "1046 threshold; threshold; threshold; threshold; \n", + "1281 threshold; threshold; threshold; threshold; \n", + "1286 threshold; threshold; threshold; threshold; \n", + "1326 threshold; threshold; threshold; threshold; \n", + "1355 threshold; threshold; threshold; threshold; \n", + "1626 threshold; threshold; threshold; threshold; \n", + "1903 threshold; threshold; threshold; threshold; \n", + "1914 threshold; threshold; threshold; threshold; \n", + "2776 threshold; threshold; threshold; threshold; \n", + "2851 threshold; threshold; threshold; threshold; \n", + "3309 threshold; threshold; threshold; threshold; \n", + "output: [ Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN]\n", + "timestamp: 2025-03-06T19:39:48.298262\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_12\n", + "status: fail\n", + "type: survival plot\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_plot = acro.surv_func(\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/kaplan-mier_0.png']\n", + "timestamp: 2025-03-06T19:39:48.450221\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "'uid: output_0\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 4, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 4 cells suppressed; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:39:46.897407\\ncomments: []\\nexception: \\n\\nuid: output_1\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 5, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\\nsummary: fail; threshold: 5 cells suppressed; \\noutcome: parents great_pret pretentious usual All\\nrecommend \\nnot_recom ok ok ok ok\\npriority ok ok ok ok\\nrecommend threshold; threshold; threshold; threshold; \\nspec_prior ok ok ok ok\\nvery_recom threshold; ok ok ok\\nAll ok ok ok ok\\noutput: [parents great_pret pretentious usual All\\nrecommend \\nnot_recom 1440.0 1440 1440 4320\\npriority 858.0 1484 1924 4266\\nspec_prior 2022.0 1264 758 4044\\nvery_recom NaN 132 196 328\\nAll 4320.0 4320 4318 12958]\\ntimestamp: 2025-03-06T19:39:46.961631\\ncomments: []\\nexception: \\n\\nuid: output_2\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': False, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 4, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(df.recommend, df.parents)\\nsummary: fail; threshold: 4 cells may need suppressing; \\noutcome: parents great_pret pretentious usual\\nrecommend \\nnot_recom ok ok ok\\npriority ok ok ok\\nrecommend threshold; threshold; threshold; \\nspec_prior ok ok ok\\nvery_recom threshold; ok ok\\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440 1440 1440\\npriority 858 1484 1924\\nrecommend 0 0 2\\nspec_prior 2022 1264 758\\nvery_recom 0 132 196]\\ntimestamp: 2025-03-06T19:39:46.980090\\ncomments: []\\nexception: \\n\\nuid: output_3\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 1, \\'p-ratio\\': 4, \\'nk-rule\\': 4, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 2]], \\'p-ratio\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'nk-rule\\': [[2, 0], [2, 1], [2, 2], [4, 0]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \\noutcome: parents great_pret pretentious \\\\\\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom p-ratio; nk-rule; ok \\n\\nparents usual \\nrecommend \\nnot_recom ok \\npriority ok \\nrecommend threshold; p-ratio; nk-rule; \\nspec_prior ok \\nvery_recom ok \\noutput: [parents great_pret pretentious usual\\nrecommend \\nnot_recom 1440.0 1440.0 1440.0\\npriority 858.0 1484.0 1924.0\\nrecommend NaN NaN NaN\\nspec_prior 2022.0 1264.0 758.0\\nvery_recom NaN 132.0 196.0]\\ntimestamp: 2025-03-06T19:39:47.019919\\ncomments: []\\nexception: \\n\\nuid: output_4\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'crosstab\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 2, \\'p-ratio\\': 8, \\'nk-rule\\': 8, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[2, 2], [2, 5]], \\'p-ratio\\': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], \\'nk-rule\\': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.crosstab(\\nsummary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \\noutcome: mode_aggfunc \\\\\\nparents great_pret pretentious \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom p-ratio; nk-rule; ok \\n\\n mean \\\\\\nparents usual great_pret \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom ok p-ratio; nk-rule; \\n\\n \\nparents pretentious usual \\nrecommend \\nnot_recom ok ok \\npriority ok ok \\nrecommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \\nspec_prior ok ok \\nvery_recom ok ok \\noutput: [ mode_aggfunc mean \\nparents great_pret pretentious usual great_pret pretentious usual\\nrecommend \\nnot_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\\npriority 1.0 1.0 1.0 2.665501 3.030323 3.116944\\nrecommend NaN NaN NaN NaN NaN NaN\\nspec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\\nvery_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\\ntimestamp: 2025-03-06T19:39:47.068066\\ncomments: []\\nexception: \\n\\nuid: output_5\\nstatus: pass\\ntype: table\\nproperties: {\\'method\\': \\'pivot_table\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 0, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: table = acro.pivot_table(\\nsummary: pass\\noutcome: mean std\\n children children\\nparents \\ngreat_pret ok ok\\npretentious ok ok\\nusual ok ok\\noutput: [ mean std\\n children children\\nparents \\ngreat_pret 3.140972 2.270396\\npretentious 3.129630 2.250436\\nusual 3.110648 2.213072]\\ntimestamp: 2025-03-06T19:39:47.105651\\ncomments: []\\nexception: \\n\\nuid: output_6\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'pivot_table\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 5, \\'p-ratio\\': 5, \\'nk-rule\\': 5, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'p-ratio\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'nk-rule\\': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.pivot_table(\\nsummary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \\noutcome: children \\\\\\nrecommend not_recom priority recommend spec_prior \\nparents \\ngreat_pret ok ok threshold; p-ratio; nk-rule; ok \\npretentious ok ok threshold; p-ratio; nk-rule; ok \\nusual ok ok threshold; p-ratio; nk-rule; ok \\nAll ok ok threshold; p-ratio; nk-rule; ok \\n\\n \\nrecommend very_recom All \\nparents \\ngreat_pret threshold; p-ratio; nk-rule; ok \\npretentious ok ok \\nusual ok ok \\nAll ok ok \\noutput: [ children \\nrecommend not_recom priority spec_prior very_recom All\\nparents \\ngreat_pret 3.125694 2.665501 3.353610 NaN 3.140972\\npretentious 3.105556 3.030323 3.370253 2.204545 3.129630\\nusual 3.074306 3.116944 3.393140 2.244898 3.111626\\nAll 3.101852 2.996015 3.366222 2.228659 3.127412]\\ntimestamp: 2025-03-06T19:39:47.231513\\ncomments: []\\nexception: \\n\\nuid: output_7\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'ols\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.ols(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ recommend R-squared: 0.001\\nDep. Variable: \\nModel: OLS Adj. R-squared: 0.001000\\nMethod: Least Squares F-statistic: 13.830000\\nDate: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\\nTime: 19:39:47 Log-Likelihood: -25121.000000\\nNo. Observations: 12960 AIC: 50250.000000\\nDf Residuals: 12958 BIC: 50260.000000\\nDf Model: 1 NaN NaN\\nCovariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\\nconst 2.2099 0.025 87.263 0.0 2.160 2.260\\nchildren 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\\nOmnibus: \\nProb(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\\nSkew: -0.486 Prob(JB): 0.00\\nKurtosis: 1.489 Cond. No. 6.90]\\ntimestamp: 2025-03-06T19:39:47.388052\\ncomments: []\\nexception: \\n\\nuid: output_8\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'olsr\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.olsr(formula=\"recommend ~ children\", data=new_df)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ recommend R-squared: 0.001\\nDep. Variable: \\nModel: OLS Adj. R-squared: 0.001000\\nMethod: Least Squares F-statistic: 13.830000\\nDate: Thu, 06 Mar 2025 Prob (F-statistic): 0.000201\\nTime: 19:39:47 Log-Likelihood: -25121.000000\\nNo. Observations: 12960 AIC: 50250.000000\\nDf Residuals: 12958 BIC: 50260.000000\\nDf Model: 1 NaN NaN\\nCovariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\\nIntercept 2.2099 0.025 87.263 0.0 2.160 2.260\\nchildren 0.0245 0.007 3.718 0.0 0.012 0.037, 77090.215 Durbin-Watson: 2.883\\nOmnibus: \\nProb(Omnibus): 0.000 Jarque-Bera (JB): 1741.57\\nSkew: -0.486 Prob(JB): 0.00\\nKurtosis: 1.489 Cond. No. 6.90]\\ntimestamp: 2025-03-06T19:39:47.414293\\ncomments: []\\nexception: \\n\\nuid: output_9\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'probit\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.probit(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ finance No. Observations: 12960\\nDep. Variable: \\nModel: Probit Df Residuals: 12958.000000\\nMethod: MLE Df Model: 1.000000\\nDate: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\\nTime: 19:39:47 Log-Likelihood: -8983.200000\\nconverged: True LL-Null: -8983.200000\\nCovariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\\nconst -0.0039 0.019 -0.207 0.836 -0.041 0.033\\nchildren 0.0012 0.005 0.254 0.799 -0.008 0.011]\\ntimestamp: 2025-03-06T19:39:47.439598\\ncomments: []\\nexception: \\n\\nuid: output_10\\nstatus: pass\\ntype: regression\\nproperties: {\\'method\\': \\'logit\\', \\'dof\\': 12958.0}\\nsdc: {}\\ncommand: results = acro.logit(y, x)\\nsummary: pass; dof=12958.0 >= 10\\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [ finance No. Observations: 12960\\nDep. Variable: \\nModel: Logit Df Residuals: 12958.000000\\nMethod: MLE Df Model: 1.000000\\nDate: Thu, 06 Mar 2025 Pseudo R-squ.: 0.000004\\nTime: 19:39:47 Log-Likelihood: -8983.200000\\nconverged: True LL-Null: -8983.200000\\nCovariance Type: nonrobust LLR p-value: 0.799200, coef std err z P>|z| [0.025 0.975]\\nconst -0.0062 0.030 -0.207 0.836 -0.065 0.053\\nchildren 0.0020 0.008 0.254 0.799 -0.013 0.017]\\ntimestamp: 2025-03-06T19:39:47.457696\\ncomments: []\\nexception: \\n\\nuid: output_11\\nstatus: fail\\ntype: table\\nproperties: {\\'method\\': \\'surv_func\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 76, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\\nsummary: fail; threshold: 76 cells suppressed; \\noutcome: Surv_prob Surv_prob_SE num_at_risk num_events\\nTime \\n51 ok ok ok ok\\n69 threshold; threshold; threshold; threshold; \\n85 threshold; threshold; threshold; threshold; \\n91 threshold; threshold; threshold; threshold; \\n115 threshold; threshold; threshold; threshold; \\n372 threshold; threshold; threshold; threshold; \\n667 threshold; threshold; threshold; threshold; \\n874 threshold; threshold; threshold; threshold; \\n1039 threshold; threshold; threshold; threshold; \\n1046 threshold; threshold; threshold; threshold; \\n1281 threshold; threshold; threshold; threshold; \\n1286 threshold; threshold; threshold; threshold; \\n1326 threshold; threshold; threshold; threshold; \\n1355 threshold; threshold; threshold; threshold; \\n1626 threshold; threshold; threshold; threshold; \\n1903 threshold; threshold; threshold; threshold; \\n1914 threshold; threshold; threshold; threshold; \\n2776 threshold; threshold; threshold; threshold; \\n2851 threshold; threshold; threshold; threshold; \\n3309 threshold; threshold; threshold; threshold; \\noutput: [ Surv prob Surv prob SE num at risk num events\\nTime \\n51 0.95 0.048734 20.0 1.0\\n69 NaN NaN NaN NaN\\n85 NaN NaN NaN NaN\\n91 NaN NaN NaN NaN\\n115 NaN NaN NaN NaN\\n372 NaN NaN NaN NaN\\n667 NaN NaN NaN NaN\\n874 NaN NaN NaN NaN\\n1039 NaN NaN NaN NaN\\n1046 NaN NaN NaN NaN\\n1281 NaN NaN NaN NaN\\n1286 NaN NaN NaN NaN\\n1326 NaN NaN NaN NaN\\n1355 NaN NaN NaN NaN\\n1626 NaN NaN NaN NaN\\n1903 NaN NaN NaN NaN\\n1914 NaN NaN NaN NaN\\n2776 NaN NaN NaN NaN\\n2851 NaN NaN NaN NaN\\n3309 NaN NaN NaN NaN]\\ntimestamp: 2025-03-06T19:39:48.298262\\ncomments: []\\nexception: \\n\\nuid: output_12\\nstatus: fail\\ntype: survival plot\\nproperties: {\\'method\\': \\'surv_func\\'}\\nsdc: {\\'summary\\': {\\'suppressed\\': True, \\'negative\\': 0, \\'missing\\': 0, \\'threshold\\': 76, \\'p-ratio\\': 0, \\'nk-rule\\': 0, \\'all-values-are-same\\': 0}, \\'cells\\': {\\'negative\\': [], \\'missing\\': [], \\'threshold\\': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], \\'p-ratio\\': [], \\'nk-rule\\': [], \\'all-values-are-same\\': []}}\\ncommand: safe_plot = acro.surv_func(\\nsummary: fail; threshold: 76 cells suppressed; \\noutcome: Empty DataFrame\\nColumns: []\\nIndex: []\\noutput: [\\'acro_artifacts/kaplan-mier_0.png\\']\\ntimestamp: 2025-03-06T19:39:48.450221\\ncomments: []\\nexception: \\n\\n'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "id": "3136bc78", + "metadata": {}, + "source": [ + "### 2: Remove some ACRO outputs before finalising \n", + "This is an example of deleting some of the ACRO outputs. \n", + "The name of the output that needs to be removed should be passed to the function remove_output. \n", + "- Currently, all outputs names contain timestamp; that is the time when the output was created. \n", + "- The output name can be taken from the outputs listed by the print_outputs function, \n", + "- or by listing the results and choosing the specific output that needs to be removed" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "e4ee985e", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_0 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_0\")" + ] + }, + { + "cell_type": "markdown", + "id": "df2a02e0", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 3: Rename ACRO outputs before finalising\n", + "This is an example of renaming the outputs to provide a more descriptive name. \n", + "The timestamp associated with the output name will not get overwritten" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "b9d0b9ac", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_2 renamed to pivot_table\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_2\", \"pivot_table\")" + ] + }, + { + "cell_type": "markdown", + "id": "56d2b6a1", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 4: Add a comment to output\n", + "This is an example to add a comment to outputs. \n", + "It can be used to provide a description or to pass additional information to the output checkers." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "8e21f7b0", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to output_1\n", + "INFO:acro:records:a comment was added to output_1\n" + ] + } + ], + "source": [ + "acro.add_comments(\"output_1\", \"Please let me have this data.\")\n", + "acro.add_comments(\"output_1\", \"6 cells were suppressed in this table\")" + ] + }, + { + "cell_type": "markdown", + "id": "8496fed4", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 5: Add an unsupported output to the list of outputs\n", + "This is an example to add an unsupported outputs (such as images) to the list of outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "1e8000a1", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add_custom(): output_13\n" + ] + } + ], + "source": [ + "acro.custom_output(\n", + " \"XandY.jpeg\", \"This output is an image showing the relationship between X and Y\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5a586694", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## 6: (the big one) Finalise ACRO\n", + "This is an example of the function _finalise()_ which the users must call at the end of each session. \n", + "- It takes each output and saves it to a CSV file. \n", + "- It also saves the SDC analysis for each output to a json file or Excel file \n", + " (depending on the extension of the name of the file provided as an input to the function)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "f941aca2", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [2, 3], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents, margins=True)\n", + "summary: fail; threshold: 5 cells suppressed; \n", + "outcome: parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom ok ok ok ok\n", + "priority ok ok ok ok\n", + "recommend threshold; threshold; threshold; threshold; \n", + "spec_prior ok ok ok ok\n", + "very_recom threshold; ok ok ok\n", + "All ok ok ok ok\n", + "output: [parents great_pret pretentious usual All\n", + "recommend \n", + "not_recom 1440.0 1440 1440 4320\n", + "priority 858.0 1484 1924 4266\n", + "spec_prior 2022.0 1264 758 4044\n", + "very_recom NaN 132 196 328\n", + "All 4320.0 4320 4318 12958]\n", + "timestamp: 2025-03-06T19:39:46.961631\n", + "comments: ['Please let me have this data.', '6 cells were suppressed in this table']\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 1, 'p-ratio': 4, 'nk-rule': 4, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [4, 0]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [4, 0]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 1 cells suppressed; p-ratio: 4 cells suppressed; nk-rule: 4 cells suppressed; \n", + "outcome: parents great_pret pretentious \\\n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + "parents usual \n", + "recommend \n", + "not_recom ok \n", + "priority ok \n", + "recommend threshold; p-ratio; nk-rule; \n", + "spec_prior ok \n", + "very_recom ok \n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440.0 1440.0 1440.0\n", + "priority 858.0 1484.0 1924.0\n", + "recommend NaN NaN NaN\n", + "spec_prior 2022.0 1264.0 758.0\n", + "very_recom NaN 132.0 196.0]\n", + "timestamp: 2025-03-06T19:39:47.019919\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 2, 'p-ratio': 8, 'nk-rule': 8, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 2], [2, 5]], 'p-ratio': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'nk-rule': [[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [4, 0], [4, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 2 cells suppressed; p-ratio: 8 cells suppressed; nk-rule: 8 cells suppressed; \n", + "outcome: mode_aggfunc \\\n", + "parents great_pret pretentious \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom p-ratio; nk-rule; ok \n", + "\n", + " mean \\\n", + "parents usual great_pret \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend threshold; p-ratio; nk-rule; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok p-ratio; nk-rule; \n", + "\n", + " \n", + "parents pretentious usual \n", + "recommend \n", + "not_recom ok ok \n", + "priority ok ok \n", + "recommend p-ratio; nk-rule; threshold; p-ratio; nk-rule; \n", + "spec_prior ok ok \n", + "very_recom ok ok \n", + "output: [ mode_aggfunc mean \n", + "parents great_pret pretentious usual great_pret pretentious usual\n", + "recommend \n", + "not_recom 2.0 1.0 1.0 3.125694 3.105556 3.074306\n", + "priority 1.0 1.0 1.0 2.665501 3.030323 3.116944\n", + "recommend NaN NaN NaN NaN NaN NaN\n", + "spec_prior 3.0 3.0 3.0 3.353610 3.370253 3.393140\n", + "very_recom NaN 1.0 1.0 NaN 2.204545 2.244898]\n", + "timestamp: 2025-03-06T19:39:47.068066\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " exception requested\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 5, 'p-ratio': 5, 'nk-rule': 5, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'p-ratio': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'nk-rule': [[0, 2], [0, 4], [1, 2], [2, 2], [3, 2]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.pivot_table(\n", + "summary: fail; threshold: 5 cells suppressed; p-ratio: 5 cells suppressed; nk-rule: 5 cells suppressed; \n", + "outcome: children \\\n", + "recommend not_recom priority recommend spec_prior \n", + "parents \n", + "great_pret ok ok threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok threshold; p-ratio; nk-rule; ok \n", + "usual ok ok threshold; p-ratio; nk-rule; ok \n", + "All ok ok threshold; p-ratio; nk-rule; ok \n", + "\n", + " \n", + "recommend very_recom All \n", + "parents \n", + "great_pret threshold; p-ratio; nk-rule; ok \n", + "pretentious ok ok \n", + "usual ok ok \n", + "All ok ok \n", + "output: [ children \n", + "recommend not_recom priority spec_prior very_recom All\n", + "parents \n", + "great_pret 3.125694 2.665501 3.353610 NaN 3.140972\n", + "pretentious 3.105556 3.030323 3.370253 2.204545 3.129630\n", + "usual 3.074306 3.116944 3.393140 2.244898 3.111626\n", + "All 3.101852 2.996015 3.366222 2.228659 3.127412]\n", + "timestamp: 2025-03-06T19:39:47.231513\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " some reason\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_11\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.surv_func(data.futime, data.death, output=\"table\")\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Surv_prob Surv_prob_SE num_at_risk num_events\n", + "Time \n", + "51 ok ok ok ok\n", + "69 threshold; threshold; threshold; threshold; \n", + "85 threshold; threshold; threshold; threshold; \n", + "91 threshold; threshold; threshold; threshold; \n", + "115 threshold; threshold; threshold; threshold; \n", + "372 threshold; threshold; threshold; threshold; \n", + "667 threshold; threshold; threshold; threshold; \n", + "874 threshold; threshold; threshold; threshold; \n", + "1039 threshold; threshold; threshold; threshold; \n", + "1046 threshold; threshold; threshold; threshold; \n", + "1281 threshold; threshold; threshold; threshold; \n", + "1286 threshold; threshold; threshold; threshold; \n", + "1326 threshold; threshold; threshold; threshold; \n", + "1355 threshold; threshold; threshold; threshold; \n", + "1626 threshold; threshold; threshold; threshold; \n", + "1903 threshold; threshold; threshold; threshold; \n", + "1914 threshold; threshold; threshold; threshold; \n", + "2776 threshold; threshold; threshold; threshold; \n", + "2851 threshold; threshold; threshold; threshold; \n", + "3309 threshold; threshold; threshold; threshold; \n", + "output: [ Surv prob Surv prob SE num at risk num events\n", + "Time \n", + "51 0.95 0.048734 20.0 1.0\n", + "69 NaN NaN NaN NaN\n", + "85 NaN NaN NaN NaN\n", + "91 NaN NaN NaN NaN\n", + "115 NaN NaN NaN NaN\n", + "372 NaN NaN NaN NaN\n", + "667 NaN NaN NaN NaN\n", + "874 NaN NaN NaN NaN\n", + "1039 NaN NaN NaN NaN\n", + "1046 NaN NaN NaN NaN\n", + "1281 NaN NaN NaN NaN\n", + "1286 NaN NaN NaN NaN\n", + "1326 NaN NaN NaN NaN\n", + "1355 NaN NaN NaN NaN\n", + "1626 NaN NaN NaN NaN\n", + "1903 NaN NaN NaN NaN\n", + "1914 NaN NaN NaN NaN\n", + "2776 NaN NaN NaN NaN\n", + "2851 NaN NaN NaN NaN\n", + "3309 NaN NaN NaN NaN]\n", + "timestamp: 2025-03-06T19:39:48.298262\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " some other reason\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_12\n", + "status: fail\n", + "type: survival plot\n", + "properties: {'method': 'surv_func'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 76, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3], [4, 0], [4, 1], [4, 2], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], [6, 0], [6, 1], [6, 2], [6, 3], [7, 0], [7, 1], [7, 2], [7, 3], [8, 0], [8, 1], [8, 2], [8, 3], [9, 0], [9, 1], [9, 2], [9, 3], [10, 0], [10, 1], [10, 2], [10, 3], [11, 0], [11, 1], [11, 2], [11, 3], [12, 0], [12, 1], [12, 2], [12, 3], [13, 0], [13, 1], [13, 2], [13, 3], [14, 0], [14, 1], [14, 2], [14, 3], [15, 0], [15, 1], [15, 2], [15, 3], [16, 0], [16, 1], [16, 2], [16, 3], [17, 0], [17, 1], [17, 2], [17, 3], [18, 0], [18, 1], [18, 2], [18, 3], [19, 0], [19, 1], [19, 2], [19, 3]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_plot = acro.surv_func(\n", + "summary: fail; threshold: 76 cells suppressed; \n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/kaplan-mier_0.png']\n", + "timestamp: 2025-03-06T19:39:48.450221\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " suppressed\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: pivot_table\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[2, 0], [2, 1], [2, 2], [4, 0]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.recommend, df.parents)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: parents great_pret pretentious usual\n", + "recommend \n", + "not_recom ok ok ok\n", + "priority ok ok ok\n", + "recommend threshold; threshold; threshold; \n", + "spec_prior ok ok ok\n", + "very_recom threshold; ok ok\n", + "output: [parents great_pret pretentious usual\n", + "recommend \n", + "not_recom 1440 1440 1440\n", + "priority 858 1484 1924\n", + "recommend 0 0 2\n", + "spec_prior 2022 1264 758\n", + "very_recom 0 132 196]\n", + "timestamp: 2025-03-06T19:39:46.980090\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason is provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_13\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['XandY.jpeg']\n", + "timestamp: 2025-03-06T19:39:48.518030\n", + "comments: ['This output is an image showing the relationship between X and Y']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " image is not disclosive\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: NURSERY\n" + ] + } + ], + "source": [ + "output = acro.finalise(\"NURSERY\", \"json\")" + ] + }, + { + "cell_type": "markdown", + "id": "113d84ec", + "metadata": {}, + "source": [ + "### 7: Add a directory of outputs to an acro object \n", + "This is an example of adding a list of files (produced by the researcher without using ACRO) to an acro object and creates a results file for checking." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "fdea993a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n", + "INFO:acro:records:add_custom(): output_0\n", + "INFO:acro:records:rename_output(): output_0 renamed to crosstab.pkl\n", + "INFO:acro:records:\n", + "uid: crosstab.pkl\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['test_add_to_acro/crosstab.pkl']\n", + "timestamp: 2025-03-06T19:41:22.128464\n", + "comments: ['']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " pickle file need some explanation \n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: SDC_results\n" + ] + } + ], + "source": [ + "import shutil\n", + "\n", + "table = pd.crosstab(df.recommend, df.parents)\n", + "# save the output table to a file and add this file to a directory\n", + "src_path = \"test_add_to_acro\"\n", + "file_path = \"crosstab.pkl\"\n", + "dest_path = \"SDC_results\"\n", + "if not os.path.exists(src_path):\n", + " table.to_pickle(file_path)\n", + " os.mkdir(src_path)\n", + " shutil.move(file_path, src_path, copy_function=shutil.copytree)\n", + "\n", + "# add the output to acro\n", + "add_to_acro(src_path, dest_path)" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/_build/_sources/notebooks/test.ipynb.txt b/docs/source/_build/_sources/notebooks/test.ipynb.txt new file mode 100644 index 00000000..1cf9ee87 --- /dev/null +++ b/docs/source/_build/_sources/notebooks/test.ipynb.txt @@ -0,0 +1,4393 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "00cac1f9", + "metadata": {}, + "source": [ + "# ACRO Tests" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e33fd4fb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c01cfe12", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# uncomment this line if acro is not installed\n", + "# ie you are in development mode\n", + "# sys.path.insert(0, os.path.abspath(\"..\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc8d993a", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [], + "source": [ + "from acro import ACRO, add_constant, utils" + ] + }, + { + "cell_type": "markdown", + "id": "530efcfe", + "metadata": {}, + "source": [ + "### Instantiate ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "4b8a77e2", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:version: 0.4.8\n", + "INFO:acro:config: {'safe_threshold': 10, 'safe_dof_threshold': 10, 'safe_nk_n': 2, 'safe_nk_k': 0.9, 'safe_pratio_p': 0.1, 'check_missing_values': False, 'survival_safe_threshold': 10, 'zeros_are_disclosive': True}\n", + "INFO:acro:automatic suppression: False\n" + ] + } + ], + "source": [ + "acro = ACRO(suppress=False)" + ] + }, + { + "cell_type": "markdown", + "id": "27a2baaa", + "metadata": {}, + "source": [ + "### Load test data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8722735f", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
charitygrant_typeindexyearinc_activityinc_grantsinc_donationsinc_otherinc_totaltotal_costs...sh_staff_grants_givensh_assets_grants_givensh_income_balancesh_staff_balancesh_assets_balancesh_income_assetssh_staff_assetssh_income_staff_costssh_assets_staff_costswgt
04ChildrenR1.020112880902.09603182.091404.0310947.012886435.012127472.0...NaNNaN0.0726360.1359710.7678090.0946020.1770900.5342035.6468431.0
14ChildrenR1.020146810520.018768904.058002.0401879.026039304.025493796.0...NaNNaN0.0576410.0891501.0013960.0575600.0890260.64656111.2327291.0
24ChildrenR1.020157199403.021638036.0132191.0512654.029482284.032290108.0...NaNNaN-0.049619-0.079828-0.6202100.0800040.1287110.6215837.7693651.0
34ChildrenR1.020135573013.015194731.0228844.0267156.021263744.020989048.0...NaNNaN0.0457400.0682511.0082590.0453650.0676920.67016614.7727491.0
44ChildrenR1.020102056816.07335103.0110256.0424628.09926803.09769816.0...NaNNaN0.0576960.1225320.5675390.1016600.2159010.4708624.6317491.0
\n", + "

5 rows × 44 columns

\n", + "
" + ], + "text/plain": [ + " charity grant_type index year inc_activity inc_grants inc_donations \\\n", + "0 4Children R 1.0 2011 2880902.0 9603182.0 91404.0 \n", + "1 4Children R 1.0 2014 6810520.0 18768904.0 58002.0 \n", + "2 4Children R 1.0 2015 7199403.0 21638036.0 132191.0 \n", + "3 4Children R 1.0 2013 5573013.0 15194731.0 228844.0 \n", + "4 4Children R 1.0 2010 2056816.0 7335103.0 110256.0 \n", + "\n", + " inc_other inc_total total_costs ... sh_staff_grants_given \\\n", + "0 310947.0 12886435.0 12127472.0 ... NaN \n", + "1 401879.0 26039304.0 25493796.0 ... NaN \n", + "2 512654.0 29482284.0 32290108.0 ... NaN \n", + "3 267156.0 21263744.0 20989048.0 ... NaN \n", + "4 424628.0 9926803.0 9769816.0 ... NaN \n", + "\n", + " sh_assets_grants_given sh_income_balance sh_staff_balance \\\n", + "0 NaN 0.072636 0.135971 \n", + "1 NaN 0.057641 0.089150 \n", + "2 NaN -0.049619 -0.079828 \n", + "3 NaN 0.045740 0.068251 \n", + "4 NaN 0.057696 0.122532 \n", + "\n", + " sh_assets_balance sh_income_assets sh_staff_assets sh_income_staff_costs \\\n", + "0 0.767809 0.094602 0.177090 0.534203 \n", + "1 1.001396 0.057560 0.089026 0.646561 \n", + "2 -0.620210 0.080004 0.128711 0.621583 \n", + "3 1.008259 0.045365 0.067692 0.670166 \n", + "4 0.567539 0.101660 0.215901 0.470862 \n", + "\n", + " sh_assets_staff_costs wgt \n", + "0 5.646843 1.0 \n", + "1 11.232729 1.0 \n", + "2 7.769365 1.0 \n", + "3 14.772749 1.0 \n", + "4 4.631749 1.0 \n", + "\n", + "[5 rows x 44 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "path = os.path.join(\"../data\", \"test_data.dta\")\n", + "df = pd.read_stata(path)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "4ae844a0", + "metadata": {}, + "source": [ + "### Pandas crosstab" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "961684cb", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
grant_typeGRGNRR/G
year
20103471259248
20113471259248
20123471259248
20133471259248
20143471259248
20153471259248
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = pd.crosstab(df.year, [df.survivor, df.grant_type])\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "d642ed00", + "metadata": {}, + "source": [ + "### ACRO crosstab" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "bb4b2677", + "metadata": { + "scrolled": true, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 12 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015 |\n", + "grant_type |G R |G N R R/G |\n", + "year | | |\n", + "----------------------------------------------------------------|\n", + "2010 | threshold; ok | ok ok ok threshold; |\n", + "2011 | threshold; ok | ok ok ok threshold; |\n", + "2012 | threshold; ok | ok ok ok threshold; |\n", + "2013 | threshold; ok | ok ok ok threshold; |\n", + "2014 | threshold; ok | ok ok ok threshold; |\n", + "2015 | threshold; ok | ok ok ok threshold; |\n", + "----------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_0\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
grant_typeGRGNRR/G
year
20103471259248
20113471259248
20123471259248
20133471259248
20143471259248
20153471259248
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, [df.survivor, df.grant_type])\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "59b223fb-7b88-4f51-9bdf-7dbb797849d1", + "metadata": { + "tags": [] + }, + "source": [ + "### same table with column hierarchy reversed to make sure spaces in variable name. dealt with properly" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d01f7437-ceee-41b3-84ad-07976e0d58c3", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 12 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |\n", + "survivor |Dead_in_2015 Alive_in_2015 |Alive_in_2015 |Dead_in_2015 Alive_in_2015 |Alive_in_2015|\n", + "year | | | | |\n", + "------------------------------------------------------------------------------------------------|\n", + "2010 | threshold; ok | ok | ok ok | threshold; |\n", + "2011 | threshold; ok | ok | ok ok | threshold; |\n", + "2012 | threshold; ok | ok | ok ok | threshold; |\n", + "2013 | threshold; ok | ok | ok ok | threshold; |\n", + "2014 | threshold; ok | ok | ok ok | threshold; |\n", + "2015 | threshold; ok | ok | ok ok | threshold; |\n", + "------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_1\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
survivorDead in 2015Alive in 2015Alive in 2015Dead in 2015Alive in 2015Alive in 2015
year
20103125947248
20113125947248
20123125947248
20133125947248
20143125947248
20153125947248
\n", + "
" + ], + "text/plain": [ + "grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 3 12 59 47 \n", + "2011 3 12 59 47 \n", + "2012 3 12 59 47 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 47 \n", + "2015 3 12 59 47 \n", + "\n", + "grant_type R/G \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 24 8 \n", + "2011 24 8 \n", + "2012 24 8 \n", + "2013 24 8 \n", + "2014 24 8 \n", + "2015 24 8 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, [df.grant_type, df.survivor])\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "1c34d5ba-8200-4181-9440-ca02f4bfd2f4", + "metadata": {}, + "source": [ + "### checking for testing purposes" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e4382b14-cfcf-4d01-a25a-97106852bd65", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 4 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "-------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015|\n", + "year | | |\n", + "-------------------------------------|\n", + "2010 | threshold; | threshold; |\n", + "2011 | threshold; | threshold; |\n", + "-------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_2\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
year
201022
201122
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mydata = df[(df[\"charity\"].str[0] == \"W\")]\n", + "mydata = mydata[mydata[\"year\"] < 2012]\n", + "acro.crosstab(mydata.year, mydata.survivor)" + ] + }, + { + "cell_type": "markdown", + "id": "6d4730c4", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "37ddb939", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells suppressed; p-ratio: 2 cells suppressed; nk-rule: 1 cells suppressed; \n", + "INFO:acro:outcome_df:\n", + "---------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |\n", + "year | | | | |\n", + "---------------------------------------------------------------------------|\n", + "2010 | ok | threshold; p-ratio; | ok | threshold; p-ratio; nk-rule; |\n", + "2011 | ok | ok | ok | threshold; |\n", + "2012 | ok | ok | ok | threshold; |\n", + "2013 | ok | ok | ok | threshold; |\n", + "2014 | ok | ok | ok | threshold; |\n", + "2015 | ok | ok | ok | threshold; |\n", + "---------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_3\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
year
20109921906.0NaN8402284.0NaN
20118502246.0124013.8593757716880.0NaN
201211458580.0131859.0625006958050.5NaN
201313557147.0147937.7968757202273.5NaN
201413748147.0133198.2500008277525.0NaN
201511133433.0146572.18750010812888.0NaN
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 NaN 8402284.0 NaN\n", + "2011 8502246.0 124013.859375 7716880.0 NaN\n", + "2012 11458580.0 131859.062500 6958050.5 NaN\n", + "2013 13557147.0 147937.796875 7202273.5 NaN\n", + "2014 13748147.0 133198.250000 8277525.0 NaN\n", + "2015 11133433.0 146572.187500 10812888.0 NaN" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acro.suppress = True\n", + "\n", + "safe_table = acro.crosstab(df.year, df.grant_type, values=df.inc_grants, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "0c695e09", + "metadata": {}, + "source": [ + "### ACRO crosstab with suppression and totals" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ef42beb6", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:Empty columns: ('N', 'Dead in 2015'), ('R/G', 'Dead in 2015') were deleted.\n", + "INFO:acro:get_summary(): fail; threshold: 14 cells may need suppressing; p-ratio: 8 cells may need suppressing; nk-rule: 7 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |All|\n", + "survivor |Dead_in_2015 Alive_in_2015 |Alive_in_2015 |Dead_in_2015 Alive_in_2015 |Alive_in_2015 | |\n", + "year | | | | | |\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "2010 | threshold; p-ratio; nk-rule; ok | threshold; p-ratio; | ok ok | threshold; p-ratio; nk-rule; | ok|\n", + "2011 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2012 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2013 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2014 | threshold; p-ratio; nk-rule; ok | ok | ok ok | threshold; | ok|\n", + "2015 | threshold; p-ratio; nk-rule; threshold; | ok | ok ok | threshold; | ok|\n", + "All | ok ok | ok | ok ok | ok | ok|\n", + "------------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 2 12 5 40 \n", + "2011 3 12 58 45 \n", + "2012 3 12 59 45 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 43 \n", + "2015 3 9 58 28 \n", + "All 17 69 298 248 \n", + "\n", + "grant_type R/G All \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 20 4 83 \n", + "2011 24 8 150 \n", + "2012 24 8 151 \n", + "2013 24 8 153 \n", + "2014 24 8 149 \n", + "2015 23 8 129 \n", + "All 139 44 815 \n" + ] + } + ], + "source": [ + "acro.suppress = False\n", + "table = acro.crosstab(\n", + " df.year,\n", + " [df.grant_type, df.survivor],\n", + " values=df.inc_grants,\n", + " aggfunc=\"count\",\n", + " margins=True,\n", + ")\n", + "print(table)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "506135e0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "id": "8b603548", + "metadata": {}, + "source": [ + "### ACRO crosstab with aggregation function" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "83718cb1", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "-------------------------------------|\n", + "survivor |Dead_in_2015 |Alive_in_2015|\n", + "year | | |\n", + "-------------------------------------|\n", + "2010 | ok | ok |\n", + "2011 | ok | ok |\n", + "2012 | ok | ok |\n", + "2013 | ok | ok |\n", + "2014 | ok | ok |\n", + "2015 | ok | ok |\n", + "-------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_5\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
survivorDead in 2015Alive in 2015
year
20101320337.75015466672.0
20111295468.0007190086.5
20121270522.1257119017.5
20131325315.5007682584.0
20141282249.6258276287.5
20151608412.2508060488.5
\n", + "
" + ], + "text/plain": [ + "survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 1320337.750 15466672.0\n", + "2011 1295468.000 7190086.5\n", + "2012 1270522.125 7119017.5\n", + "2013 1325315.500 7682584.0\n", + "2014 1282249.625 8276287.5\n", + "2015 1608412.250 8060488.5" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(df.year, df.survivor, values=df.inc_grants, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "de4266cd-b4d4-417b-ae44-5d972e8bfdde", + "metadata": {}, + "source": [ + "### ACRO crosstab with multiple aggregation functions and totals" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "fb7abfc9-e428-4b71-9066-01ac9a08d655", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 14 cells may need suppressing; p-ratio: 4 cells may need suppressing; nk-rule: 2 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + " mean |std |\n", + "grant_type G N R R/G All |G N R R/G All|\n", + "year | |\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok | ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok|\n", + "2011 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2012 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2013 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2014 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "2015 ok ok ok threshold; ok | ok ok ok threshold; ok|\n", + "All ok ok ok ok ok | ok ok ok ok ok|\n", + "----------------------------------------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_6\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
grant_typeGNRR/GAllGNRR/GAll
year
20109921906.00.0000008402284.011636000.08308286.51.855055e+070.0000003.059557e+071.701088e+072.727398e+07
20118502246.0124013.8593757716880.016047500.05303808.51.688595e+07205959.4929032.954322e+071.561638e+072.137658e+07
201211458580.0131859.0625006958050.516810000.05259893.52.061090e+07210476.5391752.721184e+071.646449e+072.026400e+07
201313557147.0147937.7968757202273.516765625.05605045.52.486844e+07203747.4170172.989833e+071.671112e+072.251787e+07
201413748147.0133198.2500008277525.017845750.06117054.53.134559e+07181865.9255803.546348e+071.741251e+072.641722e+07
201511133433.0146572.18750010812888.018278624.06509989.52.553919e+07201602.8008324.130935e+071.730471e+072.784636e+07
All11412787.0134431.8906258098502.016648273.05997796.52.283220e+07198873.7266563.204495e+071.583532e+072.405324e+07
\n", + "
" + ], + "text/plain": [ + " mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 9921906.0 0.000000 8402284.0 11636000.0 8308286.5 \n", + "2011 8502246.0 124013.859375 7716880.0 16047500.0 5303808.5 \n", + "2012 11458580.0 131859.062500 6958050.5 16810000.0 5259893.5 \n", + "2013 13557147.0 147937.796875 7202273.5 16765625.0 5605045.5 \n", + "2014 13748147.0 133198.250000 8277525.0 17845750.0 6117054.5 \n", + "2015 11133433.0 146572.187500 10812888.0 18278624.0 6509989.5 \n", + "All 11412787.0 134431.890625 8098502.0 16648273.0 5997796.5 \n", + "\n", + " std \\\n", + "grant_type G N R R/G \n", + "year \n", + "2010 1.855055e+07 0.000000 3.059557e+07 1.701088e+07 \n", + "2011 1.688595e+07 205959.492903 2.954322e+07 1.561638e+07 \n", + "2012 2.061090e+07 210476.539175 2.721184e+07 1.646449e+07 \n", + "2013 2.486844e+07 203747.417017 2.989833e+07 1.671112e+07 \n", + "2014 3.134559e+07 181865.925580 3.546348e+07 1.741251e+07 \n", + "2015 2.553919e+07 201602.800832 4.130935e+07 1.730471e+07 \n", + "All 2.283220e+07 198873.726656 3.204495e+07 1.583532e+07 \n", + "\n", + " \n", + "grant_type All \n", + "year \n", + "2010 2.727398e+07 \n", + "2011 2.137658e+07 \n", + "2012 2.026400e+07 \n", + "2013 2.251787e+07 \n", + "2014 2.641722e+07 \n", + "2015 2.784636e+07 \n", + "All 2.405324e+07 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "safe_table = acro.crosstab(\n", + " df.year, df.grant_type, values=df.inc_grants, aggfunc=[\"mean\", \"std\"], margins=True\n", + ")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "0aa99fbf", + "metadata": {}, + "source": [ + "### ACRO crosstab with missing values\n", + "- This is an example of a crosstab where there are missing values that have not been filled or dealt with in the data.\n", + "- Note that you need to change the value of the CHECK_MISSING_VALUES variable in the acro object to True. Then run the crosstab command.\n", + "- In this example, ten values in the column inc_grants were set to nan to represent missing data.\n", + "- In this version of acro checking the disclosiveness of an output with missing values is not supported.\n", + "- The status of the command will be \"review\" to indicate that the output needs to be checked by the output checker to review if the output is disclosive or not.\n", + "- In the outcome_df each cell with missing value/values will be shown as missing.\n", + "- The output hist will not be suppressed even if the suppress=True." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "bf132239", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------------------------------|\n", + "grant_type |G |N |R |R/G |All|\n", + "year | | | | | |\n", + "--------------------------------------------------------------------------------|\n", + "2010 | ok | threshold; p-ratio; | ok | threshold; p-ratio; nk-rule; | ok|\n", + "2011 | ok | ok | ok | threshold; | ok|\n", + "2012 | ok | ok | ok | threshold; | ok|\n", + "2013 | ok | ok | ok | threshold; | ok|\n", + "2014 | ok | ok | ok | threshold; | ok|\n", + "2015 | ok | ok | ok | threshold; | ok|\n", + "All | ok | ok | ok | ok | ok|\n", + "--------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_7\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/GAll
year
20109921906.00.0000008420373.011636000.08320154.5
20118502246.0125663.2265627689140.016047500.05310392.0
201211458580.0131859.0625006896304.016810000.05220580.5
201313557147.0150488.4531257088095.516765625.05578657.0
201413748147.0135494.7812508118565.517845750.06072600.0
201511133433.0149143.62500010596385.018278624.06442131.0
All11412787.0136158.8593758006361.016648273.05968295.5
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.CHECK_MISSING_VALUES = True\n", + "\n", + "missing = df.inc_grants.copy()\n", + "missing[0:10] = np.nan\n", + "\n", + "safe_table = acro.crosstab(\n", + " df.year, df.grant_type, values=missing, aggfunc=\"mean\", margins=True\n", + ")\n", + "safe_table" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "7cc417a0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "utils.CHECK_MISSING_VALUES = False" + ] + }, + { + "cell_type": "markdown", + "id": "fcc81e98", + "metadata": {}, + "source": [ + "### ACRO crosstab with negative values" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "15bcdc7c", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): review; negative values found\n", + "INFO:acro:outcome_df:\n", + "----------------------------------------|\n", + "grant_type |G |N |R |R/G|\n", + "year | | | | |\n", + "----------------------------------------|\n", + "2010 | | | negative | |\n", + "2011 | | negative | negative | |\n", + "2012 | | | negative | |\n", + "2013 | | negative | negative | |\n", + "2014 | | negative | negative | |\n", + "2015 | | negative | negative | |\n", + "----------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_8\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
grant_typeGNRR/G
year
20109921906.00.0000008280032.511636000.0
20118502246.0123496.4453127577703.516047500.0
201211458580.0131859.0625006796357.516810000.0
201313557147.0147937.6250006988263.016765625.0
201413748147.0133198.0781257997392.017845750.0
201511133433.0146572.01562510388612.018278624.0
\n", + "
" + ], + "text/plain": [ + "grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "negative = df.inc_grants.copy()\n", + "negative[0:10] = -10\n", + "\n", + "safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "safe_table" + ] + }, + { + "cell_type": "markdown", + "id": "d66e565b", + "metadata": {}, + "source": [ + "### ACRO pivot_table" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "b13b5f7e", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "INFO:acro:outcome_df:\n", + "--------------------------------------------------------------------------------------------------------------|\n", + " inc_grants |\n", + "year 2010 2011 2012 2013 2014 2015 All|\n", + "grant_type |\n", + "--------------------------------------------------------------------------------------------------------------|\n", + "G ok ok ok ok ok ok ok|\n", + "N threshold; p-ratio; ok ok ok ok ok ok|\n", + "R ok ok ok ok ok ok ok|\n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; threshold; threshold; threshold; ok|\n", + "All ok ok ok ok ok ok ok|\n", + "--------------------------------------------------------------------------------------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_9\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inc_grants
year201020112012201320142015All
grant_type
G138906688.0127533696.0171878704.0203357200.0206222208.0133601200.09.814997e+08
N0.07192804.07779685.08728330.07858697.08501187.04.006070e+07
R504137056.0532464704.0480105472.0511361408.0554594176.0551457280.03.134120e+09
R/G46544000.0128380000.0134480000.0134125000.0142766000.0146228992.07.325240e+08
All689587776.0795571264.0794243904.0857571968.0911441088.0839788672.04.888204e+09
\n", + "
" + ], + "text/plain": [ + " inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 " + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df,\n", + " index=[\"grant_type\"],\n", + " columns=[\"year\"],\n", + " values=[\"inc_grants\"],\n", + " margins=True,\n", + " aggfunc=\"sum\",\n", + ")\n", + "table" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f72162c8", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G ok | ok |\n", + "N ok | ok |\n", + "R ok | ok |\n", + "R/G ok | ok |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_10\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.344319e+051.988737e+05
R8.098502e+063.204495e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.344319e+05 1.988737e+05\n", + "R 8.098502e+06 3.204495e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "dc99fa71", + "metadata": {}, + "source": [ + "### ACRO pivot_table with missing values" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f3a87c20", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): pass\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G ok | ok |\n", + "N ok | ok |\n", + "R ok | ok |\n", + "R/G ok | ok |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_11\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.364700e+051.999335e+05
R8.006361e+063.228216e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.364700e+05 1.999335e+05\n", + "R 8.006361e+06 3.228216e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.CHECK_MISSING_VALUES = True\n", + "\n", + "df.loc[0:10, \"inc_grants\"] = np.nan\n", + "\n", + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "8cabd236", + "metadata": {}, + "outputs": [], + "source": [ + "utils.CHECK_MISSING_VALUES = False" + ] + }, + { + "cell_type": "markdown", + "id": "b1f77749", + "metadata": {}, + "source": [ + "### ACRO pivot_table with negative values" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "864d39f4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:get_summary(): review; negative values found\n", + "INFO:acro:outcome_df:\n", + "---------------------------------|\n", + " mean |std |\n", + " inc_grants |inc_grants|\n", + "grant_type | |\n", + "---------------------------------|\n", + "G | |\n", + "N negative | negative |\n", + "R negative | negative |\n", + "R/G | |\n", + "---------------------------------|\n", + "\n", + "INFO:acro:records:add(): output_12\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
meanstd
inc_grantsinc_grants
grant_type
G1.141279e+072.283220e+07
N1.341800e+051.990196e+05
R7.882230e+063.204558e+07
R/G1.664827e+071.583532e+07
\n", + "
" + ], + "text/plain": [ + " mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.loc[0:10, \"inc_grants\"] = -10\n", + "\n", + "table = acro.pivot_table(\n", + " df, index=[\"grant_type\"], values=[\"inc_grants\"], aggfunc=[\"mean\", \"std\"]\n", + ")\n", + "table" + ] + }, + { + "cell_type": "markdown", + "id": "45ec04ef", + "metadata": {}, + "source": [ + "### ACRO OLS" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "d0724d08-6969-4f0a-8a32-e00d253f3597", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:ols() outcome: pass; dof=807.0 >= 10\n", + "INFO:acro:records:add(): output_13\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: inc_activity R-squared: 0.894
Model: OLS Adj. R-squared: 0.893
Method: Least Squares F-statistic: 2261.
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00
Time: 19:43:34 Log-Likelihood: -14495.
No. Observations: 811 AIC: 2.900e+04
Df Residuals: 807 BIC: 2.902e+04
Df Model: 3
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
const 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06
inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836
inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633
total_costs 0.8313 0.011 78.674 0.000 0.811 0.852
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 1339.956 Durbin-Watson: 1.414
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706
Skew: 9.899 Prob(JB): 0.00
Kurtosis: 194.566 Cond. No. 1.05e+08


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.05e+08. This might indicate that there are
strong multicollinearity or other numerical problems." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & inc\\_activity & \\textbf{ R-squared: } & 0.894 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.893 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 2261. \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.00 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -14495. \\\\\n", + "\\textbf{No. Observations:} & 811 & \\textbf{ AIC: } & 2.900e+04 \\\\\n", + "\\textbf{Df Residuals:} & 807 & \\textbf{ BIC: } & 2.902e+04 \\\\\n", + "\\textbf{Df Model:} & 3 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 3.01e+05 & 5.33e+05 & 0.565 & 0.572 & -7.45e+05 & 1.35e+06 \\\\\n", + "\\textbf{inc\\_grants} & -0.8846 & 0.025 & -35.956 & 0.000 & -0.933 & -0.836 \\\\\n", + "\\textbf{inc\\_donations} & -0.6647 & 0.016 & -40.721 & 0.000 & -0.697 & -0.633 \\\\\n", + "\\textbf{total\\_costs} & 0.8313 & 0.011 & 78.674 & 0.000 & 0.811 & 0.852 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 1339.956 & \\textbf{ Durbin-Watson: } & 1.414 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1253317.706 \\\\\n", + "\\textbf{Skew:} & 9.899 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 194.566 & \\textbf{ Cond. No. } & 1.05e+08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified. \\newline\n", + " [2] The condition number is large, 1.05e+08. This might indicate that there are \\newline\n", + " strong multicollinearity or other numerical problems." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: inc_activity R-squared: 0.894\n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00\n", + "Time: 19:43:34 Log-Likelihood: -14495.\n", + "No. Observations: 811 AIC: 2.900e+04\n", + "Df Residuals: 807 BIC: 2.902e+04\n", + "Df Model: 3 \n", + "Covariance Type: nonrobust \n", + "=================================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852\n", + "==============================================================================\n", + "Omnibus: 1339.956 Durbin-Watson: 1.414\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706\n", + "Skew: 9.899 Prob(JB): 0.00\n", + "Kurtosis: 194.566 Cond. No. 1.05e+08\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "[2] The condition number is large, 1.05e+08. This might indicate that there are\n", + "strong multicollinearity or other numerical problems.\n", + "\"\"\"" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df = df[[\"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"inc_activity\"]\n", + "x = new_df[[\"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.ols(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c826271", + "metadata": {}, + "source": [ + "### ACRO OLSR" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "cc90f7c9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:olsr() outcome: pass; dof=807.0 >= 10\n", + "INFO:acro:records:add(): output_14\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
OLS Regression Results
Dep. Variable: inc_activity R-squared: 0.894
Model: OLS Adj. R-squared: 0.893
Method: Least Squares F-statistic: 2261.
Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00
Time: 19:43:34 Log-Likelihood: -14495.
No. Observations: 811 AIC: 2.900e+04
Df Residuals: 807 BIC: 2.902e+04
Df Model: 3
Covariance Type: nonrobust
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err t P>|t| [0.025 0.975]
Intercept 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06
inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836
inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633
total_costs 0.8313 0.011 78.674 0.000 0.811 0.852
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Omnibus: 1339.956 Durbin-Watson: 1.414
Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706
Skew: 9.899 Prob(JB): 0.00
Kurtosis: 194.566 Cond. No. 1.05e+08


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.05e+08. This might indicate that there are
strong multicollinearity or other numerical problems." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & inc\\_activity & \\textbf{ R-squared: } & 0.894 \\\\\n", + "\\textbf{Model:} & OLS & \\textbf{ Adj. R-squared: } & 0.893 \\\\\n", + "\\textbf{Method:} & Least Squares & \\textbf{ F-statistic: } & 2261. \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Prob (F-statistic):} & 0.00 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -14495. \\\\\n", + "\\textbf{No. Observations:} & 811 & \\textbf{ AIC: } & 2.900e+04 \\\\\n", + "\\textbf{Df Residuals:} & 807 & \\textbf{ BIC: } & 2.902e+04 \\\\\n", + "\\textbf{Df Model:} & 3 & \\textbf{ } & \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ } & \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{t} & \\textbf{P$> |$t$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{Intercept} & 3.01e+05 & 5.33e+05 & 0.565 & 0.572 & -7.45e+05 & 1.35e+06 \\\\\n", + "\\textbf{inc\\_grants} & -0.8846 & 0.025 & -35.956 & 0.000 & -0.933 & -0.836 \\\\\n", + "\\textbf{inc\\_donations} & -0.6647 & 0.016 & -40.721 & 0.000 & -0.697 & -0.633 \\\\\n", + "\\textbf{total\\_costs} & 0.8313 & 0.011 & 78.674 & 0.000 & 0.811 & 0.852 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lclc}\n", + "\\textbf{Omnibus:} & 1339.956 & \\textbf{ Durbin-Watson: } & 1.414 \\\\\n", + "\\textbf{Prob(Omnibus):} & 0.000 & \\textbf{ Jarque-Bera (JB): } & 1253317.706 \\\\\n", + "\\textbf{Skew:} & 9.899 & \\textbf{ Prob(JB): } & 0.00 \\\\\n", + "\\textbf{Kurtosis:} & 194.566 & \\textbf{ Cond. No. } & 1.05e+08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{OLS Regression Results}\n", + "\\end{center}\n", + "\n", + "Notes: \\newline\n", + " [1] Standard Errors assume that the covariance matrix of the errors is correctly specified. \\newline\n", + " [2] The condition number is large, 1.05e+08. This might indicate that there are \\newline\n", + " strong multicollinearity or other numerical problems." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " OLS Regression Results \n", + "==============================================================================\n", + "Dep. Variable: inc_activity R-squared: 0.894\n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.00\n", + "Time: 19:43:34 Log-Likelihood: -14495.\n", + "No. Observations: 811 AIC: 2.900e+04\n", + "Df Residuals: 807 BIC: 2.902e+04\n", + "Df Model: 3 \n", + "Covariance Type: nonrobust \n", + "=================================================================================\n", + " coef std err t P>|t| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "Intercept 3.01e+05 5.33e+05 0.565 0.572 -7.45e+05 1.35e+06\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852\n", + "==============================================================================\n", + "Omnibus: 1339.956 Durbin-Watson: 1.414\n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1253317.706\n", + "Skew: 9.899 Prob(JB): 0.00\n", + "Kurtosis: 194.566 Cond. No. 1.05e+08\n", + "==============================================================================\n", + "\n", + "Notes:\n", + "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", + "[2] The condition number is large, 1.05e+08. This might indicate that there are\n", + "strong multicollinearity or other numerical problems.\n", + "\"\"\"" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.olsr(\n", + " formula=\"inc_activity ~ inc_grants + inc_donations + total_costs\", data=new_df\n", + ")\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "0c489203", + "metadata": {}, + "source": [ + "### ACRO Probit" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "5b1a1611", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:probit() outcome: pass; dof=806.0 >= 10\n", + "INFO:acro:records:add(): output_15\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.493791\n", + " Iterations 10\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Probit Regression Results
Dep. Variable: survivor No. Observations: 811
Model: Probit Df Residuals: 806
Method: MLE Df Model: 4
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2140
Time: 19:43:34 Log-Likelihood: -400.46
converged: True LL-Null: -509.50
Covariance Type: nonrobust LLR p-value: 4.875e-46
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const 0.0474 0.057 0.838 0.402 -0.063 0.158
inc_activity 1.836e-07 5.16e-08 3.559 0.000 8.25e-08 2.85e-07
inc_grants 8.576e-08 3.9e-08 2.197 0.028 9.25e-09 1.62e-07
inc_donations 2.406e-07 4.54e-08 5.297 0.000 1.52e-07 3.3e-07
total_costs -8.644e-08 3.68e-08 -2.351 0.019 -1.59e-07 -1.44e-08


Possibly complete quasi-separation: A fraction 0.18 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & survivor & \\textbf{ No. Observations: } & 811 \\\\\n", + "\\textbf{Model:} & Probit & \\textbf{ Df Residuals: } & 806 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 4 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 0.2140 \\\\\n", + "\\textbf{Time:} & 19:43:34 & \\textbf{ Log-Likelihood: } & -400.46 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -509.50 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 4.875e-46 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 0.0474 & 0.057 & 0.838 & 0.402 & -0.063 & 0.158 \\\\\n", + "\\textbf{inc\\_activity} & 1.836e-07 & 5.16e-08 & 3.559 & 0.000 & 8.25e-08 & 2.85e-07 \\\\\n", + "\\textbf{inc\\_grants} & 8.576e-08 & 3.9e-08 & 2.197 & 0.028 & 9.25e-09 & 1.62e-07 \\\\\n", + "\\textbf{inc\\_donations} & 2.406e-07 & 4.54e-08 & 5.297 & 0.000 & 1.52e-07 & 3.3e-07 \\\\\n", + "\\textbf{total\\_costs} & -8.644e-08 & 3.68e-08 & -2.351 & 0.019 & -1.59e-07 & -1.44e-08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Probit Regression Results}\n", + "\\end{center}\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be \\newline\n", + " perfectly predicted. This might indicate that there is complete \\newline\n", + " quasi-separation. In this case some parameters will not be identified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Probit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: survivor No. Observations: 811\n", + "Model: Probit Df Residuals: 806\n", + "Method: MLE Df Model: 4\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2140\n", + "Time: 19:43:34 Log-Likelihood: -400.46\n", + "converged: True LL-Null: -509.50\n", + "Covariance Type: nonrobust LLR p-value: 4.875e-46\n", + "=================================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 0.0474 0.057 0.838 0.402 -0.063 0.158\n", + "inc_activity 1.836e-07 5.16e-08 3.559 0.000 8.25e-08 2.85e-07\n", + "inc_grants 8.576e-08 3.9e-08 2.197 0.028 9.25e-09 1.62e-07\n", + "inc_donations 2.406e-07 4.54e-08 5.297 0.000 1.52e-07 3.3e-07\n", + "total_costs -8.644e-08 3.68e-08 -2.351 0.019 -1.59e-07 -1.44e-08\n", + "=================================================================================\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be\n", + "perfectly predicted. This might indicate that there is complete\n", + "quasi-separation. In this case some parameters will not be identified.\n", + "\"\"\"" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_df = df[[\"survivor\", \"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "new_df = new_df.dropna()\n", + "\n", + "y = new_df[\"survivor\"].astype(\"category\").cat.codes # numeric\n", + "y.name = \"survivor\"\n", + "x = new_df[[\"inc_activity\", \"inc_grants\", \"inc_donations\", \"total_costs\"]]\n", + "x = add_constant(x)\n", + "\n", + "results = acro.probit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "22efa3df", + "metadata": {}, + "source": [ + "### ACRO Logit" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "dcf30f8f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:logit() outcome: pass; dof=806.0 >= 10\n", + "INFO:acro:records:add(): output_16\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimization terminated successfully.\n", + " Current function value: 0.490836\n", + " Iterations 12\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
Logit Regression Results
Dep. Variable: survivor No. Observations: 811
Model: Logit Df Residuals: 806
Method: MLE Df Model: 4
Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2187
Time: 19:43:35 Log-Likelihood: -398.07
converged: True LL-Null: -509.50
Covariance Type: nonrobust LLR p-value: 4.532e-47
\n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "\n", + " \n", + "\n", + "
coef std err z P>|z| [0.025 0.975]
const 0.0512 0.091 0.561 0.575 -0.128 0.230
inc_activity 2.981e-07 8.95e-08 3.330 0.001 1.23e-07 4.74e-07
inc_grants 1.351e-07 6.67e-08 2.026 0.043 4.39e-09 2.66e-07
inc_donations 5.123e-07 1.04e-07 4.927 0.000 3.08e-07 7.16e-07
total_costs -1.442e-07 6.26e-08 -2.304 0.021 -2.67e-07 -2.15e-08


Possibly complete quasi-separation: A fraction 0.18 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified." + ], + "text/latex": [ + "\\begin{center}\n", + "\\begin{tabular}{lclc}\n", + "\\toprule\n", + "\\textbf{Dep. Variable:} & survivor & \\textbf{ No. Observations: } & 811 \\\\\n", + "\\textbf{Model:} & Logit & \\textbf{ Df Residuals: } & 806 \\\\\n", + "\\textbf{Method:} & MLE & \\textbf{ Df Model: } & 4 \\\\\n", + "\\textbf{Date:} & Thu, 06 Mar 2025 & \\textbf{ Pseudo R-squ.: } & 0.2187 \\\\\n", + "\\textbf{Time:} & 19:43:35 & \\textbf{ Log-Likelihood: } & -398.07 \\\\\n", + "\\textbf{converged:} & True & \\textbf{ LL-Null: } & -509.50 \\\\\n", + "\\textbf{Covariance Type:} & nonrobust & \\textbf{ LLR p-value: } & 4.532e-47 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "\\begin{tabular}{lcccccc}\n", + " & \\textbf{coef} & \\textbf{std err} & \\textbf{z} & \\textbf{P$> |$z$|$} & \\textbf{[0.025} & \\textbf{0.975]} \\\\\n", + "\\midrule\n", + "\\textbf{const} & 0.0512 & 0.091 & 0.561 & 0.575 & -0.128 & 0.230 \\\\\n", + "\\textbf{inc\\_activity} & 2.981e-07 & 8.95e-08 & 3.330 & 0.001 & 1.23e-07 & 4.74e-07 \\\\\n", + "\\textbf{inc\\_grants} & 1.351e-07 & 6.67e-08 & 2.026 & 0.043 & 4.39e-09 & 2.66e-07 \\\\\n", + "\\textbf{inc\\_donations} & 5.123e-07 & 1.04e-07 & 4.927 & 0.000 & 3.08e-07 & 7.16e-07 \\\\\n", + "\\textbf{total\\_costs} & -1.442e-07 & 6.26e-08 & -2.304 & 0.021 & -2.67e-07 & -2.15e-08 \\\\\n", + "\\bottomrule\n", + "\\end{tabular}\n", + "%\\caption{Logit Regression Results}\n", + "\\end{center}\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be \\newline\n", + " perfectly predicted. This might indicate that there is complete \\newline\n", + " quasi-separation. In this case some parameters will not be identified." + ], + "text/plain": [ + "\n", + "\"\"\"\n", + " Logit Regression Results \n", + "==============================================================================\n", + "Dep. Variable: survivor No. Observations: 811\n", + "Model: Logit Df Residuals: 806\n", + "Method: MLE Df Model: 4\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 0.2187\n", + "Time: 19:43:35 Log-Likelihood: -398.07\n", + "converged: True LL-Null: -509.50\n", + "Covariance Type: nonrobust LLR p-value: 4.532e-47\n", + "=================================================================================\n", + " coef std err z P>|z| [0.025 0.975]\n", + "---------------------------------------------------------------------------------\n", + "const 0.0512 0.091 0.561 0.575 -0.128 0.230\n", + "inc_activity 2.981e-07 8.95e-08 3.330 0.001 1.23e-07 4.74e-07\n", + "inc_grants 1.351e-07 6.67e-08 2.026 0.043 4.39e-09 2.66e-07\n", + "inc_donations 5.123e-07 1.04e-07 4.927 0.000 3.08e-07 7.16e-07\n", + "total_costs -1.442e-07 6.26e-08 -2.304 0.021 -2.67e-07 -2.15e-08\n", + "=================================================================================\n", + "\n", + "Possibly complete quasi-separation: A fraction 0.18 of observations can be\n", + "perfectly predicted. This might indicate that there is complete\n", + "quasi-separation. In this case some parameters will not be identified.\n", + "\"\"\"" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = acro.logit(y, x)\n", + "results.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "3631a59d", + "metadata": {}, + "source": [ + "### ACRO Histogram without suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "af2f4313", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:status: fail\n", + "INFO:acro:records:add(): output_17\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "acro_artifacts/histogram_0.png\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAHDCAYAAAAOZuFZAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAANSRJREFUeJzt3X9UVPed//HXCMMgBFAxzkhDDHEx1kisxQSlSaRFsDZGrd2aLWljG7O1NbGl6FqNzWZ0E4xsF+lioptdjlJdSrZNyLZn01SskdQaW/zVRNOatBoTslCSiICBDiPc7x/5crsjqAzC8EGej3PmnNzPfd97P/fNJby888thWZYlAAAAgwwb6AkAAABciIACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIMMdu3b5fD4dBbb7010FMxygsvvCCv1zvQ0wDw/zn4qHtgaHnvvff0pz/9SVOnTpXL5Rro6RjjoYce0pNPPin+lwiYIXygJwAgtK699lpde+21Az2NXvH7/XI4HAoP539dwNWOp3iAIebCp3gyMjI0efJkVVdX64477lBUVJRuvPFGPfHEE+ro6AjY9uzZs1qxYoVuvPFGuVwujRkzRp/73Of0hz/8ocfHtyxL+fn5GjdunCIjIzVt2jRVVlYqIyNDGRkZdt3evXvlcDi0Y8cOrVixQh/72Mfkcrn0xz/+Ue+9956WLVumSZMm6ZprrtGYMWP0mc98Rr/61a8CjvXWW2/J4XDo+9//vgoLC5WUlKRrrrlGM2bM0IEDB+y6r371q3ryySclSQ6Hw3509ujHP/6x0tLSFBcXZ/fn/vvvD6LrAILFP0MAqK6uTvfee69WrFihRx99VBUVFVqzZo0SEhJ03333SZKam5t1++2366233tJ3v/tdpaWl6dy5c3r55ZdVW1uriRMn9uhYa9eu1YYNG/T1r39dCxcu1DvvvKMHHnhAfr9fEyZM6FK/Zs0azZgxQ1u3btWwYcM0ZswYvffee5KkRx99VB6PR+fOnVNFRYUyMjL0y1/+MiDoSNKTTz6piRMnqqioSJL0yCOP6HOf+5xOnTqluLg4PfLII/rwww/1k5/8RK+88oq93dixY/XKK6/onnvu0T333COv16vIyEidPn1ae/bs6UWnAfSYBWBI2bZtmyXJOnXqlGVZljVz5kxLkvWb3/wmoG7SpEnW7Nmz7eX169dbkqzKyspeH/vMmTOWy+Wy7rnnnoDxV155xZJkzZw50x576aWXLEnWnXfeedn9nj9/3vL7/VZmZqb1+c9/3h4/deqUJclKSUmxzp8/b4//9re/tSRZP/rRj+yxBx980Oruf4nf//73LUnW2bNngzlVAFeIp3gAyOPx6LbbbgsYu+WWW3T69Gl7+ec//7kmTJigWbNm9fo4Bw4ckM/n06JFiwLGp0+frhtuuKHbbb7whS90O75161Z98pOfVGRkpMLDw+V0OvXLX/5Sv//977vU3nXXXQoLC7OXb7nlFkkKOL+LufXWWyVJixYt0n/913/p3Xffvew2AK4cAQWA4uPju4y5XC61trbay++9956uu+66KzrOBx98IElyu91d1nU3Jn30NMuFCgsL9c1vflNpaWl69tlndeDAAVVXV+uzn/1swJw7XXh+ne9e6q72Qnfeeaeef/55nT9/Xvfdd5+uu+46TZ48WT/60Y8uuy2A3uM1KAB65Nprr1VNTc0V7aMzKPz5z3/usq6urq7buygOh6PL2M6dO5WRkaEtW7YEjDc3N1/R/C5m/vz5mj9/vnw+nw4cOKANGzYoJydHN9xwg2bMmNEvxwSGOu6gAOiROXPm6I033riiF4empaXJ5XLpmWeeCRg/cOBAj55u6eRwOLp8hsurr74a8ALXYPXkrorL5dLMmTO1ceNGSdKRI0d6fTwAl8YdFAA9kpubq2eeeUbz58/X6tWrddttt6m1tVVVVVWaO3euPv3pT192H6NGjVJeXp42bNigkSNH6vOf/7xqamq0bt06jR07VsOG9ezfTHPnztU//dM/6dFHH9XMmTN14sQJrV+/XklJSTp//nyvzi8lJUWStHHjRs2ZM0dhYWG65ZZb9Nhjj6mmpkaZmZm67rrrdPbsWf3gBz+Q0+nUzJkze3UsAJdHQAHQIzExMdq3b5+8Xq+efvpprVu3TiNHjtStt96qr3/96z3ez+OPP67o6Ght3bpV27Zt08SJE7VlyxatXbtWI0aM6NE+1q5dq5aWFpWUlKigoECTJk3S1q1bVVFRob179/bq/HJycvTrX/9aTz31lNavXy/LsnTq1CmlpaXp4MGD+u53v6v33ntPI0aM0LRp07Rnzx7dfPPNvToWgMvjo+4BDLhTp05p4sSJevTRR/Xwww8P9HQAGICAAiCkfve73+lHP/qR0tPTFRsbqxMnTqigoEBNTU06duzYRd/NA2Bo4SkeAH2ivb39kl+053A4FBYWpujoaB08eFAlJSU6e/as4uLilJGRoccff5xwAsDGHRQAfSIjI0NVVVUXXT9u3Dj7u20A4HIIKAD6xIkTJy75OSQul8t+pwwAXA4BBQAAGCeoD2o7f/68vve97ykpKUnDhw/XjTfeqPXr1wd8JbtlWfJ6vUpISNDw4cOVkZGh48ePB+zH5/Np+fLlGj16tKKjozVv3rwr/oRKAABw9QjqRbIbN27U1q1bVVpaqptvvlkHDx7U1772NcXFxenb3/62JKmgoECFhYXavn27JkyYoMcee0xZWVk6ceKEYmJiJH30gU8/+9nPVF5ervj4eK1YsUJz587VoUOHAr7Q62I6Ojr0v//7v4qJien2Y7ABAIB5LMtSc3OzEhISLv/BjMF89fFdd91l3X///QFjCxcutL785S9blmVZHR0dlsfjsZ544gl7/V/+8hcrLi7O2rp1q2VZlnX27FnL6XRa5eXlds27775rDRs2zHrxxRe7Pe5f/vIXq7Gx0X68/vrrliQePHjw4MGDxyB8vPPOO5fNHEHdQbn99tu1detWvfHGG5owYYJ+97vfad++fSoqKpL00Yct1dXVKTs7296m87sr9u/fr6VLl+rQoUPy+/0BNQkJCZo8ebL279+v2bNndznuhg0btG7dui7j//Ef/6GoqKhgTgEAAAyQlpYWPfDAA/YzKpcSVED57ne/q8bGRk2cOFFhYWFqb2/X448/ri996UuSPvo2Uqnr16a73W77i8Dq6uoUERGhkSNHdqnp3P5Ca9asUV5enr3c1NSkxMRELViwQLGxscGcwmX5/X5VVlYqKytLTqezT/eNv6LPoUOvQ4M+hw69Do3+6HNTU5MeeOCBHr08I6iA8swzz2jnzp0qKyvTzTffrKNHjyo3N1cJCQlavHixXXfhgS3LuuxkLlXjcrm6fHOpJDmdzn67OPtz3/gr+hw69Do06HPo0OvQ6Ms+B7OfoALKP/zDP2j16tX6u7/7O0kfffvn6dOntWHDBi1evFgej0fSR3dJxo4da29XX19v31XxeDxqa2tTQ0NDwF2U+vp6paenBzMdAABwlQrqbcYtLS1dXnUbFhZmv804KSlJHo9HlZWV9vq2tjZVVVXZ4SM1NVVOpzOgpra2VseOHSOgAAAASUHeQbn77rv1+OOP6/rrr9fNN9+sI0eOqLCwUPfff7+kj57ayc3NVX5+vpKTk5WcnKz8/HxFRUUpJydHkhQXF6clS5ZoxYoVio+P16hRo7Ry5UqlpKRo1qxZfX+GAABg0AkqoBQXF+uRRx7RsmXLVF9fr4SEBC1dulT/+I//aNesWrVKra2tWrZsmRoaGpSWlqZdu3YFvGJ306ZNCg8P16JFi9Ta2qrMzExt3769R5+BAgAArn5BBZSYmBgVFRXZbyvujsPhkNfrldfrvWhNZGSkiouLVVxcHMzhAQDAEBHUa1AAAABCgYACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADBOUB/UNpRM9v5CvvbLfx20Kd564q6BngIAAH2GOygAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGCcoALKDTfcIIfD0eXx4IMPSpIsy5LX61VCQoKGDx+ujIwMHT9+PGAfPp9Py5cv1+jRoxUdHa158+appqam784IAAAMekEFlOrqatXW1tqPyspKSdIXv/hFSVJBQYEKCwu1efNmVVdXy+PxKCsrS83NzfY+cnNzVVFRofLycu3bt0/nzp3T3Llz1d7e3oenBQAABrPwYIqvvfbagOUnnnhC48eP18yZM2VZloqKirR27VotXLhQklRaWiq3262ysjItXbpUjY2NKikp0Y4dOzRr1ixJ0s6dO5WYmKjdu3dr9uzZ3R7X5/PJ5/PZy01NTZIkv98vv98fzClcVuf+XMOsPt1vf+vrPvS3zvkOtnkPRvQ6NOhz6NDr0OiPPgezL4dlWb36S9zW1qaEhATl5eXp4Ycf1smTJzV+/HgdPnxYU6dOtevmz5+vESNGqLS0VHv27FFmZqbOnDmjkSNH2jVTpkzRggULtG7dum6P5fV6u11XVlamqKio3kwfAACEWEtLi3JyctTY2KjY2NhL1gZ1B+X/ev7553X27Fl99atflSTV1dVJktxud0Cd2+3W6dOn7ZqIiIiAcNJZ07l9d9asWaO8vDx7uampSYmJicrOzr7sCQbL7/ersrJSjxwcJl+Ho0/33Z+Oebu/+2Sqzj5nZWXJ6XQO9HSuavQ6NOhz6NDr0OiPPnc+A9ITvQ4oJSUlmjNnjhISEgLGHY7AP+qWZXUZu9Dlalwul1wuV5dxp9PZbxenr8MhX/vgCSiD9Ze0P3+GCESvQ4M+hw69Do2+7HMw++nV24xPnz6t3bt364EHHrDHPB6PJHW5E1JfX2/fVfF4PGpra1NDQ8NFawAAAHoVULZt26YxY8borrvusseSkpLk8Xjsd/ZIH71OpaqqSunp6ZKk1NRUOZ3OgJra2lodO3bMrgEAAAj6KZ6Ojg5t27ZNixcvVnj4Xzd3OBzKzc1Vfn6+kpOTlZycrPz8fEVFRSknJ0eSFBcXpyVLlmjFihWKj4/XqFGjtHLlSqWkpNjv6gEAAAg6oOzevVtvv/227r///i7rVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY9ds2rRJ4eHhWrRokVpbW5WZmant27crLCzsys4EAABcNYIOKNnZ2brYO5MdDoe8Xq+8Xu9Ft4+MjFRxcbGKi4uDPTQAABgi+C4eAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABgn6IDy7rvv6stf/rLi4+MVFRWlT3ziEzp06JC93rIseb1eJSQkaPjw4crIyNDx48cD9uHz+bR8+XKNHj1a0dHRmjdvnmpqaq78bAAAwFUhqIDS0NCgT33qU3I6nfr5z3+u119/Xf/yL/+iESNG2DUFBQUqLCzU5s2bVV1dLY/Ho6ysLDU3N9s1ubm5qqioUHl5ufbt26dz585p7ty5am9v77MTAwAAg1d4MMUbN25UYmKitm3bZo/dcMMN9n9blqWioiKtXbtWCxculCSVlpbK7XarrKxMS5cuVWNjo0pKSrRjxw7NmjVLkrRz504lJiZq9+7dmj17dpfj+nw++Xw+e7mpqUmS5Pf75ff7gzmFy+rcn2uY1af77W993Yf+1jnfwTbvwYhehwZ9Dh16HRr90edg9uWwLKvHf4knTZqk2bNnq6amRlVVVfrYxz6mZcuW6e///u8lSSdPntT48eN1+PBhTZ061d5u/vz5GjFihEpLS7Vnzx5lZmbqzJkzGjlypF0zZcoULViwQOvWretyXK/X2+14WVmZoqKienyyAABg4LS0tCgnJ0eNjY2KjY29ZG1Qd1BOnjypLVu2KC8vTw8//LB++9vf6lvf+pZcLpfuu+8+1dXVSZLcbnfAdm63W6dPn5Yk1dXVKSIiIiCcdNZ0bn+hNWvWKC8vz15uampSYmKisrOzL3uCwfL7/aqsrNQjB4fJ1+Ho0333p2PerneeTNbZ56ysLDmdzoGezlWNXocGfQ4deh0a/dHnzmdAeiKogNLR0aFp06YpPz9fkjR16lQdP35cW7Zs0X333WfXORyBf9gty+oydqFL1bhcLrlcri7jTqez3y5OX4dDvvbBE1AG6y9pf/4MEYhehwZ9Dh16HRp92edg9hPUi2THjh2rSZMmBYx9/OMf19tvvy1J8ng8ktTlTkh9fb19V8Xj8aitrU0NDQ0XrQEAAENbUAHlU5/6lE6cOBEw9sYbb2jcuHGSpKSkJHk8HlVWVtrr29raVFVVpfT0dElSamqqnE5nQE1tba2OHTtm1wAAgKEtqKd4vvOd7yg9PV35+flatGiRfvvb3+rpp5/W008/Lemjp3Zyc3OVn5+v5ORkJScnKz8/X1FRUcrJyZEkxcXFacmSJVqxYoXi4+M1atQorVy5UikpKfa7egAAwNAWVEC59dZbVVFRoTVr1mj9+vVKSkpSUVGR7r33Xrtm1apVam1t1bJly9TQ0KC0tDTt2rVLMTExds2mTZsUHh6uRYsWqbW1VZmZmdq+fbvCwsL67swAAMCgFVRAkaS5c+dq7ty5F13vcDjk9Xrl9XovWhMZGani4mIVFxcHe3gAADAE8F08AADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADBOUAHF6/XK4XAEPDwej73esix5vV4lJCRo+PDhysjI0PHjxwP24fP5tHz5co0ePVrR0dGaN2+eampq+uZsAADAVSHoOyg333yzamtr7cdrr71mrysoKFBhYaE2b96s6upqeTweZWVlqbm52a7Jzc1VRUWFysvLtW/fPp07d05z585Ve3t735wRAAAY9MKD3iA8POCuSSfLslRUVKS1a9dq4cKFkqTS0lK53W6VlZVp6dKlamxsVElJiXbs2KFZs2ZJknbu3KnExETt3r1bs2fP7vaYPp9PPp/PXm5qapIk+f1++f3+YE/hkjr35xpm9el++1tf96G/dc53sM17MKLXoUGfQ4deh0Z/9DmYfTksy+rxX2Kv16t//ud/VlxcnFwul9LS0pSfn68bb7xRJ0+e1Pjx43X48GFNnTrV3mb+/PkaMWKESktLtWfPHmVmZurMmTMaOXKkXTNlyhQtWLBA69atu+hxu1tXVlamqKioHp8sAAAYOC0tLcrJyVFjY6NiY2MvWRvUHZS0tDT98Ic/1IQJE/TnP/9Zjz32mNLT03X8+HHV1dVJktxud8A2brdbp0+fliTV1dUpIiIiIJx01nRu3501a9YoLy/PXm5qalJiYqKys7Mve4LB8vv9qqys1CMHh8nX4ejTffenY97u7z6ZqrPPWVlZcjqdAz2dqxq9Dg36HDr0OjT6o8+dz4D0RFABZc6cOfZ/p6SkaMaMGRo/frxKS0s1ffp0SZLDEfhH3bKsLmMXulyNy+WSy+XqMu50Ovvt4vR1OORrHzwBZbD+kvbnzxCB6HVo0OfQodeh0Zd9DmY/V/Q24+joaKWkpOjNN9+0X5dy4Z2Q+vp6+66Kx+NRW1ubGhoaLloDAABwRQHF5/Pp97//vcaOHaukpCR5PB5VVlba69va2lRVVaX09HRJUmpqqpxOZ0BNbW2tjh07ZtcAAAAE9RTPypUrdffdd+v6669XfX29HnvsMTU1NWnx4sVyOBzKzc1Vfn6+kpOTlZycrPz8fEVFRSknJ0eSFBcXpyVLlmjFihWKj4/XqFGjtHLlSqWkpNjv6gEAAAgqoNTU1OhLX/qS3n//fV177bWaPn26Dhw4oHHjxkmSVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY+9j06ZNCg8P16JFi9Ta2qrMzExt375dYWFhfXtmAABg0AoqoJSXl19yvcPhkNfrldfrvWhNZGSkiouLVVxcHMyhAQDAEMJ38QAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOFcUUDZs2CCHw6Hc3Fx7zLIseb1eJSQkaPjw4crIyNDx48cDtvP5fFq+fLlGjx6t6OhozZs3TzU1NVcyFQAAcBXpdUCprq7W008/rVtuuSVgvKCgQIWFhdq8ebOqq6vl8XiUlZWl5uZmuyY3N1cVFRUqLy/Xvn37dO7cOc2dO1ft7e29PxMAAHDVCO/NRufOndO9996rf//3f9djjz1mj1uWpaKiIq1du1YLFy6UJJWWlsrtdqusrExLly5VY2OjSkpKtGPHDs2aNUuStHPnTiUmJmr37t2aPXt2l+P5fD75fD57uampSZLk9/vl9/t7cwoX1bk/1zCrT/fb3/q6D/2tc76Dbd6DEb0ODfocOvQ6NPqjz8Hsy2FZVtB/iRcvXqxRo0Zp06ZNysjI0Cc+8QkVFRXp5MmTGj9+vA4fPqypU6fa9fPnz9eIESNUWlqqPXv2KDMzU2fOnNHIkSPtmilTpmjBggVat25dl+N5vd5ux8vKyhQVFRXs9AEAwABoaWlRTk6OGhsbFRsbe8naoO+glJeX6/Dhw6quru6yrq6uTpLkdrsDxt1ut06fPm3XREREBISTzprO7S+0Zs0a5eXl2ctNTU1KTExUdnb2ZU8wWH6/X5WVlXrk4DD5Ohx9uu/+dMzb9c6TyTr7nJWVJafTOdDTuarR69Cgz6FDr0OjP/rc+QxITwQVUN555x19+9vf1q5duxQZGXnROocj8A+7ZVldxi50qRqXyyWXy9Vl3Ol09tvF6etwyNc+eALKYP0l7c+fIQLR69Cgz6FDr0OjL/sczH6CepHsoUOHVF9fr9TUVIWHhys8PFxVVVX613/9V4WHh9t3Ti68E1JfX2+v83g8amtrU0NDw0VrAADA0BZUQMnMzNRrr72mo0eP2o9p06bp3nvv1dGjR3XjjTfK4/GosrLS3qatrU1VVVVKT0+XJKWmpsrpdAbU1NbW6tixY3YNAAAY2oJ6iicmJkaTJ08OGIuOjlZ8fLw9npubq/z8fCUnJys5OVn5+fmKiopSTk6OJCkuLk5LlizRihUrFB8fr1GjRmnlypVKSUmx39UDAACGtl69zfhSVq1apdbWVi1btkwNDQ1KS0vTrl27FBMTY9ds2rRJ4eHhWrRokVpbW5WZmant27crLCysr6cDAAAGoSsOKHv37g1Ydjgc8nq98nq9F90mMjJSxcXFKi4uvtLDAwCAqxDfxQMAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4wQVULZs2aJbbrlFsbGxio2N1YwZM/Tzn//cXm9ZlrxerxISEjR8+HBlZGTo+PHjAfvw+Xxavny5Ro8erejoaM2bN081NTV9czYAAOCqEFRAue666/TEE0/o4MGDOnjwoD7zmc9o/vz5dggpKChQYWGhNm/erOrqank8HmVlZam5udneR25urioqKlReXq59+/bp3Llzmjt3rtrb2/v2zAAAwKAVHkzx3XffHbD8+OOPa8uWLTpw4IAmTZqkoqIirV27VgsXLpQklZaWyu12q6ysTEuXLlVjY6NKSkq0Y8cOzZo1S5K0c+dOJSYmavfu3Zo9e3a3x/X5fPL5fPZyU1OTJMnv98vv9wdzCpfVuT/XMKtP99vf+roP/a1zvoNt3oMRvQ4N+hw69Do0+qPPwezLYVlWr/4St7e368c//rEWL16sI0eOKDIyUuPHj9fhw4c1depUu27+/PkaMWKESktLtWfPHmVmZurMmTMaOXKkXTNlyhQtWLBA69at6/ZYXq+323VlZWWKiorqzfQBAECItbS0KCcnR42NjYqNjb1kbVB3UCTptdde04wZM/SXv/xF11xzjSoqKjRp0iTt379fkuR2uwPq3W63Tp8+LUmqq6tTREREQDjprKmrq7voMdesWaO8vDx7uampSYmJicrOzr7sCQbL7/ersrJSjxwcJl+Ho0/33Z+Oebu/+2Sqzj5nZWXJ6XQO9HSuavQ6NOhz6NDr0OiPPnc+A9ITQQeUm266SUePHtXZs2f17LPPavHixaqqqrLXOxyBf9Qty+oydqHL1bhcLrlcri7jTqez3y5OX4dDvvbBE1AG6y9pf/4MEYhehwZ9Dh16HRp92edg9hP024wjIiL0N3/zN5o2bZo2bNigKVOm6Ac/+IE8Ho8kdbkTUl9fb99V8Xg8amtrU0NDw0VrAAAArvhzUCzLks/nU1JSkjwejyorK+11bW1tqqqqUnp6uiQpNTVVTqczoKa2tlbHjh2zawAAAIJ6iufhhx/WnDlzlJiYqObmZpWXl2vv3r168cUX5XA4lJubq/z8fCUnJys5OVn5+fmKiopSTk6OJCkuLk5LlizRihUrFB8fr1GjRmnlypVKSUmx39UDAAAQVED585//rK985Suqra1VXFycbrnlFr344ovKysqSJK1atUqtra1atmyZGhoalJaWpl27dikmJsbex6ZNmxQeHq5FixaptbVVmZmZ2r59u8LCwvr2zAAAwKAVVEApKSm55HqHwyGv1yuv13vRmsjISBUXF6u4uDiYQwMAgCGE7+IBAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHGCCigbNmzQrbfeqpiYGI0ZM0YLFizQiRMnAmosy5LX61VCQoKGDx+ujIwMHT9+PKDG5/Np+fLlGj16tKKjozVv3jzV1NRc+dkAAICrQlABpaqqSg8++KAOHDigyspKnT9/XtnZ2frwww/tmoKCAhUWFmrz5s2qrq6Wx+NRVlaWmpub7Zrc3FxVVFSovLxc+/bt07lz5zR37ly1t7f33ZkBAIBBKzyY4hdffDFgedu2bRozZowOHTqkO++8U5ZlqaioSGvXrtXChQslSaWlpXK73SorK9PSpUvV2NiokpIS7dixQ7NmzZIk7dy5U4mJidq9e7dmz57d5bg+n08+n89ebmpqkiT5/X75/f7gzvgyOvfnGmb16X77W1/3ob91znewzXswotehQZ9Dh16HRn/0OZh9OSzL6vVf4j/+8Y9KTk7Wa6+9psmTJ+vkyZMaP368Dh8+rKlTp9p18+fP14gRI1RaWqo9e/YoMzNTZ86c0ciRI+2aKVOmaMGCBVq3bl2X43i93m7Hy8rKFBUV1dvpAwCAEGppaVFOTo4aGxsVGxt7ydqg7qD8X5ZlKS8vT7fffrsmT54sSaqrq5Mkud3ugFq3263Tp0/bNREREQHhpLOmc/sLrVmzRnl5efZyU1OTEhMTlZ2dfdkTDJbf71dlZaUeOThMvg5Hn+67Px3zdr3zZLLOPmdlZcnpdA70dK5q9Do06HPo0OvQ6I8+dz4D0hO9DigPPfSQXn31Ve3bt6/LOocj8A+7ZVldxi50qRqXyyWXy9Vl3Ol09tvF6etwyNc+eALKYP0l7c+fIQLR69Cgz6FDr0OjL/sczH569Tbj5cuX66c//aleeuklXXfddfa4x+ORpC53Qurr6+27Kh6PR21tbWpoaLhoDQAAGNqCCiiWZemhhx7Sc889pz179igpKSlgfVJSkjwejyorK+2xtrY2VVVVKT09XZKUmpoqp9MZUFNbW6tjx47ZNQAAYGgL6imeBx98UGVlZfrv//5vxcTE2HdK4uLiNHz4cDkcDuXm5io/P1/JyclKTk5Wfn6+oqKilJOTY9cuWbJEK1asUHx8vEaNGqWVK1cqJSXFflcPAAAY2oIKKFu2bJEkZWRkBIxv27ZNX/3qVyVJq1atUmtrq5YtW6aGhgalpaVp165diomJses3bdqk8PBwLVq0SK2trcrMzNT27dsVFhZ2ZWcDAACuCkEFlJ68I9nhcMjr9crr9V60JjIyUsXFxSouLg7m8AAAYIjgu3gAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDhBB5SXX35Zd999txISEuRwOPT8888HrLcsS16vVwkJCRo+fLgyMjJ0/PjxgBqfz6fly5dr9OjRio6O1rx581RTU3NFJwIAAK4eQQeUDz/8UFOmTNHmzZu7XV9QUKDCwkJt3rxZ1dXV8ng8ysrKUnNzs12Tm5uriooKlZeXa9++fTp37pzmzp2r9vb23p8JAAC4aoQHu8GcOXM0Z86cbtdZlqWioiKtXbtWCxculCSVlpbK7XarrKxMS5cuVWNjo0pKSrRjxw7NmjVLkrRz504lJiZq9+7dmj179hWcDgAAuBoEHVAu5dSpU6qrq1N2drY95nK5NHPmTO3fv19Lly7VoUOH5Pf7A2oSEhI0efJk7d+/v9uA4vP55PP57OWmpiZJkt/vl9/v78tTsPfnGmb16X77W1/3ob91znewzXswotehQZ9Dh16HRn/0OZh99WlAqaurkyS53e6AcbfbrdOnT9s1ERERGjlyZJeazu0vtGHDBq1bt67L+K5duxQVFdUXU+/in6Z19Mt++8sLL7ww0FPolcrKyoGewpBBr0ODPocOvQ6NvuxzS0tLj2v7NKB0cjgcAcuWZXUZu9ClatasWaO8vDx7uampSYmJicrOzlZsbOyVT/j/8Pv9qqys1CMHh8nXcek5m+SYd3A9NdbZ56ysLDmdzoGezlWNXocGfQ4deh0a/dHnzmdAeqJPA4rH45H00V2SsWPH2uP19fX2XRWPx6O2tjY1NDQE3EWpr69Xenp6t/t1uVxyuVxdxp1OZ79dnL4Oh3ztgyegDNZf0v78GSIQvQ4N+hw69Do0+rLPweynTz8HJSkpSR6PJ+B2UFtbm6qqquzwkZqaKqfTGVBTW1urY8eOXTSgAACAoSXoOyjnzp3TH//4R3v51KlTOnr0qEaNGqXrr79eubm5ys/PV3JyspKTk5Wfn6+oqCjl5ORIkuLi4rRkyRKtWLFC8fHxGjVqlFauXKmUlBT7XT0AAGBoCzqgHDx4UJ/+9Kft5c7XhixevFjbt2/XqlWr1NraqmXLlqmhoUFpaWnatWuXYmJi7G02bdqk8PBwLVq0SK2trcrMzNT27dsVFhbWB6cEAAAGu6ADSkZGhizr4m/BdTgc8nq98nq9F62JjIxUcXGxiouLgz08AAAYAvguHgAAYBwCCgAAMA4BBQAAGIeAAgAAjENAAQAAxiGgAAAA4xBQAACAcQgoAADAOAQUAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYBwCCgAAME74QE8AfeOG1f8z0FMIiivMUsFtAz0LAICpuIMCAACMQ0ABAADGIaAAAADjEFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABhnQAPKU089paSkJEVGRio1NVW/+tWvBnI6AADAEAP2SbLPPPOMcnNz9dRTT+lTn/qU/u3f/k1z5szR66+/ruuvv36gpoUQm+z9hXztjoGeRo+99cRdAz0FGGqy9xcquI1rGugrA3YHpbCwUEuWLNEDDzygj3/84yoqKlJiYqK2bNkyUFMCAACGGJA7KG1tbTp06JBWr14dMJ6dna39+/d3qff5fPL5fPZyY2OjJOnMmTPy+/19Oje/36+WlhaF+4epvWPw/CtosAnvsNTS0jHo+vw3K/9roKcQtH0r71RLS4s++OADOZ3OgZ7OVSvc/yHXdIgMxms6bcMvB3oKQeuPPjc3N0uSLMu6bO2ABJT3339f7e3tcrvdAeNut1t1dXVd6jds2KB169Z1GU9KSuq3OaL/5Qz0BIaIsf8y0DMYOrimQ4NrOjT6s8/Nzc2Ki4u7ZM2AfpuxwxH4rwzLsrqMSdKaNWuUl5dnL3d0dOjMmTOKj4/vtv5KNDU1KTExUe+8845iY2P7dN/4K/ocOvQ6NOhz6NDr0OiPPluWpebmZiUkJFy2dkACyujRoxUWFtblbkl9fX2XuyqS5HK55HK5AsZGjBjRn1NUbGwsF34I0OfQodehQZ9Dh16HRl/3+XJ3TjoNyItkIyIilJqaqsrKyoDxyspKpaenD8SUAACAQQbsKZ68vDx95Stf0bRp0zRjxgw9/fTTevvtt/WNb3xjoKYEAAAMMWAB5Z577tEHH3yg9evXq7a2VpMnT9YLL7ygcePGDdSUJH30dNKjjz7a5Skl9C36HDr0OjToc+jQ69AY6D47rJ681wcAACCE+C4eAABgHAIKAAAwDgEFAAAYh4ACAACMQ0ABAADGGZIB5amnnlJSUpIiIyOVmpqqX/3qV5esr6qqUmpqqiIjI3XjjTdq69atIZrp4BZMn/fu3SuHw9Hl8Yc//CGEMx58Xn75Zd19991KSEiQw+HQ888/f9ltuJ57J9hec033zoYNG3TrrbcqJiZGY8aM0YIFC3TixInLbsd1HZze9DnU1/SQCyjPPPOMcnNztXbtWh05ckR33HGH5syZo7fffrvb+lOnTulzn/uc7rjjDh05ckQPP/ywvvWtb+nZZ58N8cwHl2D73OnEiROqra21H8nJySGa8eD04YcfasqUKdq8eXOP6rmeey/YXnfimg5OVVWVHnzwQR04cECVlZU6f/68srOz9eGHH150G67r4PWmz51Cdk1bQ8xtt91mfeMb3wgYmzhxorV69epu61etWmVNnDgxYGzp0qXW9OnT+22OV4Ng+/zSSy9ZkqyGhoYQzO7qJMmqqKi4ZA3Xc9/oSa+5pvtGfX29Jcmqqqq6aA3X9ZXrSZ9DfU0PqTsobW1tOnTokLKzswPGs7OztX///m63eeWVV7rUz549WwcPHpTf7++3uQ5mvelzp6lTp2rs2LHKzMzUSy+91J/THJK4nkOPa/rKNDY2SpJGjRp10Rqu6yvXkz53CtU1PaQCyvvvv6/29vYu35jsdru7fLNyp7q6um7rz58/r/fff7/f5jqY9abPY8eO1dNPP61nn31Wzz33nG666SZlZmbq5ZdfDsWUhwyu59Dhmr5ylmUpLy9Pt99+uyZPnnzROq7rK9PTPof6mh6w7+IZSA6HI2DZsqwuY5er724cgYLp80033aSbbrrJXp4xY4beeecdff/739edd97Zr/McarieQ4Nr+so99NBDevXVV7Vv377L1nJd915P+xzqa3pI3UEZPXq0wsLCuvwrvr6+vkv67uTxeLqtDw8PV3x8fL/NdTDrTZ+7M336dL355pt9Pb0hjet5YHFN99zy5cv105/+VC+99JKuu+66S9ZyXfdeMH3uTn9e00MqoERERCg1NVWVlZUB45WVlUpPT+92mxkzZnSp37Vrl6ZNmyan09lvcx3MetPn7hw5ckRjx47t6+kNaVzPA4tr+vIsy9JDDz2k5557Tnv27FFSUtJlt+G6Dl5v+tydfr2mQ/JSXIOUl5dbTqfTKikpsV5//XUrNzfXio6Ott566y3Lsixr9erV1le+8hW7/uTJk1ZUVJT1ne98x3r99detkpISy+l0Wj/5yU8G6hQGhWD7vGnTJquiosJ64403rGPHjlmrV6+2JFnPPvvsQJ3CoNDc3GwdOXLEOnLkiCXJKiwstI4cOWKdPn3asiyu574UbK+5pnvnm9/8phUXF2ft3bvXqq2ttR8tLS12Ddf1letNn0N9TQ+5gGJZlvXkk09a48aNsyIiIqxPfvKTAW+rWrx4sTVz5syA+r1791pTp061IiIirBtuuMHasmVLiGc8OAXT540bN1rjx4+3IiMjrZEjR1q333679T//8z8DMOvBpfNtfxc+Fi9ebFkW13NfCrbXXNO9012PJVnbtm2za7iur1xv+hzqa9rx/ycKAABgjCH1GhQAADA4EFAAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAALaXX35Zd999txISEuRwOPT8888HvY9f/OIXmj59umJiYnTttdfqC1/4gk6dOhXUPggoAADA9uGHH2rKlCnavHlzr7Y/efKk5s+fr8985jM6evSofvGLX+j999/XwoULg9oPnyQLAAC65XA4VFFRoQULFthjbW1t+t73vqf//M//1NmzZzV58mRt3LhRGRkZkqSf/OQn+tKXviSfz6dhwz66D/Kzn/1M8+fPl8/n6/EXOHIHBQAA9NjXvvY1/frXv1Z5ebleffVVffGLX9RnP/tZvfnmm5KkadOmKSwsTNu2bVN7e7saGxu1Y8cOZWdnB/Xt0txBAQAA3brwDsqf/vQnJScnq6amRgkJCXbdrFmzdNtttyk/P1/SR69j+eIXv6gPPvhA7e3tmjFjhl544QWNGDGix8fmDgoAAOiRw4cPy7IsTZgwQddcc439qKqq0p/+9CdJUl1dnR544AEtXrxY1dXVqqqqUkREhP72b/9WwdwTCe+vkwAAAFeXjo4OhYWF6dChQwoLCwtYd80110iSnnzyScXGxqqgoMBet3PnTiUmJuo3v/mNpk+f3qNjEVAAAECPTJ06Ve3t7aqvr9cdd9zRbU1LS0uX8NK53NHR0eNj8RQPAACwnTt3TkePHtXRo0clSadOndLRo0f19ttva8KECbr33nt133336bnnntOpU6dUXV2tjRs36oUXXpAk3XXXXaqurtb69ev15ptv6vDhw/ra176mcePGaerUqT2eBy+SBQAAtr179+rTn/50l/HFixdr+/bt8vv9euyxx/TDH/5Q7777ruLj4zVjxgytW7dOKSkpkqTy8nIVFBTojTfeUFRUlGbMmKGNGzdq4sSJPZ4HAQUAABiHp3gAAIBxCCgAAMA4BBQAAGAcAgoAADAOAQUAABiHgAIAAIxDQAEAAMYhoAAAAOMQUAAAgHEIKAAAwDgEFAAAYJz/B3neUANYkvVvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "hist = acro.hist(df, \"inc_grants\")\n", + "print(hist)" + ] + }, + { + "cell_type": "markdown", + "id": "5faf9a98", + "metadata": {}, + "source": [ + "### ACRO Histogram with suppression" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "349d8a29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:acro:Histogram will not be shown as the inc_grants column is disclosive.\n", + "INFO:acro:status: fail\n", + "INFO:acro:records:add(): output_18\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "acro.suppress = True\n", + "hist = acro.hist(df, \"inc_grants\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "ab0fe892", + "metadata": {}, + "outputs": [], + "source": [ + "acro.suppress = False" + ] + }, + { + "cell_type": "markdown", + "id": "589fedc6", + "metadata": {}, + "source": [ + "### List current ACRO outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "ec960039", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "uid: output_0\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 12, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 5]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, [df.survivor, df.grant_type])\n", + "summary: fail; threshold: 12 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 threshold; ok ok ok ok threshold; \n", + "2011 threshold; ok ok ok ok threshold; \n", + "2012 threshold; ok ok ok ok threshold; \n", + "2013 threshold; ok ok ok ok threshold; \n", + "2014 threshold; ok ok ok ok threshold; \n", + "2015 threshold; ok ok ok ok threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015 \n", + "grant_type G R G N R R/G\n", + "year \n", + "2010 3 47 12 59 24 8\n", + "2011 3 47 12 59 24 8\n", + "2012 3 47 12 59 24 8\n", + "2013 3 47 12 59 24 8\n", + "2014 3 47 12 59 24 8\n", + "2015 3 47 12 59 24 8]\n", + "timestamp: 2025-03-06T19:43:34.383332\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_1\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 12, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 5]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, [df.grant_type, df.survivor])\n", + "summary: fail; threshold: 12 cells may need suppressing; \n", + "outcome: grant_type G N R \\\n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 Dead_in_2015 \n", + "year \n", + "2010 threshold; ok ok ok \n", + "2011 threshold; ok ok ok \n", + "2012 threshold; ok ok ok \n", + "2013 threshold; ok ok ok \n", + "2014 threshold; ok ok ok \n", + "2015 threshold; ok ok ok \n", + "\n", + "grant_type R/G \n", + "survivor Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 ok threshold; \n", + "2011 ok threshold; \n", + "2012 ok threshold; \n", + "2013 ok threshold; \n", + "2014 ok threshold; \n", + "2015 ok threshold; \n", + "output: [grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 3 12 59 47 \n", + "2011 3 12 59 47 \n", + "2012 3 12 59 47 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 47 \n", + "2015 3 12 59 47 \n", + "\n", + "grant_type R/G \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 24 8 \n", + "2011 24 8 \n", + "2012 24 8 \n", + "2013 24 8 \n", + "2014 24 8 \n", + "2015 24 8 ]\n", + "timestamp: 2025-03-06T19:43:34.405157\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_2\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 1], [1, 0], [1, 1]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: acro.crosstab(mydata.year, mydata.survivor)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 threshold; threshold; \n", + "2011 threshold; threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2]\n", + "timestamp: 2025-03-06T19:43:34.423743\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_3\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': True, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=df.inc_grants, aggfunc=\"mean\")\n", + "summary: fail; threshold: 7 cells suppressed; p-ratio: 2 cells suppressed; nk-rule: 1 cells suppressed; \n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; \n", + "2011 ok ok ok threshold; \n", + "2012 ok ok ok threshold; \n", + "2013 ok ok ok threshold; \n", + "2014 ok ok ok threshold; \n", + "2015 ok ok ok threshold; \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 NaN 8402284.0 NaN\n", + "2011 8502246.0 124013.859375 7716880.0 NaN\n", + "2012 11458580.0 131859.062500 6958050.5 NaN\n", + "2013 13557147.0 147937.796875 7202273.5 NaN\n", + "2014 13748147.0 133198.250000 8277525.0 NaN\n", + "2015 11133433.0 146572.187500 10812888.0 NaN]\n", + "timestamp: 2025-03-06T19:43:34.454789\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_4\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 14, 'p-ratio': 8, 'nk-rule': 7, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 2], [0, 5], [1, 0], [1, 5], [2, 0], [2, 5], [3, 0], [3, 5], [4, 0], [4, 5], [5, 0], [5, 1], [5, 5]], 'p-ratio': [[0, 0], [0, 2], [0, 5], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]], 'nk-rule': [[0, 0], [0, 5], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.crosstab(\n", + "summary: fail; threshold: 14 cells may need suppressing; p-ratio: 8 cells may need suppressing; nk-rule: 7 cells may need suppressing; \n", + "outcome: grant_type G N \\\n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 threshold; p-ratio; nk-rule; ok threshold; p-ratio; \n", + "2011 threshold; p-ratio; nk-rule; ok ok \n", + "2012 threshold; p-ratio; nk-rule; ok ok \n", + "2013 threshold; p-ratio; nk-rule; ok ok \n", + "2014 threshold; p-ratio; nk-rule; ok ok \n", + "2015 threshold; p-ratio; nk-rule; threshold; ok \n", + "All ok ok ok \n", + "\n", + "grant_type R R/G All \n", + "survivor Dead_in_2015 Alive_in_2015 Alive_in_2015 \n", + "year \n", + "2010 ok ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok threshold; ok \n", + "2012 ok ok threshold; ok \n", + "2013 ok ok threshold; ok \n", + "2014 ok ok threshold; ok \n", + "2015 ok ok threshold; ok \n", + "All ok ok ok ok \n", + "output: [grant_type G N R \\\n", + "survivor Dead in 2015 Alive in 2015 Alive in 2015 Dead in 2015 \n", + "year \n", + "2010 2 12 5 40 \n", + "2011 3 12 58 45 \n", + "2012 3 12 59 45 \n", + "2013 3 12 59 47 \n", + "2014 3 12 59 43 \n", + "2015 3 9 58 28 \n", + "All 17 69 298 248 \n", + "\n", + "grant_type R/G All \n", + "survivor Alive in 2015 Alive in 2015 \n", + "year \n", + "2010 20 4 83 \n", + "2011 24 8 150 \n", + "2012 24 8 151 \n", + "2013 24 8 153 \n", + "2014 24 8 149 \n", + "2015 23 8 129 \n", + "All 139 44 815 ]\n", + "timestamp: 2025-03-06T19:43:34.523720\n", + "comments: [\"Empty columns: ('N', 'Dead in 2015'), ('R/G', 'Dead in 2015') were deleted.\"]\n", + "exception: \n", + "\n", + "uid: output_5\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.survivor, values=df.inc_grants, aggfunc=\"mean\")\n", + "summary: pass\n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 ok ok\n", + "2011 ok ok\n", + "2012 ok ok\n", + "2013 ok ok\n", + "2014 ok ok\n", + "2015 ok ok\n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 1320337.750 15466672.0\n", + "2011 1295468.000 7190086.5\n", + "2012 1270522.125 7119017.5\n", + "2013 1325315.500 7682584.0\n", + "2014 1282249.625 8276287.5\n", + "2015 1608412.250 8060488.5]\n", + "timestamp: 2025-03-06T19:43:34.555960\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_6\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 14, 'p-ratio': 4, 'nk-rule': 2, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [0, 6], [0, 8], [1, 3], [1, 8], [2, 3], [2, 8], [3, 3], [3, 8], [4, 3], [4, 8], [5, 3], [5, 8]], 'p-ratio': [[0, 1], [0, 3], [0, 6], [0, 8]], 'nk-rule': [[0, 3], [0, 8]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 14 cells may need suppressing; p-ratio: 4 cells may need suppressing; nk-rule: 2 cells may need suppressing; \n", + "outcome: mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok ok threshold; ok \n", + "2012 ok ok ok threshold; ok \n", + "2013 ok ok ok threshold; ok \n", + "2014 ok ok ok threshold; ok \n", + "2015 ok ok ok threshold; ok \n", + "All ok ok ok ok ok \n", + "\n", + " std \n", + "grant_type G N R R/G All \n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok \n", + "2011 ok ok ok threshold; ok \n", + "2012 ok ok ok threshold; ok \n", + "2013 ok ok ok threshold; ok \n", + "2014 ok ok ok threshold; ok \n", + "2015 ok ok ok threshold; ok \n", + "All ok ok ok ok ok \n", + "output: [ mean \\\n", + "grant_type G N R R/G All \n", + "year \n", + "2010 9921906.0 0.000000 8402284.0 11636000.0 8308286.5 \n", + "2011 8502246.0 124013.859375 7716880.0 16047500.0 5303808.5 \n", + "2012 11458580.0 131859.062500 6958050.5 16810000.0 5259893.5 \n", + "2013 13557147.0 147937.796875 7202273.5 16765625.0 5605045.5 \n", + "2014 13748147.0 133198.250000 8277525.0 17845750.0 6117054.5 \n", + "2015 11133433.0 146572.187500 10812888.0 18278624.0 6509989.5 \n", + "All 11412787.0 134431.890625 8098502.0 16648273.0 5997796.5 \n", + "\n", + " std \\\n", + "grant_type G N R R/G \n", + "year \n", + "2010 1.855055e+07 0.000000 3.059557e+07 1.701088e+07 \n", + "2011 1.688595e+07 205959.492903 2.954322e+07 1.561638e+07 \n", + "2012 2.061090e+07 210476.539175 2.721184e+07 1.646449e+07 \n", + "2013 2.486844e+07 203747.417017 2.989833e+07 1.671112e+07 \n", + "2014 3.134559e+07 181865.925580 3.546348e+07 1.741251e+07 \n", + "2015 2.553919e+07 201602.800832 4.130935e+07 1.730471e+07 \n", + "All 2.283220e+07 198873.726656 3.204495e+07 1.583532e+07 \n", + "\n", + " \n", + "grant_type All \n", + "year \n", + "2010 2.727398e+07 \n", + "2011 2.137658e+07 \n", + "2012 2.026400e+07 \n", + "2013 2.251787e+07 \n", + "2014 2.641722e+07 \n", + "2015 2.784636e+07 \n", + "All 2.405324e+07 ]\n", + "timestamp: 2025-03-06T19:43:34.639249\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_7\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: grant_type G N R R/G All\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok\n", + "2011 ok ok ok threshold; ok\n", + "2012 ok ok ok threshold; ok\n", + "2013 ok ok ok threshold; ok\n", + "2014 ok ok ok threshold; ok\n", + "2015 ok ok ok threshold; ok\n", + "All ok ok ok ok ok\n", + "output: [grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5]\n", + "timestamp: 2025-03-06T19:43:34.695759\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_8\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 10, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [[0, 2], [1, 1], [1, 2], [2, 2], [3, 1], [3, 2], [4, 1], [4, 2], [5, 1], [5, 2]], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "summary: review; negative values found\n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 negative \n", + "2011 negative negative \n", + "2012 negative \n", + "2013 negative negative \n", + "2014 negative negative \n", + "2015 negative negative \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0]\n", + "timestamp: 2025-03-06T19:43:34.727227\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_9\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]], 'p-ratio': [[1, 0], [3, 0]], 'nk-rule': [[3, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: inc_grants \\\n", + "year 2010 2011 2012 \n", + "grant_type \n", + "G ok ok ok \n", + "N threshold; p-ratio; ok ok \n", + "R ok ok ok \n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; \n", + "All ok ok ok \n", + "\n", + " \n", + "year 2013 2014 2015 All \n", + "grant_type \n", + "G ok ok ok ok \n", + "N ok ok ok ok \n", + "R ok ok ok ok \n", + "R/G threshold; threshold; threshold; ok \n", + "All ok ok ok ok \n", + "output: [ inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 ]\n", + "timestamp: 2025-03-06T19:43:34.782873\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_10\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G ok ok\n", + "N ok ok\n", + "R ok ok\n", + "R/G ok ok\n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.344319e+05 1.988737e+05\n", + "R 8.098502e+06 3.204495e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.814054\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_11\n", + "status: pass\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: pass\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G ok ok\n", + "N ok ok\n", + "R ok ok\n", + "R/G ok ok\n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.364700e+05 1.999335e+05\n", + "R 8.006361e+06 3.228216e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.844023\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_12\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 4, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [[1, 0], [1, 1], [2, 0], [2, 1]], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: review; negative values found\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G \n", + "N negative negative\n", + "R negative negative\n", + "R/G \n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.872024\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_13\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'ols', 'dof': 807.0}\n", + "sdc: {}\n", + "command: results = acro.ols(y, x)\n", + "summary: pass; dof=807.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ inc_activity R-squared: 0.894\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000\n", + "Time: 19:43:34 Log-Likelihood: -14495.000\n", + "No. Observations: 811 AIC: 29000.000\n", + "Df Residuals: 807 BIC: 29020.000\n", + "Df Model: 3 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "const 301000.0000 533000.000 0.565 0.572 -745000.000 1350000.000\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852, 1339.956 Durbin-Watson: 1.414\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1.253318e+06\n", + "Skew: 9.899 Prob(JB): 0.000000e+00\n", + "Kurtosis: 194.566 Cond. No. 1.050000e+08]\n", + "timestamp: 2025-03-06T19:43:34.926753\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_14\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'olsr', 'dof': 807.0}\n", + "sdc: {}\n", + "command: results = acro.olsr(\n", + "summary: pass; dof=807.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ inc_activity R-squared: 0.894\n", + "Dep. Variable: \n", + "Model: OLS Adj. R-squared: 0.893\n", + "Method: Least Squares F-statistic: 2261.000\n", + "Date: Thu, 06 Mar 2025 Prob (F-statistic): 0.000\n", + "Time: 19:43:34 Log-Likelihood: -14495.000\n", + "No. Observations: 811 AIC: 29000.000\n", + "Df Residuals: 807 BIC: 29020.000\n", + "Df Model: 3 NaN NaN\n", + "Covariance Type: nonrobust NaN NaN, coef std err t P>|t| [0.025 0.975]\n", + "Intercept 301000.0000 533000.000 0.565 0.572 -745000.000 1350000.000\n", + "inc_grants -0.8846 0.025 -35.956 0.000 -0.933 -0.836\n", + "inc_donations -0.6647 0.016 -40.721 0.000 -0.697 -0.633\n", + "total_costs 0.8313 0.011 78.674 0.000 0.811 0.852, 1339.956 Durbin-Watson: 1.414\n", + "Omnibus: \n", + "Prob(Omnibus): 0.000 Jarque-Bera (JB): 1.253318e+06\n", + "Skew: 9.899 Prob(JB): 0.000000e+00\n", + "Kurtosis: 194.566 Cond. No. 1.050000e+08]\n", + "timestamp: 2025-03-06T19:43:34.954407\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_15\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'probit', 'dof': 806.0}\n", + "sdc: {}\n", + "command: results = acro.probit(y, x)\n", + "summary: pass; dof=806.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ survivor No. Observations: 811\n", + "Dep. Variable: \n", + "Model: Probit Df Residuals: 8.060000e+02\n", + "Method: MLE Df Model: 4.000000e+00\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 2.140000e-01\n", + "Time: 19:43:34 Log-Likelihood: -4.004600e+02\n", + "converged: True LL-Null: -5.095000e+02\n", + "Covariance Type: nonrobust LLR p-value: 4.875000e-46, coef std err z P>|z| [0.025 \\\n", + "const 4.740000e-02 5.700000e-02 0.838 0.402 -6.300000e-02 \n", + "inc_activity 1.836000e-07 5.160000e-08 3.559 0.000 8.250000e-08 \n", + "inc_grants 8.576000e-08 3.900000e-08 2.197 0.028 9.250000e-09 \n", + "inc_donations 2.406000e-07 4.540000e-08 5.297 0.000 1.520000e-07 \n", + "total_costs -8.644000e-08 3.680000e-08 -2.351 0.019 -1.590000e-07 \n", + "\n", + " 0.975] \n", + "const 1.580000e-01 \n", + "inc_activity 2.850000e-07 \n", + "inc_grants 1.620000e-07 \n", + "inc_donations 3.300000e-07 \n", + "total_costs -1.440000e-08 ]\n", + "timestamp: 2025-03-06T19:43:34.982272\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_16\n", + "status: pass\n", + "type: regression\n", + "properties: {'method': 'logit', 'dof': 806.0}\n", + "sdc: {}\n", + "command: results = acro.logit(y, x)\n", + "summary: pass; dof=806.0 >= 10\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: [ survivor No. Observations: 811\n", + "Dep. Variable: \n", + "Model: Logit Df Residuals: 8.060000e+02\n", + "Method: MLE Df Model: 4.000000e+00\n", + "Date: Thu, 06 Mar 2025 Pseudo R-squ.: 2.187000e-01\n", + "Time: 19:43:35 Log-Likelihood: -3.980700e+02\n", + "converged: True LL-Null: -5.095000e+02\n", + "Covariance Type: nonrobust LLR p-value: 4.532000e-47, coef std err z P>|z| [0.025 \\\n", + "const 5.120000e-02 9.100000e-02 0.561 0.575 -1.280000e-01 \n", + "inc_activity 2.981000e-07 8.950000e-08 3.330 0.001 1.230000e-07 \n", + "inc_grants 1.351000e-07 6.670000e-08 2.026 0.043 4.390000e-09 \n", + "inc_donations 5.123000e-07 1.040000e-07 4.927 0.000 3.080000e-07 \n", + "total_costs -1.442000e-07 6.260000e-08 -2.304 0.021 -2.670000e-07 \n", + "\n", + " 0.975] \n", + "const 2.300000e-01 \n", + "inc_activity 4.740000e-07 \n", + "inc_grants 2.660000e-07 \n", + "inc_donations 7.160000e-07 \n", + "total_costs -2.150000e-08 ]\n", + "timestamp: 2025-03-06T19:43:35.004433\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_17\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_0.png']\n", + "timestamp: 2025-03-06T19:43:35.149884\n", + "comments: []\n", + "exception: \n", + "\n", + "uid: output_18\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_1.png']\n", + "timestamp: 2025-03-06T19:43:35.204307\n", + "comments: []\n", + "exception: \n", + "\n", + "\n" + ] + } + ], + "source": [ + "results_str = acro.print_outputs()" + ] + }, + { + "cell_type": "markdown", + "id": "f78b5a08", + "metadata": {}, + "source": [ + "### Remove some ACRO outputs before finalising" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "6211a9cf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:remove(): output_1 removed\n", + "INFO:acro:records:remove(): output_4 removed\n" + ] + } + ], + "source": [ + "acro.remove_output(\"output_1\")\n", + "acro.remove_output(\"output_4\")" + ] + }, + { + "cell_type": "markdown", + "id": "df2a02e0", + "metadata": {}, + "source": [ + "### Rename ACRO outputs before finalising" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "c9864a29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:rename_output(): output_2 renamed to pivot_table\n" + ] + } + ], + "source": [ + "acro.rename_output(\"output_2\", \"pivot_table\")" + ] + }, + { + "cell_type": "markdown", + "id": "56d2b6a1", + "metadata": {}, + "source": [ + "### Add a comment to output" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "b392be9f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:a comment was added to output_0\n", + "INFO:acro:records:a comment was added to output_0\n" + ] + } + ], + "source": [ + "acro.add_comments(\"output_0\", \"This is a cross table between year and grant_type\")\n", + "acro.add_comments(\"output_0\", \"6 cells were suppressed in this table\")" + ] + }, + { + "cell_type": "markdown", + "id": "8496fed4", + "metadata": {}, + "source": [ + "### Add an unsupported output to the list of outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "2816eac7", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:add_custom(): output_19\n" + ] + } + ], + "source": [ + "acro.custom_output(\n", + " \"XandY.jpeg\", \"This output is an image showing the relationship between X and Y\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6efde761", + "metadata": {}, + "source": [ + "### Request an exception for some of the outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "f38b4334", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:exception request was added to output_0\n", + "INFO:acro:records:exception request was added to output_3\n", + "INFO:acro:records:exception request was added to output_5\n", + "INFO:acro:records:exception request was added to output_6\n" + ] + } + ], + "source": [ + "acro.add_exception(\"output_0\", \"I really need this.\")\n", + "acro.add_exception(\"output_3\", \"This one is safe. Trust me, I'm a professor.\")\n", + "acro.add_exception(\"output_5\", \"It's not disclosive, I promise.\")\n", + "acro.add_exception(\"output_6\", \"I need this one too\")" + ] + }, + { + "cell_type": "markdown", + "id": "5a586694", + "metadata": {}, + "source": [ + "### Finalise ACRO" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "9e554eea", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_7\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: grant_type G N R R/G All\n", + "year \n", + "2010 ok threshold; p-ratio; ok threshold; p-ratio; nk-rule; ok\n", + "2011 ok ok ok threshold; ok\n", + "2012 ok ok ok threshold; ok\n", + "2013 ok ok ok threshold; ok\n", + "2014 ok ok ok threshold; ok\n", + "2015 ok ok ok threshold; ok\n", + "All ok ok ok ok ok\n", + "output: [grant_type G N R R/G All\n", + "year \n", + "2010 9921906.0 0.000000 8420373.0 11636000.0 8320154.5\n", + "2011 8502246.0 125663.226562 7689140.0 16047500.0 5310392.0\n", + "2012 11458580.0 131859.062500 6896304.0 16810000.0 5220580.5\n", + "2013 13557147.0 150488.453125 7088095.5 16765625.0 5578657.0\n", + "2014 13748147.0 135494.781250 8118565.5 17845750.0 6072600.0\n", + "2015 11133433.0 149143.625000 10596385.0 18278624.0 6442131.0\n", + "All 11412787.0 136158.859375 8006361.0 16648273.0 5968295.5]\n", + "timestamp: 2025-03-06T19:43:34.695759\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason should be provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_8\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 10, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [[0, 2], [1, 1], [1, 2], [2, 2], [3, 1], [3, 2], [4, 1], [4, 2], [5, 1], [5, 2]], 'missing': [], 'threshold': [[0, 1], [0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]], 'p-ratio': [[0, 1], [0, 3]], 'nk-rule': [[0, 3]], 'all-values-are-same': []}}\n", + "command: safe_table = acro.crosstab(df.year, df.grant_type, values=negative, aggfunc=\"mean\")\n", + "summary: review; negative values found\n", + "outcome: grant_type G N R R/G\n", + "year \n", + "2010 negative \n", + "2011 negative negative \n", + "2012 negative \n", + "2013 negative negative \n", + "2014 negative negative \n", + "2015 negative negative \n", + "output: [grant_type G N R R/G\n", + "year \n", + "2010 9921906.0 0.000000 8280032.5 11636000.0\n", + "2011 8502246.0 123496.445312 7577703.5 16047500.0\n", + "2012 11458580.0 131859.062500 6796357.5 16810000.0\n", + "2013 13557147.0 147937.625000 6988263.0 16765625.0\n", + "2014 13748147.0 133198.078125 7997392.0 17845750.0\n", + "2015 11133433.0 146572.015625 10388612.0 18278624.0]\n", + "timestamp: 2025-03-06T19:43:34.727227\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " negative values are valid financial losses\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_9\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 7, 'p-ratio': 2, 'nk-rule': 1, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[1, 0], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]], 'p-ratio': [[1, 0], [3, 0]], 'nk-rule': [[3, 0]], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: fail; threshold: 7 cells may need suppressing; p-ratio: 2 cells may need suppressing; nk-rule: 1 cells may need suppressing; \n", + "outcome: inc_grants \\\n", + "year 2010 2011 2012 \n", + "grant_type \n", + "G ok ok ok \n", + "N threshold; p-ratio; ok ok \n", + "R ok ok ok \n", + "R/G threshold; p-ratio; nk-rule; threshold; threshold; \n", + "All ok ok ok \n", + "\n", + " \n", + "year 2013 2014 2015 All \n", + "grant_type \n", + "G ok ok ok ok \n", + "N ok ok ok ok \n", + "R ok ok ok ok \n", + "R/G threshold; threshold; threshold; ok \n", + "All ok ok ok ok \n", + "output: [ inc_grants \\\n", + "year 2010 2011 2012 2013 2014 \n", + "grant_type \n", + "G 138906688.0 127533696.0 171878704.0 203357200.0 206222208.0 \n", + "N 0.0 7192804.0 7779685.0 8728330.0 7858697.0 \n", + "R 504137056.0 532464704.0 480105472.0 511361408.0 554594176.0 \n", + "R/G 46544000.0 128380000.0 134480000.0 134125000.0 142766000.0 \n", + "All 689587776.0 795571264.0 794243904.0 857571968.0 911441088.0 \n", + "\n", + " \n", + "year 2015 All \n", + "grant_type \n", + "G 133601200.0 9.814997e+08 \n", + "N 8501187.0 4.006070e+07 \n", + "R 551457280.0 3.134120e+09 \n", + "R/G 146228992.0 7.325240e+08 \n", + "All 839788672.0 4.888204e+09 ]\n", + "timestamp: 2025-03-06T19:43:34.782873\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " a reason should be provided\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_12\n", + "status: review\n", + "type: table\n", + "properties: {'method': 'pivot_table'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 4, 'missing': 0, 'threshold': 0, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [[1, 0], [1, 1], [2, 0], [2, 1]], 'missing': [], 'threshold': [], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: table = acro.pivot_table(\n", + "summary: review; negative values found\n", + "outcome: mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G \n", + "N negative negative\n", + "R negative negative\n", + "R/G \n", + "output: [ mean std\n", + " inc_grants inc_grants\n", + "grant_type \n", + "G 1.141279e+07 2.283220e+07\n", + "N 1.341800e+05 1.990196e+05\n", + "R 7.882230e+06 3.204558e+07\n", + "R/G 1.664827e+07 1.583532e+07]\n", + "timestamp: 2025-03-06T19:43:34.872024\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " negative values are valid financial losses\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_17\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_0.png']\n", + "timestamp: 2025-03-06T19:43:35.149884\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_18\n", + "status: fail\n", + "type: histogram\n", + "properties: {'method': 'histogram'}\n", + "sdc: {}\n", + "command: hist = acro.hist(df, \"inc_grants\")\n", + "summary: Please check the minimum and the maximum values. The minimum value of the inc_grants column is: -10.0. The maximum value of the inc_grants column is: 249327008.0\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['acro_artifacts/histogram_1.png']\n", + "timestamp: 2025-03-06T19:43:35.204307\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: pivot_table\n", + "status: fail\n", + "type: table\n", + "properties: {'method': 'crosstab'}\n", + "sdc: {'summary': {'suppressed': False, 'negative': 0, 'missing': 0, 'threshold': 4, 'p-ratio': 0, 'nk-rule': 0, 'all-values-are-same': 0}, 'cells': {'negative': [], 'missing': [], 'threshold': [[0, 0], [0, 1], [1, 0], [1, 1]], 'p-ratio': [], 'nk-rule': [], 'all-values-are-same': []}}\n", + "command: acro.crosstab(mydata.year, mydata.survivor)\n", + "summary: fail; threshold: 4 cells may need suppressing; \n", + "outcome: survivor Dead_in_2015 Alive_in_2015\n", + "year \n", + "2010 threshold; threshold; \n", + "2011 threshold; threshold; \n", + "output: [survivor Dead in 2015 Alive in 2015\n", + "year \n", + "2010 2 2\n", + "2011 2 2]\n", + "timestamp: 2025-03-06T19:43:34.423743\n", + "comments: []\n", + "exception: \n", + "\n", + "The status of the record above is: fail.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " to be fair this is probably disclosive ...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:\n", + "uid: output_19\n", + "status: review\n", + "type: custom\n", + "properties: {}\n", + "sdc: {}\n", + "command: custom\n", + "summary: review\n", + "outcome: Empty DataFrame\n", + "Columns: []\n", + "Index: []\n", + "output: ['XandY.jpeg']\n", + "timestamp: 2025-03-06T19:43:35.258648\n", + "comments: ['This output is an image showing the relationship between X and Y']\n", + "exception: \n", + "\n", + "The status of the record above is: review.\n", + "Please explain why an exception should be granted.\n", + "\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + " please review- this image is not disclosive\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:acro:records:outputs written to: ACRO_RES\n" + ] + } + ], + "source": [ + "SAVE_PATH = \"ACRO_RES\"\n", + "\n", + "# output = acro.finalise(SAVE_PATH, \"xlsx\")\n", + "output = acro.finalise(SAVE_PATH, \"json\")" + ] + }, + { + "cell_type": "markdown", + "id": "64e00920", + "metadata": {}, + "source": [ + "### List files generated" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "96b72072", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XandY.jpeg\n", + "config.json\n", + "histogram_0.png\n", + "histogram_1.png\n", + "output_0_0.csv\n", + "output_10_0.csv\n", + "output_11_0.csv\n", + "output_12_0.csv\n", + "output_13_0.csv\n", + "output_13_1.csv\n", + "output_13_2.csv\n", + "output_14_0.csv\n", + "output_14_1.csv\n", + "output_14_2.csv\n", + "output_15_0.csv\n", + "output_15_1.csv\n", + "output_16_0.csv\n", + "output_16_1.csv\n", + "output_3_0.csv\n", + "output_5_0.csv\n", + "output_6_0.csv\n", + "output_7_0.csv\n", + "output_8_0.csv\n", + "output_9_0.csv\n", + "pivot_table_0.csv\n", + "results.json\n" + ] + } + ], + "source": [ + "files = []\n", + "for name in os.listdir(SAVE_PATH):\n", + " if os.path.isfile(os.path.join(SAVE_PATH, name)):\n", + " files.append(name)\n", + "files.sort()\n", + "for f in files:\n", + " print(f)" + ] + }, + { + "cell_type": "markdown", + "id": "a0e77cbe", + "metadata": {}, + "source": [ + "### Checksums" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "f5f6364e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XandY.jpeg.txt\n", + "histogram_0.png.txt\n", + "histogram_1.png.txt\n", + "output_0_0.csv.txt\n", + "output_10_0.csv.txt\n", + "output_11_0.csv.txt\n", + "output_12_0.csv.txt\n", + "output_13_0.csv.txt\n", + "output_13_1.csv.txt\n", + "output_13_2.csv.txt\n", + "output_14_0.csv.txt\n", + "output_14_1.csv.txt\n", + "output_14_2.csv.txt\n", + "output_15_0.csv.txt\n", + "output_15_1.csv.txt\n", + "output_16_0.csv.txt\n", + "output_16_1.csv.txt\n", + "output_3_0.csv.txt\n", + "output_5_0.csv.txt\n", + "output_6_0.csv.txt\n", + "output_7_0.csv.txt\n", + "output_8_0.csv.txt\n", + "output_9_0.csv.txt\n", + "pivot_table_0.csv.txt\n", + "results.json.txt\n" + ] + } + ], + "source": [ + "files = []\n", + "checksum_dir = os.path.join(SAVE_PATH, \"checksums\")\n", + "for name in os.listdir(checksum_dir):\n", + " if os.path.isfile(os.path.join(checksum_dir, name)):\n", + " files.append(name)\n", + "files.sort()\n", + "for f in files:\n", + " print(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f241054a-c91e-4a91-bdc0-0395bbe084dd", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testacro", + "language": "python", + "name": "testacro" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/_build/_sources/record.rst.txt b/docs/source/_build/_sources/record.rst.txt new file mode 100644 index 00000000..5834ab23 --- /dev/null +++ b/docs/source/_build/_sources/record.rst.txt @@ -0,0 +1,6 @@ +Records +======= + +.. automodule:: acro.record + :members: + :no-index: diff --git a/docs/source/_build/_sources/sacro_ml.rst.txt b/docs/source/_build/_sources/sacro_ml.rst.txt new file mode 100644 index 00000000..5c612400 --- /dev/null +++ b/docs/source/_build/_sources/sacro_ml.rst.txt @@ -0,0 +1,7 @@ +:orphan: + +======== +SACRO-ML +======== + +This page contains SACRO-ML documentation. diff --git a/docs/source/_build/_sources/sacro_viewer.rst.txt b/docs/source/_build/_sources/sacro_viewer.rst.txt new file mode 100644 index 00000000..841adca4 --- /dev/null +++ b/docs/source/_build/_sources/sacro_viewer.rst.txt @@ -0,0 +1,7 @@ +:orphan: + +============ +SACRO-Viewer +============ + +This page contains SACRO-Viewer documentation. diff --git a/docs/source/_build/_sources/support.rst.txt b/docs/source/_build/_sources/support.rst.txt new file mode 100644 index 00000000..719b3280 --- /dev/null +++ b/docs/source/_build/_sources/support.rst.txt @@ -0,0 +1,249 @@ +================== +What ACRO Supports +================== + +This page provides a comprehensive overview of ACRO's capabilities for researchers, developers, and non-technical stakeholders. ACRO supports a wide range of statistical analysis functions with semi-automated disclosure control. + +.. note:: + **Drop-in Replacements:** ACRO functions are designed as direct replacements for standard analysis commands. Simply prefix your existing commands with ``acro.`` to enable automatic disclosure checking. + +Supported Data Analysis Functions +================================== + +Table Creation & Cross-tabulation +---------------------------------- + +**For Researchers:** +Create frequency tables and cross-tabulations with automatic cell suppression for small counts. + +**What ACRO Supports:** + +* **crosstab()** - Cross-tabulation of two or more variables with frequency counting +* **pivot_table()** - Spreadsheet-style pivot tables with aggregation functions +* **table()** - Simple frequency tables for categorical data (R interface only) + +**Technical Details:** +- ACRO suppresses, and reports the reason why, the value of an aggregation statistic (mean, median, variance, etc.) for any cell is deemed to be sensitive +- The current version of ACRO supports the three most common tests for sensitivity: ensuring the number of contributors is above a frequency threshold, and testing for dominance via N-K rules +- **N-K Rule**: A dominance test where if the top N contributors account for more than K% of the total, the cell is considered disclosive +- **Frequency Threshold**: Cells with fewer than a specified number of contributors are suppressed +- All thresholds are configurable via YAML configuration files +- For detailed methodology, see our `research paper `_ +- Automatic flagging of negative or missing values for human review + +**Example Use Cases:** +- Survey response analysis by demographics +- Clinical trial outcome tables +- Market research cross-tabulations +- Educational assessment reporting + +Statistical Modeling +--------------------- + +**For Researchers:** +Run regression analyses with semi-automated checks on model outputs and residual degrees of freedom. + +**What ACRO Supports:** + +* **ols()** - Ordinary Least Squares linear regression +* **logit()** - Logistic regression for binary outcomes +* **probit()** - Probit regression for binary outcomes + +**Technical Details:** +- For regressions such as linear, probit, and logit, the tests verify that the number of residual degrees of freedom exceeds a threshold +- Within the ACRO Python package, the functionality of the 'ACRO' class is split into a number of separate classes for maintainability and extensibility. A 'Tables' class contains the code necessary to perform disclosure checks on tabular data, such as crosstab. A separate 'Regression' class contains the code for checking regressions such as logit and probit + +**Example Use Cases:** +- Economic modeling and policy analysis +- Medical research and clinical studies +- Social science research +- Business analytics and forecasting + +Programming Language Support +============================ + +Python (Primary) +----------------- + +**For Developers:** +The ACRO package is a lightweight Python tool that sits over well-known analysis tools that produce outputs such as tables, plots, and statistical models + +**Supported Libraries:** +- **Pandas** - For data manipulation and table creation +- **Statsmodels** - For statistical modeling and regression analysis +- **NumPy** - For numerical computations + +**Python Version Support:** +- Python 3.10, 3.11, 3.12, 3.13 + +R Language +---------- + +**For R Users:** +Additional programming languages such as this R package are supported by providing front-end packages that interface with the core ACRO Python back-end + +**Integration Features:** +- Native R syntax and workflows +- R Markdown and Shiny application support +- Tidyverse compatibility +- CRAN package availability + +**Getting Started in R:** +```r +library("acro") +acro_init(suppress = TRUE) +``` + +Stata Integration +----------------- + +**For Stata Users:** +ACRO is designed to let you use familiar commands in R, Stata and Python + +**Features:** +- Drop-in replacement for standard Stata commands +- Familiar Stata syntax and workflows +- Integration with existing Stata scripts + +Disclosure Control Features +=========================== + +semi-automated Sensitivity Testing +------------------------------ + +**What ACRO Checks:** + +**For Tables:** +- Minimum cell counts (frequency thresholds) +- Dominance rules (N-K rules for concentration) +- Presence of negative or missing values + +**For Statistical Models:** +- Residual degrees of freedom thresholds +- Model fit diagnostics +- Parameter significance testing + +**For Non-Technical Users:** +ACRO automatically identifies when research outputs might reveal sensitive information about individuals or organizations, applying industry-standard privacy protection rules. It must be used by trained researchers and checked by skilled checkers. + +Output Management +----------------- + +**What ACRO Provides:** + +* **Suppression Masks** - Clear indication of which results are hidden and why +* **Summary Reports** - Detailed explanation of all disclosure checks performed +* **Audit Trails** - Complete record of all analysis steps and decisions +* **Exception Handling** - Process for requesting release of flagged outputs + +**Workflow Integration:** +The finalise function will: Check that each output with "fail" or "review" status has an exception, if not you will be asked to enter one. Write the outputs to a directory. This directory contains everything that the output checkers need to make a decision + +Supported Environments +====================== + +Research Environments +---------------------- + +**Where ACRO Works:** +- Trusted Research Environments (TREs) +- Data safe havens +- Secure data centers +- Academic research computing facilities +- Government statistical offices +- Healthcare research environments + +**Installation:** +See :doc:`installation` for complete installation instructions and system requirements. + +Integration Capabilities +======================== + +Analysis Workflows +------------------ + +**For Research Teams:** +ACRO integrates seamlessly into existing data analysis workflows, requiring minimal changes to current practices while adding comprehensive privacy protection. + +**Supported Workflows:** +- Jupyter notebook analysis +- R Markdown documents +- Stata do-files and scripts +- Batch processing and automation +- Interactive analysis sessions + +**Data Sources:** +- CSV and Excel files +- Database connections +- Survey data platforms +- Administrative datasets +- Clinical trial databases + +Output Formats +-------------- + +**What ACRO Produces:** +- Standard CSV files for tables +- JSON metadata files for automation +- Excel workbooks for human reviewers + +**Review Process Support:** +Compatible with SACRO-Viewer for interactive output review by data controllers and compliance officers. + +Technical Architecture +====================== + +**For System Administrators:** + +**Core Technology:** +Lightweight translation scripts intercept your commands and pass them through to a python 'engine', based on industry-standard packages that run your commands and perform statistical disclosure checks on them + +**System Requirements:** +- Python 3.10+ runtime environment +- Standard scientific computing libraries (pandas, numpy, statsmodels) +- Minimal computational overhead +- No external network dependencies during analysis + +**Security Features:** +- Local processing only (no cloud dependencies) +- Audit logging and tracking +- Configurable disclosure thresholds +- Role-based access controls (through integration with TRE systems) + +**Documentation and Support:** +Standard Python coding and naming practices have been used throughout. GitHub continuous integration (CI) runners automatically generate and publish API documentation using the Python docstrings written in numpydoc format + +What ACRO Does NOT Support +=========================== + +**Current Limitations:** +- Complex visualizations and plots (coming in future versions) +- Time series analysis (specialized disclosure rules needed) +- Machine learning models (use SACRO-ML for AI/ML workflows) +- Real-time data streams +- Distributed computing frameworks + +**Alternative Solutions:** +- **SACRO-ML** - For machine learning and AI model disclosure control +- **SACRO-Viewer** - For interactive output review and approval +- **Traditional SDC tools** - For specialized use cases not covered by ACRO + +Getting Help +============ + +**For All Users:** +- Comprehensive online documentation at GitHub Pages +- Built-in help system: ``help(acro.function_name)`` +- Example notebooks and tutorials in the GitHub repository +- Active community support and issue tracking + +**For Researchers:** +- Step-by-step tutorials for common analysis patterns +- Best practices guides for different research domains +- Integration examples for popular data science workflows + +**For Developers:** +- Complete API documentation with examples +- Contributing guidelines and development setup +- Continuous integration and testing frameworks +- Open source development model with community contributions diff --git a/docs/source/_build/_sources/user_guide.rst.txt b/docs/source/_build/_sources/user_guide.rst.txt new file mode 100644 index 00000000..86104b58 --- /dev/null +++ b/docs/source/_build/_sources/user_guide.rst.txt @@ -0,0 +1,75 @@ +========== +User Guide +========== + +Complete guide to using ACRO for statistical disclosure control. + +.. toctree:: + :maxdepth: 2 + :caption: User Guide + + user_guide/getting_started + user_guide/core_concepts + user_guide/configuration + +Overview +======== + +This user guide provides comprehensive documentation for using ACRO effectively in your research workflows. + +Quick Navigation +================ + +* **New Users**: Start with the :doc:`examples` section +* **Advanced Features**: Explore custom disclosure checking +* **Integration**: Check integration workflows in :doc:`examples` + +Key Topics +========== + +Data Analysis +------------- + +* **Cross-tabulations** - Safe table creation with disclosure control +* **Statistical modeling** - Regression analysis with privacy protection +* **Summary statistics** - Descriptive statistics with safety checks +* **Data visualization** - Safe plotting and chart generation + +Configuration +------------- + +* **Safety parameters** - Customizing disclosure thresholds +* **Custom checks** - Advanced safety rule configuration + +Integration +----------- + +* **Python workflows** - Jupyter notebook integration +* **R integration** - Using ACRO-R package +* **Stata workflows** - Statistical software integration + +Best Practices +============== + +1. **Always use suppress=True** in production environments +2. **Review disclosure checks** before finalizing outputs +3. **Use meaningful output names** for better tracking +4. **Document your analysis workflow** for reproducibility +5. **Test with sample data** before running on sensitive data + +Troubleshooting +=============== + +Common issues and solutions: + +* **Import errors**: Check Python environment and ACRO installation +* **Configuration issues**: Verify YAML syntax and parameter values +* **Disclosure warnings**: Review safety thresholds and data characteristics +* **Output problems**: Check file permissions and directory structure + +Getting Help +============ + +* :doc:`api` - Complete API reference +* `GitHub Issues `_ - Report bugs +* `Discussions `_ - Community support diff --git a/docs/source/_build/_sources/user_guide/configuration.rst.txt b/docs/source/_build/_sources/user_guide/configuration.rst.txt new file mode 100644 index 00000000..ac193863 --- /dev/null +++ b/docs/source/_build/_sources/user_guide/configuration.rst.txt @@ -0,0 +1,114 @@ +============= +Configuration +============= + +Comprehensive guide to configuring ACRO for your research environment and organizational policies. + +Configuration Methods +===================== + +YAML Configuration Files +------------------------ + +Primary method for persistent configuration: + +.. code-block:: yaml + + # acro_config.yaml + safe_threshold: 10 + safe_dof_threshold: 10 + safe_nk_n: 2 + safe_nk_k: 0.9 + safe_p_percent: 0.1 + check_missing_values: true + survival_safe_threshold: 10 + +Runtime Parameters +------------------ + +You can specify the configuration file and whether to suppress unsafe outputs at runtime: + +.. code-block:: python + + import acro + + # Initialize with custom parameters + session = acro.ACRO( + config="custom_config.yaml", # Path to your config YAML file + suppress=True # Suppress unsafe outputs + ) + +.. note:: + The `config` parameter in `acro.ACRO()` expects the *name of a YAML file* (e.g., 'my_config.yaml'), not a Python dictionary directly. If you wish to use a custom configuration, please save your parameters in a YAML file and provide its name. + + +Safety Parameters +================= + +TRE Risk Appetite Settings +-------------------------- + +*These settings are typically controlled by TRE administrators and cannot be changed by researchers.* + +**safe_threshold** (default: 10) + Minimum number of observations required in table cells + +**safe_dof_threshold** (default: 10) + Minimum degrees of freedom for statistical models + +**safe_nk_n** (default: 2) + Number of largest contributors for NK dominance rule + +**safe_nk_k** (default: 0.9) + Percentage threshold for NK dominance rule + +**safe_p_percent** (default: 0.1) + P-percent rule threshold for concentration + +**check_missing_values** (default: true) + Include missing values in safety calculations + +**survival_safe_threshold** (default: 10) + Minimum observations for survival analysis outputs + +**zeros_are_disclosive** (default: true) + Whether zero values are considered disclosive. TREs can set this to false for datasets where class disclosure is not relevant + +Behavioral Settings +------------------- + +*These settings can be controlled by researchers to choose how they want to mitigate risk.* + +**suppress** (default: false) + Whether to suppress unsafe outputs automatically. + +Configuration Precedence +======================== + +Settings are applied in order of precedence: + +1. Runtime method parameters (where supported) +2. Configuration file settings +3. Default values + +Best Practices +============== + +Version Control +--------------- + +* Store configuration files in version control +* Use meaningful commit messages for config changes + +Documentation +------------- + +* Document all custom settings and their rationale +* Maintain configuration change logs +* Include configuration in analysis documentation + +Testing +------- + +* Test configuration changes with sample data +* Validate against organizational policies diff --git a/docs/source/_build/_sources/user_guide/core_concepts.rst.txt b/docs/source/_build/_sources/user_guide/core_concepts.rst.txt new file mode 100644 index 00000000..e0ae084e --- /dev/null +++ b/docs/source/_build/_sources/user_guide/core_concepts.rst.txt @@ -0,0 +1,162 @@ +============= +Core Concepts +============= + +Understanding the fundamental concepts behind ACRO's statistical disclosure control methodology. + +Principles-Based SDC +==================== + +ACRO implements a principles-based approach to statistical disclosure control, focusing on: + +Risk Assessment +--------------- + +* **Automatic detection** of disclosure risks +* **Context-aware** evaluation of outputs +* **Proportionate response** to identified risks + +Human-in-the-Loop +----------------- + +* **Researcher guidance** rather than blocking +* **Transparent reasoning** for all decisions +* **Flexible override** capabilities for experts + +Audit and Accountability +------------------------ + +* **Complete audit trails** for all outputs +* **Reproducible workflows** with version control +* **Clear documentation** of all decisions + +Disclosure Control Methods +========================== + +Cell Suppression +----------------- + +Current implementation: + +* **Primary suppression** - Hide risky cells + +.. note:: + **Roadmap Feature**: Secondary and complementary suppression are planned for future releases. + +Planned suppression methods: + +* **Secondary suppression** - Protect against inference +* **Complementary suppression** - Additional protection + +Statistical Perturbation +------------------------ + +.. note:: + **Roadmap Feature**: Statistical perturbation methods are planned for future releases. + +Planned perturbation methods: + +* **Cell-level perturbation** - Modify individual values +* **Table-level perturbation** - Systematic adjustments +* **Controlled rounding** - Round to safe multiples + +Output Restriction +------------------ + +Limiting what can be released: + +* **Threshold enforcement** - Minimum cell requirements +* **Aggregation requirements** - Force higher-level summaries +* **Model coefficient restrictions** - Limit regression detail + +ACRO Implementation +=================== + +Safety Checks +------------- + +ACRO performs multiple safety checks: + +1. **Threshold checks** - Minimum observation counts +2. **Dominance checks** - Concentration of values +3. **Model disclosure** - Regression coefficient safety + +Configuration System +-------------------- + +Flexible configuration through: + +* **YAML configuration files** - Environment-specific settings +* **Policy templates** - Organizational standards + +.. note:: + **Roadmap Feature**: Method-specific runtime parameter overrides are planned for future releases. + +Integration Points +================== + +Data Analysis Libraries +----------------------- + +ACRO integrates with: + +* **pandas** - DataFrame operations and aggregations +* **statsmodels** - Statistical modeling and regression +* **matplotlib/seaborn** - Visualization with safety checks + +Research Environments +--------------------- + +Designed for: + +* **Trusted Research Environments (TREs)** - Secure analysis platforms +* **Data enclaves** - Controlled access environments +* **Multi-user systems** - Collaborative research settings + +Quality Assurance +================= + +Validation Framework +-------------------- + +ACRO includes comprehensive validation: + +* **Unit testing** - Individual function verification +* **Integration testing** - End-to-end workflow validation +* **Regression testing** - Consistency across versions + +Performance Monitoring +---------------------- + +.. note:: + **Roadmap Feature**: Performance monitoring capabilities are planned for future releases. + +Planned performance tracking features: + +* **Execution timing** - Analysis performance metrics +* **Memory usage** - Resource consumption monitoring +* **Scalability testing** - Large dataset handling + +Best Practices +============== + +Configuration Management +------------------------ + +* Use version-controlled configuration files +* Document all threshold customizations +* Test configurations with sample data + +Workflow Design +--------------- + +* Plan analysis workflows in advance +* Use meaningful output names and descriptions +* Implement regular checkpoint saves + +Quality Control +--------------- + +* Review all disclosure warnings before finalizing +* Validate results against expected patterns +* Maintain detailed analysis documentation diff --git a/docs/source/_build/_sources/user_guide/getting_started.rst.txt b/docs/source/_build/_sources/user_guide/getting_started.rst.txt new file mode 100644 index 00000000..6f46cefa --- /dev/null +++ b/docs/source/_build/_sources/user_guide/getting_started.rst.txt @@ -0,0 +1,64 @@ +=============== +Getting Started +=============== + +This guide helps you get up and running with ACRO for statistical disclosure control. + +What is ACRO? +============= + +ACRO (Automatic Checking of Research Outputs) is a Python package that provides statistical disclosure control for research outputs. It acts as a wrapper around common analysis functions, automatically checking for potential privacy disclosures. + +Key Concepts +============ + +Statistical Disclosure Control (SDC) +------------------------------------ + +SDC is the process of protecting confidential information in statistical data releases. ACRO implements principles-based SDC that: + +* Identifies potentially disclosive outputs +* Applies mitigation strategies when needed +* Maintains detailed audit trails +* Supports human checker workflows + +Disclosure Types +---------------- + +ACRO checks for several types of disclosure: + +* **Identity disclosure** - When individuals can be identified +* **Attribute disclosure** - When sensitive attributes can be inferred +* **Inferential disclosure** - When statistical inference reveals information + +Safety Thresholds +----------------- + +ACRO uses configurable thresholds to determine safety: + +* **Minimum cell count** - Default: 10 observations +* **P-ratio threshold** - Default: 0.1 for dominance +* **NK-rule** - Default: n=2, k=85% for concentration + +Basic Workflow +============== + +1. **Initialize ACRO session** +2. **Run analysis with ACRO methods** +3. **Review disclosure warnings** +4. **Finalize outputs for checking** + +Installation Requirements +========================= + +* Python 3.10 or higher +* pandas >= 1.5.0 +* statsmodels >= 0.13.0 +* PyYAML for configuration + +Next Steps +========== + +* See :doc:`core_concepts` for detailed methodology +* Check :doc:`configuration` for customization options +* Visit :doc:`../examples` for hands-on tutorials diff --git a/docs/source/_build/_sphinx_design_static/design-tabs.js b/docs/source/_build/_sphinx_design_static/design-tabs.js new file mode 100644 index 00000000..b25bd6a4 --- /dev/null +++ b/docs/source/_build/_sphinx_design_static/design-tabs.js @@ -0,0 +1,101 @@ +// @ts-check + +// Extra JS capability for selected tabs to be synced +// The selection is stored in local storage so that it persists across page loads. + +/** + * @type {Record} + */ +let sd_id_to_elements = {}; +const storageKeyPrefix = "sphinx-design-tab-id-"; + +/** + * Create a key for a tab element. + * @param {HTMLElement} el - The tab element. + * @returns {[string, string, string] | null} - The key. + * + */ +function create_key(el) { + let syncId = el.getAttribute("data-sync-id"); + let syncGroup = el.getAttribute("data-sync-group"); + if (!syncId || !syncGroup) return null; + return [syncGroup, syncId, syncGroup + "--" + syncId]; +} + +/** + * Initialize the tab selection. + * + */ +function ready() { + // Find all tabs with sync data + + /** @type {string[]} */ + let groups = []; + + document.querySelectorAll(".sd-tab-label").forEach((label) => { + if (label instanceof HTMLElement) { + let data = create_key(label); + if (data) { + let [group, id, key] = data; + + // add click event listener + // @ts-ignore + label.onclick = onSDLabelClick; + + // store map of key to elements + if (!sd_id_to_elements[key]) { + sd_id_to_elements[key] = []; + } + sd_id_to_elements[key].push(label); + + if (groups.indexOf(group) === -1) { + groups.push(group); + // Check if a specific tab has been selected via URL parameter + const tabParam = new URLSearchParams(window.location.search).get( + group + ); + if (tabParam) { + console.log( + "sphinx-design: Selecting tab id for group '" + + group + + "' from URL parameter: " + + tabParam + ); + window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); + } + } + + // Check is a specific tab has been selected previously + let previousId = window.sessionStorage.getItem( + storageKeyPrefix + group + ); + if (previousId === id) { + // console.log( + // "sphinx-design: Selecting tab from session storage: " + id + // ); + // @ts-ignore + label.previousElementSibling.checked = true; + } + } + } + }); +} + +/** + * Activate other tabs with the same sync id. + * + * @this {HTMLElement} - The element that was clicked. + */ +function onSDLabelClick() { + let data = create_key(this); + if (!data) return; + let [group, id, key] = data; + for (const label of sd_id_to_elements[key]) { + if (label === this) continue; + // @ts-ignore + label.previousElementSibling.checked = true; + } + window.sessionStorage.setItem(storageKeyPrefix + group, id); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/docs/source/_build/_sphinx_design_static/sphinx-design.min.css b/docs/source/_build/_sphinx_design_static/sphinx-design.min.css new file mode 100644 index 00000000..860c36da --- /dev/null +++ b/docs/source/_build/_sphinx_design_static/sphinx-design.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative;font-size:var(--sd-fontsize-dropdown)}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary.sd-summary-title{padding:.5em .6em .5em 1em;font-size:var(--sd-fontsize-dropdown-title);font-weight:var(--sd-fontweight-dropdown-title);user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;list-style:none;display:inline-flex;justify-content:space-between}details.sd-dropdown summary.sd-summary-title::-webkit-details-marker{display:none}details.sd-dropdown summary.sd-summary-title:focus{outline:none}details.sd-dropdown summary.sd-summary-title .sd-summary-icon{margin-right:.6em;display:inline-flex;align-items:center}details.sd-dropdown summary.sd-summary-title .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary.sd-summary-title .sd-summary-text{flex-grow:1;line-height:1.5;padding-right:.5rem}details.sd-dropdown summary.sd-summary-title .sd-summary-state-marker{pointer-events:none;display:inline-flex;align-items:center}details.sd-dropdown summary.sd-summary-title .sd-summary-state-marker svg{opacity:.6}details.sd-dropdown summary.sd-summary-title:hover .sd-summary-state-marker svg{opacity:1;transform:scale(1.1)}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown .sd-summary-chevron-right{transition:.25s}details.sd-dropdown[open]>.sd-summary-title .sd-summary-chevron-right{transform:rotate(90deg)}details.sd-dropdown[open]>.sd-summary-title .sd-summary-chevron-down{transform:rotate(180deg)}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-bg: rgba(0, 113, 188, 0.2);--sd-color-secondary-bg: rgba(108, 117, 125, 0.2);--sd-color-success-bg: rgba(40, 167, 69, 0.2);--sd-color-info-bg: rgba(23, 162, 184, 0.2);--sd-color-warning-bg: rgba(240, 179, 126, 0.2);--sd-color-danger-bg: rgba(220, 53, 69, 0.2);--sd-color-light-bg: rgba(248, 249, 250, 0.2);--sd-color-muted-bg: rgba(108, 117, 125, 0.2);--sd-color-dark-bg: rgba(33, 37, 41, 0.2);--sd-color-black-bg: rgba(0, 0, 0, 0.2);--sd-color-white-bg: rgba(255, 255, 255, 0.2);--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem;--sd-fontsize-dropdown: inherit;--sd-fontsize-dropdown-title: 1rem;--sd-fontweight-dropdown-title: 700} diff --git a/docs/source/_build/_static/SACRO_Logo_final.png b/docs/source/_build/_static/SACRO_Logo_final.png new file mode 100644 index 00000000..d5a8e80a Binary files /dev/null and b/docs/source/_build/_static/SACRO_Logo_final.png differ diff --git a/docs/source/_build/_static/basic.css b/docs/source/_build/_static/basic.css new file mode 100644 index 00000000..93a4776b --- /dev/null +++ b/docs/source/_build/_static/basic.css @@ -0,0 +1,914 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} diff --git a/docs/source/_build/_static/css/custom.css b/docs/source/_build/_static/css/custom.css new file mode 100644 index 00000000..dfd96fd6 --- /dev/null +++ b/docs/source/_build/_static/css/custom.css @@ -0,0 +1,89 @@ + +.sd-card { + transition: transform 0.2s ease-in-out; + border-radius: 0.5rem; + overflow: hidden; + word-wrap: break-word; +} + +.sd-card:hover { + transform: translateY(-5px); +} + +.sd-card-title { + font-weight: 600; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.sd-card-body { + overflow: hidden; +} + +[data-theme="light"] .sd-card-title { + color: #212529 !important; +} + +[data-theme="dark"] .sd-card-title { + color: #fff !important; +} + +@media (prefers-color-scheme: light) { + .sd-card-title { + color: #212529 !important; + } +} + +@media (prefers-color-scheme: dark) { + .sd-card-title { + color: #fff !important; + } +} + +.sd-card-footer { + border-top: 1px solid rgba(0,0,0,0.1); + padding-top: 0.5rem; + word-wrap: break-word; +} + + +@media (max-width: 992px) { + .sd-container-fluid .sd-row { + flex-direction: column; + } + + .sd-col { + flex: 0 0 100% !important; + max-width: 100% !important; + width: 100% !important; + } + + .sd-card { + margin-bottom: 1rem; + width: 100% !important; + } +} + +@media (max-width: 1280px) { + .navbar-brand { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 120px; + } + + .navbar-nav { + font-size: 0.85rem; + } + + .navbar-nav .nav-link { + padding-left: 0.5rem; + padding-right: 0.5rem; + } +} + +@media (max-width: 992px) { + .navbar-brand { + max-width: 100px; + } +} diff --git a/docs/source/_build/_static/design-tabs.js b/docs/source/_build/_static/design-tabs.js new file mode 100644 index 00000000..b25bd6a4 --- /dev/null +++ b/docs/source/_build/_static/design-tabs.js @@ -0,0 +1,101 @@ +// @ts-check + +// Extra JS capability for selected tabs to be synced +// The selection is stored in local storage so that it persists across page loads. + +/** + * @type {Record} + */ +let sd_id_to_elements = {}; +const storageKeyPrefix = "sphinx-design-tab-id-"; + +/** + * Create a key for a tab element. + * @param {HTMLElement} el - The tab element. + * @returns {[string, string, string] | null} - The key. + * + */ +function create_key(el) { + let syncId = el.getAttribute("data-sync-id"); + let syncGroup = el.getAttribute("data-sync-group"); + if (!syncId || !syncGroup) return null; + return [syncGroup, syncId, syncGroup + "--" + syncId]; +} + +/** + * Initialize the tab selection. + * + */ +function ready() { + // Find all tabs with sync data + + /** @type {string[]} */ + let groups = []; + + document.querySelectorAll(".sd-tab-label").forEach((label) => { + if (label instanceof HTMLElement) { + let data = create_key(label); + if (data) { + let [group, id, key] = data; + + // add click event listener + // @ts-ignore + label.onclick = onSDLabelClick; + + // store map of key to elements + if (!sd_id_to_elements[key]) { + sd_id_to_elements[key] = []; + } + sd_id_to_elements[key].push(label); + + if (groups.indexOf(group) === -1) { + groups.push(group); + // Check if a specific tab has been selected via URL parameter + const tabParam = new URLSearchParams(window.location.search).get( + group + ); + if (tabParam) { + console.log( + "sphinx-design: Selecting tab id for group '" + + group + + "' from URL parameter: " + + tabParam + ); + window.sessionStorage.setItem(storageKeyPrefix + group, tabParam); + } + } + + // Check is a specific tab has been selected previously + let previousId = window.sessionStorage.getItem( + storageKeyPrefix + group + ); + if (previousId === id) { + // console.log( + // "sphinx-design: Selecting tab from session storage: " + id + // ); + // @ts-ignore + label.previousElementSibling.checked = true; + } + } + } + }); +} + +/** + * Activate other tabs with the same sync id. + * + * @this {HTMLElement} - The element that was clicked. + */ +function onSDLabelClick() { + let data = create_key(this); + if (!data) return; + let [group, id, key] = data; + for (const label of sd_id_to_elements[key]) { + if (label === this) continue; + // @ts-ignore + label.previousElementSibling.checked = true; + } + window.sessionStorage.setItem(storageKeyPrefix + group, id); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/docs/source/_build/_static/doctools.js b/docs/source/_build/_static/doctools.js new file mode 100644 index 00000000..0398ebb9 --- /dev/null +++ b/docs/source/_build/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/source/_build/_static/documentation_options.js b/docs/source/_build/_static/documentation_options.js new file mode 100644 index 00000000..c8ad8912 --- /dev/null +++ b/docs/source/_build/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.4.11', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; diff --git a/docs/source/_build/_static/file.png b/docs/source/_build/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/docs/source/_build/_static/file.png differ diff --git a/docs/source/_build/_static/language_data.js b/docs/source/_build/_static/language_data.js new file mode 100644 index 00000000..a5ea78e1 --- /dev/null +++ b/docs/source/_build/_static/language_data.js @@ -0,0 +1,191 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} diff --git a/docs/source/_build/_static/minus.png b/docs/source/_build/_static/minus.png new file mode 100644 index 00000000..d96755fd Binary files /dev/null and b/docs/source/_build/_static/minus.png differ diff --git a/docs/source/_build/_static/nbsphinx-broken-thumbnail.svg b/docs/source/_build/_static/nbsphinx-broken-thumbnail.svg new file mode 100644 index 00000000..4919ca88 --- /dev/null +++ b/docs/source/_build/_static/nbsphinx-broken-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/docs/source/_build/_static/nbsphinx-code-cells.css b/docs/source/_build/_static/nbsphinx-code-cells.css new file mode 100644 index 00000000..f3547347 --- /dev/null +++ b/docs/source/_build/_static/nbsphinx-code-cells.css @@ -0,0 +1,258 @@ +/* remove conflicting styling from Sphinx themes */ +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt *, +div.nbinput.container div.input_area pre, +div.nboutput.container div.output_area pre, +div.nbinput.container div.input_area .highlight, +div.nboutput.container div.output_area .highlight { + border: none; + padding: 0; + margin: 0; + box-shadow: none; +} + +div.nbinput.container > div[class*=highlight], +div.nboutput.container > div[class*=highlight] { + margin: 0; +} + +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt * { + background: none; +} + +div.nboutput.container div.output_area .highlight, +div.nboutput.container div.output_area pre { + background: unset; +} + +div.nboutput.container div.output_area div.highlight { + color: unset; /* override Pygments text color */ +} + +/* avoid gaps between output lines */ +div.nboutput.container div[class*=highlight] pre { + line-height: normal; +} + +/* input/output containers */ +div.nbinput.container, +div.nboutput.container { + display: -webkit-flex; + display: flex; + align-items: flex-start; + margin: 0; + width: 100%; +} +@media (max-width: 540px) { + div.nbinput.container, + div.nboutput.container { + flex-direction: column; + } +} + +/* input container */ +div.nbinput.container { + padding-top: 5px; +} + +/* last container */ +div.nblast.container { + padding-bottom: 5px; +} + +/* input prompt */ +div.nbinput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nbinput.container div.prompt pre > code { + color: #307FC1; +} + +/* output prompt */ +div.nboutput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nboutput.container div.prompt pre > code { + color: #BF5B3D; +} + +/* all prompts */ +div.nbinput.container div.prompt, +div.nboutput.container div.prompt { + width: 4.5ex; + padding-top: 5px; + position: relative; + user-select: none; +} + +div.nbinput.container div.prompt > div, +div.nboutput.container div.prompt > div { + position: absolute; + right: 0; + margin-right: 0.3ex; +} + +@media (max-width: 540px) { + div.nbinput.container div.prompt, + div.nboutput.container div.prompt { + width: unset; + text-align: left; + padding: 0.4em; + } + div.nboutput.container div.prompt.empty { + padding: 0; + } + + div.nbinput.container div.prompt > div, + div.nboutput.container div.prompt > div { + position: unset; + } +} + +/* disable scrollbars and line breaks on prompts */ +div.nbinput.container div.prompt pre, +div.nboutput.container div.prompt pre { + overflow: hidden; + white-space: pre; +} + +/* input/output area */ +div.nbinput.container div.input_area, +div.nboutput.container div.output_area { + -webkit-flex: 1; + flex: 1; + overflow: auto; +} +@media (max-width: 540px) { + div.nbinput.container div.input_area, + div.nboutput.container div.output_area { + width: 100%; + } +} + +/* input area */ +div.nbinput.container div.input_area { + border: 1px solid #e0e0e0; + border-radius: 2px; + /*background: #f5f5f5;*/ +} + +/* override MathJax center alignment in output cells */ +div.nboutput.container div[class*=MathJax] { + text-align: left !important; +} + +/* override sphinx.ext.imgmath center alignment in output cells */ +div.nboutput.container div.math p { + text-align: left; +} + +/* standard error */ +div.nboutput.container div.output_area.stderr { + background: #fdd; +} + +/* ANSI colors */ +.ansi-black-fg { color: #3E424D; } +.ansi-black-bg { background-color: #3E424D; } +.ansi-black-intense-fg { color: #282C36; } +.ansi-black-intense-bg { background-color: #282C36; } +.ansi-red-fg { color: #E75C58; } +.ansi-red-bg { background-color: #E75C58; } +.ansi-red-intense-fg { color: #B22B31; } +.ansi-red-intense-bg { background-color: #B22B31; } +.ansi-green-fg { color: #00A250; } +.ansi-green-bg { background-color: #00A250; } +.ansi-green-intense-fg { color: #007427; } +.ansi-green-intense-bg { background-color: #007427; } +.ansi-yellow-fg { color: #DDB62B; } +.ansi-yellow-bg { background-color: #DDB62B; } +.ansi-yellow-intense-fg { color: #B27D12; } +.ansi-yellow-intense-bg { background-color: #B27D12; } +.ansi-blue-fg { color: #208FFB; } +.ansi-blue-bg { background-color: #208FFB; } +.ansi-blue-intense-fg { color: #0065CA; } +.ansi-blue-intense-bg { background-color: #0065CA; } +.ansi-magenta-fg { color: #D160C4; } +.ansi-magenta-bg { background-color: #D160C4; } +.ansi-magenta-intense-fg { color: #A03196; } +.ansi-magenta-intense-bg { background-color: #A03196; } +.ansi-cyan-fg { color: #60C6C8; } +.ansi-cyan-bg { background-color: #60C6C8; } +.ansi-cyan-intense-fg { color: #258F8F; } +.ansi-cyan-intense-bg { background-color: #258F8F; } +.ansi-white-fg { color: #C5C1B4; } +.ansi-white-bg { background-color: #C5C1B4; } +.ansi-white-intense-fg { color: #A1A6B2; } +.ansi-white-intense-bg { background-color: #A1A6B2; } + +.ansi-default-inverse-fg { color: #FFFFFF; } +.ansi-default-inverse-bg { background-color: #000000; } + +.ansi-bold { font-weight: bold; } +.ansi-underline { text-decoration: underline; } + + +div.nbinput.container div.input_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight].math, +div.nboutput.container div.output_area.rendered_html, +div.nboutput.container div.output_area > div.output_javascript, +div.nboutput.container div.output_area:not(.rendered_html) > img{ + padding: 5px; + margin: 0; +} + +/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */ +div.nbinput.container div.input_area > div[class^='highlight'], +div.nboutput.container div.output_area > div[class^='highlight']{ + overflow-y: hidden; +} + +/* hide copy button on prompts for 'sphinx_copybutton' extension ... */ +.prompt .copybtn, +/* ... and 'sphinx_immaterial' theme */ +.prompt .md-clipboard.md-icon { + display: none; +} + +/* Some additional styling taken form the Jupyter notebook CSS */ +.jp-RenderedHTMLCommon table, +div.rendered_html table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 12px; + table-layout: fixed; +} +.jp-RenderedHTMLCommon thead, +div.rendered_html thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} +.jp-RenderedHTMLCommon tr, +.jp-RenderedHTMLCommon th, +.jp-RenderedHTMLCommon td, +div.rendered_html tr, +div.rendered_html th, +div.rendered_html td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +.jp-RenderedHTMLCommon th, +div.rendered_html th { + font-weight: bold; +} +.jp-RenderedHTMLCommon tbody tr:nth-child(odd), +div.rendered_html tbody tr:nth-child(odd) { + background: #f5f5f5; +} +.jp-RenderedHTMLCommon tbody tr:hover, +div.rendered_html tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} diff --git a/docs/source/_build/_static/nbsphinx-gallery.css b/docs/source/_build/_static/nbsphinx-gallery.css new file mode 100644 index 00000000..365c27a9 --- /dev/null +++ b/docs/source/_build/_static/nbsphinx-gallery.css @@ -0,0 +1,31 @@ +.nbsphinx-gallery { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 5px; + margin-top: 1em; + margin-bottom: 1em; +} + +.nbsphinx-gallery > a { + padding: 5px; + border: 1px dotted currentColor; + border-radius: 2px; + text-align: center; +} + +.nbsphinx-gallery > a:hover { + border-style: solid; +} + +.nbsphinx-gallery img { + max-width: 100%; + max-height: 100%; +} + +.nbsphinx-gallery > a > div:first-child { + display: flex; + align-items: start; + justify-content: center; + height: 120px; + margin-bottom: 5px; +} diff --git a/docs/source/_build/_static/nbsphinx-no-thumbnail.svg b/docs/source/_build/_static/nbsphinx-no-thumbnail.svg new file mode 100644 index 00000000..9dca7588 --- /dev/null +++ b/docs/source/_build/_static/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/docs/source/_build/_static/plus.png b/docs/source/_build/_static/plus.png new file mode 100644 index 00000000..7107cec9 Binary files /dev/null and b/docs/source/_build/_static/plus.png differ diff --git a/docs/source/_build/_static/pygments.css b/docs/source/_build/_static/pygments.css new file mode 100644 index 00000000..34cb0e9d --- /dev/null +++ b/docs/source/_build/_static/pygments.css @@ -0,0 +1,152 @@ +html[data-theme="light"] .highlight pre { line-height: 125%; } +html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight .hll { background-color: #fae4c2 } +html[data-theme="light"] .highlight { background: #fefefe; color: #080808 } +html[data-theme="light"] .highlight .c { color: #515151 } /* Comment */ +html[data-theme="light"] .highlight .err { color: #A12236 } /* Error */ +html[data-theme="light"] .highlight .k { color: #6730C5 } /* Keyword */ +html[data-theme="light"] .highlight .l { color: #7F4707 } /* Literal */ +html[data-theme="light"] .highlight .n { color: #080808 } /* Name */ +html[data-theme="light"] .highlight .o { color: #00622F } /* Operator */ +html[data-theme="light"] .highlight .p { color: #080808 } /* Punctuation */ +html[data-theme="light"] .highlight .ch { color: #515151 } /* Comment.Hashbang */ +html[data-theme="light"] .highlight .cm { color: #515151 } /* Comment.Multiline */ +html[data-theme="light"] .highlight .cp { color: #515151 } /* Comment.Preproc */ +html[data-theme="light"] .highlight .cpf { color: #515151 } /* Comment.PreprocFile */ +html[data-theme="light"] .highlight .c1 { color: #515151 } /* Comment.Single */ +html[data-theme="light"] .highlight .cs { color: #515151 } /* Comment.Special */ +html[data-theme="light"] .highlight .gd { color: #005B82 } /* Generic.Deleted */ +html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="light"] .highlight .gh { color: #005B82 } /* Generic.Heading */ +html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="light"] .highlight .gu { color: #005B82 } /* Generic.Subheading */ +html[data-theme="light"] .highlight .kc { color: #6730C5 } /* Keyword.Constant */ +html[data-theme="light"] .highlight .kd { color: #6730C5 } /* Keyword.Declaration */ +html[data-theme="light"] .highlight .kn { color: #6730C5 } /* Keyword.Namespace */ +html[data-theme="light"] .highlight .kp { color: #6730C5 } /* Keyword.Pseudo */ +html[data-theme="light"] .highlight .kr { color: #6730C5 } /* Keyword.Reserved */ +html[data-theme="light"] .highlight .kt { color: #7F4707 } /* Keyword.Type */ +html[data-theme="light"] .highlight .ld { color: #7F4707 } /* Literal.Date */ +html[data-theme="light"] .highlight .m { color: #7F4707 } /* Literal.Number */ +html[data-theme="light"] .highlight .s { color: #00622F } /* Literal.String */ +html[data-theme="light"] .highlight .na { color: #912583 } /* Name.Attribute */ +html[data-theme="light"] .highlight .nb { color: #7F4707 } /* Name.Builtin */ +html[data-theme="light"] .highlight .nc { color: #005B82 } /* Name.Class */ +html[data-theme="light"] .highlight .no { color: #005B82 } /* Name.Constant */ +html[data-theme="light"] .highlight .nd { color: #7F4707 } /* Name.Decorator */ +html[data-theme="light"] .highlight .ni { color: #00622F } /* Name.Entity */ +html[data-theme="light"] .highlight .ne { color: #6730C5 } /* Name.Exception */ +html[data-theme="light"] .highlight .nf { color: #005B82 } /* Name.Function */ +html[data-theme="light"] .highlight .nl { color: #7F4707 } /* Name.Label */ +html[data-theme="light"] .highlight .nn { color: #080808 } /* Name.Namespace */ +html[data-theme="light"] .highlight .nx { color: #080808 } /* Name.Other */ +html[data-theme="light"] .highlight .py { color: #005B82 } /* Name.Property */ +html[data-theme="light"] .highlight .nt { color: #005B82 } /* Name.Tag */ +html[data-theme="light"] .highlight .nv { color: #A12236 } /* Name.Variable */ +html[data-theme="light"] .highlight .ow { color: #6730C5 } /* Operator.Word */ +html[data-theme="light"] .highlight .pm { color: #080808 } /* Punctuation.Marker */ +html[data-theme="light"] .highlight .w { color: #080808 } /* Text.Whitespace */ +html[data-theme="light"] .highlight .mb { color: #7F4707 } /* Literal.Number.Bin */ +html[data-theme="light"] .highlight .mf { color: #7F4707 } /* Literal.Number.Float */ +html[data-theme="light"] .highlight .mh { color: #7F4707 } /* Literal.Number.Hex */ +html[data-theme="light"] .highlight .mi { color: #7F4707 } /* Literal.Number.Integer */ +html[data-theme="light"] .highlight .mo { color: #7F4707 } /* Literal.Number.Oct */ +html[data-theme="light"] .highlight .sa { color: #00622F } /* Literal.String.Affix */ +html[data-theme="light"] .highlight .sb { color: #00622F } /* Literal.String.Backtick */ +html[data-theme="light"] .highlight .sc { color: #00622F } /* Literal.String.Char */ +html[data-theme="light"] .highlight .dl { color: #00622F } /* Literal.String.Delimiter */ +html[data-theme="light"] .highlight .sd { color: #00622F } /* Literal.String.Doc */ +html[data-theme="light"] .highlight .s2 { color: #00622F } /* Literal.String.Double */ +html[data-theme="light"] .highlight .se { color: #00622F } /* Literal.String.Escape */ +html[data-theme="light"] .highlight .sh { color: #00622F } /* Literal.String.Heredoc */ +html[data-theme="light"] .highlight .si { color: #00622F } /* Literal.String.Interpol */ +html[data-theme="light"] .highlight .sx { color: #00622F } /* Literal.String.Other */ +html[data-theme="light"] .highlight .sr { color: #A12236 } /* Literal.String.Regex */ +html[data-theme="light"] .highlight .s1 { color: #00622F } /* Literal.String.Single */ +html[data-theme="light"] .highlight .ss { color: #005B82 } /* Literal.String.Symbol */ +html[data-theme="light"] .highlight .bp { color: #7F4707 } /* Name.Builtin.Pseudo */ +html[data-theme="light"] .highlight .fm { color: #005B82 } /* Name.Function.Magic */ +html[data-theme="light"] .highlight .vc { color: #A12236 } /* Name.Variable.Class */ +html[data-theme="light"] .highlight .vg { color: #A12236 } /* Name.Variable.Global */ +html[data-theme="light"] .highlight .vi { color: #A12236 } /* Name.Variable.Instance */ +html[data-theme="light"] .highlight .vm { color: #7F4707 } /* Name.Variable.Magic */ +html[data-theme="light"] .highlight .il { color: #7F4707 } /* Literal.Number.Integer.Long */ +html[data-theme="dark"] .highlight pre { line-height: 125%; } +html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e } +html[data-theme="dark"] .highlight { background: #2b2b2b; color: #F8F8F2 } +html[data-theme="dark"] .highlight .c { color: #FFD900 } /* Comment */ +html[data-theme="dark"] .highlight .err { color: #FFA07A } /* Error */ +html[data-theme="dark"] .highlight .k { color: #DCC6E0 } /* Keyword */ +html[data-theme="dark"] .highlight .l { color: #FFD900 } /* Literal */ +html[data-theme="dark"] .highlight .n { color: #F8F8F2 } /* Name */ +html[data-theme="dark"] .highlight .o { color: #ABE338 } /* Operator */ +html[data-theme="dark"] .highlight .p { color: #F8F8F2 } /* Punctuation */ +html[data-theme="dark"] .highlight .ch { color: #FFD900 } /* Comment.Hashbang */ +html[data-theme="dark"] .highlight .cm { color: #FFD900 } /* Comment.Multiline */ +html[data-theme="dark"] .highlight .cp { color: #FFD900 } /* Comment.Preproc */ +html[data-theme="dark"] .highlight .cpf { color: #FFD900 } /* Comment.PreprocFile */ +html[data-theme="dark"] .highlight .c1 { color: #FFD900 } /* Comment.Single */ +html[data-theme="dark"] .highlight .cs { color: #FFD900 } /* Comment.Special */ +html[data-theme="dark"] .highlight .gd { color: #00E0E0 } /* Generic.Deleted */ +html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="dark"] .highlight .gh { color: #00E0E0 } /* Generic.Heading */ +html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="dark"] .highlight .gu { color: #00E0E0 } /* Generic.Subheading */ +html[data-theme="dark"] .highlight .kc { color: #DCC6E0 } /* Keyword.Constant */ +html[data-theme="dark"] .highlight .kd { color: #DCC6E0 } /* Keyword.Declaration */ +html[data-theme="dark"] .highlight .kn { color: #DCC6E0 } /* Keyword.Namespace */ +html[data-theme="dark"] .highlight .kp { color: #DCC6E0 } /* Keyword.Pseudo */ +html[data-theme="dark"] .highlight .kr { color: #DCC6E0 } /* Keyword.Reserved */ +html[data-theme="dark"] .highlight .kt { color: #FFD900 } /* Keyword.Type */ +html[data-theme="dark"] .highlight .ld { color: #FFD900 } /* Literal.Date */ +html[data-theme="dark"] .highlight .m { color: #FFD900 } /* Literal.Number */ +html[data-theme="dark"] .highlight .s { color: #ABE338 } /* Literal.String */ +html[data-theme="dark"] .highlight .na { color: #FFD900 } /* Name.Attribute */ +html[data-theme="dark"] .highlight .nb { color: #FFD900 } /* Name.Builtin */ +html[data-theme="dark"] .highlight .nc { color: #00E0E0 } /* Name.Class */ +html[data-theme="dark"] .highlight .no { color: #00E0E0 } /* Name.Constant */ +html[data-theme="dark"] .highlight .nd { color: #FFD900 } /* Name.Decorator */ +html[data-theme="dark"] .highlight .ni { color: #ABE338 } /* Name.Entity */ +html[data-theme="dark"] .highlight .ne { color: #DCC6E0 } /* Name.Exception */ +html[data-theme="dark"] .highlight .nf { color: #00E0E0 } /* Name.Function */ +html[data-theme="dark"] .highlight .nl { color: #FFD900 } /* Name.Label */ +html[data-theme="dark"] .highlight .nn { color: #F8F8F2 } /* Name.Namespace */ +html[data-theme="dark"] .highlight .nx { color: #F8F8F2 } /* Name.Other */ +html[data-theme="dark"] .highlight .py { color: #00E0E0 } /* Name.Property */ +html[data-theme="dark"] .highlight .nt { color: #00E0E0 } /* Name.Tag */ +html[data-theme="dark"] .highlight .nv { color: #FFA07A } /* Name.Variable */ +html[data-theme="dark"] .highlight .ow { color: #DCC6E0 } /* Operator.Word */ +html[data-theme="dark"] .highlight .pm { color: #F8F8F2 } /* Punctuation.Marker */ +html[data-theme="dark"] .highlight .w { color: #F8F8F2 } /* Text.Whitespace */ +html[data-theme="dark"] .highlight .mb { color: #FFD900 } /* Literal.Number.Bin */ +html[data-theme="dark"] .highlight .mf { color: #FFD900 } /* Literal.Number.Float */ +html[data-theme="dark"] .highlight .mh { color: #FFD900 } /* Literal.Number.Hex */ +html[data-theme="dark"] .highlight .mi { color: #FFD900 } /* Literal.Number.Integer */ +html[data-theme="dark"] .highlight .mo { color: #FFD900 } /* Literal.Number.Oct */ +html[data-theme="dark"] .highlight .sa { color: #ABE338 } /* Literal.String.Affix */ +html[data-theme="dark"] .highlight .sb { color: #ABE338 } /* Literal.String.Backtick */ +html[data-theme="dark"] .highlight .sc { color: #ABE338 } /* Literal.String.Char */ +html[data-theme="dark"] .highlight .dl { color: #ABE338 } /* Literal.String.Delimiter */ +html[data-theme="dark"] .highlight .sd { color: #ABE338 } /* Literal.String.Doc */ +html[data-theme="dark"] .highlight .s2 { color: #ABE338 } /* Literal.String.Double */ +html[data-theme="dark"] .highlight .se { color: #ABE338 } /* Literal.String.Escape */ +html[data-theme="dark"] .highlight .sh { color: #ABE338 } /* Literal.String.Heredoc */ +html[data-theme="dark"] .highlight .si { color: #ABE338 } /* Literal.String.Interpol */ +html[data-theme="dark"] .highlight .sx { color: #ABE338 } /* Literal.String.Other */ +html[data-theme="dark"] .highlight .sr { color: #FFA07A } /* Literal.String.Regex */ +html[data-theme="dark"] .highlight .s1 { color: #ABE338 } /* Literal.String.Single */ +html[data-theme="dark"] .highlight .ss { color: #00E0E0 } /* Literal.String.Symbol */ +html[data-theme="dark"] .highlight .bp { color: #FFD900 } /* Name.Builtin.Pseudo */ +html[data-theme="dark"] .highlight .fm { color: #00E0E0 } /* Name.Function.Magic */ +html[data-theme="dark"] .highlight .vc { color: #FFA07A } /* Name.Variable.Class */ +html[data-theme="dark"] .highlight .vg { color: #FFA07A } /* Name.Variable.Global */ +html[data-theme="dark"] .highlight .vi { color: #FFA07A } /* Name.Variable.Instance */ +html[data-theme="dark"] .highlight .vm { color: #FFD900 } /* Name.Variable.Magic */ +html[data-theme="dark"] .highlight .il { color: #FFD900 } /* Literal.Number.Integer.Long */ diff --git a/docs/source/_build/_static/scripts/bootstrap.js b/docs/source/_build/_static/scripts/bootstrap.js new file mode 100644 index 00000000..16db590b --- /dev/null +++ b/docs/source/_build/_static/scripts/bootstrap.js @@ -0,0 +1,3 @@ +/*! For license information please see bootstrap.js.LICENSE.txt */ +(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>E,afterRead:()=>v,afterWrite:()=>C,applyStyles:()=>$,arrow:()=>J,auto:()=>a,basePlacements:()=>l,beforeMain:()=>y,beforeRead:()=>_,beforeWrite:()=>A,bottom:()=>s,clippingParents:()=>d,computeStyles:()=>it,createPopper:()=>Dt,createPopperBase:()=>St,createPopperLite:()=>$t,detectOverflow:()=>_t,end:()=>h,eventListeners:()=>st,flip:()=>bt,hide:()=>wt,left:()=>r,main:()=>w,modifierPhases:()=>O,offset:()=>Et,placements:()=>g,popper:()=>f,popperGenerator:()=>Lt,popperOffsets:()=>At,preventOverflow:()=>Tt,read:()=>b,reference:()=>p,right:()=>o,start:()=>c,top:()=>n,variationPlacements:()=>m,viewport:()=>u,write:()=>T});var i={};t.r(i),t.d(i,{Alert:()=>Oe,Button:()=>ke,Carousel:()=>li,Collapse:()=>Ei,Dropdown:()=>Ki,Modal:()=>Ln,Offcanvas:()=>Kn,Popover:()=>bs,ScrollSpy:()=>Ls,Tab:()=>Js,Toast:()=>po,Tooltip:()=>fs});var n="top",s="bottom",o="right",r="left",a="auto",l=[n,s,o,r],c="start",h="end",d="clippingParents",u="viewport",f="popper",p="reference",m=l.reduce((function(t,e){return t.concat([e+"-"+c,e+"-"+h])}),[]),g=[].concat(l,[a]).reduce((function(t,e){return t.concat([e,e+"-"+c,e+"-"+h])}),[]),_="beforeRead",b="read",v="afterRead",y="beforeMain",w="main",E="afterMain",A="beforeWrite",T="write",C="afterWrite",O=[_,b,v,y,w,E,A,T,C];function x(t){return t?(t.nodeName||"").toLowerCase():null}function k(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function L(t){return t instanceof k(t).Element||t instanceof Element}function S(t){return t instanceof k(t).HTMLElement||t instanceof HTMLElement}function D(t){return"undefined"!=typeof ShadowRoot&&(t instanceof k(t).ShadowRoot||t instanceof ShadowRoot)}const $={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];S(s)&&x(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});S(n)&&x(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function I(t){return t.split("-")[0]}var N=Math.max,P=Math.min,M=Math.round;function j(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function F(){return!/^((?!chrome|android).)*safari/i.test(j())}function H(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&S(t)&&(s=t.offsetWidth>0&&M(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&M(n.height)/t.offsetHeight||1);var r=(L(t)?k(t):window).visualViewport,a=!F()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function B(t){var e=H(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function W(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&D(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function z(t){return k(t).getComputedStyle(t)}function R(t){return["table","td","th"].indexOf(x(t))>=0}function q(t){return((L(t)?t.ownerDocument:t.document)||window.document).documentElement}function V(t){return"html"===x(t)?t:t.assignedSlot||t.parentNode||(D(t)?t.host:null)||q(t)}function Y(t){return S(t)&&"fixed"!==z(t).position?t.offsetParent:null}function K(t){for(var e=k(t),i=Y(t);i&&R(i)&&"static"===z(i).position;)i=Y(i);return i&&("html"===x(i)||"body"===x(i)&&"static"===z(i).position)?e:i||function(t){var e=/firefox/i.test(j());if(/Trident/i.test(j())&&S(t)&&"fixed"===z(t).position)return null;var i=V(t);for(D(i)&&(i=i.host);S(i)&&["html","body"].indexOf(x(i))<0;){var n=z(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Q(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function X(t,e,i){return N(t,P(e,i))}function U(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function G(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const J={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,a=t.name,c=t.options,h=i.elements.arrow,d=i.modifiersData.popperOffsets,u=I(i.placement),f=Q(u),p=[r,o].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return U("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:G(t,l))}(c.padding,i),g=B(h),_="y"===f?n:r,b="y"===f?s:o,v=i.rects.reference[p]+i.rects.reference[f]-d[f]-i.rects.popper[p],y=d[f]-i.rects.reference[f],w=K(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=X(T,O,C),k=f;i.modifiersData[a]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&W(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Z(t){return t.split("-")[1]}var tt={top:"auto",right:"auto",bottom:"auto",left:"auto"};function et(t){var e,i=t.popper,a=t.popperRect,l=t.placement,c=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=r,C=n,O=window;if(p){var x=K(i),L="clientHeight",S="clientWidth";x===k(i)&&"static"!==z(x=q(i)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===n||(l===r||l===o)&&c===h)&&(C=s,y-=(g&&x===O&&O.visualViewport?O.visualViewport.height:x[L])-a.height,y*=f?1:-1),l!==r&&(l!==n&&l!==s||c!==h)||(T=o,b-=(g&&x===O&&O.visualViewport?O.visualViewport.width:x[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&tt),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:M(i*s)/s||0,y:M(n*s)/s||0}}({x:b,y},k(i)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const it={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:I(e.placement),variation:Z(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,et(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,et(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var nt={passive:!0};const st={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=k(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,nt)})),a&&l.addEventListener("resize",i.update,nt),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,nt)})),a&&l.removeEventListener("resize",i.update,nt)}},data:{}};var ot={left:"right",right:"left",bottom:"top",top:"bottom"};function rt(t){return t.replace(/left|right|bottom|top/g,(function(t){return ot[t]}))}var at={start:"end",end:"start"};function lt(t){return t.replace(/start|end/g,(function(t){return at[t]}))}function ct(t){var e=k(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ht(t){return H(q(t)).left+ct(t).scrollLeft}function dt(t){var e=z(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function ut(t){return["html","body","#document"].indexOf(x(t))>=0?t.ownerDocument.body:S(t)&&dt(t)?t:ut(V(t))}function ft(t,e){var i;void 0===e&&(e=[]);var n=ut(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=k(n),r=s?[o].concat(o.visualViewport||[],dt(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ft(V(r)))}function pt(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function mt(t,e,i){return e===u?pt(function(t,e){var i=k(t),n=q(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=F();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ht(t),y:l}}(t,i)):L(e)?function(t,e){var i=H(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):pt(function(t){var e,i=q(t),n=ct(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=N(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=N(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ht(t),l=-n.scrollTop;return"rtl"===z(s||i).direction&&(a+=N(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(q(t)))}function gt(t){var e,i=t.reference,a=t.element,l=t.placement,d=l?I(l):null,u=l?Z(l):null,f=i.x+i.width/2-a.width/2,p=i.y+i.height/2-a.height/2;switch(d){case n:e={x:f,y:i.y-a.height};break;case s:e={x:f,y:i.y+i.height};break;case o:e={x:i.x+i.width,y:p};break;case r:e={x:i.x-a.width,y:p};break;default:e={x:i.x,y:i.y}}var m=d?Q(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case c:e[m]=e[m]-(i[g]/2-a[g]/2);break;case h:e[m]=e[m]+(i[g]/2-a[g]/2)}}return e}function _t(t,e){void 0===e&&(e={});var i=e,r=i.placement,a=void 0===r?t.placement:r,c=i.strategy,h=void 0===c?t.strategy:c,m=i.boundary,g=void 0===m?d:m,_=i.rootBoundary,b=void 0===_?u:_,v=i.elementContext,y=void 0===v?f:v,w=i.altBoundary,E=void 0!==w&&w,A=i.padding,T=void 0===A?0:A,C=U("number"!=typeof T?T:G(T,l)),O=y===f?p:f,k=t.rects.popper,D=t.elements[E?O:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ft(V(t)),i=["absolute","fixed"].indexOf(z(t).position)>=0&&S(t)?K(t):t;return L(i)?e.filter((function(t){return L(t)&&W(t,i)&&"body"!==x(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=mt(t,i,n);return e.top=N(s.top,e.top),e.right=P(s.right,e.right),e.bottom=P(s.bottom,e.bottom),e.left=N(s.left,e.left),e}),mt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(L(D)?D:D.contextElement||q(t.elements.popper),g,b,h),I=H(t.elements.reference),M=gt({reference:I,element:k,strategy:"absolute",placement:a}),j=pt(Object.assign({},k,M)),F=y===f?j:I,B={top:$.top-F.top+C.top,bottom:F.bottom-$.bottom+C.bottom,left:$.left-F.left+C.left,right:F.right-$.right+C.right},R=t.modifiersData.offset;if(y===f&&R){var Y=R[a];Object.keys(B).forEach((function(t){var e=[o,s].indexOf(t)>=0?1:-1,i=[n,s].indexOf(t)>=0?"y":"x";B[t]+=Y[i]*e}))}return B}const bt={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=i.mainAxis,u=void 0===d||d,f=i.altAxis,p=void 0===f||f,_=i.fallbackPlacements,b=i.padding,v=i.boundary,y=i.rootBoundary,w=i.altBoundary,E=i.flipVariations,A=void 0===E||E,T=i.allowedAutoPlacements,C=e.options.placement,O=I(C),x=_||(O!==C&&A?function(t){if(I(t)===a)return[];var e=rt(t);return[lt(t),e,lt(e)]}(C):[rt(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat(I(i)===a?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?g:c,d=Z(n),u=d?a?m:m.filter((function(t){return Z(t)===d})):l,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var p=f.reduce((function(e,i){return e[i]=_t(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[I(i)],e}),{});return Object.keys(p).sort((function(t,e){return p[t]-p[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,$=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=_t(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?o:r:F?s:n;L[B]>S[B]&&(z=rt(z));var R=rt(z),q=[];if(u&&q.push(W[j]<=0),p&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,$=!1;break}D.set(M,q)}if($)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function vt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function yt(t){return[n,o,s,r].some((function(e){return t[e]>=0}))}const wt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=_t(e,{elementContext:"reference"}),a=_t(e,{altBoundary:!0}),l=vt(r,n),c=vt(a,s,o),h=yt(l),d=yt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Et={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,s=t.name,a=i.offset,l=void 0===a?[0,0]:a,c=g.reduce((function(t,i){return t[i]=function(t,e,i){var s=I(t),a=[r,n].indexOf(s)>=0?-1:1,l="function"==typeof i?i(Object.assign({},e,{placement:t})):i,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[r,o].indexOf(s)>=0?{x:h,y:c}:{x:c,y:h}}(i,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[s]=c}},At={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=gt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Tt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,a=t.name,l=i.mainAxis,h=void 0===l||l,d=i.altAxis,u=void 0!==d&&d,f=i.boundary,p=i.rootBoundary,m=i.altBoundary,g=i.padding,_=i.tether,b=void 0===_||_,v=i.tetherOffset,y=void 0===v?0:v,w=_t(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=I(e.placement),A=Z(e.placement),T=!A,C=Q(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),$=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?n:r,H="y"===C?s:o,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[H],V=b?-L[W]/2:0,Y=A===c?k[W]:L[W],U=A===c?-L[W]:-k[W],G=e.elements.arrow,J=b&&G?B(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[H],nt=X(0,k[W],J[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:Y-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&K(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==$?void 0:$[C])?j:0,ct=z+ot-lt,ht=X(b?P(R,z+st-lt-at):R,z,b?N(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?n:r,ft="x"===C?s:o,pt=x[O],mt="y"===O?"height":"width",gt=pt+w[ut],bt=pt-w[ft],vt=-1!==[n,r].indexOf(E),yt=null!=(dt=null==$?void 0:$[O])?dt:0,wt=vt?gt:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=X(t,e,i);return n>i?i:n}(wt,pt,Et):X(b?wt:gt,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Ct(t,e,i){void 0===i&&(i=!1);var n,s,o=S(e),r=S(e)&&function(t){var e=t.getBoundingClientRect(),i=M(e.width)/t.offsetWidth||1,n=M(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=q(e),l=H(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==x(e)||dt(a))&&(c=(n=e)!==k(n)&&S(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ct(n)),S(e)?((h=H(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ht(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ot(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var xt={placement:"bottom",modifiers:[],strategy:"absolute"};function kt(){for(var t=arguments.length,e=new Array(t),i=0;iIt.has(t)&&It.get(t).get(e)||null,remove(t,e){if(!It.has(t))return;const i=It.get(t);i.delete(e),0===i.size&&It.delete(t)}},Pt="transitionend",Mt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),jt=t=>{t.dispatchEvent(new Event(Pt))},Ft=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ht=t=>Ft(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Mt(t)):null,Bt=t=>{if(!Ft(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Wt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),zt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?zt(t.parentNode):null},Rt=()=>{},qt=t=>{t.offsetHeight},Vt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Yt=[],Kt=()=>"rtl"===document.documentElement.dir,Qt=t=>{var e;e=()=>{const e=Vt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Yt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Yt)t()})),Yt.push(e)):e()},Xt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Ut=(t,e,i=!0)=>{if(!i)return void Xt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Pt,o),Xt(t))};e.addEventListener(Pt,o),setTimeout((()=>{s||jt(e)}),n)},Gt=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Jt=/[^.]*(?=\..*)\.|.*/,Zt=/\..*/,te=/::\d+$/,ee={};let ie=1;const ne={mouseenter:"mouseover",mouseleave:"mouseout"},se=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function oe(t,e){return e&&`${e}::${ie++}`||t.uidEvent||ie++}function re(t){const e=oe(t);return t.uidEvent=e,ee[e]=ee[e]||{},ee[e]}function ae(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function le(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=ue(t);return se.has(o)||(o=t),[n,s,o]}function ce(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=le(e,i,n);if(e in ne){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=re(t),c=l[a]||(l[a]={}),h=ae(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=oe(r,e.replace(Jt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return pe(s,{delegateTarget:r}),n.oneOff&&fe.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return pe(n,{delegateTarget:t}),i.oneOff&&fe.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function he(t,e,i,n,s){const o=ae(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function de(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&he(t,e,i,r.callable,r.delegationSelector)}function ue(t){return t=t.replace(Zt,""),ne[t]||t}const fe={on(t,e,i,n){ce(t,e,i,n,!1)},one(t,e,i,n){ce(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=le(e,i,n),a=r!==e,l=re(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))de(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(te,"");a&&!e.includes(s)||he(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;he(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=Vt();let s=null,o=!0,r=!0,a=!1;e!==ue(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=pe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function pe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function me(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function ge(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const _e={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${ge(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${ge(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=me(t.dataset[n])}return e},getDataAttribute:(t,e)=>me(t.getAttribute(`data-bs-${ge(e)}`))};class be{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=Ft(e)?_e.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...Ft(e)?_e.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=Ft(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class ve extends be{constructor(t,e){super(),(t=Ht(t))&&(this._element=t,this._config=this._getConfig(e),Nt.set(this._element,this.constructor.DATA_KEY,this))}dispose(){Nt.remove(this._element,this.constructor.DATA_KEY),fe.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Ut(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return Nt.get(Ht(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ye=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>Mt(t))).join(","):null},we={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Wt(t)&&Bt(t)))},getSelectorFromElement(t){const e=ye(t);return e&&we.findOne(e)?e:null},getElementFromSelector(t){const e=ye(t);return e?we.findOne(e):null},getMultipleElementsFromSelector(t){const e=ye(t);return e?we.find(e):[]}},Ee=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;fe.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Wt(this))return;const s=we.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ae=".bs.alert",Te=`close${Ae}`,Ce=`closed${Ae}`;class Oe extends ve{static get NAME(){return"alert"}close(){if(fe.trigger(this._element,Te).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),fe.trigger(this._element,Ce),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Oe.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}Ee(Oe,"close"),Qt(Oe);const xe='[data-bs-toggle="button"]';class ke extends ve{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=ke.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}fe.on(document,"click.bs.button.data-api",xe,(t=>{t.preventDefault();const e=t.target.closest(xe);ke.getOrCreateInstance(e).toggle()})),Qt(ke);const Le=".bs.swipe",Se=`touchstart${Le}`,De=`touchmove${Le}`,$e=`touchend${Le}`,Ie=`pointerdown${Le}`,Ne=`pointerup${Le}`,Pe={endCallback:null,leftCallback:null,rightCallback:null},Me={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class je extends be{constructor(t,e){super(),this._element=t,t&&je.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Pe}static get DefaultType(){return Me}static get NAME(){return"swipe"}dispose(){fe.off(this._element,Le)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Xt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Xt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(fe.on(this._element,Ie,(t=>this._start(t))),fe.on(this._element,Ne,(t=>this._end(t))),this._element.classList.add("pointer-event")):(fe.on(this._element,Se,(t=>this._start(t))),fe.on(this._element,De,(t=>this._move(t))),fe.on(this._element,$e,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Fe=".bs.carousel",He=".data-api",Be="ArrowLeft",We="ArrowRight",ze="next",Re="prev",qe="left",Ve="right",Ye=`slide${Fe}`,Ke=`slid${Fe}`,Qe=`keydown${Fe}`,Xe=`mouseenter${Fe}`,Ue=`mouseleave${Fe}`,Ge=`dragstart${Fe}`,Je=`load${Fe}${He}`,Ze=`click${Fe}${He}`,ti="carousel",ei="active",ii=".active",ni=".carousel-item",si=ii+ni,oi={[Be]:Ve,[We]:qe},ri={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ai={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class li extends ve{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=we.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===ti&&this.cycle()}static get Default(){return ri}static get DefaultType(){return ai}static get NAME(){return"carousel"}next(){this._slide(ze)}nextWhenVisible(){!document.hidden&&Bt(this._element)&&this.next()}prev(){this._slide(Re)}pause(){this._isSliding&&jt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?fe.one(this._element,Ke,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void fe.one(this._element,Ke,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ze:Re;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&fe.on(this._element,Qe,(t=>this._keydown(t))),"hover"===this._config.pause&&(fe.on(this._element,Xe,(()=>this.pause())),fe.on(this._element,Ue,(()=>this._maybeEnableCycle()))),this._config.touch&&je.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of we.find(".carousel-item img",this._element))fe.on(t,Ge,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(qe)),rightCallback:()=>this._slide(this._directionToOrder(Ve)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new je(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=oi[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=we.findOne(ii,this._indicatorsElement);e.classList.remove(ei),e.removeAttribute("aria-current");const i=we.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(ei),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ze,s=e||Gt(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>fe.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Ye).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),qt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(ei),i.classList.remove(ei,c,l),this._isSliding=!1,r(Ke)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return we.findOne(si,this._element)}_getItems(){return we.find(ni,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Kt()?t===qe?Re:ze:t===qe?ze:Re}_orderToDirection(t){return Kt()?t===Re?qe:Ve:t===Re?Ve:qe}static jQueryInterface(t){return this.each((function(){const e=li.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}fe.on(document,Ze,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=we.getElementFromSelector(this);if(!e||!e.classList.contains(ti))return;t.preventDefault();const i=li.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===_e.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),fe.on(window,Je,(()=>{const t=we.find('[data-bs-ride="carousel"]');for(const e of t)li.getOrCreateInstance(e)})),Qt(li);const ci=".bs.collapse",hi=`show${ci}`,di=`shown${ci}`,ui=`hide${ci}`,fi=`hidden${ci}`,pi=`click${ci}.data-api`,mi="show",gi="collapse",_i="collapsing",bi=`:scope .${gi} .${gi}`,vi='[data-bs-toggle="collapse"]',yi={parent:null,toggle:!0},wi={parent:"(null|element)",toggle:"boolean"};class Ei extends ve{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=we.find(vi);for(const t of i){const e=we.getSelectorFromElement(t),i=we.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return yi}static get DefaultType(){return wi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Ei.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(fe.trigger(this._element,hi).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(gi),this._element.classList.add(_i),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi,mi),this._element.style[e]="",fe.trigger(this._element,di)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(fe.trigger(this._element,ui).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,qt(this._element),this._element.classList.add(_i),this._element.classList.remove(gi,mi);for(const t of this._triggerArray){const e=we.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi),fe.trigger(this._element,fi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(mi)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ht(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(vi);for(const e of t){const t=we.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=we.find(bi,this._config.parent);return we.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Ei.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}fe.on(document,pi,vi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of we.getMultipleElementsFromSelector(this))Ei.getOrCreateInstance(t,{toggle:!1}).toggle()})),Qt(Ei);const Ai="dropdown",Ti=".bs.dropdown",Ci=".data-api",Oi="ArrowUp",xi="ArrowDown",ki=`hide${Ti}`,Li=`hidden${Ti}`,Si=`show${Ti}`,Di=`shown${Ti}`,$i=`click${Ti}${Ci}`,Ii=`keydown${Ti}${Ci}`,Ni=`keyup${Ti}${Ci}`,Pi="show",Mi='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',ji=`${Mi}.${Pi}`,Fi=".dropdown-menu",Hi=Kt()?"top-end":"top-start",Bi=Kt()?"top-start":"top-end",Wi=Kt()?"bottom-end":"bottom-start",zi=Kt()?"bottom-start":"bottom-end",Ri=Kt()?"left-start":"right-start",qi=Kt()?"right-start":"left-start",Vi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Yi={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Ki extends ve{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=we.next(this._element,Fi)[0]||we.prev(this._element,Fi)[0]||we.findOne(Fi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Vi}static get DefaultType(){return Yi}static get NAME(){return Ai}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Wt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!fe.trigger(this._element,Si,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Pi),this._element.classList.add(Pi),fe.trigger(this._element,Di,t)}}hide(){if(Wt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!fe.trigger(this._element,ki,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._popper&&this._popper.destroy(),this._menu.classList.remove(Pi),this._element.classList.remove(Pi),this._element.setAttribute("aria-expanded","false"),_e.removeDataAttribute(this._menu,"popper"),fe.trigger(this._element,Li,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!Ft(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ai.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:Ft(this._config.reference)?t=Ht(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=Dt(t,this._menu,i)}_isShown(){return this._menu.classList.contains(Pi)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Ri;if(t.classList.contains("dropstart"))return qi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?Bi:Hi:e?zi:Wi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(_e.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Xt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=we.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Bt(t)));i.length&&Gt(i,e,t===xi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=we.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Oi,xi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Mi)?this:we.prev(this,Mi)[0]||we.next(this,Mi)[0]||we.findOne(Mi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}fe.on(document,Ii,Mi,Ki.dataApiKeydownHandler),fe.on(document,Ii,Fi,Ki.dataApiKeydownHandler),fe.on(document,$i,Ki.clearMenus),fe.on(document,Ni,Ki.clearMenus),fe.on(document,$i,Mi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),Qt(Ki);const Qi="backdrop",Xi="show",Ui=`mousedown.bs.${Qi}`,Gi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ji={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Zi extends be{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Gi}static get DefaultType(){return Ji}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void Xt(t);this._append();const e=this._getElement();this._config.isAnimated&&qt(e),e.classList.add(Xi),this._emulateAnimation((()=>{Xt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),Xt(t)}))):Xt(t)}dispose(){this._isAppended&&(fe.off(this._element,Ui),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ht(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),fe.on(t,Ui,(()=>{Xt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Ut(t,this._getElement(),this._config.isAnimated)}}const tn=".bs.focustrap",en=`focusin${tn}`,nn=`keydown.tab${tn}`,sn="backward",on={autofocus:!0,trapElement:null},rn={autofocus:"boolean",trapElement:"element"};class an extends be{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return on}static get DefaultType(){return rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),fe.off(document,tn),fe.on(document,en,(t=>this._handleFocusin(t))),fe.on(document,nn,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,fe.off(document,tn))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=we.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===sn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?sn:"forward")}}const ln=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",cn=".sticky-top",hn="padding-right",dn="margin-right";class un{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,hn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e+t)),this._setElementAttributes(cn,dn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,hn),this._resetElementAttributes(ln,hn),this._resetElementAttributes(cn,dn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&_e.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=_e.getDataAttribute(t,e);null!==i?(_e.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(Ft(t))e(t);else for(const i of we.find(t,this._element))e(i)}}const fn=".bs.modal",pn=`hide${fn}`,mn=`hidePrevented${fn}`,gn=`hidden${fn}`,_n=`show${fn}`,bn=`shown${fn}`,vn=`resize${fn}`,yn=`click.dismiss${fn}`,wn=`mousedown.dismiss${fn}`,En=`keydown.dismiss${fn}`,An=`click${fn}.data-api`,Tn="modal-open",Cn="show",On="modal-static",xn={backdrop:!0,focus:!0,keyboard:!0},kn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ln extends ve{constructor(t,e){super(t,e),this._dialog=we.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new un,this._addEventListeners()}static get Default(){return xn}static get DefaultType(){return kn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||fe.trigger(this._element,_n,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Tn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(fe.trigger(this._element,pn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Cn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){fe.off(window,fn),fe.off(this._dialog,fn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new an({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=we.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),qt(this._element),this._element.classList.add(Cn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,fe.trigger(this._element,bn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){fe.on(this._element,En,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),fe.on(window,vn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),fe.on(this._element,wn,(t=>{fe.one(this._element,yn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Tn),this._resetAdjustments(),this._scrollBar.reset(),fe.trigger(this._element,gn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(fe.trigger(this._element,mn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(On)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(On),this._queueCallback((()=>{this._element.classList.remove(On),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Kt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Kt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}fe.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=we.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),fe.one(e,_n,(t=>{t.defaultPrevented||fe.one(e,gn,(()=>{Bt(this)&&this.focus()}))}));const i=we.findOne(".modal.show");i&&Ln.getInstance(i).hide(),Ln.getOrCreateInstance(e).toggle(this)})),Ee(Ln),Qt(Ln);const Sn=".bs.offcanvas",Dn=".data-api",$n=`load${Sn}${Dn}`,In="show",Nn="showing",Pn="hiding",Mn=".offcanvas.show",jn=`show${Sn}`,Fn=`shown${Sn}`,Hn=`hide${Sn}`,Bn=`hidePrevented${Sn}`,Wn=`hidden${Sn}`,zn=`resize${Sn}`,Rn=`click${Sn}${Dn}`,qn=`keydown.dismiss${Sn}`,Vn={backdrop:!0,keyboard:!0,scroll:!1},Yn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends ve{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Vn}static get DefaultType(){return Yn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||fe.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new un).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Nn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(In),this._element.classList.remove(Nn),fe.trigger(this._element,Fn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(fe.trigger(this._element,Hn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Pn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(In,Pn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new un).reset(),fe.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Zi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():fe.trigger(this._element,Bn)}:null})}_initializeFocusTrap(){return new an({trapElement:this._element})}_addEventListeners(){fe.on(this._element,qn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():fe.trigger(this._element,Bn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}fe.on(document,Rn,'[data-bs-toggle="offcanvas"]',(function(t){const e=we.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this))return;fe.one(e,Wn,(()=>{Bt(this)&&this.focus()}));const i=we.findOne(Mn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),fe.on(window,$n,(()=>{for(const t of we.find(Mn))Kn.getOrCreateInstance(t).show()})),fe.on(window,zn,(()=>{for(const t of we.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),Ee(Kn),Qt(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Un=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Gn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Un.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Jn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Zn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ts={entry:"(string|element|function|null)",selector:"(string|element)"};class es extends be{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Jn}static get DefaultType(){return Zn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},ts)}_setContent(t,e,i){const n=we.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?Ft(e)?this._putElementInTemplate(Ht(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Gn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Xt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const is=new Set(["sanitize","allowList","sanitizeFn"]),ns="fade",ss="show",os=".tooltip-inner",rs=".modal",as="hide.bs.modal",ls="hover",cs="focus",hs={AUTO:"auto",TOP:"top",RIGHT:Kt()?"left":"right",BOTTOM:"bottom",LEFT:Kt()?"right":"left"},ds={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},us={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class fs extends ve{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return ds}static get DefaultType(){return us}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),fe.off(this._element.closest(rs),as,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=fe.trigger(this._element,this.constructor.eventName("show")),e=(zt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),fe.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._queueCallback((()=>{fe.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!fe.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._activeTrigger.click=!1,this._activeTrigger[cs]=!1,this._activeTrigger[ls]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),fe.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ns,ss),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ns),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new es({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[os]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ns)}_isShown(){return this.tip&&this.tip.classList.contains(ss)}_createPopper(t){const e=Xt(this._config.placement,[this,t,this._element]),i=hs[e.toUpperCase()];return Dt(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Xt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Xt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)fe.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ls?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ls?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");fe.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?cs:ls]=!0,e._enter()})),fe.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?cs:ls]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},fe.on(this._element.closest(rs),as,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=_e.getDataAttributes(this._element);for(const t of Object.keys(e))is.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ht(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=fs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(fs);const ps=".popover-header",ms=".popover-body",gs={...fs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},_s={...fs.DefaultType,content:"(null|string|element|function)"};class bs extends fs{static get Default(){return gs}static get DefaultType(){return _s}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[ps]:this._getTitle(),[ms]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=bs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(bs);const vs=".bs.scrollspy",ys=`activate${vs}`,ws=`click${vs}`,Es=`load${vs}.data-api`,As="active",Ts="[href]",Cs=".nav-link",Os=`${Cs}, .nav-item > ${Cs}, .list-group-item`,xs={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},ks={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ls extends ve{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return xs}static get DefaultType(){return ks}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ht(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(fe.off(this._config.target,ws),fe.on(this._config.target,ws,Ts,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=we.find(Ts,this._config.target);for(const e of t){if(!e.hash||Wt(e))continue;const t=we.findOne(decodeURI(e.hash),this._element);Bt(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),fe.trigger(this._element,ys,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))we.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of we.parents(t,".nav, .list-group"))for(const t of we.prev(e,Os))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=we.find(`${Ts}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=Ls.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(window,Es,(()=>{for(const t of we.find('[data-bs-spy="scroll"]'))Ls.getOrCreateInstance(t)})),Qt(Ls);const Ss=".bs.tab",Ds=`hide${Ss}`,$s=`hidden${Ss}`,Is=`show${Ss}`,Ns=`shown${Ss}`,Ps=`click${Ss}`,Ms=`keydown${Ss}`,js=`load${Ss}`,Fs="ArrowLeft",Hs="ArrowRight",Bs="ArrowUp",Ws="ArrowDown",zs="Home",Rs="End",qs="active",Vs="fade",Ys="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Us=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Gs=`.${qs}[data-bs-toggle="tab"], .${qs}[data-bs-toggle="pill"], .${qs}[data-bs-toggle="list"]`;class Js extends ve{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),fe.on(this._element,Ms,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?fe.trigger(e,Ds,{relatedTarget:t}):null;fe.trigger(t,Is,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(qs),this._activate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),fe.trigger(t,Ns,{relatedTarget:e})):t.classList.add(Ys)}),t,t.classList.contains(Vs)))}_deactivate(t,e){t&&(t.classList.remove(qs),t.blur(),this._deactivate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),fe.trigger(t,$s,{relatedTarget:e})):t.classList.remove(Ys)}),t,t.classList.contains(Vs)))}_keydown(t){if(![Fs,Hs,Bs,Ws,zs,Rs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Wt(t)));let i;if([zs,Rs].includes(t.key))i=e[t.key===zs?0:e.length-1];else{const n=[Hs,Ws].includes(t.key);i=Gt(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Js.getOrCreateInstance(i).show())}_getChildren(){return we.find(Us,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=we.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=we.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,qs),n(".dropdown-menu",Ys),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(qs)}_getInnerElement(t){return t.matches(Us)?t:we.findOne(Us,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Js.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(document,Ps,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this)||Js.getOrCreateInstance(this).show()})),fe.on(window,js,(()=>{for(const t of we.find(Gs))Js.getOrCreateInstance(t)})),Qt(Js);const Zs=".bs.toast",to=`mouseover${Zs}`,eo=`mouseout${Zs}`,io=`focusin${Zs}`,no=`focusout${Zs}`,so=`hide${Zs}`,oo=`hidden${Zs}`,ro=`show${Zs}`,ao=`shown${Zs}`,lo="hide",co="show",ho="showing",uo={animation:"boolean",autohide:"boolean",delay:"number"},fo={animation:!0,autohide:!0,delay:5e3};class po extends ve{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return fo}static get DefaultType(){return uo}static get NAME(){return"toast"}show(){fe.trigger(this._element,ro).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(lo),qt(this._element),this._element.classList.add(co,ho),this._queueCallback((()=>{this._element.classList.remove(ho),fe.trigger(this._element,ao),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(fe.trigger(this._element,so).defaultPrevented||(this._element.classList.add(ho),this._queueCallback((()=>{this._element.classList.add(lo),this._element.classList.remove(ho,co),fe.trigger(this._element,oo)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(co),super.dispose()}isShown(){return this._element.classList.contains(co)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){fe.on(this._element,to,(t=>this._onInteraction(t,!0))),fe.on(this._element,eo,(t=>this._onInteraction(t,!1))),fe.on(this._element,io,(t=>this._onInteraction(t,!0))),fe.on(this._element,no,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=po.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function mo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}Ee(po),Qt(po),mo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new fs(t,{delay:{show:500,hide:100}})}))})),mo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),mo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))})),window.bootstrap=i})(); +//# sourceMappingURL=bootstrap.js.map diff --git a/docs/source/_build/_static/scripts/bootstrap.js.LICENSE.txt b/docs/source/_build/_static/scripts/bootstrap.js.LICENSE.txt new file mode 100644 index 00000000..28755c2c --- /dev/null +++ b/docs/source/_build/_static/scripts/bootstrap.js.LICENSE.txt @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/docs/source/_build/_static/scripts/bootstrap.js.map b/docs/source/_build/_static/scripts/bootstrap.js.map new file mode 100644 index 00000000..d090b244 --- /dev/null +++ b/docs/source/_build/_static/scripts/bootstrap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/bootstrap.js","mappings":"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","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.3';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n }\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both