Skip to content

Find each row's group natively when a grouped verb takes rows - #459

Merged
jbearak merged 2 commits into
mainfrom
claude/slice-group-owners
Oct 27, 2026
Merged

jbearak merged 2 commits into
mainfrom
claude/slice-group-owners

Conversation

@jbearak

@jbearak jbearak commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Summary

filter(), slice(), its helpers and arrange() of grouped data rebuild the result's groups from the old ones with .restore_group_metadata()'s slice policy. It first found each row's group with a loop over the groups, ids[old$.rows[[index]]] <- index, whose $ and [[ calls dispatch for every group. Over the 42,739 women in the first 100,000 rows of the India 2021 DHS births file, grouped by caseid, that loop took most of the time of slice_max(gb, b2) after #458, about 0.062 seconds, and of arrange(gb, b2), about 0.063.

This PR adds .group_row_ids(), which the policy now calls. It reads the rows once with C_dtatools_group_rows_facts() and finds each row's group with C_dtatools_group_row_owners(), as the group checks of ADR 0115 do. Those calls give each row's position among the groups with rows when the rows hold each of the table's rows once. The empty groups of .drop = FALSE shift that position, so the group's index is the position's entry in which(sizes > 0L). Rows that hold some row twice or none, or that the facts do not read, keep the loop.

Behavior

Results are unchanged. ADR 0121 records the decision. A traced or replaced $ or [[ method for the groups' tibble or a list_of no longer sees a call for each group, as ADR 0115 already allows for the group checks.

Timing

Best of three runs in fresh R sessions, in seconds, with the builds interleaved, over the first 100,000 rows of the India 2021 DHS births file grouped by caseid. Before is main.

Call Before After
slice_max(gb, b2) 0.062 0.018
arrange(gb, b2) 0.063 0.020
filter(gb, bord > 2) 0.074 0.032

Tests

  • A test builds 40 random partitions of up to 30 rows into groups in random order, with empty groups among them, as both list_of and plain lists. .group_row_ids() must give what the loop gives for each. Rows that are repeated, missing, outside the table, zero, negative or doubles must give it too, through the loop.
  • A test groups a factor with unused levels by .drop = FALSE and runs filter(), slice_max(), slice_head(), arrange() and slice() on it. Each must give the rows, group rows and keys a tibble gives.
  • The full suite passes: 169 files, 2,272 tests. The native manifest and repository guard tests pass. Codex review is clean.

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@jbearak
jbearak marked this pull request as ready for review October 27, 2026 00:53
@jbearak
jbearak merged commit efe50d8 into main Oct 27, 2026
4 checks passed
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.

1 participant