From 59f9d0d3dc0ec1a1f809d9fd54acc9c24390d1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20M=C3=BCller?= Date: Fri, 13 Sep 2024 16:37:52 +0200 Subject: [PATCH] setup: bump dclab to 0.61.3 (replace rpy2 with wrapper) (close #73, close #161) --- .github/workflows/check.yml | 6 ++++++ .readthedocs.yml | 2 -- CHANGELOG | 2 ++ docs/requirements.txt | 2 -- docs/sec_qg_lme4.rst | 16 +++------------- pyproject.toml | 2 +- shapeout2/gui/compute/comp_lme4_results.py | 7 +++++++ shapeout2/gui/main.py | 7 +------ shapeout2/gui/preferences.py | 15 ++++----------- tests/test_gui_compute_lme4.py | 5 ++++- 10 files changed, 28 insertions(+), 36 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 192782ee..d613a167 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -35,6 +35,12 @@ jobs: uses: r-lib/actions/setup-r@v2 with: r-version: '4.1.2' + - name: Install R packages for lme4 + if: matrix.extras == 'all' + shell: bash + run: | + R --version + R -e "install.packages(c('statmod','nloptr','lme4'),repos='http://cran.rstudio.org')" - name: Install Python dependencies run: | # prerequisites diff --git a/.readthedocs.yml b/.readthedocs.yml index ec96c466..3a2af007 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,8 +8,6 @@ build: jobs: post_checkout: - git fetch --unshallow || true - pre_install: - - RPY2_CFFI_MODE=ABI python: install: - requirements: docs/requirements.txt diff --git a/CHANGELOG b/CHANGELOG index 133267b0..5801bf82 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +2.18.1 + - setup: bump dclab to 0.61.3 (replace rpy2 with wrapper) (#73 #161) 2.18.0 - BREAKING CHANGE: set default viscosity model for Young's modulus computation to "buyukurganci-2022", because it is more accurate diff --git a/docs/requirements.txt b/docs/requirements.txt index 769b7a10..5243c88f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,8 +2,6 @@ dclab>=0.58.2 h5py matplotlib numpy -# Don't forget to set the environment variable RPY2_CFFI_MODE=ABI on rtd! -rpy2 pyqt5 scipy sphinx diff --git a/docs/sec_qg_lme4.rst b/docs/sec_qg_lme4.rst index aa28d867..33f76c1e 100644 --- a/docs/sec_qg_lme4.rst +++ b/docs/sec_qg_lme4.rst @@ -6,25 +6,15 @@ Statistical significance testing (R and lme4) Since version 2.5.0, Shape-Out 2 allows you to quantify the statistical significance (p-value) when comparing two measurement groups. For this to work, you need to have R installed (tested with -`R 3.6.3 `_ ). +`R 4.1.2 `_ ). The analysis uses linear mixed effects models (LMM) as described in :cite:`Herbig2018` and yields results identical to those obtained with Shape-Out 1. -.. warning:: - Unfortunately, LMM analysis currently does not work with the .app or - .pkg bundle on macOS or the Windows installer. This problem is tracked in - `issue #73 - `_ and - `issue #161 - `_. - If you are using macOS and you would like to use LMM analysis, you have to - install Shape-Out 2 as a Python package (see :ref:`sec_installation`). - The LMM analysis is performed using the lme4 R package and is based on the :ref:`implementation in dclab ` -(please read for more information on the options available) which uses -`rpy2 `_ to connect to R. +(please read for more information on the options available) which implements +a wrapper calling ``Rscript``. Here, we make use of the dataset :cite:`FigshareLMM` to illustrate this functionality. diff --git a/pyproject.toml b/pyproject.toml index dfd0a365..7a0898b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ classifiers = [ license = {text = "GPL v3"} dependencies = [ "fcswrite>=0.5.1", - "dclab[dcor,http]>=0.60.1", + "dclab[dcor,http]>=0.61.3", "h5py>=2.8.0", "numpy>=1.21", # CVE-2021-33430 "pyqt5", diff --git a/shapeout2/gui/compute/comp_lme4_results.py b/shapeout2/gui/compute/comp_lme4_results.py index c31d07d0..911af203 100644 --- a/shapeout2/gui/compute/comp_lme4_results.py +++ b/shapeout2/gui/compute/comp_lme4_results.py @@ -38,13 +38,20 @@ def __init__(self, parent, rlme4_results, *args, **kwargs): # summary text summary = [] + if res["lme4 messages"]: + summary += ["lme4 messages"] + summary += ["-------------"] + summary += str(res["lme4 messages"]).split("\n") + summary += "\n" summary += ["Model summary"] summary += ["-------------"] summary += self.parse_r_model_summary( str(res["r model summary"]).split("\n")) + summary += "\n" summary += ["Coefficient table"] summary += ["-----------------"] summary += str(res["r model coefficients"]).split("\n") + summary += "\n" summary += ["Anova test"] summary += ["----------"] summary += str(res["r anova"]).split("\n") diff --git a/shapeout2/gui/main.py b/shapeout2/gui/main.py index a4cfd3e3..98a0c502 100644 --- a/shapeout2/gui/main.py +++ b/shapeout2/gui/main.py @@ -7,7 +7,6 @@ import webbrowser import dclab -from dclab.lme4.rlibs import rpy2, MockRPackage from dclab.lme4 import rsetup import h5py import numpy @@ -126,8 +125,6 @@ def __init__(self, *arguments): self.on_action_compute_statistics) self.actionComputeSignificance.triggered.connect( self.on_action_compute_significance) - self.actionComputeSignificance.setVisible( - not isinstance(rpy2, MockRPackage)) # only show if rpy2 is there # Export menu # data self.actionExportData.triggered.connect(self.on_action_export_data) @@ -607,7 +604,7 @@ def on_action_compute_significance(self): else: QtWidgets.QMessageBox.critical( self, "R not found!", - "The R executable was not found by rpy2. Please add it " + "The R executable was not found. Please add it " + "to the PATH variable or define it manually in the " + "Shape-Out preferences.") @@ -837,8 +834,6 @@ def on_action_software(self): pg, scipy, ] - if not isinstance(rpy2, MockRPackage): - libs.append(rpy2) sw_text = f"Shape-Out {version}\n\n" sw_text += f"Python {sys.version}\n\n" diff --git a/shapeout2/gui/preferences.py b/shapeout2/gui/preferences.py index d0458883..d5ec30d2 100644 --- a/shapeout2/gui/preferences.py +++ b/shapeout2/gui/preferences.py @@ -5,23 +5,16 @@ import pkg_resources import platform -from dclab.lme4.rlibs import ( - rpy2, MockRPackage, RPY2UnavailableError, RUnavailableError) from dclab.rtdc_dataset.fmt_dcor import access_token from dclab.lme4 import rsetup from PyQt5 import uic, QtCore, QtWidgets from PyQt5.QtCore import QStandardPaths +from dclab.lme4.rsetup import RNotFoundError from .widgets import show_wait_cursor from ..extensions import ExtensionManager, SUPPORTED_FORMATS -if isinstance(rpy2, MockRPackage): - RPY2_AVAILABLE = not isinstance(rpy2.exception, RPY2UnavailableError) -else: - RPY2_AVAILABLE = True - - class ExtensionErrorWrapper: def __init__(self, ehash): self.ehash = ehash @@ -55,13 +48,13 @@ def __init__(self, parent, *args, **kwargs): self.parent = parent # Get default R path - if RPY2_AVAILABLE and rsetup.has_r(): + if rsetup.has_r(): rdefault = rsetup.get_r_path() else: rdefault = "" # disable R settings - self.tab_r.setEnabled(RPY2_AVAILABLE) + self.tab_r.setEnabled(rsetup.has_r()) #: configuration keys, corresponding widgets, and defaults self.config_pairs = [ @@ -160,7 +153,7 @@ def reload_lme4(self, install=False): if pathlib.Path(binary).is_file(): try: rsetup.set_r_path(binary) - except RUnavailableError as exc: + except RNotFoundError as exc: QtWidgets.QMessageBox.information( self, "No compatible R version found", diff --git a/tests/test_gui_compute_lme4.py b/tests/test_gui_compute_lme4.py index 328f72d0..6478dd02 100644 --- a/tests/test_gui_compute_lme4.py +++ b/tests/test_gui_compute_lme4.py @@ -2,12 +2,15 @@ import pathlib import socket +from dclab.lme4 import Rlme4, bootstrapped_median_distributions, rsetup from shapeout2.gui.main import ShapeOut2 from shapeout2 import session from shapeout2.gui.compute.comp_lme4 import ComputeSignificance import pytest -pytest.importorskip("rpy2") + +if not (rsetup.has_r() and rsetup.has_lme4()): + pytest.skip(allow_module_level=True) data_path = pathlib.Path(__file__).parent / "data" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: