-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Jonas Hoersch <jonas.hoersch@climateanalytics.org>
- Loading branch information
1 parent
764a85e
commit c5e8f9c
Showing
10 changed files
with
240 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.. currentmodule:: pyam | ||
|
||
The **IamSlice** class | ||
====================== | ||
|
||
.. autoclass:: IamSlice | ||
:members: dimensions, time, info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import pandas as pd | ||
from pyam.utils import print_list | ||
|
||
|
||
class IamSlice(pd.Series): | ||
"""A slice object of the IamDataFrame timeseries data index""" | ||
|
||
@property | ||
def _constructor(self): | ||
return IamSlice | ||
|
||
_internal_names = pd.Series._internal_names + ["_iamcache"] | ||
_internal_names_set = set(_internal_names) | ||
|
||
def __init__(self, data=None, index=None, **kwargs): | ||
super().__init__(data, index, **kwargs) | ||
self._iamcache = dict() | ||
|
||
def __dir__(self): | ||
return self.dimensions + super().__dir__() | ||
|
||
def __getattr__(self, attr): | ||
try: | ||
return super().__getattr__(attr) | ||
except AttributeError: | ||
cache = object.__getattribute__(self, "_iamcache") | ||
ret = cache.get(attr) | ||
if ret is not None: | ||
return ret.tolist() | ||
|
||
if attr in self.dimensions: | ||
ret = cache[attr] = self.index[self].unique(level=attr) | ||
return ret.tolist() | ||
|
||
raise | ||
|
||
def __len__(self): | ||
return self.sum() | ||
|
||
@property | ||
def dimensions(self): | ||
"""Return the list of index names & data coordinates""" | ||
return self.index.names | ||
|
||
@property | ||
def time(self): | ||
"""The time index, i.e., axis labels related to the time domain. | ||
Returns | ||
------- | ||
- A :class:`pandas.Int64Index` if the time-domain is 'year' | ||
- A :class:`pandas.DatetimeIndex` if the time-domain is 'datetime' | ||
- A :class:`pandas.Index` if the time-domain is 'mixed' | ||
""" | ||
ret = self._iamcache.get("time") | ||
if ret is None: | ||
ret = self._iamcache["time"] = ( | ||
self.index[self].unique(level=self.time_col).rename("time") | ||
) | ||
return ret | ||
|
||
@property | ||
def time_col(self): | ||
return "year" if "year" in self.dimensions else "time" | ||
|
||
def __repr__(self): | ||
return self.info() | ||
|
||
def info(self, n=80): | ||
"""Print a summary of the represented index dimensions and data coordinates | ||
Parameters | ||
---------- | ||
n : int | ||
The maximum line length | ||
""" | ||
# concatenate list of index dimensions and levels | ||
info = f"{type(self)}\nIndex dimensions and data coordinates:\n" | ||
c1 = max([len(i) for i in self.dimensions]) + 1 | ||
c2 = n - c1 - 5 | ||
info += "\n".join( | ||
[ | ||
f" {i:{c1}}: {print_list(getattr(self, i), c2)}" | ||
for i in self.dimensions | ||
] | ||
) | ||
|
||
return info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import pandas as pd | ||
import pytest | ||
|
||
|
||
def test_slice_len(test_df_year): | ||
"""Check the length of a slice""" | ||
|
||
assert len(test_df_year.slice(scenario="scen_a")) == 4 | ||
|
||
|
||
def test_slice_index_attributes(test_df): | ||
# assert that the index and data column attributes are set correctly in an IamSlice | ||
|
||
s = test_df.slice() | ||
|
||
assert s.model == ["model_a"] | ||
assert s.scenario == ["scen_a", "scen_b"] | ||
assert s.region == ["World"] | ||
assert s.variable == ["Primary Energy", "Primary Energy|Coal"] | ||
assert s.unit == ["EJ/yr"] | ||
if test_df.time_col == "year": | ||
assert s.year == [2005, 2010] | ||
else: | ||
match = "'IamSlice' object has no attribute 'year'" | ||
with pytest.raises(AttributeError, match=match): | ||
s.year | ||
assert s.time.equals(pd.Index(test_df.data[test_df.time_col].unique())) | ||
|
||
|
||
def test_filtered_slice_index_attributes(test_df_year): | ||
# assert that the attributes are set correctly in a filtered IamSlice | ||
|
||
s = test_df_year.slice(scenario="scen_b") | ||
assert s.scenario == ["scen_b"] | ||
|
||
|
||
def test_print(test_df_year): | ||
"""Assert that `print(IamSlice)` (and `info()`) returns as expected""" | ||
exp = "\n".join( | ||
[ | ||
"<class 'pyam.slice.IamSlice'>", | ||
"Index dimensions and data coordinates:", | ||
" model : model_a (1)", | ||
" scenario : scen_a, scen_b (2)", | ||
" region : World (1)", | ||
" variable : Primary Energy, Primary Energy|Coal (2)", | ||
" unit : EJ/yr (1)", | ||
" year : 2005, 2010 (2)", | ||
] | ||
) | ||
obs = test_df_year.slice().info() | ||
assert obs == exp |
Oops, something went wrong.