dmphillippo / multinma

Network meta-analysis of individual and aggregate data in Stan
https://dmphillippo.github.io/multinma
33 stars 15 forks source link

Failure with dev purrr #20

Closed hadley closed 1 year ago

hadley commented 1 year ago

I see:

══ Failed tests ════════════════════════════════════════════════════════════════
── Failure ('test-data_set.R:390'): set_agd_contrast - positive definite check ──
`set_agd_contrast(...)` threw an error with unexpected message.
Expected match: "not positive definite for study \"2\""
Actual message: ""
Backtrace:
    ▆
 1. ├─testthat::expect_error(...) at test-data_set.R:390:2
 2. │ └─testthat:::quasi_capture(...)
 3. │   ├─testthat (local) .capture(...)
 4. │   │ └─base::withCallingHandlers(...)
 5. │   └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
 6. └─multinma::set_agd_contrast(agd_contrast_nonpd, studyn, trtc, y = ydiff, se = sediff)
 7.   └─rlang::abort(...)
── Failure ('test-data_set.R:392'): set_agd_contrast - positive definite check ──
`set_agd_contrast(...)` threw an error with unexpected message.
Expected match: "not positive definite for study \"b\""
Actual message: ""
Backtrace:
    ▆
 1. ├─testthat::expect_error(...) at test-data_set.R:392:2
 2. │ └─testthat:::quasi_capture(...)
 3. │   ├─testthat (local) .capture(...)
 4. │   │ └─base::withCallingHandlers(...)
 5. │   └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
 6. └─multinma::set_agd_contrast(agd_contrast_nonpd, studyc, trtc, y = ydiff, se = sediff)
 7.   └─rlang::abort(...)
── Failure ('test-data_set.R:398'): set_agd_contrast - positive definite check ──
`set_agd_contrast(...)` threw an error with unexpected message.
Expected match: "not positive definite for studies \"2\" and \"3\""
Actual message: ""
Backtrace:
    ▆
 1. ├─testthat::expect_error(...) at test-data_set.R:398:2
 2. │ └─testthat:::quasi_capture(...)
 3. │   ├─testthat (local) .capture(...)
 4. │   │ └─base::withCallingHandlers(...)
 5. │   └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
 6. └─multinma::set_agd_contrast(agd_contrast_nonpd2, studyn, trtc, y = ydiff, se = sediff)
 7.   └─rlang::abort(...)
── Failure ('test-data_set.R:400'): set_agd_contrast - positive definite check ──
`set_agd_contrast(...)` threw an error with unexpected message.
Expected match: "not positive definite for studies \"b\" and \"c\""
Actual message: ""
Backtrace:
    ▆
 1. ├─testthat::expect_error(...) at test-data_set.R:400:2
 2. │ └─testthat:::quasi_capture(...)
 3. │   ├─testthat (local) .capture(...)
 4. │   │ └─base::withCallingHandlers(...)
 5. │   └─rlang::eval_bare(quo_get_expr(.quo), quo_get_env(.quo))
 6. └─multinma::set_agd_contrast(agd_contrast_nonpd2, studyc, trtc, y = ydiff, se = sediff)
 7.   └─rlang::abort(...)

[ FAIL 4 | WARN 6 | SKIP 18 | PASS 913 ]
Error: Test failures
Execution halted

If you can come up with a simple reprex, I'm happy to help debug it, but I spent 15 minutes with your package and couldn't get a clean test run. At a minimum, you need to move nowarn_on_ci() out of testthat.R and into tests/testthat/helpers-ci.R or similar.

dmphillippo commented 1 year ago

Hi @hadley, thanks for raising this and for the tip about the testthat helpers. I've relocated nowarn_on_ci() as you suggested, hopefully that gets things running for you?

The issue seems to be that the output from map_lgl() no longer has names in the dev version, which then results in an unexpectedly empty error message. The call in question is here: https://github.com/dmphillippo/multinma/blob/f3f43d455470c695cedf47c24cc5b7a64680fe25/R/nma_data.R#L698-L706

I'm not quite sure why the result posdef isn't named in this instance, I haven't managed to recreate the issue outside of the function. Perhaps it has something to do with agd_Sigma being constructed using by(., simplify=FALSE), so it isn't quite a bare list of matrices?

In any case, I can fix from my end if necessary by changing names(posdef[!posdef])) to names(agd_Sigma[!posdef])) in the glue call.

hadley commented 1 year ago

Hmmm, what sort of object is agd_Sigma()? Is there any chance it's an S4 object?

dmphillippo commented 1 year ago

It looks like purrr:::vctrs_vec_compat() is stripping the names

hadley commented 1 year ago

@dmphillippo that was my suspicion too, but when I re-read the source code, I can't see which branch would remove names.

dmphillippo commented 1 year ago

agd_Sigma is a list of matrices, produced using by() with simplify = FALSE, so I don't think it's S4?

hadley commented 1 year ago

I've seen some problems with by() elsewhere, but it normally looks more like this:

x <- by(data.frame(x = 1:3), 1:3, \(x) 1, simplify = FALSE)
names(x)
#> [1] "1" "2" "3"
purrr::map_lgl(x, \(y) TRUE)
#> Error in `purrr::map_lgl()`:
#> ! `.x` must be a vector, not a <by> object.

#> Backtrace:
#>     ▆
#>  1. └─purrr::map_lgl(x, function(y) TRUE)
#>  2.   └─purrr:::map_("logical", .x, .f, ..., .progress = .progress)
#>  3.     └─vctrs::vec_assert(.x, arg = ".x", call = ..error_call)
#>  4.       └─vctrs:::stop_scalar_type(x, arg, call = call)
#>  5.         └─vctrs:::stop_vctrs(...)
#>  6.           └─rlang::abort(message, class = c(class, "vctrs_error"), ..., call = vctrs_error_call(call))

Created on 2022-11-03 with reprex v2.0.2

Could you give me a little reprex to produce something that looks like agd_Sigma?

dmphillippo commented 1 year ago

Sure, here's a quick reprex. Looking more closely, I had unclass()-ed the output from by() to get a bare list, which was working with previous versions of purrr (i.e. posdef retained the names):

df <- data.frame(a = c(1, 2, 2))
(agd_Sigma <- unclass(by(df, df$a, \(x) diag(nrow(x)), simplify = FALSE)))
#> $`1`
#>      [,1]
#> [1,]    1
#> 
#> $`2`
#>      [,1] [,2]
#> [1,]    1    0
#> [2,]    0    1
#> 
#> attr(,"call")
#> by.data.frame(data = df, INDICES = df$a, FUN = function(x) diag(nrow(x)), 
#>     simplify = FALSE)
(posdef <- purrr::map_lgl(agd_Sigma, ~all(eigen(., only.values = TRUE)$values > sqrt(.Machine$double.eps))))
#> [1] TRUE TRUE
names(posdef)
#> NULL

Created on 2022-11-04 by the reprex package (v2.0.1)

If lists produced using by() aren't supported by purrr (should they be?) then I can find another way to create this list of matrices (I think dplyr::group_map() should work).

hadley commented 1 year ago

No, that's definitely a bug, and I'll fix it in purrr: https://github.com/tidyverse/purrr/issues/993

hadley commented 1 year ago

Oooh, it's because the result is both a list and a 1d array and the names are stored in the dimnames which are stripped when I set dim(x) <- NULL.

dmphillippo commented 1 year ago

Brilliant, thanks @hadley 🙂

I noticed that too when I dug a little deeper - never seen that before!