Skip to content

Support explicit row counts for zero-column tables - #22765

Merged
rapids-bot[bot] merged 26 commits into
NVIDIA:mainfrom
madsbk:cudf-zero-column-with-multiple-rows
Jul 13, 2026
Merged

Support explicit row counts for zero-column tables#22765
rapids-bot[bot] merged 26 commits into
NVIDIA:mainfrom
madsbk:cudf-zero-column-with-multiple-rows

Conversation

@madsbk

@madsbk madsbk commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Closes #21428
Part of #21644

Description

Implements support for zero-column tables with N rows. Such tables are representable in other ecosystems:

  • Arrow: StructArray / RecordBatch lengths are independent of fields.
  • Polars: frames can have a height with no columns.
  • pandas: supports (N, 0) DataFrames.

Until now, libcudf derived a table's row count from its columns, so a zero-column table always reported 0 rows. This PR makes the row count explicit.

Motivation

The cudf-polars streaming executor moves tables between partitions and workers using cudf::pack and cudf::unpack. Any intermediate crossing those boundaries must survive a round-trip unchanged.

Previously, zero-column tables did not. A (N, 0) table became (0, 0) after a pack/unpack round-trip, causing incorrect results in queries such as select() or len() over column-free projections.

What this PR changes

  • Adds explicit row counts to table and table_view via new two-argument constructors. When columns are present, the supplied row count is validated against column sizes.
  • Threads row counts through table-producing algorithms so zero-column inputs preserve their height.
  • Updates Arrow interop (from_arrow_host / from_arrow_device) to preserve the length of zero-field Arrow arrays.
  • Exposes and preserves explicit row counts in pylibcudf.Table.

Breaking change

This PR is largely non-breaking. No existing signatures change: the new table / table_view two-argument constructors are additional overloads, and pylibcudf.Table.num_rows is a new optional parameter.

The one behavioral change is in table::select and table_view::select. An empty selection now preserves the source table's row count. That is, select({}) on an N-row table now returns an (N, 0) table/view rather than (0, 0).

Non-empty selections are unchanged, as their row count already matched the source table. The only affected callers are those that pass an empty column selection and rely on the result reporting 0 rows.

The high-level cudf Python library requires no changes, as it tracks the row count independently.

Follow-up work

IO readers are intentionally out of scope. Reading files with rows but no projected columns still needs to preserve row counts. That work (Parquet, ORC, CSV, AVRO, JSON, and experimental readers) will be handled in a follow-up PR as part of #21644.

Tracked in #22935

@madsbk madsbk self-assigned this Jun 3, 2026
@madsbk madsbk added improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels Jun 3, 2026
@github-actions github-actions Bot added libcudf Affects libcudf (C++/CUDA) code. Python Affects Python cuDF API. pylibcudf Issues specific to the pylibcudf package labels Jun 3, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Jun 3, 2026
Comment thread cpp/src/copying/pack.cpp Outdated
@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch from b6631bf to e275230 Compare June 3, 2026 18:20
@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch from e275230 to cf7a76a Compare June 22, 2026 07:58
@madsbk madsbk closed this Jun 22, 2026
@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch from cf7a76a to 96896b1 Compare June 22, 2026 08:19
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jun 22, 2026
@madsbk madsbk reopened this Jun 22, 2026
@github-project-automation github-project-automation Bot moved this from Done to In Progress in cuDF Python Jun 22, 2026
@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch 5 times, most recently from e3a5193 to 5e9d898 Compare June 22, 2026 14:07

@Matt711 Matt711 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch from 5e9d898 to a3b3e87 Compare June 22, 2026 15:05
@madsbk

madsbk commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Can you please also remove the workarounds for zero-width tables we use in cudf_polars?

https://github.com/rapidsai/cudf/blob/96896b17420f158d0ce2a024ee0cc24ab712dd6a/python/cudf_polars/cudf_polars/containers/dataframe.py#L109

We need some follow-up work to remove the workaround completely: #22935

@madsbk
madsbk force-pushed the cudf-zero-column-with-multiple-rows branch 5 times, most recently from 97c7fdb to 3f79450 Compare June 23, 2026 12:18
@davidwendt

Copy link
Copy Markdown
Contributor

Seems likely the Java failures here are directly related to these changes
https://github.com/rapidsai/cudf/actions/runs/28999779648/job/86065910999?pr=22765#step:13:4068

@madsbk
madsbk requested a review from a team as a code owner July 10, 2026 16:39
@github-actions github-actions Bot added the Java Affects Java cuDF API. label Jul 10, 2026
}

auto keys = input_table->select(key_indices);
auto keys = key_indices.empty() ? cudf::table_view{} : input_table->select(key_indices);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this change is necessary since table_view.select() automatically returns empty table (no column).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think this change is necessary since table_view.select() automatically returns empty table (no column).

It is necessary because this PR changes that behavior. With zero-column table support, table_view::select({}) now preserves the source row count, returning an (N, 0) view rather than a (0, 0) table. This is the breaking change described in the PR.

@madsbk

madsbk commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit f90c293 into NVIDIA:main Jul 13, 2026
384 of 388 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jul 13, 2026
@madsbk
madsbk deleted the cudf-zero-column-with-multiple-rows branch July 13, 2026 10:45
rapids-bot Bot pushed a commit that referenced this pull request Jul 16, 2026
Follow-up to #22765. Propagate the row count of a zero-column table through the cudf-polars `DataFrame` container and `Projection` node, so a frame with no columns and `N` rows preserves its shape as `(N, 0)` instead of collapsing to
`(0, 0)`.

Authors:
  - Mads R. B. Kristensen (https://github.com/madsbk)

Approvers:
  - Matthew Roeschke (https://github.com/mroeschke)
  - Lawrence Mitchell (https://github.com/wence-)

URL: #23234
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking change improvement Improvement / enhancement to an existing function Java Affects Java cuDF API. libcudf Affects libcudf (C++/CUDA) code. pylibcudf Issues specific to the pylibcudf package Python Affects Python cuDF API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Arrow interop loses row count for zero-width tables

7 participants