@@ -238,6 +238,7 @@ postprocessFinemappingFits <- function(fits, dataX, dataY = NULL,
238238 region = NULL ,
239239 priorEffTol = 1e-9 ,
240240 minAbsCorr = 0.8 ,
241+ medianAbsCorr = NULL ,
241242 csInput = NULL ) {
242243 fits <- fits [! vapply(fits , is.null , logical (1 ))]
243244 if (length(fits ) == 0 ) stop(" At least one fine-mapping fit must be supplied." )
@@ -257,6 +258,7 @@ postprocessFinemappingFits <- function(fits, dataX, dataY = NULL,
257258 signalCutoff = signalCutoff , otherQuantities = otherQuantities ,
258259 region = region ,
259260 priorEffTol = priorEffTol , minAbsCorr = minAbsCorr ,
261+ medianAbsCorr = medianAbsCorr ,
260262 csInput = csInput
261263 )
262264 })
@@ -324,6 +326,7 @@ postprocessFinemappingFit.susiF <- function(fit, method = "fsusie", csInput = NU
324326 region = NULL ,
325327 priorEffTol = 1e-9 ,
326328 minAbsCorr = 0.8 ,
329+ medianAbsCorr = NULL ,
327330 csInput = c(" X" , " Xcorr" , " fsusie" )) {
328331 csInput <- match.arg(csInput )
329332 variantNames <- extractVariantNames(fit )
@@ -332,7 +335,7 @@ postprocessFinemappingFit.susiF <- function(fit, method = "fsusie", csInput = NU
332335 csTables <- computeCsTables(
333336 fit , dataX = dataX , coverage = coverage ,
334337 secondaryCoverage = secondaryCoverage , method = method ,
335- csInput = csInput , minAbsCorr = minAbsCorr
338+ csInput = csInput , minAbsCorr = minAbsCorr , medianAbsCorr = medianAbsCorr
336339 )
337340 topLoci <- buildTopLoci(
338341 fit , csTables , variantNames = variantNames , sumstats = sumstats ,
@@ -432,7 +435,7 @@ selectEffects <- function(fit, priorEffTol = 1e-9) {
432435computeCsTables <- function (fit , dataX , coverage = NULL ,
433436 secondaryCoverage = c(0.7 , 0.5 ),
434437 method = " susie" , csInput = c(" X" , " Xcorr" , " fsusie" ),
435- minAbsCorr = 0.8 ) {
438+ minAbsCorr = 0.8 , medianAbsCorr = NULL ) {
436439 csInput <- match.arg(csInput )
437440 primaryCoverage <- coverage
438441 if (is.null(primaryCoverage )) primaryCoverage <- fit $ sets $ requested_coverage
@@ -441,15 +444,16 @@ computeCsTables <- function(fit, dataX, coverage = NULL,
441444 coverages <- coverages [! is.na(coverages )]
442445
443446 tables <- lapply(coverages , function (cov ) {
444- computeCsTable(fit , dataX , coverage = cov , csInput = csInput , minAbsCorr = minAbsCorr )
447+ computeCsTable(fit , dataX , coverage = cov , csInput = csInput ,
448+ minAbsCorr = minAbsCorr , medianAbsCorr = medianAbsCorr )
445449 })
446450 names(tables ) <- vapply(coverages , formatCsColumn , character (1 ), method = method )
447451 attr(tables , " coverage" ) <- coverages
448452 tables
449453}
450454
451455computeCsTable <- function (fit , dataX , coverage , csInput = c(" X" , " Xcorr" , " fsusie" ),
452- minAbsCorr = 0.8 ) {
456+ minAbsCorr = 0.8 , medianAbsCorr = NULL ) {
453457 csInput <- match.arg(csInput )
454458 if (csInput == " fsusie" ) {
455459 sets <- tryCatch(
@@ -470,12 +474,18 @@ computeCsTable <- function(fit, dataX, coverage, csInput = c("X", "Xcorr", "fsus
470474 return (list (sets = sets , cs_corr = csCorr , pip = fit $ pip ))
471475 }
472476
477+ # Purity thresholds for credible-set extraction. min_abs_corr / median_abs_corr
478+ # are isolated from finemappingOpts upstream and routed here; pass each only
479+ # when set. `fit` is passed positionally as `res`.
480+ csArgs <- list (coverage = coverage )
481+ if (! is.null(minAbsCorr )) csArgs $ min_abs_corr <- minAbsCorr
482+ if (! is.null(medianAbsCorr )) csArgs $ median_abs_corr <- medianAbsCorr
473483 if (csInput == " X" ) {
474- sets <- susie_get_cs( fit , X = dataX , coverage = coverage , min_abs_corr = minAbsCorr )
484+ sets <- do.call( susie_get_cs , c( list ( fit ), csArgs , list ( X = dataX )) )
475485 out <- list (sets = sets , pip = fit $ pip )
476486 out $ cs_corr <- get_cs_correlation(out , X = dataX )
477487 } else {
478- sets <- susie_get_cs( fit , Xcorr = dataX , coverage = coverage , min_abs_corr = minAbsCorr )
488+ sets <- do.call( susie_get_cs , c( list ( fit ), csArgs , list ( Xcorr = dataX )) )
479489 out <- list (sets = sets , pip = fit $ pip )
480490 out $ cs_corr <- get_cs_correlation(out , Xcorr = dataX )
481491 }
@@ -896,8 +906,13 @@ adjustSusieWeights <- function(twasWeightsResults, keepVariants, runAlleleQc = T
896906# ' @param ldMat LD correlation matrix. Mutually exclusive with xMat.
897907# ' @param xMat Genotype matrix (samples x variants). Mutually exclusive with ldMat.
898908# ' @param n Sample size.
899- # ' @param L Maximum number of causal configurations (default: 30).
900- # ' @param lGreedy Initial greedy number of causal configurations (default: 5).
909+ # ' @param finemappingOpts Free-form list of fine-mapping options forwarded as-is
910+ # ' into \code{susieR::susie_rss()} (e.g. \code{L}, \code{L_greedy},
911+ # ' \code{R_finite}, \code{R_mismatch}). A key supplied is passed through; a key
912+ # ' omitted inherits \code{susie_rss}'s own default. Two purity keys are special:
913+ # ' \code{min_abs_corr} (default \code{0.8}) and \code{median_abs_corr} (default
914+ # ' \code{NULL}) are isolated from this list and routed to
915+ # ' \code{susieR::susie_get_cs()} for credible-set extraction, not the fit.
901916# ' @param analysisMethod Iteration mode for the \code{"susie_rss"} fit:
902917# ' \code{"susie_rss"} (default, normal IBSS), \code{"single_effect"} (L=1,
903918# ' single iteration), or \code{"bayesian_conditional_regression"}
@@ -919,12 +934,6 @@ adjustSusieWeights <- function(twasWeightsResults, keepVariants, runAlleleQc = T
919934# ' @param coverage Coverage level (default: 0.95).
920935# ' @param secondaryCoverage Secondary coverage levels (default: c(0.7, 0.5)).
921936# ' @param signalCutoff PIP cutoff for selecting top loci (default: 0.1).
922- # ' @param minAbsCorr Minimum absolute correlation for CS purity (default: 0.8).
923- # ' @param rFinite Controls variance inflation to account for estimating
924- # ' the R matrix from a finite reference panel. NULL (default): no
925- # ' variance inflation. Passed directly to susie_rss.
926- # ' @param rMismatch LD mismatch correction method passed directly to susie_rss.
927- # ' Default NULL disables mismatch correction.
928937# ' @param ... Additional parameters passed to susie_rss. Supplying
929938# ' \code{var_y} here, together with \code{beta} and \code{se} columns in
930939# ' \code{sumstats}, selects the \code{bhat/shat/var_y} sufficient-statistic
@@ -940,19 +949,24 @@ adjustSusieWeights <- function(twasWeightsResults, keepVariants, runAlleleQc = T
940949# ' @importFrom dplyr arrange select
941950# ' @export
942951susieRssPipeline <- function (sumstats , ldMat = NULL , xMat = NULL , n = NULL ,
943- L = 30 , lGreedy = 5 ,
944952 analysisMethod = c(" susie_rss" , " single_effect" , " bayesian_conditional_regression" ),
945953 methods = NULL ,
946954 addSusieInf = TRUE ,
947955 coverage = 0.95 ,
948956 secondaryCoverage = c(0.7 , 0.5 ),
949957 signalCutoff = 0.1 ,
950- minAbsCorr = 0.8 ,
951- rFinite = NULL , rMismatch = NULL , ... ) {
958+ finemappingOpts = list (), ... ) {
952959 analysisMethod <- match.arg(analysisMethod )
953960 if (is.null(ldMat ) && is.null(xMat )) stop(" Either ldMat or xMat must be provided." )
954961 if (! is.null(ldMat ) && ! is.null(xMat )) stop(" Only one of ldMat or xMat should be provided, not both." )
955- if (! is.null(lGreedy )) lGreedy <- min(lGreedy , L )
962+ # Fine-mapping options are a free-form passthrough to susie_rss. Isolate the
963+ # two credible-set purity options (they act at susie_get_cs extraction, not the
964+ # fit) and route them separately; everything else flows into the susie_rss call.
965+ if (! is.list(finemappingOpts )) stop(" finemappingOpts must be a list." )
966+ minAbsCorr <- finemappingOpts $ min_abs_corr %|| % 0.8
967+ medianAbsCorr <- finemappingOpts $ median_abs_corr # NULL when absent (susie_get_cs default)
968+ finemappingOpts $ min_abs_corr <- NULL
969+ finemappingOpts $ median_abs_corr <- NULL
956970
957971 # Resolve effective methods. NULL => legacy single-method via analysisMethod.
958972 validRssMethods <- c(" susie_rss" , " susie_inf_rss" , " susie_ash_rss" )
@@ -990,10 +1004,12 @@ susieRssPipeline <- function(sumstats, ldMat = NULL, xMat = NULL, n = NULL,
9901004 names(z ) <- rownames(sumstats )
9911005 }
9921006
993- dots <- list (... )
994- varY <- dots $ varY
995- dots $ varY <- NULL
996- if (! is.null(dots $ bhat ) || ! is.null(dots $ shat )) {
1007+ # Free-form passthrough to susie_rss: finemappingOpts (purity keys already
1008+ # isolated above) plus any extra named args. Forwarded verbatim into the fit.
1009+ fitOpts <- c(finemappingOpts , list (... ))
1010+ varY <- fitOpts $ varY
1011+ fitOpts $ varY <- NULL
1012+ if (! is.null(fitOpts $ bhat ) || ! is.null(fitOpts $ shat )) {
9971013 stop(" Pass summary effects as 'beta' and 'se' columns in sumstats; " ,
9981014 " susieRssPipeline constructs bhat and shat internally." )
9991015 }
@@ -1017,33 +1033,34 @@ susieRssPipeline <- function(sumstats, ldMat = NULL, xMat = NULL, n = NULL,
10171033 shat <- sumstats $ se
10181034 names(bhat ) <- names(shat ) <- names(z )
10191035 common <- c(list (bhat = bhat , shat = shat , var_y = varY , n = n ,
1020- coverage = coverage , R_finite = rFinite ,
1021- R_mismatch = rMismatch ), dots )
1036+ coverage = coverage ), fitOpts )
10221037 } else {
1023- common <- c(list (z = z , n = n , coverage = coverage ,
1024- R_finite = rFinite , R_mismatch = rMismatch ), dots )
1038+ common <- c(list (z = z , n = n , coverage = coverage ), fitOpts )
10251039 }
10261040 if (! is.null(xMat )) common $ X <- xMat else common $ R <- ldMat
10271041
1042+ # Method-specific overrides are applied with modifyList so they win over any
1043+ # passthrough key of the same name (e.g. single_effect forces L = 1) without a
1044+ # duplicate-argument error.
10281045 fitOneSusieRss <- function () {
10291046 if (analysisMethod == " single_effect" ) {
1030- do.call(susie_rss , c (common , list (L = 1 , L_greedy = NULL , max_iter = 1 )))
1047+ do.call(susie_rss , modifyList (common , list (L = 1 , L_greedy = NULL , max_iter = 1 )))
10311048 } else if (analysisMethod == " bayesian_conditional_regression" ) {
1032- do.call(susie_rss , c (common , list (L = L , L_greedy = lGreedy , max_iter = 1 )))
1049+ do.call(susie_rss , modifyList (common , list (max_iter = 1 )))
10331050 } else {
1034- do.call(susie_rss , c( common , list ( L = L , L_greedy = lGreedy )) )
1051+ do.call(susie_rss , common )
10351052 }
10361053 }
10371054 fitOneSusieInfRss <- function () {
1038- do.call(susie_rss , c (common , list ( L = L , L_greedy = lGreedy ,
1039- unmappable_effects = " inf" ,
1040- convergence_method = " pip" ,
1041- refine = FALSE , model_init = NULL )))
1055+ do.call(susie_rss , modifyList (common ,
1056+ list ( unmappable_effects = " inf" ,
1057+ convergence_method = " pip" ,
1058+ refine = FALSE , model_init = NULL )))
10421059 }
10431060 fitOneSusieAshRss <- function () {
1044- do.call(susie_rss , c (common , list ( L = L , L_greedy = lGreedy ,
1045- unmappable_effects = " ash" ,
1046- convergence_method = " pip" )))
1061+ do.call(susie_rss , modifyList (common ,
1062+ list ( unmappable_effects = " ash" ,
1063+ convergence_method = " pip" )))
10471064 }
10481065
10491066 fittedModels <- list ()
@@ -1056,11 +1073,11 @@ susieRssPipeline <- function(sumstats, ldMat = NULL, xMat = NULL, n = NULL,
10561073 identical(fitMethods , " bayesian_conditional_regression" )) {
10571074 if (chainInfToSusieRss ) {
10581075 chainedArgs <- prepareSusieFromInfArgs(
1059- list (L = L , L_greedy = lGreedy ),
1076+ list (L = common $ L , L_greedy = common $ L_greedy ),
10601077 fittedModels [[" susie_inf_rss" ]], refineDefault = TRUE ,
10611078 unmappableEffects = " none"
10621079 )
1063- rssFit <- do.call(susie_rss , c (common , chainedArgs ))
1080+ rssFit <- do.call(susie_rss , modifyList (common , chainedArgs ))
10641081 } else {
10651082 rssFit <- fitOneSusieRss()
10661083 }
@@ -1071,11 +1088,11 @@ susieRssPipeline <- function(sumstats, ldMat = NULL, xMat = NULL, n = NULL,
10711088 if (" susie_ash_rss" %in% fitMethods ) {
10721089 if (chainInfToSusieAshRss ) {
10731090 chainedArgs <- prepareSusieFromInfArgs(
1074- list (L = L , L_greedy = lGreedy ),
1091+ list (L = common $ L , L_greedy = common $ L_greedy ),
10751092 fittedModels [[" susie_inf_rss" ]], refineDefault = NULL ,
10761093 unmappableEffects = " ash"
10771094 )
1078- ashFit <- do.call(susie_rss , c (common , chainedArgs ))
1095+ ashFit <- do.call(susie_rss , modifyList (common , chainedArgs ))
10791096 } else {
10801097 ashFit <- fitOneSusieAshRss()
10811098 }
@@ -1113,6 +1130,7 @@ susieRssPipeline <- function(sumstats, ldMat = NULL, xMat = NULL, n = NULL,
11131130 secondaryCoverage = secondaryCoverage ,
11141131 signalCutoff = signalCutoff ,
11151132 minAbsCorr = minAbsCorr ,
1133+ medianAbsCorr = medianAbsCorr ,
11161134 csInput = ppCsInput
11171135 )
11181136 # Primary method preference: "susie_rss" > other names > first fit
0 commit comments