Skip to content

Commit 300b0b9

Browse files
committed
Add Leaderboard.add_category, add_model, add_unit
This commit adds functionality to add a single or multiple categories, models, or units.
1 parent 106b205 commit 300b0b9

File tree

5 files changed

+212
-1
lines changed

5 files changed

+212
-1
lines changed

NEWS.md

+13
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@ rmse_var[2,5] = 11.2
355355
rmse_var[:, :]
356356
```
357357

358+
#### Adding categories, models, and units
359+
Adding categories (e.g., seasons, months, years, etc.), models, and units to a `RMSEVariable`
360+
can be done using `add_category`, `add_model`, and `add_units!`.
361+
362+
See the example below for how to use this functionality.
363+
364+
Adding categories (e.g., seasons, months, years, etc.), models, and units to a `RMSEVariable`
365+
rmse_var2 = ClimaAnalysis.add_category(rmse_var, "Jan") # can take in mode than one category
366+
rmse_var = ClimaAnalysis.add_model(rmse_var, "CliMA") # can take in more than one model name
367+
ClimaAnalysis.add_unit!(rmse_var, "CliMA", "K")
368+
ClimaAnalysis.add_unit!(rmse_var, Dict("CliMA" => "K")) # for adding multiple units
369+
```
370+
358371
## Bug fixes
359372
360373
- Increased the default value for `warp_string` to 72.

docs/src/api.md

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ Base.getindex(rmse_var::RMSEVariable, model_name, category)
8585
Base.getindex(rmse_var::RMSEVariable, model_name::String)
8686
Base.setindex!(rmse_var::RMSEVariable, rmse, model_name, category)
8787
Base.setindex!(rmse_var::RMSEVariable, rmse, model_name::String)
88+
Leaderboard.add_category
89+
Leaderboard.add_model
90+
Leaderboard.add_unit!
8891
```
8992

9093
## Utilities

docs/src/rmse_var.md

+24
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,28 @@ rmse_var[:, "MAM"]
110110
rmse_var["ACCESS-CM2", ["ANN", "DJF", "MAM"]]
111111
rmse_var[2,5] = 11.2;
112112
rmse_var[:, :]
113+
```
114+
115+
## Adding categories, models, and units
116+
117+
It may be the case that the CSV file does not contain all the models you want to analyze, or
118+
you want to consider another category but do not want to go in and manually edit the CSV
119+
file to add it. `ClimaAnalysis` provides `add_category`, `add_model`, and `add_unit!` for
120+
adding categories, models, and units respectively. Multiple model or categories can be
121+
provided (e.g., `add_model(rmse_var, "model1", "model2")`) in the functions. For adding
122+
multiple units, one can pass in a dictionary mapping model names to units. See the example
123+
below using this functionality.
124+
125+
```@julia rmse_var
126+
rmse_var2 = ClimaAnalysis.add_category(rmse_var, "Jan") # can take in more than one category
127+
rmse_var = ClimaAnalysis.add_model(rmse_var, "CliMA") # can take in more than one model name
128+
ClimaAnalysis.add_unit!(rmse_var, "CliMA", "K")
129+
ClimaAnalysis.add_unit!(rmse_var, Dict("CliMA" => "K")) # for adding multiple units
130+
```
131+
132+
```@repl rmse_var
133+
ClimaAnalysis.category_names(rmse_var2)
134+
ClimaAnalysis.model_names(rmse_var)
135+
ClimaAnalysis.rmse_units(rmse_var)
136+
rmse_var[:,:]
113137
```

src/Leaderboard.jl

+97-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export RMSEVariable,
99
rmse_units,
1010
read_rmses,
1111
getindex,
12-
setindex!
12+
setindex!,
13+
add_category,
14+
add_model,
15+
add_unit!
1316

1417
"""
1518
Holding root mean squared errors over multiple categories and models for a single
@@ -422,4 +425,97 @@ function Base.setindex!(rmse_var::RMSEVariable, rmse, model_name::String)
422425
rmse_var.RMSEs[model_idx, :] = rmse
423426
end
424427

428+
"""
429+
add_category(rmse_var::RMSEVariable, categories::String...)
430+
431+
Add one or more categories named `categories` to `rmse_var`.
432+
"""
433+
function add_category(rmse_var::RMSEVariable, categories::String...)
434+
# Add new category
435+
categ_names = category_names(rmse_var)
436+
push!(categ_names, categories...)
437+
438+
# Add new column
439+
mdl_names = model_names(rmse_var)
440+
num_mdl_names = length(mdl_names)
441+
nan_vecs = (fill(NaN, num_mdl_names) for _ in categories)
442+
rmses = hcat(rmse_var.RMSEs, nan_vecs...)
443+
return RMSEVariable(
444+
rmse_var.short_name,
445+
mdl_names,
446+
categ_names,
447+
rmses,
448+
rmse_var.units |> deepcopy,
449+
)
450+
end
451+
452+
"""
453+
add_model(rmse_var::RMSEVariable, models::String...)
454+
455+
Add one or more models named `models` to `rmse_var`.
456+
"""
457+
function add_model(rmse_var::RMSEVariable, models::String...)
458+
# Add new model name
459+
mdl_names = model_names(rmse_var)
460+
push!(mdl_names, models...)
461+
462+
# Add new row
463+
categ_names = category_names(rmse_var)
464+
num_categ_names = length(categ_names)
465+
nan_vecs = (fill(NaN, num_categ_names)' for _ in models)
466+
rmses = vcat(rmse_var.RMSEs, nan_vecs...)
467+
468+
# Add missing units for model
469+
units = rmse_var.units |> deepcopy
470+
for name in models
471+
units[name] = ""
472+
end
473+
return RMSEVariable(
474+
rmse_var.short_name,
475+
mdl_names,
476+
categ_names,
477+
rmses,
478+
units,
479+
)
480+
end
481+
482+
"""
483+
_model_name_check(rmse_var::RMSEVariable, model_name)
484+
485+
Check if `model_name` is present in the model names of `rmse_var`.
486+
487+
Return nothing if `model_name` is present in the model names of `rmse_var`. Otherwise,
488+
return an error.
489+
"""
490+
function _model_name_check(rmse_var::RMSEVariable, model_name)
491+
mdl_names = model_names(rmse_var)
492+
(model_name in mdl_names) ||
493+
error("Model name ($model_name) is not in $mdl_names")
494+
return nothing
495+
end
496+
497+
"""
498+
add_unit!(rmse_var::RMSEVariable, model_name, unit)
499+
500+
Add a unit named `unit` to a model named `model_name` in `rmse_var`.
501+
"""
502+
function add_unit!(rmse_var::RMSEVariable, model_name, unit)
503+
_model_name_check(rmse_var, model_name)
504+
rmse_var.units[model_name] = unit
505+
return nothing
506+
end
507+
508+
"""
509+
add_unit!(rmse_var::RMSEVariable, model_name2unit::Dict)
510+
511+
Add all model name and unit pairs in the dictionary `model_name2unit` to `rmse_var`.
512+
"""
513+
function add_unit!(rmse_var::RMSEVariable, model_name2unit::Dict)
514+
for (model_name, unit) in model_name2unit
515+
_model_name_check(rmse_var, model_name)
516+
rmse_var.units[model_name] = unit
517+
end
518+
return nothing
519+
end
520+
425521
end

test/test_Leaderboard.jl

+75
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,78 @@ end
188188
[120.0, 130.0, 140.0]
189189
@test_throws DimensionMismatch rmse_var[1, :] = [120.0, 130.0, 140.0]
190190
end
191+
192+
@testset "Adding model and category" begin
193+
# Add single model
194+
csv_file_path = joinpath(@__DIR__, "sample_data/test_csv.csv")
195+
rmse_var = ClimaAnalysis.read_rmses(csv_file_path, "ta")
196+
rmse_var = ClimaAnalysis.add_model(rmse_var, "new model")
197+
@test ClimaAnalysis.model_names(rmse_var) ==
198+
["ACCESS-CM2", "ACCESS-ESM1-5", "new model"]
199+
@test ClimaAnalysis.category_names(rmse_var) ==
200+
["DJF", "MAM", "JJA", "SON", "ANN"]
201+
@test ClimaAnalysis.rmse_units(rmse_var) ==
202+
Dict("ACCESS-CM2" => "", "ACCESS-ESM1-5" => "", "new model" => "")
203+
@test all(isnan.(rmse_var["new model"]))
204+
205+
# Add single category
206+
rmse_var = ClimaAnalysis.add_category(rmse_var, "new cat")
207+
@test ClimaAnalysis.model_names(rmse_var) ==
208+
["ACCESS-CM2", "ACCESS-ESM1-5", "new model"]
209+
@test ClimaAnalysis.category_names(rmse_var) ==
210+
["DJF", "MAM", "JJA", "SON", "ANN", "new cat"]
211+
@test ClimaAnalysis.rmse_units(rmse_var) ==
212+
Dict("ACCESS-CM2" => "", "ACCESS-ESM1-5" => "", "new model" => "")
213+
@test all(isnan.(rmse_var[:, "new cat"]))
214+
215+
# Add multiple models
216+
csv_file_path = joinpath(@__DIR__, "sample_data/test_csv.csv")
217+
rmse_var = ClimaAnalysis.read_rmses(csv_file_path, "ta")
218+
rmse_var = ClimaAnalysis.add_model(rmse_var, "new model", "new model 2")
219+
@test ClimaAnalysis.model_names(rmse_var) ==
220+
["ACCESS-CM2", "ACCESS-ESM1-5", "new model", "new model 2"]
221+
@test ClimaAnalysis.category_names(rmse_var) ==
222+
["DJF", "MAM", "JJA", "SON", "ANN"]
223+
@test ClimaAnalysis.rmse_units(rmse_var) == Dict(
224+
"ACCESS-CM2" => "",
225+
"ACCESS-ESM1-5" => "",
226+
"new model" => "",
227+
"new model 2" => "",
228+
)
229+
@test all(isnan.(rmse_var["new model"]))
230+
@test all(isnan.(rmse_var["new model 2"]))
231+
232+
# Add multiple categories
233+
rmse_var = ClimaAnalysis.add_category(rmse_var, "new cat", "new cat 2")
234+
@test ClimaAnalysis.model_names(rmse_var) ==
235+
["ACCESS-CM2", "ACCESS-ESM1-5", "new model", "new model 2"]
236+
@test ClimaAnalysis.category_names(rmse_var) ==
237+
["DJF", "MAM", "JJA", "SON", "ANN", "new cat", "new cat 2"]
238+
@test ClimaAnalysis.rmse_units(rmse_var) == Dict(
239+
"ACCESS-CM2" => "",
240+
"ACCESS-ESM1-5" => "",
241+
"new model" => "",
242+
"new model 2" => "",
243+
)
244+
@test all(isnan.(rmse_var[:, "new cat"]))
245+
@test all(isnan.(rmse_var[:, "new cat 2"]))
246+
end
247+
248+
@testset "Adding units" begin
249+
csv_file_path = joinpath(@__DIR__, "sample_data/test_csv.csv")
250+
rmse_var = ClimaAnalysis.read_rmses(csv_file_path, "ta")
251+
252+
# Adding a single unit
253+
rmse_var = ClimaAnalysis.add_model(rmse_var, "hi")
254+
ClimaAnalysis.add_unit!(rmse_var, "hi", "test")
255+
@test ClimaAnalysis.rmse_units(rmse_var)["hi"] == "test"
256+
257+
# Adding multiple units
258+
rmse_var = ClimaAnalysis.add_model(rmse_var, "hello1", "hello2")
259+
ClimaAnalysis.add_unit!(
260+
rmse_var,
261+
Dict("hello1" => "units1", "hello2" => "units2"),
262+
)
263+
@test ClimaAnalysis.rmse_units(rmse_var)["hello1"] == "units1"
264+
@test ClimaAnalysis.rmse_units(rmse_var)["hello2"] == "units2"
265+
end

0 commit comments

Comments
 (0)