Skip to content

Commit 7b5a402

Browse files
committed
Add missing rows support
1 parent 4513cb7 commit 7b5a402

File tree

2 files changed

+93
-10
lines changed

2 files changed

+93
-10
lines changed

src/pandas_openscm/unit_conversion.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,9 @@ def convert_unit(
185185
then the unit conversion will not happen.
186186
187187
If this is a [pd.Series][pandas.Series],
188-
then it will be passed directly to [convert_unit_from_target_series][]
189-
and the requirements for that function apply.
188+
then it will be passed to [convert_unit_from_target_series][]
189+
after filling any rows in `df` that are not in `desired_unit`
190+
with the unit from `df` (i.e. unspecified rows are not converted).
190191
191192
For further details, see examples
192193
@@ -205,7 +206,6 @@ def convert_unit(
205206
:
206207
`df` with converted units
207208
208-
209209
Examples
210210
--------
211211
>>> import pandas as pd
@@ -253,22 +253,22 @@ def convert_unit(
253253
>>> convert_unit(
254254
... start,
255255
... pd.Series(
256-
... ["K", "mK", "degF"],
256+
... ["K", "degF"],
257257
... index=pd.MultiIndex.from_tuples(
258258
... (
259259
... ("sa", "temperature"),
260-
... ("sb", "temperature"),
260+
... # ("sb", "temperature") not included therefore not converted
261261
... ("sb", "body temperature"),
262262
... ),
263263
... names=["scenario", "variable"],
264264
... ),
265265
... ),
266266
... )
267-
2020 2030 2050
267+
2020 2030 2050
268268
scenario variable unit
269-
sa temperature K 0.001 0.002 0.003
270-
sb temperature mK 1100.000 1200.000 1300.000
271-
body temperature degF 98.600 100.580 100.220
269+
sa temperature K 0.001 0.002 0.003
270+
sb temperature K 1.100 1.200 1.300
271+
body temperature degF 98.600 100.580 100.220
272272
"""
273273
df_units_s = df.index.get_level_values(unit_level).to_series(
274274
index=df.index.droplevel(unit_level), name="df_unit"
@@ -288,7 +288,13 @@ def convert_unit(
288288
desired_unit_s = df_units_s.replace(desired_unit)
289289

290290
elif isinstance(desired_unit, pd.Series):
291-
desired_unit_s = desired_unit
291+
missing = df_units_s.index.difference(desired_unit.index)
292+
if missing.empty:
293+
desired_unit_s = desired_unit
294+
else:
295+
desired_unit_s = pd.concat(
296+
[desired_unit, multi_index_lookup(df_units_s, missing)]
297+
)
292298

293299
else:
294300
raise NotImplementedError(type(desired_unit))

tests/integration/test_unit_conversion.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,86 @@ def test_convert_series():
135135
timepoints=np.array([1850.0, 2000.0, 2050.0, 2100.0]),
136136
)
137137

138+
target_units = (
139+
start.loc[start.index.get_level_values("variable") != "temperature"]
140+
.reset_index("unit")["unit"]
141+
.replace({"W / m^2": "ZJ / yr / m^2", "ZJ": "PJ"})
142+
)
143+
144+
res = convert_unit(start, target_units)
145+
146+
np.testing.assert_allclose(
147+
res.loc[res.index.get_level_values("variable") == "temperature", :].values,
148+
start.loc[start.index.get_level_values("variable") == "temperature", :].values,
149+
)
150+
151+
np.testing.assert_allclose(
152+
res.loc[res.index.get_level_values("variable") == "erf", :].values,
153+
(60.0 * 60.0 * 24.0 * 365.25)
154+
* 1e-21
155+
* start.loc[start.index.get_level_values("variable") == "erf", :].values,
156+
)
157+
158+
np.testing.assert_allclose(
159+
res.loc[res.index.get_level_values("variable") == "ohc", :].values,
160+
1e6 * start.loc[start.index.get_level_values("variable") == "ohc", :].values,
161+
)
162+
163+
164+
def test_convert_series_all_rows():
165+
# Check that conversion works if user supplies a Series of target units
166+
start = create_test_df(
167+
variables=[
168+
("temperature", "K"),
169+
("erf", "W / m^2"),
170+
("ohc", "ZJ"),
171+
],
172+
n_scenarios=2,
173+
n_runs=2,
174+
timepoints=np.array([1850.0, 2000.0, 2050.0, 2100.0]),
175+
)
176+
177+
target_units = start.reset_index("unit")["unit"].replace(
178+
{"W / m^2": "ZJ / yr / m^2", "ZJ": "PJ"}
179+
)
180+
181+
res = convert_unit(start, target_units)
182+
183+
np.testing.assert_allclose(
184+
res.loc[res.index.get_level_values("variable") == "temperature", :].values,
185+
start.loc[start.index.get_level_values("variable") == "temperature", :].values,
186+
)
187+
188+
np.testing.assert_allclose(
189+
res.loc[res.index.get_level_values("variable") == "erf", :].values,
190+
(60.0 * 60.0 * 24.0 * 365.25)
191+
* 1e-21
192+
* start.loc[start.index.get_level_values("variable") == "erf", :].values,
193+
)
194+
195+
np.testing.assert_allclose(
196+
res.loc[res.index.get_level_values("variable") == "ohc", :].values,
197+
1e6 * start.loc[start.index.get_level_values("variable") == "ohc", :].values,
198+
)
199+
200+
201+
def test_convert_series_extra_rows():
202+
# Check that conversion works if user supplies a Series of target units
203+
start = create_test_df(
204+
variables=[
205+
("temperature", "K"),
206+
("erf", "W / m^2"),
207+
("ohc", "ZJ"),
208+
],
209+
n_scenarios=2,
210+
n_runs=2,
211+
timepoints=np.array([1850.0, 2000.0, 2050.0, 2100.0]),
212+
)
213+
138214
target_units = start.reset_index("unit")["unit"].replace(
139215
{"W / m^2": "ZJ / yr / m^2", "ZJ": "PJ"}
140216
)
217+
target_units.loc[("scenario_2", "temperature", 0)] = "kK"
141218

142219
res = convert_unit(start, target_units)
143220

0 commit comments

Comments
 (0)