diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5f40909..45d2641 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,7 +8,7 @@ jobs: strategy: max-parallel: 1 matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 59eac38..2ca09a5 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ sd.generate_report( ## 🛠 Installation -Eurybia is intended to work with Python versions 3.7 to 3.10. Installation can be done with pip: +Eurybia is intended to work with Python versions 3.8 to 3.10. Installation can be done with pip: ``` pip install eurybia diff --git a/docs/source/installation-instructions/index.rst b/docs/source/installation-instructions/index.rst index e90b11c..1373342 100644 --- a/docs/source/installation-instructions/index.rst +++ b/docs/source/installation-instructions/index.rst @@ -4,7 +4,7 @@ Installation instructions Installing ---------- -**Eurybia** is intended to work with Python versions 3.7 to 3.9. Installation can be done with pip: +**Eurybia** is intended to work with Python versions 3.8 to 3.10. Installation can be done with pip: .. code:: bash diff --git a/eurybia/assets/report_template.html b/eurybia/assets/report_template.html deleted file mode 100644 index 35cccee..0000000 --- a/eurybia/assets/report_template.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - -
- - -
- -
- - diff --git a/eurybia/core/smartdrift.py b/eurybia/core/smartdrift.py index c465669..ec21f75 100644 --- a/eurybia/core/smartdrift.py +++ b/eurybia/core/smartdrift.py @@ -382,7 +382,7 @@ def generate_report( project_info_file=project_info_file, explainer=self.xpl, smartdrift=self, - config=dict(title_story=title_story, title_description=title_description), + config_report=dict(title_story=title_story, title_description=title_description), output_file=output_file, ) finally: diff --git a/eurybia/report/generation.py b/eurybia/report/generation.py index 374eb6d..cdecd0c 100644 --- a/eurybia/report/generation.py +++ b/eurybia/report/generation.py @@ -1,43 +1,18 @@ """ Report generation helper module. """ -from base64 import b64encode from datetime import datetime from typing import Optional import datapane as dp -import importlib_resources as ir import pandas as pd -from jinja2 import Environment, FileSystemLoader from shapash.explainer.smart_explainer import SmartExplainer from eurybia import SmartDrift from eurybia.report.project_report import DriftReport -def _load_custom_template(report: dp.Report) -> dp.Report: - """ - This function feeds a customised html template to Datapane - - Parameters - ---------- - report : datapane.Report - Report object - Returns - ---------- - datapane.Report - """ - report._local_writer.assets = ir.files("eurybia.assets") - logo_img = (report._local_writer.assets / "logo_eurybia_dp.png").read_bytes() - report._local_writer.logo = f"data:image/png;base64,{b64encode(logo_img).decode('ascii')}" - template_loader = FileSystemLoader(report._local_writer.assets) - template_env = Environment(loader=template_loader) - template_env.globals["include_raw"] = dp.client.api.report.core.include_raw - report._local_writer.template = template_env.get_template("report_template.html") - return report - - -def _get_index(dr: DriftReport, project_info_file: str, config: Optional[dict]) -> dp.Page: +def _get_index(dr: DriftReport, project_info_file: str, config_report: Optional[dict]) -> dp.Page: """ This function generates and returns a Datapane page containing the Eurybia report index @@ -47,7 +22,7 @@ def _get_index(dr: DriftReport, project_info_file: str, config: Optional[dict]) DriftReport object project_info_file : str Path to the file used to display some information about the project in the report. - config : dict, optional + config_report : dict, optional Report configuration options. Returns ---------- @@ -66,8 +41,12 @@ def _get_index(dr: DriftReport, project_info_file: str, config: Optional[dict]) # Title and logo index_block += [dp.Group(dp.HTML(eurybia_logo), dp.Text(f"# {dr.title_story}"), columns=2)] - if config is not None and "title_description" in config.keys() and config["title_description"] != "": - raw_title = config["title_description"] + if ( + config_report is not None + and "title_description" in config_report.keys() + and config_report["title_description"] != "" + ): + raw_title = config_report["title_description"] index_block += [dp.Text(f"## {raw_title}")] index_str = "## Eurybia Report contents \n" @@ -317,8 +296,8 @@ def _get_datadrift(dr: DriftReport) -> dp.Page: Features are sorted according to their respective importance in the datadrift classifier. For categorical features, the possible values are sorted by descending difference between the two datasets.""" ), - dp.Select(blocks=plot_dataset_analysis), - dp.Select(blocks=table_dataset_analysis), + dp.Select(blocks=plot_dataset_analysis, type=dp.SelectType.DROPDOWN), + dp.Select(blocks=table_dataset_analysis, type=dp.SelectType.DROPDOWN), ] if dr.smartdrift.deployed_model is not None: blocks += [ @@ -350,7 +329,7 @@ def _get_datadrift(dr: DriftReport) -> dp.Page: This representation constitutes a support to understand the drift when the analysis of the dataset is unclear. In the drop-down menu, features are sorted by importance in the data drift detection.""" ), - dp.Select(blocks=plot_datadrift_contribution), + dp.Select(blocks=plot_datadrift_contribution, type=dp.SelectType.DROPDOWN), ] if dr.smartdrift.historical_auc is not None: blocks += [ @@ -387,7 +366,7 @@ def _get_modeldrift(dr: DriftReport) -> dp.Page: else: for i in range(len(labels)): plot_modeldrift.append(dp.Plot(fig_list[i], label=labels[i])) - modeldrift_plot = dp.Select(blocks=plot_modeldrift, label="reference_columns") + modeldrift_plot = dp.Select(blocks=plot_modeldrift, label="reference_columns", type=dp.SelectType.DROPDOWN) else: modeldrift_plot = dp.Text("## Smartdrift.data_modeldrift is None") blocks = [ @@ -409,7 +388,7 @@ def execute_report( explainer: SmartExplainer, project_info_file: str, output_file: str, - config: Optional[dict] = None, + config_report: Optional[dict] = None, ): """ Creates the report @@ -422,24 +401,24 @@ def execute_report( Compiled shapash explainer. project_info_file : str Path to the file used to display some information about the project in the report. - config : dict, optional + config_report : dict, optional Report configuration options. output_file : str Path to the HTML file to write """ - if config is None: - config = {} + if config_report is None: + config_report = {} dr = DriftReport( smartdrift=smartdrift, explainer=explainer, # rename to match kwarg project_info_file=project_info_file, - config=config, + config_report=config_report, ) pages = [] - pages.append(_get_index(dr, project_info_file, config)) + pages.append(_get_index(dr, project_info_file, config_report)) if project_info_file is not None: pages.append(_get_project_info(dr)) pages.append(_get_consistency_analysis(dr)) @@ -447,8 +426,5 @@ def execute_report( if dr.smartdrift.data_modeldrift is not None: pages.append(_get_modeldrift(dr)) - report = dp.Report(blocks=pages) - report = _load_custom_template(report) - report._save( - path=output_file, open=False, formatting=dp.ReportFormatting(light_prose=False, width=dp.ReportWidth.MEDIUM) - ) + report = dp.View(blocks=pages) + dp.save_report(report, path=output_file, open=False, name="report.html") diff --git a/eurybia/report/project_report.py b/eurybia/report/project_report.py index eaeb081..d1b9780 100644 --- a/eurybia/report/project_report.py +++ b/eurybia/report/project_report.py @@ -46,7 +46,7 @@ class DriftReport: Dataframe of predicted values computed on both df_baseline and df_current feature_importance : pd.DataFrame, optional (default: None) Dataframe of feature importance from production model and drift model - config : dict, optional + config_report : dict, optional Configuration options for the report """ @@ -55,7 +55,7 @@ def __init__( smartdrift: SmartDrift, explainer: SmartExplainer, project_info_file: Optional[str] = None, - config: Optional[Dict] = None, + config_report: Optional[Dict] = None, ): """ Parameters @@ -66,7 +66,7 @@ def __init__( A shapash SmartExplainer object that has already be compiled project_info_file : str Path to the yml file containing information about the project (author, description, ...) - config : dict, optional + config_report : dict, optional Contains configuration options for the report features_imp_list : list list of features order by importance @@ -79,7 +79,7 @@ def __init__( if self.explainer.features_imp is None: self.explainer.compute_features_import(force=True) self.features_imp_list = self.explainer.features_imp[0].sort_values(ascending=False).index.to_list() # type: ignore - self.config = config if config is not None else dict() + self.config_report = config_report if config_report is not None else dict() self.data_concat = self._create_data_drift( df_current=self.smartdrift.df_current, @@ -92,8 +92,8 @@ def __init__( else: self.metadata = load_yml(path=project_info_file) - if "title_story" in self.config.keys(): - self.title_story = self.config["title_story"] + if "title_story" in self.config_report.keys(): + self.title_story = self.config_report["title_story"] else: self.title_story = "Eurybia report" diff --git a/requirements.dev.txt b/requirements.dev.txt index 6f54e91..108db69 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,9 +1,9 @@ pip>=21.1 -catboost>=0.22 -category-encoders==2.1.0 -lightgbm==2.3.1 -numpy>1.18.0 -pandas>1.0.2 +catboost>=1.0.1 +category-encoders>=2.6.0 +lightgbm>=2.3.1 +numpy>=1.18.0 +pandas>=1.0.2 plotly>=4.12.0 shapash>=2.0.0 Sphinx==4.5.0 @@ -17,17 +17,16 @@ nbsphinx==0.8.8 sphinx_material==0.0.35 pytest>=5.2.3 pytest-cov==2.8.1 -scikit-learn>=0.24.2 +scikit-learn>=1.0.1 xgboost>=1.0.0 nbformat>4.2.0 numba>=0.53.0 nbconvert>=6.3 papermill>=2.0.0 matplotlib>=3.3.0 -seaborn==0.11.1 +seaborn>=0.12.2 notebook>=6.0.0 Jinja2>=2.11.0 scipy>=1.1.0 -types-PyYAML==6.0.5 -datapane==0.14.0 +datapane>=0.16.7 pre-commit==2.18.1 diff --git a/setup.py b/setup.py index 2bbb8e6..b142d0a 100755 --- a/setup.py +++ b/setup.py @@ -16,8 +16,8 @@ exec(f.read(), version_d) requirements = [ - "catboost>=0.22", - "datapane==0.14.0", + "catboost>=1.0.1", + "datapane>=0.16.7", "ipywidgets>=7.4.2", "jinja2>=2.11.0", "scipy>=1.4.0", @@ -38,7 +38,7 @@ setup( name="eurybia", # Replace with your own username version=version_d["__version__"], - python_requires=">3.6, < 3.11", + python_requires=">3.7, < 3.11", url="https://github.com/MAIF/eurybia", author="Nicolas Roux, Johann Martin, Thomas Bouché", author_email="thomas.bouche@maif.fr", @@ -47,7 +47,6 @@ long_description_content_type="text/markdown", classifiers=[ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -84,7 +83,6 @@ "eurybia/assets/local-report-base.css", "eurybia/assets/local-report-base.js", "eurybia/assets/logo_eurybia_dp.png", - "eurybia/assets/report_template.html", ], ), ], diff --git a/tests/integration_tests/test_report_generation.py b/tests/integration_tests/test_report_generation.py index 1837632..d463c7f 100644 --- a/tests/integration_tests/test_report_generation.py +++ b/tests/integration_tests/test_report_generation.py @@ -9,6 +9,7 @@ import pandas as pd from category_encoders import OrdinalEncoder +from datapane.client import config from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split @@ -27,6 +28,7 @@ def setUp(self): """ Initialize data for testing part """ + config.init() script_path = Path(path.abspath(__file__)).parent.parent.parent titanic_original = path.join(script_path, "eurybia/data/titanicdata.csv") titan_df = pd.read_csv(titanic_original, index_col=0) @@ -58,7 +60,6 @@ def test_execute_report_1(self): """ Test execute_report() method """ - execute_report( smartdrift=self.smartdrift, explainer=self.xpl, @@ -91,7 +92,7 @@ def test_execute_report_3(self): explainer=self.xpl, project_info_file=os.path.join(current_path, "../data/project_info.yml"), output_file="./report.html", - config=dict(title_story="Test integration", title_description="Title of test integration"), + config_report=dict(title_story="Test integration", title_description="Title of test integration"), ) assert os.path.exists("./report.html") @@ -107,7 +108,7 @@ def test_execute_report_modeldrift_1(self): explainer=self.xpl, project_info_file=os.path.join(current_path, "../data/project_info.yml"), output_file="./report.html", - config=dict(title_story="Test integration", title_description="Title of test integration"), + config_report=dict(title_story="Test integration", title_description="Title of test integration"), ) assert os.path.exists("./report.html") @@ -133,7 +134,7 @@ def test_execute_report_modeldrift_2(self): explainer=self.xpl, project_info_file=os.path.join(current_path, "../data/project_info.yml"), output_file="./report.html", - config=dict(title_story="Test integration", title_description="Title of test integration"), + config_report=dict(title_story="Test integration", title_description="Title of test integration"), ) assert os.path.exists("./report.html") diff --git a/tests/unit_tests/report/test_project_report.py b/tests/unit_tests/report/test_project_report.py index 255178b..ecddd96 100644 --- a/tests/unit_tests/report/test_project_report.py +++ b/tests/unit_tests/report/test_project_report.py @@ -16,7 +16,7 @@ from eurybia import SmartDrift from eurybia.report.project_report import DriftReport -expected_attrs = ["smartdrift", "explainer", "config"] +expected_attrs = ["smartdrift", "explainer", "config_report"] current_path = os.path.dirname(os.path.abspath(__file__)) @@ -67,7 +67,7 @@ def setUp(self): smartdrift=smartdrift, explainer=smartdrift.xpl, project_info_file=self.project_info_file, - config=dict(title_story="Drift Report", title_description="Test drift report"), + config_report=dict(title_story="Drift Report", title_description="Test drift report"), ) def test_init_1(self): @@ -143,7 +143,7 @@ def test_display_data_modeldrift_2(self): report = DriftReport( smartdrift=smartdrift, explainer=smartdrift.xpl, - config=dict(title_story="Drift Report", title_description="Test drift report"), + config_report=dict(title_story="Drift Report", title_description="Test drift report"), ) fig = report.display_data_modeldrift() assert isinstance(fig[0][0], plotly.graph_objs._figure.Figure)