Skip to content

Commit 9e1e910

Browse files
authored
fix(Datapoints): restore plain identifier column names in returned dataframes (#2603)
1 parent 2633e59 commit 9e1e910

4 files changed

Lines changed: 47 additions & 11 deletions

File tree

cognite/client/utils/_pandas_helpers.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,10 @@ def _create_multi_index_from_columns(
352352
include_aggregate: bool,
353353
include_granularity: bool,
354354
include_unit: bool,
355-
) -> pd.MultiIndex:
355+
) -> pd.Index:
356356
import pandas as pd
357357

358-
column_ids_df = pd.DataFrame(
358+
column_ids = pd.DataFrame(
359359
[
360360
col.as_multi_index_tuple(
361361
include_aggregate=include_aggregate,
@@ -368,9 +368,13 @@ def _create_multi_index_from_columns(
368368
)
369369
# Key operation is to drop all-nan columns, which in the multi-index translates to dropping
370370
# the corresponding levels:
371-
non_id_levels = column_ids_df.iloc[:, 1:].dropna(axis="columns", how="all").fillna("")
371+
non_id_levels = column_ids.iloc[:, 1:].dropna(axis="columns", how="all").fillna("")
372+
# When none of the extra levels survive (status/agg./gran./unit), return a plain Index so
373+
# columns are the bare identifiers rather than 1-tuples:
374+
if non_id_levels.columns.empty:
375+
return pd.Index(column_ids["identifier"])
372376
# ...but we always keep the identifier column:
373-
return pd.MultiIndex.from_frame(pd.concat((column_ids_df.iloc[:, [0]], non_id_levels), axis=1, copy=False))
377+
return pd.MultiIndex.from_frame(pd.concat((column_ids[["identifier"]], non_id_levels), axis=1, copy=False))
374378

375379

376380
def _create_timestamp_index(

scripts/test-pyodide.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ server.listen(PORT, () => {
4646
// stlite bumps to a Pyodide release that ships cryptography>=45.0.1
4747
// (Pyodide 0.29.0 already does). After the expiry date, the workaround is
4848
// skipped — if it's still needed the install will fail loudly.
49-
if (new Date() < new Date("2026-05-03")) {
49+
if (new Date() < new Date("2026-07-04")) {
5050
await pyodide.loadPackage(["cryptography", "ssl"]);
5151
await micropip.install("authlib<1.7");
5252
}

tests/tests_unit/test_api/test_datapoints.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ def test_datapoints_no_names(self) -> None:
646646

647647
d = Datapoints(id=1, is_string=False, is_step=False, type="numeric", timestamp=[1, 2, 3], average=[2, 3, 4])
648648
expected_df = pd.DataFrame({1: [2, 3, 4.0]}, index=pd.to_datetime(range(1, 4), unit="ms"))
649-
expected_df.columns = pd.MultiIndex.from_tuples([(1,)], names=["identifier"])
649+
expected_df.columns = pd.Index([1], name="identifier")
650650
pd.testing.assert_frame_equal(expected_df, d.to_pandas(include_aggregate_name=False))
651651

652652
expected_df = pd.DataFrame({1: [2, 3, 4.0]}, index=pd.to_datetime(range(1, 4), unit="ms"))
@@ -676,6 +676,40 @@ def test_id_and_external_id_set_gives_external_id_columns(self) -> None:
676676
)
677677
pd.testing.assert_frame_equal(expected_df, d.to_pandas())
678678

679+
def test_raw_datapoints_external_id_gives_plain_index(self) -> None:
680+
import pandas as pd
681+
682+
d = Datapoints(
683+
id=1,
684+
external_id="my-ts",
685+
is_string=False,
686+
is_step=False,
687+
type="numeric",
688+
timestamp=[1, 2],
689+
value=[3.0, 4.0],
690+
)
691+
df = d.to_pandas()
692+
assert isinstance(df.columns, pd.Index) and not isinstance(df.columns, pd.MultiIndex)
693+
assert list(df.columns) == ["my-ts"]
694+
695+
def test_raw_datapoints_with_status_codes_gives_multi_index(self) -> None:
696+
import pandas as pd
697+
698+
d = Datapoints(
699+
id=1,
700+
external_id="my-ts",
701+
is_string=False,
702+
is_step=False,
703+
type="numeric",
704+
timestamp=[1, 2],
705+
value=[3.0, 4.0],
706+
status_code=[0, 0],
707+
status_symbol=["Good", "Good"],
708+
)
709+
df = d.to_pandas()
710+
assert isinstance(df.columns, pd.MultiIndex)
711+
assert df.columns.names == ["identifier", "status"]
712+
679713
def test_datapoints_empty(self) -> None:
680714
d = Datapoints(id=0, is_string=False, is_step=False, type="numeric", external_id="1", timestamp=[], value=[])
681715
assert d.to_pandas().empty
@@ -729,7 +763,7 @@ def test_datapoints_list_names(self) -> None:
729763
expected_df = pd.DataFrame({1: [2, 3, 4.0], 2: [1, None, 3]}, index=pd.to_datetime(range(1, 4), unit="ms"))
730764
expected_df.columns = pd.MultiIndex.from_tuples([(2, "max"), (3, "average")], names=["identifier", "aggregate"])
731765
pd.testing.assert_frame_equal(expected_df, dps_list.to_pandas(), check_freq=False)
732-
expected_df.columns = pd.MultiIndex.from_tuples([(2,), (3,)], names=["identifier"])
766+
expected_df.columns = pd.Index([2, 3], name="identifier")
733767
pd.testing.assert_frame_equal(expected_df, dps_list.to_pandas(include_aggregate_name=False), check_freq=False)
734768

735769
def test_datapoints_list_names_dup(self) -> None:
@@ -759,7 +793,7 @@ def test_datapoints_list_non_aligned(self) -> None:
759793
{1: [1, 2, 3, None, None], 2: [None, None, 3, 4, 5]},
760794
index=pd.to_datetime(range(1, 6), unit="ms"),
761795
)
762-
expected_df.columns = pd.MultiIndex.from_tuples([(1,), (2,)], names=["identifier"])
796+
expected_df.columns = pd.Index([1, 2], name="identifier")
763797
pd.testing.assert_frame_equal(expected_df, dps_list.to_pandas(), check_freq=False)
764798

765799
def test_datapoints_list_empty(self) -> None:

tests/tests_unit/test_data_classes/test_datapoints.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,5 @@ def test_identifier_priority(self, dps_lst_cls: type[CogniteResourceList]) -> No
113113
{1: 2.0, 2: 4.0, 3: 6.0},
114114
index=np.array([1234 * 1_000_000], dtype="datetime64[ns]"),
115115
)
116-
exp_df.columns = pd.MultiIndex.from_tuples(
117-
[(123,), ("foo",), (NodeId(space="s", external_id="x"),)], names=["identifier"]
118-
)
116+
exp_df.columns = pd.Index([123, "foo", NodeId(space="s", external_id="x")], name="identifier")
119117
pd.testing.assert_frame_equal(df, exp_df)

0 commit comments

Comments
 (0)