-
Notifications
You must be signed in to change notification settings - Fork 991
Fix ggplot2 v4 compatibility #10225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix ggplot2 v4 compatibility #10225
Changes from 7 commits
8695a66
6355ba9
f1e7fac
aedb10a
ca3c0ee
1c3ce50
af7adc4
760a091
3b2614a
5b4e3e2
d1e9d46
3e4fa08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # ggplot2 v4.0.0 Known Issues in Seurat | ||
|
|
||
| ## Overview | ||
| ggplot2 version 4.0.0 (released September 2025) introduced breaking changes by migrating from S3 to S7 object system. This document tracks known compatibility issues and their status. | ||
|
|
||
| ## Fixed Issues | ||
|
|
||
| ### 1. DarkTheme() - Deprecated `size` parameter ✅ | ||
| **Status**: FIXED | ||
| **Files**: `R/visualization.R` (lines 6433-6445) | ||
| **Changes**: | ||
| - Replaced `element_rect(fill = 'black', size = 0)` with `linewidth = 0` | ||
| - Replaced `element_line(colour = 'white', size = 1)` with `linewidth = 1` | ||
| - Replaced `element_line(size = 0)` with `linewidth = 0` | ||
|
|
||
| ### 2. LabelClusters() - S7 object color retrieval ✅ | ||
| **Status**: FIXED | ||
| **Issues**: #10156, #10176 | ||
| **Files**: `R/visualization.R` (lines 6134-6227) | ||
| **Problem**: With S7 objects, `pb$data` returns numeric coordinates instead of valid colors | ||
| **Solution**: Extract colors directly from plot scales using `pb$plot$scales$get_scales()` | ||
| **Changes**: | ||
| - Added color scale extraction from plot scales | ||
| - Created group_colors mapping with validation | ||
| - Fixed data.medians$color assignment to use explicit group_colors mapping | ||
|
|
||
| ## Outstanding Issues (Require ggplot2 Package Fix) | ||
|
|
||
| ### 3. VlnPlot - S4SXP Error 🔴 | ||
| **Status**: NEEDS GGPLOT2 FIX | ||
| **Issues**: #10101, #10188, #10160 | ||
| **Files**: Not Seurat-specific code | ||
| **Error**: | ||
| ``` | ||
| Error in deparse(substitute(e2, env = caller_env(2))) : | ||
| 'S4SXP': should not happen - please report | ||
| ``` | ||
| **Root Cause**: Deep incompatibility between ggplot2 v4 S7 objects and R's `substitute()` function calls within ggplot2 internals | ||
| **Workaround**: Downgrade to ggplot2 3.5.2 | ||
| ```r | ||
| remotes::install_version("ggplot2", version = "3.5.2", repos = "https://cran.r-project.org") | ||
| ``` | ||
| **Note**: Some packages (e.g., clusterProfiler >= 4.12.0) require ggplot2 >= 4.0.0, creating dependency conflicts | ||
|
|
||
| ## Potential Issues (To Monitor) | ||
|
|
||
| ### 4. ggplot_build()$plot$data Access Pattern ⚠️ | ||
| **Status**: MONITORING | ||
| **Files**: `R/visualization.R` (lines 5948, 7795) | ||
| **Functions**: `GGpointToPlotly()`, `GGpointToPlotlyBuild()` | ||
| **Concern**: Direct `$plot$data` access may behave differently with S7 objects | ||
| **Impact**: Unknown - needs testing with real-world usage | ||
|
|
||
| ### 5. plot$layers Access Pattern ⚠️ | ||
| **Status**: MONITORING | ||
| **Files**: `R/visualization.R` (multiple locations) | ||
| **Concern**: `plot$layers[[i]]$data` and `plot$layers[[i]]$mapping` access may need S7 updates | ||
| **Impact**: Unknown - backward compatibility should handle this, but monitor for issues | ||
|
|
||
| ## Migration Notes | ||
|
|
||
| ### What Changed in ggplot2 v4 | ||
| - **S7 Object System**: ggplot objects now use S7 instead of S3 | ||
| - **Deprecated Parameters**: | ||
| - `size` → `linewidth` in `element_rect()` and `element_line()` | ||
| - Keep `size` in `element_text()` (NOT deprecated) | ||
| - **S7 Backward Compatibility**: `$data` and `$layers` access still works, but internal data structures changed | ||
| - **New Features**: | ||
| - Theme improvements (ink/paper/accent, palette.*, theme_sub_*) | ||
| - `geom_label()` new aesthetics (linewidth, border.colour, text.colour) | ||
|
|
||
| ### Testing Recommendations | ||
| 1. Test all visualization functions with ggplot2 v4 | ||
| 2. Verify label.box functionality in DimPlot variants | ||
| 3. Check VlnPlot alternatives if S4SXP persists | ||
| 4. Monitor Plotly integration functions for S7 issues | ||
|
|
||
| ### Dependencies | ||
| - **Minimum ggplot2 version**: 3.5.2 (for compatibility) | ||
| - **Target ggplot2 version**: 4.0.0+ (once S4SXP issue resolved upstream) | ||
|
|
||
| ## References | ||
| - ggplot2 v4 blog post: https://www.tidyverse.org/blog/2025/09/ggplot2-4-0-0/ | ||
| - Issue #10101: VlnPlot S4SXP error | ||
| - Issue #10156: DimPlot label.box color error | ||
| - Issue #10176: label.box returning numeric values | ||
| - Issue #10188: Temporary S4SXP solution thread | ||
|
|
||
| ## Last Updated | ||
| 2025-11-27 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -823,10 +823,26 @@ FindTransferAnchors <- function( | |
| features = features, | ||
| verbose = FALSE | ||
| )) | ||
| # Check if scale.data exists and has features | ||
| if (is.null(reference[[reference.assay]]$scale.data) || | ||
| nrow(reference[[reference.assay]]$scale.data) == 0) { | ||
| stop( | ||
| "No scale.data found in the ", reference.assay, " assay of the reference object. ", | ||
| "Please run ScaleData() on the reference object before using FindTransferAnchors with SCT normalization.", | ||
|
BenjaminDEMAILLE marked this conversation as resolved.
Outdated
|
||
| call. = FALSE | ||
| ) | ||
| } | ||
| features <- intersect( | ||
| x = features, | ||
| y = rownames(reference[[reference.assay]]$scale.data) | ||
| ) | ||
| if (length(features) == 0) { | ||
| stop( | ||
| "No features remaining after intersecting with scale.data in ", reference.assay, " assay. ", | ||
| "Please ensure ScaleData() has been run on the reference object with appropriate features.", | ||
|
BenjaminDEMAILLE marked this conversation as resolved.
Outdated
|
||
| call. = FALSE | ||
| ) | ||
| } | ||
| VariableFeatures(reference) <- features | ||
| } | ||
| if (IsSCT(assay = query[[query.assay]])) { | ||
|
|
@@ -3617,7 +3633,31 @@ TransferData <- function( | |
| prediction.scores <- (prediction.scores + bridge.prediction.scores)/2 | ||
| prediction.scores <- as.matrix(x = prediction.scores) | ||
| } | ||
| prediction.ids <- possible.ids[apply(X = prediction.scores, MARGIN = 1, FUN = which.max)] | ||
|
|
||
| # Check for NaN values in prediction scores and handle them | ||
| if (any(is.nan(prediction.scores))) { | ||
| warning( | ||
| "NaN values detected in prediction scores. This may indicate issues with ", | ||
| "normalization, k.weight parameter, or data quality. NaN scores will be replaced with 0.", | ||
| call. = FALSE, | ||
| immediate. = TRUE | ||
| ) | ||
| prediction.scores[is.nan(prediction.scores)] <- 0 | ||
| } | ||
|
|
||
| # Use a safer version of which.max that handles edge cases | ||
| safe_which_max <- function(x) { | ||
| if (all(is.na(x)) || all(x == 0)) { | ||
| return(1L) # Default to first class if all are NA or 0 | ||
|
BenjaminDEMAILLE marked this conversation as resolved.
Outdated
|
||
| } | ||
| result <- which.max(x) | ||
| if (length(result) == 0) { | ||
| return(1L) # Default to first class if which.max returns empty | ||
| } | ||
| return(result) | ||
|
Comment on lines
+3652
to
+3660
|
||
| } | ||
|
|
||
| prediction.ids <- possible.ids[apply(X = prediction.scores, MARGIN = 1, FUN = safe_which_max)] | ||
| prediction.ids <- as.character(prediction.ids) | ||
| prediction.max <- apply(X = prediction.scores, MARGIN = 1, FUN = max) | ||
| if (is.null(x = query)) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.