diff --git a/crates/store/re_chunk_store/src/dataframe.rs b/crates/store/re_chunk_store/src/dataframe.rs index dfd90362facb..c6b17aff0cf0 100644 --- a/crates/store/re_chunk_store/src/dataframe.rs +++ b/crates/store/re_chunk_store/src/dataframe.rs @@ -340,7 +340,11 @@ impl ComponentColumnDescriptor { #[inline] pub fn to_arrow_field(&self) -> ArrowField { ArrowField::new( - self.component_name.short_name().to_owned(), + format!( + "{}:{}", + self.entity_path, + self.component_name.short_name().to_owned() + ), self.returned_datatype(), true, /* nullable */ ) diff --git a/rerun_py/rerun_sdk/rerun/dataframe.py b/rerun_py/rerun_sdk/rerun/dataframe.py index 13b6128bfbec..56a466682d1a 100644 --- a/rerun_py/rerun_sdk/rerun/dataframe.py +++ b/rerun_py/rerun_sdk/rerun/dataframe.py @@ -3,11 +3,11 @@ from rerun_bindings import ( ComponentColumnDescriptor as ComponentColumnDescriptor, ComponentColumnSelector as ComponentColumnSelector, + IndexColumnDescriptor as IndexColumnDescriptor, + IndexColumnSelector as IndexColumnSelector, Recording as Recording, RRDArchive as RRDArchive, Schema as Schema, - TimeColumnDescriptor as TimeColumnDescriptor, - TimeColumnSelector as TimeColumnSelector, load_archive as load_archive, load_recording as load_recording, ) diff --git a/rerun_py/src/dataframe.rs b/rerun_py/src/dataframe.rs index 9000254ebf2d..37501f48b0e6 100644 --- a/rerun_py/src/dataframe.rs +++ b/rerun_py/src/dataframe.rs @@ -41,8 +41,8 @@ pub(crate) fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -/// Python binding for [`TimeColumnDescriptor`] -#[pyclass(frozen, name = "TimeColumnDescriptor")] +/// Python binding for `IndexColumnDescriptor` +#[pyclass(frozen, name = "IndexColumnDescriptor")] #[derive(Clone)] struct PyIndexColumnDescriptor(TimeColumnDescriptor); @@ -59,8 +59,8 @@ impl From for PyIndexColumnDescriptor { } } -/// Python binding for [`TimeColumnSelector`] -#[pyclass(frozen, name = "TimeColumnSelector")] +/// Python binding for `IndexColumnSelector` +#[pyclass(frozen, name = "IndexColumnSelector")] #[derive(Clone)] struct PyIndexColumnSelector(TimeColumnSelector); diff --git a/rerun_py/tests/unit/test_dataframe.py b/rerun_py/tests/unit/test_dataframe.py index 98b046dfdda5..993269e0dfe3 100644 --- a/rerun_py/tests/unit/test_dataframe.py +++ b/rerun_py/tests/unit/test_dataframe.py @@ -10,7 +10,9 @@ class TestDataframe: def setup_method(self) -> None: rr.init("rerun_example_test_recording") + rr.set_time_sequence("my_index", 1) rr.log("points", rr.Points3D([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) + rr.set_time_sequence("my_index", 7) rr.log("points", rr.Points3D([[10, 11, 12]], colors=[[255, 0, 0]])) with tempfile.TemporaryDirectory() as tmpdir: @@ -21,26 +23,38 @@ def setup_method(self) -> None: self.recording = rr.dataframe.load_recording(rrd) def test_full_view(self) -> None: - view = self.recording.view(index="log_time", contents="points") + view = self.recording.view(index="my_index", contents="points") batches = view.select() table = pa.Table.from_batches(batches, batches.schema) - # log_time, log_tick, indicator, points, colors - assert table.num_columns == 5 + # my_index, log_time, log_tick, indicator, points, colors + assert table.num_columns == 6 assert table.num_rows == 2 - def test_select_column(self) -> None: - view = self.recording.view(index="log_time", contents="points") + def test_select_columns(self) -> None: + view = self.recording.view(index="my_index", contents="points") + log_time = rr.dataframe.IndexColumnSelector("my_index") pos = rr.dataframe.ComponentColumnSelector("points", rr.components.Position3D) - batches = view.select(pos) + + batches = view.select(log_time, pos) table = pa.Table.from_batches(batches, batches.schema) # points - assert table.num_columns == 1 + assert table.num_columns == 2 assert table.num_rows == 2 - expected0 = pa.array( + expected_index0 = pa.array( + [1], + type=pa.int64(), + ) + + expected_index1 = pa.array( + [7], + type=pa.int64(), + ) + + expected_pos0 = pa.array( [ [1, 2, 3], [4, 5, 6], @@ -49,15 +63,19 @@ def test_select_column(self) -> None: type=rr.components.Position3D.arrow_type(), ) - expected1 = pa.array( + expected_pos1 = pa.array( [ [10, 11, 12], ], type=rr.components.Position3D.arrow_type(), ) - assert table.column(0)[0].values.equals(expected0) - assert table.column(0)[1].values.equals(expected1) + print(table.schema) + + assert table.column("my_index")[0].equals(expected_index0[0]) + assert table.column("my_index")[1].equals(expected_index1[0]) + assert table.column("/points:Position3D")[0].values.equals(expected_pos0) + assert table.column("/points:Position3D")[1].values.equals(expected_pos1) def test_view_syntax(self) -> None: good_content_expressions = [ @@ -68,12 +86,12 @@ def test_view_syntax(self) -> None: ] for expr in good_content_expressions: - view = self.recording.view(index="log_time", contents=expr) + view = self.recording.view(index="my_index", contents=expr) batches = view.select() table = pa.Table.from_batches(batches, batches.schema) - # log_time, log_tick, points - assert table.num_columns == 3 + # my_index, log_time, log_tick, points + assert table.num_columns == 4 assert table.num_rows == 2 bad_content_expressions = [ @@ -82,10 +100,10 @@ def test_view_syntax(self) -> None: ] for expr in bad_content_expressions: - view = self.recording.view(index="log_time", contents=expr) + view = self.recording.view(index="my_index", contents=expr) batches = view.select() - # log_time, log_tick + # my_index, log_time, log_tick table = pa.Table.from_batches(batches, batches.schema) - assert table.num_columns == 2 + assert table.num_columns == 3 assert table.num_rows == 0