From f8e50aa6b64b0cc0f0baca403fab8803a17aaa85 Mon Sep 17 00:00:00 2001 From: Philip Hackstock Date: Tue, 24 May 2022 09:59:24 +0200 Subject: [PATCH] Fix/concat (#665) * Add index transfer to concat * Add test for non-standard concat * Add details to concat docstring * Apply suggestions from code review Co-authored-by: Daniel Huppmann Co-authored-by: Daniel Huppmann --- pyam/core.py | 8 +++++++- tests/test_feature_append_concat.py | 32 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pyam/core.py b/pyam/core.py index 55a86af0d..cb958c9f1 100755 --- a/pyam/core.py +++ b/pyam/core.py @@ -2793,6 +2793,8 @@ def concat(objs, ignore_meta_conflict=False, **kwargs): The *meta* attributes are merged only for those objects of *objs* that are passed as :class:`IamDataFrame` instances. + The :attr:`dimensions` and :attr:`index` names of all elements of *dfs* must be + identical. The returned IamDataFrame inherits the dimensions and index names. """ if not islistable(objs) or isinstance(objs, pd.DataFrame): raise TypeError(f"'{objs.__class__.__name__}' object is not iterable") @@ -2844,7 +2846,11 @@ def as_iamdataframe(df): ) # return as new IamDataFrame, this will verify integrity as part of `__init__()` - return IamDataFrame(pd.concat(ret_data, verify_integrity=False), meta=ret_meta) + return IamDataFrame( + pd.concat(ret_data, verify_integrity=False), + meta=ret_meta, + index=ret_meta.index.names, + ) def read_datapackage(path, data="data", meta="meta"): diff --git a/tests/test_feature_append_concat.py b/tests/test_feature_append_concat.py index 0e030ec0e..0ea0f90cb 100644 --- a/tests/test_feature_append_concat.py +++ b/tests/test_feature_append_concat.py @@ -104,6 +104,38 @@ def test_concat(test_df, reverse, iterable): npt.assert_array_equal(ts.iloc[2].values, ts.iloc[3].values) +def test_concat_non_default_index(): + # Test that merging two IamDataFrames with identical, non-standard index dimensions + # preserves the index. + + df1 = IamDataFrame( + pd.DataFrame( + [["model_a", "scenario_a", "region_a", "variable_a", "unit", 1, 1]], + columns=IAMC_IDX + ["version", 2005], + ), + index=META_IDX + ["version"], + ) + df2 = IamDataFrame( + pd.DataFrame( + [["model_a", "scenario_a", "region_a", "variable_a", "unit", 2, 2]], + columns=IAMC_IDX + ["version", 2005], + ), + index=META_IDX + ["version"], + ) + exp = IamDataFrame( + pd.DataFrame( + [ + ["model_a", "scenario_a", "region_a", "variable_a", "unit", 1, 1], + ["model_a", "scenario_a", "region_a", "variable_a", "unit", 2, 2], + ], + columns=IAMC_IDX + ["version", 2005], + ), + index=META_IDX + ["version"], + ) + + assert_iamframe_equal(exp, concat([df1, df2])) + + @pytest.mark.parametrize("reverse", (False, True)) def test_concat_with_pd_dataframe(test_df, reverse): other = test_df.filter(scenario="scen_b").rename({"scenario": {"scen_b": "scen_c"}})