Skip to content

Optimize return object preparation from gs_power_npe()#624

Merged
LittleBeannie merged 1 commit into
Merck:mainfrom
jdblischak:gs-power-npe-return
May 5, 2026
Merged

Optimize return object preparation from gs_power_npe()#624
LittleBeannie merged 1 commit into
Merck:mainfrom
jdblischak:gs-power-npe-return

Conversation

@jdblischak
Copy link
Copy Markdown
Collaborator

As discussed in group meeting, this PR optimizes the preparation of the return object by gs_power_npe().

Sorry @yihui. I had prepared this yesterday afternoon, but didn't have time to submit the PR. Could we review/merge this targeted PR first, and then rebase your more comprehensive PR on top?

xref: #623

@jdblischak jdblischak requested review from LittleBeannie and yihui May 5, 2026 13:50
@jdblischak jdblischak self-assigned this May 5, 2026
@jdblischak
Copy link
Copy Markdown
Collaborator Author

Explanation of changes:

  • tibble() -> data.frame() - no advantage to using tibble here, and we know from experience it is noticeably slower
  • mutate() - unnecessary because info0 and info1 can be defined in the data.frame() call above
  • filter() - removed this dead code. It was already commented out 4 years ago when the function was originally added in 18337df (upload #26)
  • arrange() - removed. This sorting is unnecessary. By construction of the columns analysis and bound, the order will always be upper followed by lower, and the analyses in ascending order (1:n_analysis)

I updated the tests by running as.data.frame() on the output of the legacy implementation gs_power_npe_(). I felt this was the most transparent option. Another option would have been to ignore the attributes on the objects, but I felt this was more opaque (and more verbose), eg:

  local_edition(3)
  expect_equal(x1_c, x2, ignore_attr = TRUE)

And lastly, we could continue to return a tibble via as_tibble() as Yihui did in #623. Just let me know if you prefer one of these alternative solutions.

@jdblischak
Copy link
Copy Markdown
Collaborator Author

To benchmark the improvement, I put my changes in a separate gs_power_npe2() so that I could directly compare to the original. It reduced the median runtime from ~7 ms to ~4 ms. The most noticeable difference is in the worst case maximum time. This was reduced from ~41 ms to ~12 ms.

microbenchmark::microbenchmark(
  before = gs_power_npe(
    theta = c(.1, .2, .3),
    info = (1:3) * 40,
    upper = gs_b,
    upar = c(Inf, 3, 2),
    lower = gs_b,
    lpar = c(qnorm(.1), -Inf, -Inf)
  ),
  after = gs_power_npe2(
    theta = c(.1, .2, .3),
    info = (1:3) * 40,
    upper = gs_b,
    upar = c(Inf, 3, 2),
    lower = gs_b,
    lpar = c(qnorm(.1), -Inf, -Inf)
  ),
  check = "equivalent"
)
## Unit: milliseconds
##    expr    min      lq     mean  median     uq     max neval cld
##  before 6.5527 6.81630 7.635802 7.14665 7.4633 41.6138   100  a 
##   after 4.0639 4.16815 4.518994 4.27110 4.5039 12.0563   100   b

Copy link
Copy Markdown
Collaborator

@yihui yihui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. I'm all for using data.frame() here! Thanks!

I simply asked Claude to optimize gs_design_ahr() without specific instructions yesterday, and it naturally chose base R. I hope it didn't do so just to accommodate my personal taste.

@jdblischak
Copy link
Copy Markdown
Collaborator Author

it naturally chose base R. I hope it didn't do so just to accommodate my personal taste.

No, I doubt it. Base R is clearly the right choice in this context. The data is too small to justify using something like {data.table}. For a simple gathering of results into a table, data.frame() is the way to go.

@jdblischak
Copy link
Copy Markdown
Collaborator Author

@LittleBeannie After you approve, please squash and merge this PR. I don't think it justifies a NEWS bullet, but please let me know if you feel otherwise.

Copy link
Copy Markdown
Collaborator

@LittleBeannie LittleBeannie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

@LittleBeannie LittleBeannie merged commit 32ba152 into Merck:main May 5, 2026
7 checks passed
@jdblischak jdblischak deleted the gs-power-npe-return branch May 5, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants