Skip to content

Conversation

@myandpr
Copy link
Contributor

@myandpr myandpr commented Dec 23, 2025

Description

improve the UX by making this look like polars when using Ray Dataset.

test demo

>>> import numpy as np
>>> import ray
>>> from ray.data import DataContext

ray.init(include_dashboard=False, ignore_reinit_error=True)
>>> 
>>> ray.init(include_dashboard=False, ignore_reinit_error=True)
2025-12-25 03:38:11,358 INFO worker.py:2010 -- Started a local Ray instance.
/Users/xxx/work/community/ray/python/ray/_private/worker.py:2049: FutureWarning: Tip: In future versions of Ray, Ray will no longer override accelerator visible devices env var if num_gpus=0 or num_gpus=None (default). To enable this behavior and turn off this error message, set RAY_ACCEL_ENV_VAR_OVERRIDE_ON_ZERO=0
  warnings.warn(
RayContext(dashboard_url='', python_version='3.10.19', ray_version='3.0.0.dev0', ray_commit='{{RAY_COMMIT_SHA}}')

# Configure truncation: show 4 rows total (1 from the head, the rest from the tail)
# and display up to 5 columns (2 from the head, 2 from the tail, plus an ellipsis).
>>> ctx = DataContext.get_current()
>>> ctx.dataset_repr_max_rows = 4        # Display a total of 4 rows
>>> ctx.dataset_repr_head_rows = 1       # To display 1 row from the head and the remaining from the tail
>>> ctx.dataset_repr_max_columns = 5     # Show 5 columns in total, with middle columns truncated (represented by ellipsis ...).
>>> ctx.dataset_repr_head_columns = 2    # Display the first 2 columns at the head, and the remaining columns at the tail

# Create a demo dataset with 10 rows and 6 columns.
>>> items = [
...     {f"col{i}": i + row for i in range(6)}
...     for row in range(10)
... ]
>>> ds = ray.data.from_items(items)

>>> 
>>> print("Before materialization (schema preview only):")
Before materialization (schema preview only):
>>> print(ds)                    
shape: (10, 6)
╭───────┬───────┬─────┬───────┬───────╮
│ col0col1  ┆ …   ┆ col4col5  │
│ ---------------   │
│ int64int64 ┆ …   ┆ int64int64 │
╰───────┴───────┴─────┴───────┴───────╯
(Showing 0 of 10 rows)
(Showing 4 of 6 columns)
>>> print("\nAfter materialization (shows head/tail rows):")

After materialization (shows head/tail rows):
>>> print(ds.materialize())      # To display a head/tail summary of 4 rows with truncated columns/ellipsis in a tabular format
shape: (10, 6)
╭───────┬───────┬─────┬───────┬───────╮
│ col0col1  ┆ …   ┆ col4col5  │
│ ---------------   │
│ int64int64 ┆ …   ┆ int64int64 │
╞═══════╪═══════╪═════╪═══════╪═══════╡
│ 01     ┆ …   ┆ 45     │
│ …     ┆ …     ┆ …   ┆ …     ┆ …     │
│ 78     ┆ …   ┆ 1112    │
│ 89     ┆ …   ┆ 1213    │
│ 910    ┆ …   ┆ 1314    │
╰───────┴───────┴─────┴───────┴───────╯
(Showing 4 of 10 rows)
(Showing 4 of 6 columns)

Related issues

Link related issues: #59482

Fixes #59482

@myandpr myandpr requested a review from a team as a code owner December 23, 2025 15:44
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new tabular representation for ray.data.Dataset, similar to polars, which significantly improves the user experience for inspecting datasets. The implementation is well-structured, with a fallback mechanism to the old plan-based representation to ensure robustness. The changes include new helper functions for building the ASCII table, and the tests have been updated accordingly to cover both the new and old repr behaviors.

I've found one area for performance improvement in the logic for collecting tail rows for the representation, which can be made more efficient. Overall, this is a great enhancement to Ray Data.

@myandpr
Copy link
Contributor Author

myandpr commented Dec 23, 2025

Hi @bveeramani — I put together this PR to implement the Polars-style repr requested in #59482. When you have a moment, could you take a quick look and let me know if this matches what you had in mind? Happy to adjust or follow up on any feedback.

column_widths: List[int],
) -> List[str]:
lines: List[str] = []
top = _render_border("┌", "┬", "┐", "─", column_widths)
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Could we use rounded corners? I think people find them more aesthetically pleasing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have used rounded corners, and it looks like more aesthetically. Thanks for your suggestion


logger = logging.getLogger(__name__)

_DATASET_REPR_MAX_ROWS = 10
Copy link
Member

Choose a reason for hiding this comment

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

What happens if you have 1000 columns? How does polars or other DataFrame libraries handle this?

Copy link
Contributor

Choose a reason for hiding this comment

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

@myandpr I would suggest reading through: https://github.com/pola-rs/polars/blob/ac0b7519/crates/polars-core/src/fmt.rs .

They handle truncation at both column and row level, and both should be configurable from the user's perspective.

We should test rendering of large vectors, strings etc. in repr to ensure that truncation works as expected. Each dtype should have its own config

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@goutamvenkat-anyscale , Thanks for the pointers! Dataset.repr now uses the polars-style table, truncation knobs (rows/columns/value per dtype) are exposed via DataContext, and tests cover row/column truncation plus long string/list/ndarray cases.

I'm not entirely sure if this matches what you were envisioning, so please let me know if anything still needs adjusting.

@bveeramani
Copy link
Member

bveeramani commented Dec 23, 2025

@myandpr Nice!! This is great 🔥

@owenowenisme could you review the implementation pls?

@ray-gardener ray-gardener bot added data Ray Data-related issues community-contribution Contributed by the community labels Dec 23, 2025
@myandpr myandpr requested a review from a team as a code owner December 25, 2025 06:47
return block.to_arrow()


@dataclass
Copy link
Member

Choose a reason for hiding this comment

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

Could we move these implementations into a separate module? dataset.py is already ~7,000 lines, and continuing to append to it will hurt maintainability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your review, and I have moved these implementations into a separate dataset_repr.py file.

return head_rows, tail_rows, show_gap


def _determine_preview_row_targets(
Copy link
Member

Choose a reason for hiding this comment

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

Can we add some comments for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

upadted.

@owenowenisme
Copy link
Member

@myandpr The test is failing, could you fix that?

@bveeramani bveeramani added the go add ONLY when ready to merge, run all tests label Dec 26, 2025
@myandpr myandpr force-pushed the improve-repr-dataset branch from 5d5ed05 to b26992f Compare December 29, 2025 12:47
@myandpr
Copy link
Contributor Author

myandpr commented Dec 30, 2025

@myandpr The test is failing, could you fix that?

Hi @owenowenisme ,I’ve fixed the failing tests—could you take another look when you have time? Thanks a lot!

Copy link
Member

@bveeramani bveeramani left a comment

Choose a reason for hiding this comment

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

ty! Just left some comments.

Ping me whenever you're ready for another round of feedback, and I can give you a fast review

Comment on lines 397 to 401
:options: +ELLIPSIS

MaterializedDataset(
num_blocks=3,
num_rows=3,
schema={food: string, price: double}
)
shape: (3, 2)
...
(Showing 3 of 3 rows)
Copy link
Member

Choose a reason for hiding this comment

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

(Here and elsewhere) What happens if we show the actual repr rather than ellipsis?

I'm sure it's a pain to update, but I think it'll be a lot more useful to our readers. For example, in the screenshot below, it's not clear what the schema or data look like.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the doc examples in the files touched by this PR to show the full actual repr instead of .../ELLIPSIS (including loading-data.rst and quickstart.rst).

Comment on lines 158 to 177
DEFAULT_DATASET_REPR_MAX_ROWS = env_integer("RAY_DATASET_REPR_MAX_ROWS", 10)
DEFAULT_DATASET_REPR_HEAD_ROWS = env_integer("RAY_DATASET_REPR_HEAD_ROWS", 5)
DEFAULT_DATASET_REPR_MAX_COLUMNS = env_integer("RAY_DATASET_REPR_MAX_COLUMNS", 10)
DEFAULT_DATASET_REPR_HEAD_COLUMNS = env_integer("RAY_DATASET_REPR_HEAD_COLUMNS", 5)
DEFAULT_DATASET_REPR_MAX_COLUMN_WIDTH = env_integer(
"RAY_DATASET_REPR_MAX_COLUMN_WIDTH", 40
)
DEFAULT_DATASET_REPR_MAX_STRING_LENGTH = env_integer(
"RAY_DATASET_REPR_MAX_STRING_LENGTH", 40
)
DEFAULT_DATASET_REPR_MAX_BYTES_LENGTH = env_integer(
"RAY_DATASET_REPR_MAX_BYTES_LENGTH", 40
)
DEFAULT_DATASET_REPR_MAX_COLLECTION_ITEMS = env_integer(
"RAY_DATASET_REPR_MAX_COLLECTION_ITEMS", 5
)
DEFAULT_DATASET_REPR_MAX_TENSOR_ELEMENTS = env_integer(
"RAY_DATASET_REPR_MAX_TENSOR_ELEMENTS", 8
)

Copy link
Member

Choose a reason for hiding this comment

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

  1. What happens if we don't expose these configurations? Is it important that users are able to configure these?
  2. If it is important that we expose these to the user, I think it'd be cleaner if we encapsulate this within a single dataclass to avoid flooding the top-level DataContext with several repr-specific parameters

Copy link
Member

Choose a reason for hiding this comment

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

IMO I don't think it's super important to expose these knobs, but would love to get your take

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback! I agree these knobs don’t need to be user‑exposed right now. I removed the dataset_repr_* fields from DataContext and kept the defaults internal to the repr implementation (and dropped the config-based tests accordingly).

Comment on lines 6698 to 6726
except Exception:
logger.debug("Falling back to plan-based Dataset repr.", exc_info=True)
return self._plan.get_plan_as_string(self.__class__)
Copy link
Member

Choose a reason for hiding this comment

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

When would this fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a safety net for failures in the tabular repr path (e.g., schema fetch / BlockAccessor / pyarrow issues or formatting errors).
If anything in that path throws, we fall back to the plan-based repr so repr(ds) never crashes user workflows.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should do a bare except because it prevents us from removing the old code path, and it'll also mask bugs that we would want to hear about and fix

Comment on lines +6704 to +6747
if schema is None or not isinstance(schema, Schema):
return self._plan.get_plan_as_string(self.__class__)
Copy link
Member

Choose a reason for hiding this comment

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

Is the only code path where we use get_plan_as_string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

get_plan_as_string is used as a fallback in repr (when tabular rendering fails or schema is missing) and also when the dataset has no columns in
_build_dataset_ascii_repr. Since it’s a low‑level detail, I removed tests that asserted it directly.



def test_dataset_repr(ray_start_regular_shared):
def test_dataset_plan_string(ray_start_regular_shared):
Copy link
Member

Choose a reason for hiding this comment

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

get_plan_as_string is a low-level implementation detail, and I don't think we need to test it.

I think we should either test the actual repr(ds), or if itds redundant with the tests below, I think we can jsut nuke this test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I removed the tests that directly assert get_plan_as_string (both test_dataset_plan_string and test_dataset_plan_as_string) to avoid coupling to low‑level plan formatting.

Comment on lines 455 to 457
assert "Dataset isn't materialized" in text
assert "shape: (5, 1)" in text
assert "│ id" in text
Copy link
Member

Choose a reason for hiding this comment

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

Also, should we explicitly assert that the repr looks like the expected string? I think it might be easier to read and catch more errors, but I guess the tradeoff is that it'd make the test pretty brittle

Copy link
Contributor Author

Choose a reason for hiding this comment

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

update

Comment on lines 476 to 477
ctx.dataset_repr_max_rows = 4
ctx.dataset_repr_head_rows = 1
Copy link
Member

Choose a reason for hiding this comment

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

Related to my comment above -- if we don't need to expose these configuration, I don't think we need these tests

Comment on lines 1211 to 1303
]
assert "MapBatches(func)" in ds.__repr__()
plan_repr = ds._plan.get_plan_as_string(type(ds))
assert "MapBatches(func)" in plan_repr

Copy link
Member

Choose a reason for hiding this comment

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

With this and test_map -- would it make sense to assert against Dataset.explain() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes—updated both test_groupby_e2e and test_map to assert against Dataset.explain() output instead of repr / get_plan_as_string.

Comment on lines +72 to +90
def _truncate_repr_if_needed(representation: str) -> str:
"""Ensure repr strings remain shorter than MAX_REPR_LENGTH."""
if len(representation) < MAX_REPR_LENGTH:
return representation

ellipsis = "..."
closing = ""
if representation:
closing = representation[-1]
representation = representation[:-1]

max_body_len = MAX_REPR_LENGTH - len(ellipsis) - len(closing) - 1
if max_body_len < 0:
max_body_len = 0

truncated_body = representation[:max_body_len]
return f"{truncated_body}{ellipsis}{closing}"


Copy link
Member

Choose a reason for hiding this comment

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

What happens if we don't include this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this, repr(trainer) can exceed MAX_REPR_LENGTH when non‑default args (like datasets/config) are large. There’s an existing test (test_base_trainer.py::test_repr) that asserts len(repr(trainer)) < MAX_REPR_LENGTH, so this truncation keeps that invariant.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see. I think the problem is that for Train V1, it uses the dataset repr in the Trainer repr, and that looks pretty bad with the table format:

"<DummyTrainer datasets={'train': shape: (3, 1)\n╭───────╮\n│ item  │\n│ ---   │\n│ int64 │\n╞═══════╡\n│ 1     │\n│ 2     ...>"

Rather than truncate the repr, I think we should special-case datasets in the BaseTrainer repr and just do dataset._plan.get_plan_as_string(...). It's not great because it breaks abstraction barriers and uses a brittle low-level API, but I think it might be the best option for now because it's simple and we can't break Train V1

is_gap: bool = False


def _prepare_columns_for_repr(
Copy link
Member

Choose a reason for hiding this comment

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

As I read the functions in this module, it wasn't obvious to me what each of the functions did without reading the implementation. Would you mind adding docstrings summarizing what each function does (not just repeating the funtion name), and describing the input and output types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added docstring

@myandpr myandpr force-pushed the improve-repr-dataset branch 2 times, most recently from e32fa05 to 6af81f2 Compare January 9, 2026 16:29
@myandpr
Copy link
Contributor Author

myandpr commented Jan 9, 2026

Conflicts fixed.

@myandpr myandpr requested a review from bveeramani January 10, 2026 03:35
Copy link
Member

@bveeramani bveeramani left a comment

Choose a reason for hiding this comment

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

ty! Left a bunch of comments.

Some high-level feedback:

  1. I had difficulty reading dataset_repr.py. To make the module easier to read, I think we should remove unnecessary indirection (e.g., _DatasetReprConfig ) and cut scope (e.g., remove special cases that might not be necessary for this initial implementation)
  2. Could you split out the pre-requisite test refactors into different PRs? We can land the changes faster that way

"food": ["spam", "ham", "eggs"],
"price": [9.34, 5.37, 0.94]
})
df["food"] = df["food"].astype("string")
Copy link
Member

Choose a reason for hiding this comment

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

What does the output look like if we exclude this? I think readers of the documentation might feel confused as to why we include this in the code snippet


DEFAULT_ENABLE_GET_OBJECT_LOCATIONS_FOR_METRICS = False


Copy link
Member

Choose a reason for hiding this comment

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

Nit: Unintended diff?

Comment on lines 44 to 56
@dataclass
class _DatasetReprConfig:
max_rows: int
head_rows: int
max_columns: int
head_columns: int
max_column_width: int
max_string_length: int
max_bytes_length: int
max_collection_items: int
max_tensor_elements: int


Copy link
Member

Choose a reason for hiding this comment

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

Now that we've removed these configurations from the DataContext, this dataclass is a layer of indirection. Could you remove it, and also all of the dead logic? I think it'd really simplify some of this code

For example, once we remove _DatasetReprConfig , we can remove this snippet:

Before

    max_columns = config.max_columns
    if max_columns is None or max_columns <= 0:
        max_columns = len(columns)
    max_columns = max(1, max_columns)

    if num_columns <= max_columns:
        ...

After

    if num_columns <= _DATASET_REPR_MAX_COLUMNS:
        ...

Comment on lines 33 to 34
_DATASET_REPR_MAX_STRING_LENGTH = 40
_DATASET_REPR_MAX_BYTES_LENGTH = 40
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to define lengths for strings and bytes? Can we just truncate based on the column width?

Comment on lines 26 to 31
_DATASET_REPR_ELLIPSIS = "…"
_DATASET_REPR_PREVIEW_MAX_DEPTH = 2
_DATASET_REPR_MAX_ROWS = 10
_DATASET_REPR_HEAD_ROWS = 5
_DATASET_REPR_MAX_COLUMNS = 10
_DATASET_REPR_HEAD_COLUMNS = 5
Copy link
Member

Choose a reason for hiding this comment

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

Could you add comments describing what each of these constants represent? I felt confused by _DATASET_REPR_PREVIEW_MAX_DEPTH and _DATASET_REPR_HEAD_COLUMNS in particular

Comment on lines 6698 to 6726
except Exception:
logger.debug("Falling back to plan-based Dataset repr.", exc_info=True)
return self._plan.get_plan_as_string(self.__class__)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should do a bare except because it prevents us from removing the old code path, and it'll also mask bugs that we would want to hear about and fix

Comment on lines 1910 to 1914
plan_str = ds._plan.get_plan_as_string(type(ds))
assert plan_str == (
"MapBatches(<lambda>)\n"
"+- Dataset(name=test_ds, num_rows=100, schema={id: int64})"
)
Copy link
Member

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 assertion will make a lot of sense after this PR, because it's testing against an extremely low-level API that's mostly obsolete. I think the intention of this assertion is to make sure we include the dataset name in the repr and that the dataset name is correct.

If the user has specified a name, should we include that somewhere in the repr?

For the assertion here and later in this function, I think we should just test that the name is in the repr, like assert "test_ds" in repr(ds)

Comment on lines 332 to 333
assert str(ds) == (
plan_str = ds._plan.get_plan_as_string(type(ds))
assert plan_str == (
Copy link
Member

Choose a reason for hiding this comment

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

Could you update the assertions in this file to explicitly check the count and scheme rather than test against get_plan_as_string?

Comment on lines +72 to +90
def _truncate_repr_if_needed(representation: str) -> str:
"""Ensure repr strings remain shorter than MAX_REPR_LENGTH."""
if len(representation) < MAX_REPR_LENGTH:
return representation

ellipsis = "..."
closing = ""
if representation:
closing = representation[-1]
representation = representation[:-1]

max_body_len = MAX_REPR_LENGTH - len(ellipsis) - len(closing) - 1
if max_body_len < 0:
max_body_len = 0

truncated_body = representation[:max_body_len]
return f"{truncated_body}{ellipsis}{closing}"


Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see. I think the problem is that for Train V1, it uses the dataset repr in the Trainer repr, and that looks pretty bad with the table format:

"<DummyTrainer datasets={'train': shape: (3, 1)\n╭───────╮\n│ item  │\n│ ---   │\n│ int64 │\n╞═══════╡\n│ 1     │\n│ 2     ...>"

Rather than truncate the repr, I think we should special-case datasets in the BaseTrainer repr and just do dataset._plan.get_plan_as_string(...). It's not great because it breaks abstraction barriers and uses a brittle low-level API, but I think it might be the best option for now because it's simple and we can't break Train V1

Comment on lines 148 to 27
def _build_dataset_ascii_repr(
dataset: "Dataset",
schema: "Schema",
is_materialized: bool,
config: _DatasetReprConfig,
) -> str:
Copy link
Member

Choose a reason for hiding this comment

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

This isn't easily testable because it requires a Ray cluster and can't be truly unit tested.

To make this easy to quickly test (and so that we can easily test all of the edge and special cases), could you split this into two functions:

  1. One higher-level functions that accepts a dataset and maybe some other parameters as input
  2. A lower-level function that just accepts a schema, row count, dataset name, and head and tail dict[str, Any] rows?

And then we can write tests for most of the cases with (2), put them in data/tests/unit, and they'll run immediately

@myandpr myandpr force-pushed the improve-repr-dataset branch from 6af81f2 to a7565c5 Compare January 13, 2026 21:12
def _format_value(value: Any) -> str:
if isinstance(value, np.generic):
value = value.item()
return str(value)
Copy link

Choose a reason for hiding this comment

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

Newlines in cell values break table rendering

Medium Severity

The _format_value function uses str(value) directly, which for numpy arrays produces strings containing embedded newlines (e.g., '[[1. 1.]\n [1. 1.]]' for a 2x2 array). Since this 20-character string is shorter than _DATASET_REPR_MAX_COLUMN_WIDTH (40), it won't be truncated by _truncate_to_cell_width. The newline character is then passed to _render_row, where ljust() doesn't handle it, causing the table border structure to break across multiple lines and corrupting the ASCII table output.

Fix in Cursor Fix in Web

@myandpr myandpr force-pushed the improve-repr-dataset branch from a7565c5 to 0b8fc4d Compare January 14, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community data Ray Data-related issues go add ONLY when ready to merge, run all tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Data] Improve appearance of repr(dataset)

4 participants