|
4 | 4 |
|
5 | 5 | from __future__ import annotations |
6 | 6 |
|
7 | | -from collections.abc import Mapping |
| 7 | +from collections.abc import Collection, Mapping |
8 | 8 | from typing import TYPE_CHECKING, Any, Callable, TypeVar |
9 | 9 |
|
10 | 10 | import numpy as np |
@@ -714,3 +714,56 @@ def update_levels_from_other( |
714 | 714 | res = pd.MultiIndex(levels=levels, codes=codes, names=names) |
715 | 715 |
|
716 | 716 | return res |
| 717 | + |
| 718 | + |
| 719 | +def set_levels( |
| 720 | + ini: pd.MultiIndex, levels_to_set: dict[str, Any | Collection[Any]] |
| 721 | +) -> pd.MultiIndex: |
| 722 | + """ |
| 723 | + Set the levels of a MultiIndex to the provided values |
| 724 | +
|
| 725 | + Parameters |
| 726 | + ---------- |
| 727 | + ini |
| 728 | + Input MultiIndex |
| 729 | +
|
| 730 | + levels_to_set |
| 731 | + Mapping of level names to values to set |
| 732 | +
|
| 733 | + Returns |
| 734 | + ------- |
| 735 | + New MultiIndex with the levels set to the provided values |
| 736 | +
|
| 737 | + Raises |
| 738 | + ------ |
| 739 | + TypeError |
| 740 | + If `ini` is not a MultiIndex |
| 741 | + ValueError |
| 742 | + If the length of the values is not equal to the length of the index |
| 743 | + """ |
| 744 | + # set the levels specified in levels_to_set to the provided values |
| 745 | + # |
| 746 | + # We should support both single values e.g. levels_to_set={"variable": "Emissions"} |
| 747 | + # and values with the same length as the index itself e.g. |
| 748 | + # levels_to_set={"variable": ["a", "b", "c"]}. |
| 749 | + # Values of any other length should raise an error (because we don't know what to do |
| 750 | + # if the index is say 4 elements long but the user only gives us 3 values) |
| 751 | + # |
| 752 | + # This should work whether the level to be set exists or not |
| 753 | + # TODO: move to pandas-openscm |
| 754 | + # TODO: split out method that just works on MultiIndex |
| 755 | + |
| 756 | + new_names = levels_to_set.keys() |
| 757 | + new_values = levels_to_set.values() |
| 758 | + |
| 759 | + if not isinstance(ini, pd.MultiIndex): |
| 760 | + raise TypeError(ini) |
| 761 | + |
| 762 | + return pd.MultiIndex( |
| 763 | + codes=[ |
| 764 | + *ini.codes, # type: ignore # not sure why check above isn't working |
| 765 | + *([[0] * ini.shape[0]] * len(new_values)), # type: ignore # fix when moving to pandas-openscm |
| 766 | + ], |
| 767 | + levels=[*ini.levels, *[pd.Index([value]) for value in new_values]], # type: ignore # fix when moving to pandas-openscm |
| 768 | + names=[*ini.names, *new_names], # type: ignore # fix when moving to pandas-openscm |
| 769 | + ) |
0 commit comments