Skip to content

Commit faa16b9

Browse files
authored
Merge pull request #671 from remlapmot/fix-670
TwoSampleMR 0.7.0
2 parents 07311dd + 7775338 commit faa16b9

15 files changed

Lines changed: 177 additions & 65 deletions

.github/workflows/test-coverage.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767

6868
- name: Upload test results
6969
if: failure()
70-
uses: actions/upload-artifact@v5
70+
uses: actions/upload-artifact@v6
7171
with:
7272
name: coverage-test-failures
7373
path: ${{ runner.temp }}/package

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: TwoSampleMR
22
Title: Two Sample MR Functions and Interface to MRC Integrative
33
Epidemiology Unit OpenGWAS Database
4-
Version: 0.6.30
4+
Version: 0.7.0
55
Authors@R: c(
66
person("Gibran", "Hemani", , "g.hemani@bristol.ac.uk", role = c("aut", "cre"),
77
comment = c(ORCID = "0000-0003-0920-1055")),

NEWS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# TwoSampleMR v0.7.0
2+
3+
(Release date 2026-02-24)
4+
5+
* Fixed a bug in the calculation in one of the code paths for the inferred p-value in `format_data()` (thanks @j-brody)
6+
* Fixed the calculation of a p-value in `mr_rucker_internal()`
7+
* Fixed a replacement length warning in `get_population_allele_frequency()`
8+
* Reformatted code base with `air`
9+
110
# TwoSampleMR v0.6.30
211

312
(Release date 2026-02-06)

R/add_rsq.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ get_population_allele_frequency <- function(af, prop, odds_ratio, prevalence) {
351351
co <- contingency(af[i], prop[i], odds_ratio[i])
352352
af_controls <- co[1, 2] / (co[1, 2] + co[2, 2])
353353
af_cases <- co[1, 1] / (co[1, 1] + co[2, 1])
354-
af[i] <- af_controls * (1 - prevalence) + af_cases * prevalence
354+
af[i] <- af_controls * (1 - prevalence[i]) + af_cases * prevalence[i]
355355
}
356356
return(af)
357357
}

R/format_mr_results2.R

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,22 @@ combine_all_mrresults <- function(
163163

164164
res <- merge(res, het, by = c("id.outcome", "id.exposure", "Method"), all.x = TRUE)
165165
res <- data.table::rbindlist(
166-
list(res, sin[, c("exposure", "outcome", "id.exposure", "id.outcome", "SNP", "b", "se", "pval", "Method")]),
167-
fill = TRUE, use.names = TRUE
166+
list(
167+
res,
168+
sin[, c(
169+
"exposure",
170+
"outcome",
171+
"id.exposure",
172+
"id.outcome",
173+
"SNP",
174+
"b",
175+
"se",
176+
"pval",
177+
"Method"
178+
)]
179+
),
180+
fill = TRUE,
181+
use.names = TRUE
168182
)
169183
data.table::setDF(res)
170184

R/harmonise.R

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ harmonise_data <- function(exposure_dat, outcome_dat, action = 2) {
114114
# return(x)
115115
# })
116116

117-
jlog <- data.table::rbindlist(lapply(fix.tab, function(x) attr(x, "log")), fill = TRUE, use.names = TRUE)
117+
jlog <- data.table::rbindlist(
118+
lapply(fix.tab, function(x) attr(x, "log")),
119+
fill = TRUE,
120+
use.names = TRUE
121+
)
118122
data.table::setDF(jlog)
119123
fix.tab <- data.table::rbindlist(fix.tab, fill = TRUE, use.names = TRUE)
120124
data.table::setDF(fix.tab)
@@ -686,7 +690,8 @@ harmonise <- function(dat, tolerance, action) {
686690
as.data.frame(attr(d12, "log"), stringsAsFactors = FALSE),
687691
as.data.frame(attr(d11, "log"), stringsAsFactors = FALSE)
688692
),
689-
fill = TRUE, use.names = TRUE
693+
fill = TRUE,
694+
use.names = TRUE
690695
)
691696
data.table::setDF(jlog)
692697
jlog <- cbind(

R/instruments.R

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@ extract_instruments <- function(
2222
opengwas_jwt = ieugwasr::get_opengwas_jwt(),
2323
force_server = FALSE
2424
) {
25-
if (!(clump %in% c(0, 1, FALSE, TRUE))) stop("The clump argument should be 0 or 1.")
26-
if (clump) clump <- 1
27-
if (!clump) clump <- 0
25+
if (!(clump %in% c(0, 1, FALSE, TRUE))) {
26+
stop("The clump argument should be 0 or 1.")
27+
}
28+
if (clump) {
29+
clump <- 1
30+
}
31+
if (!clump) {
32+
clump <- 0
33+
}
2834
# .Deprecated("ieugwasr::tophits()")
2935
outcomes <- ieugwasr::legacy_ids(unique(outcomes))
3036

R/knit.R

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,14 @@ mr_report <- function(
123123
)
124124

125125
dat_dt <- data.table::as.data.table(dat)
126-
combinations <- dat_dt[, .(
127-
n = .N,
128-
exposure = exposure[1],
129-
outcome = outcome[1]
130-
), by = c("id.exposure", "id.outcome")]
126+
combinations <- dat_dt[,
127+
.(
128+
n = .N,
129+
exposure = exposure[1],
130+
outcome = outcome[1]
131+
),
132+
by = c("id.exposure", "id.outcome")
133+
]
131134
data.table::setDF(combinations)
132135

133136
output_file <- array("", nrow(combinations))

R/leaveoneout.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mr_leaveoneout <- function(dat, parameters = default_parameters(), method = mr_i
2020

2121
dat_dt <- data.table::as.data.table(dat)
2222
combos <- unique(dat_dt[, .(id.exposure, id.outcome)])
23-
23+
2424
results <- lapply(seq_len(nrow(combos)), function(i) {
2525
exp_id <- combos$id.exposure[i]
2626
out_id <- combos$id.outcome[i]
@@ -104,7 +104,7 @@ mr_leaveoneout <- function(dat, parameters = default_parameters(), method = mr_i
104104
mr_leaveoneout_plot <- function(leaveoneout_results) {
105105
dat_dt <- data.table::as.data.table(leaveoneout_results)
106106
combos <- unique(dat_dt[, .(id.exposure, id.outcome)])
107-
107+
108108
res <- lapply(seq_len(nrow(combos)), function(i) {
109109
exp_id <- combos$id.exposure[i]
110110
out_id <- combos$id.outcome[i]

R/mr.R

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ mr <- function(
2626
# Convert to data.table for efficient grouped operations
2727

2828
dat_dt <- data.table::as.data.table(dat)
29-
29+
3030
# Get unique combinations of id.exposure and id.outcome
3131
combos <- unique(dat_dt[, .(id.exposure, id.outcome)])
32-
32+
3333
# Pre-compute method names once, outside the loop
3434
methl <- mr_method_list()
3535
method_names <- methl$name[match(method_list, methl$obj)]
@@ -40,7 +40,7 @@ mr <- function(
4040
out_id <- combos$id.outcome[i]
4141
x1 <- dat_dt[id.exposure == exp_id & id.outcome == out_id]
4242
x <- x1[mr_keep == TRUE]
43-
43+
4444
if (nrow(x) == 0) {
4545
message(
4646
"No SNPs available for MR analysis of '",
@@ -71,7 +71,7 @@ mr <- function(
7171
mr_tab <- mr_tab[!(is.na(mr_tab$b) & is.na(mr_tab$se) & is.na(mr_tab$pval)), ]
7272
return(mr_tab)
7373
})
74-
74+
7575
mr_tab <- data.table::rbindlist(results, fill = TRUE, use.names = TRUE)
7676
data.table::setDF(mr_tab)
7777

@@ -662,12 +662,16 @@ mr_egger_regression_bootstrap <- function(b_exp, b_out, se_exp, se_out, paramete
662662

663663
# Vectorized bootstrap: generate all random values at once
664664
# Matrix dimensions: nboot rows x nsnp columns
665-
xs_mat <- matrix(stats::rnorm(nboot * nsnp, mean = rep(b_exp, each = nboot),
666-
sd = rep(se_exp, each = nboot)),
667-
nrow = nboot, ncol = nsnp)
668-
ys_mat <- matrix(stats::rnorm(nboot * nsnp, mean = rep(b_out, each = nboot),
669-
sd = rep(se_out, each = nboot)),
670-
nrow = nboot, ncol = nsnp)
665+
xs_mat <- matrix(
666+
stats::rnorm(nboot * nsnp, mean = rep(b_exp, each = nboot), sd = rep(se_exp, each = nboot)),
667+
nrow = nboot,
668+
ncol = nsnp
669+
)
670+
ys_mat <- matrix(
671+
stats::rnorm(nboot * nsnp, mean = rep(b_out, each = nboot), sd = rep(se_out, each = nboot)),
672+
nrow = nboot,
673+
ncol = nsnp
674+
)
671675

672676
# Apply sign correction for Egger regression (vectorized)
673677
ys_mat <- ys_mat * sign(xs_mat)
@@ -676,15 +680,19 @@ mr_egger_regression_bootstrap <- function(b_exp, b_out, se_exp, se_out, paramete
676680
# Vectorized weighted linear regression for all bootstrap iterations
677681
# For each bootstrap iteration, compute weighted regression coefficients
678682
# Using the formula: bhat = cov(x*w, y*w) / var(x*w), ahat = mean(y) - mean(x)*bhat
679-
res <- t(vapply(seq_len(nboot), function(i) {
680-
xs <- xs_mat[i, ]
681-
ys <- ys_mat[i, ]
682-
xw <- xs * weights
683-
yw <- ys * weights
684-
bhat <- stats::cov(xw, yw, use = "pair") / stats::var(xw, na.rm = TRUE)
685-
ahat <- mean(ys, na.rm = TRUE) - mean(xs, na.rm = TRUE) * bhat
686-
c(ahat, bhat)
687-
}, numeric(2)))
683+
res <- t(vapply(
684+
seq_len(nboot),
685+
function(i) {
686+
xs <- xs_mat[i, ]
687+
ys <- ys_mat[i, ]
688+
xw <- xs * weights
689+
yw <- ys * weights
690+
bhat <- stats::cov(xw, yw, use = "pair") / stats::var(xw, na.rm = TRUE)
691+
ahat <- mean(ys, na.rm = TRUE) - mean(xs, na.rm = TRUE) * bhat
692+
c(ahat, bhat)
693+
},
694+
numeric(2)
695+
))
688696

689697
return(list(
690698
b = mean(res[, 2], na.rm = TRUE),
@@ -808,20 +816,28 @@ weighted_median_bootstrap <- function(b_exp, b_out, se_exp, se_out, weights, nbo
808816

809817
# Vectorized bootstrap: generate all random values at once
810818
# Matrix dimensions: nboot rows x nsnp columns
811-
b_exp_mat <- matrix(stats::rnorm(nboot * nsnp, mean = rep(b_exp, each = nboot),
812-
sd = rep(se_exp, each = nboot)),
813-
nrow = nboot, ncol = nsnp)
814-
b_out_mat <- matrix(stats::rnorm(nboot * nsnp, mean = rep(b_out, each = nboot),
815-
sd = rep(se_out, each = nboot)),
816-
nrow = nboot, ncol = nsnp)
819+
b_exp_mat <- matrix(
820+
stats::rnorm(nboot * nsnp, mean = rep(b_exp, each = nboot), sd = rep(se_exp, each = nboot)),
821+
nrow = nboot,
822+
ncol = nsnp
823+
)
824+
b_out_mat <- matrix(
825+
stats::rnorm(nboot * nsnp, mean = rep(b_out, each = nboot), sd = rep(se_out, each = nboot)),
826+
nrow = nboot,
827+
ncol = nsnp
828+
)
817829

818830
# Compute Wald ratios for all bootstrap samples (element-wise division)
819831
betaIV_mat <- b_out_mat / b_exp_mat
820832

821833
# Apply weighted_median to each bootstrap iteration
822-
med <- vapply(seq_len(nboot), function(i) {
823-
weighted_median(betaIV_mat[i, ], weights)
824-
}, numeric(1))
834+
med <- vapply(
835+
seq_len(nboot),
836+
function(i) {
837+
weighted_median(betaIV_mat[i, ], weights)
838+
},
839+
numeric(1)
840+
)
825841

826842
return(stats::sd(med))
827843
}
@@ -1118,7 +1134,11 @@ mr_raps <- function(b_exp, b_out, se_exp, se_out, parameters = default_parameter
11181134
}
11191135

11201136
if (utils::packageVersion('mr.raps') < '0.4.3') {
1121-
message(paste("The version of mr.raps is", utils::packageVersion('mr.raps'), "please consider updating to version 0.4.3 or higher, e.g., install.packages('mr.raps', repos = c('https://mrcieu.r-universe.dev', 'https://cloud.r-project.org')) "))
1137+
message(paste(
1138+
"The version of mr.raps is",
1139+
utils::packageVersion('mr.raps'),
1140+
"please consider updating to version 0.4.3 or higher, e.g., install.packages('mr.raps', repos = c('https://mrcieu.r-universe.dev', 'https://cloud.r-project.org')) "
1141+
))
11221142
}
11231143

11241144
data <- data.frame(

0 commit comments

Comments
 (0)