Skip to content

get_region_per_countries() #1034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Removed:

### Added

- `climada.entity.impact_funcs.trop_cyclone.ImpfSetTropCyclone.get_countries_per_region` function [#1034](https://github.com/CLIMADA-project/climada_python/pull/1034)
- `climada.hazard.tc_tracks.TCTracks.subset_years` function [#1023](https://github.com/CLIMADA-project/climada_python/pull/1023)
- `climada.hazard.tc_tracks.TCTracks.from_FAST` function, add Australia basin (AU) [#993](https://github.com/CLIMADA-project/climada_python/pull/993)
- Add `osm-flex` package to CLIMADA core [#981](https://github.com/CLIMADA-project/climada_python/pull/981)
Expand Down
42 changes: 42 additions & 0 deletions climada/entity/impact_funcs/test/test_tc.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,48 @@ def test_get_countries_per_region(self):
self.assertListEqual(out[2], [124, 840])
self.assertListEqual(out[3], ["CAN", "USA"])

def test_get_region_per_countries(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add at least one test with multiple countries.

"""Test get_impf_id_regions_per_countries()"""
ifs = ImpfSetTropCyclone()
out = ifs.get_impf_id_regions_per_countries(
countries=["CHE"], code_type="ISO3A"
)
self.assertEqual(out[0][0], 10)
self.assertEqual(out[1][0], "ROW")
self.assertEqual(out[2][0], "Rest of The World")
out = ifs.get_impf_id_regions_per_countries(countries=[756], code_type="ISO3N")
self.assertEqual(out[0][0], 10)
self.assertEqual(out[1][0], "ROW")
self.assertEqual(out[2][0], "Rest of The World")
with self.assertRaises(ValueError) as context:
ImpfSetTropCyclone.get_impf_id_regions_per_countries(
countries=["SWE"], code_type="invalid_code"
)
self.assertEqual(
str(context.exception), "code_type must be either 'iso3a' or 'iso3n'"
)
with self.assertRaises(ValueError) as context:
ImpfSetTropCyclone.get_impf_id_regions_per_countries(
countries=["SWE", 840], code_type="ISO3A"
)
self.assertEqual(
str(context.exception), "All elements in the list must be of the same type."
)
with self.assertRaises(ValueError) as context:
ImpfSetTropCyclone.get_impf_id_regions_per_countries(
countries=[840, 124], code_type="ISO3A"
)
self.assertEqual(
str(context.exception), "ISO3A code type cannot have integer values."
)
with self.assertRaises(ValueError) as context:
ImpfSetTropCyclone.get_impf_id_regions_per_countries(
countries=["MEX"], code_type="ISO3N"
)
self.assertEqual(
str(context.exception), "ISO3N code type cannot have string values."
)


# Execute Tests
if __name__ == "__main__":
Expand Down
304 changes: 195 additions & 109 deletions climada/entity/impact_funcs/trop_cyclone.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
__all__ = ["ImpfTropCyclone", "ImpfSetTropCyclone", "IFTropCyclone"]

import logging
from enum import Enum

import numpy as np
import pandas as pd
Expand All @@ -34,6 +35,135 @@
LOGGER = logging.getLogger(__name__)


class CountryCode(Enum):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this enum contain numerical and alpha-3 country codes? Conversion between the two can be done by country_to_iso and this effectively doubles the functionality

"""
Enum class that links ISO country codes (both iso3a and iso3n) to specific regions and
associated impact function IDs.

Attributes
----------
ISO3A: dict
A mapping of region names to lists of 3-letter ISO country codes (iso3a).
ISO3N: dict
A mapping of region names to lists of numeric ISO country codes (iso3n).
IMPF_ID: dict
A mapping of region names to corresponding impact function IDs.
REGION_NAME: dict
A mapping of region names to their descriptive names.
"""

# fmt: off
ISO3N = {
"NA1": [
660, 28, 32, 533, 44, 52, 84, 60, 68, 132,
136, 152, 170, 188, 192, 212, 214, 218, 222, 238,
254, 308, 312, 320, 328, 332, 340, 388, 474, 484,
500, 558, 591, 600, 604, 630, 654, 659, 662, 670,
534, 740, 780, 796, 858, 862, 92, 850,
],
"NA2": [124, 840],
"NI": [
4, 51, 31, 48, 50, 64, 262, 232, 231, 268,
356, 364, 368, 376, 400, 398, 414, 417, 422, 462,
496, 104, 524, 512, 586, 634, 682, 706, 144, 760,
762, 795, 800, 784, 860, 887,
],
"OC": [
16, 36, 184, 242, 258, 316, 296, 584, 583, 520,
540, 554, 570, 574, 580, 585, 598, 612, 882, 90,
626, 772, 776, 798, 548, 876,
],
"SI": [174, 180, 748, 450, 454, 466, 480, 508, 710, 834, 716],
"WP1": [116, 360, 418, 458, 764, 704],
"WP2": [608],
"WP3": [156],
"WP4": [344, 392, 410, 446, 158],
"ROW": [
8, 12, 20, 24, 10, 40, 112, 56, 204, 535,
70, 72, 74, 76, 86, 96, 100, 854, 108, 120,
140, 148, 162, 166, 178, 191, 531, 196, 203, 384,
208, 818, 226, 233, 234, 246, 250, 260, 266, 270,
276, 288, 292, 300, 304, 831, 324, 624, 334, 336,
348, 352, 372, 833, 380, 832, 404, 408, 983, 428,
426, 430, 434, 438, 440, 442, 470, 478, 175, 498,
492, 499, 504, 516, 528, 562, 566, 807, 578, 275,
616, 620, 642, 643, 646, 638, 652, 663, 666, 674,
678, 686, 688, 690, 694, 702, 703, 705, 239, 728,
724, 729, 744, 752, 756, 768, 788, 792, 804, 826,
581, 732, 894, 248,
],
}
ISO3A = {
"NA1": [
"AIA", "ATG", "ARG", "ABW", "BHS", "BRB", "BLZ", "BMU", "BOL", "CPV",
"CYM", "CHL", "COL", "CRI", "CUB", "DMA", "DOM", "ECU", "SLV", "FLK",
"GUF", "GRD", "GLP", "GTM", "GUY", "HTI", "HND", "JAM", "MTQ", "MEX",
"MSR", "NIC", "PAN", "PRY", "PER", "PRI", "SHN", "KNA", "LCA", "VCT",
"SXM", "SUR", "TTO", "TCA", "URY", "VEN", "VGB", "VIR",
],
"NA2": ["CAN", "USA"],
"NI": [
"AFG", "ARM", "AZE", "BHR", "BGD", "BTN", "DJI", "ERI", "ETH", "GEO",
"IND", "IRN", "IRQ", "ISR", "JOR", "KAZ", "KWT", "KGZ", "LBN", "MDV",
"MNG", "MMR", "NPL", "OMN", "PAK", "QAT", "SAU", "SOM", "LKA", "SYR",
"TJK", "TKM", "UGA", "ARE", "UZB", "YEM",
],
"OC": [
"ASM", "AUS", "COK", "FJI", "PYF", "GUM", "KIR", "MHL", "FSM", "NRU",
"NCL", "NZL", "NIU", "NFK", "MNP", "PLW", "PNG", "PCN", "WSM", "SLB",
"TLS", "TKL", "TON", "TUV", "VUT", "WLF",
],
"SI": [
"COM", "COD", "SWZ", "MDG", "MWI", "MLI", "MUS", "MOZ", "ZAF", "TZA",
"ZWE",
],
"WP1": ["KHM", "IDN", "LAO", "MYS", "THA", "VNM"],
"WP2": ["PHL"],
"WP3": ["CHN"],
"WP4": ["HKG", "JPN", "KOR", "MAC", "TWN"],
"ROW": [
"ALB", "DZA", "AND", "AGO", "ATA", "AUT", "BLR", "BEL", "BEN", "BES",
"BIH", "BWA", "BVT", "BRA", "IOT", "BRN", "BGR", "BFA", "BDI", "CMR",
"CAF", "TCD", "CXR", "CCK", "COG", "HRV", "CUW", "CYP", "CZE", "CIV",
"DNK", "EGY", "GNQ", "EST", "FRO", "FIN", "FRA", "ATF", "GAB", "GMB",
"DEU", "GHA", "GIB", "GRC", "GRL", "GGY", "GIN", "GNB", "HMD", "VAT",
"HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "KEN", "PRK", "XKX", "LVA",
"LSO", "LBR", "LBY", "LIE", "LTU", "LUX", "MLT", "MRT", "MYT", "MDA",
"MCO", "MNE", "MAR", "NAM", "NLD", "NER", "NGA", "MKD", "NOR", "PSE",
"POL", "PRT", "ROU", "RUS", "RWA", "REU", "BLM", "MAF", "SPM", "SMR",
"STP", "SEN", "SRB", "SYC", "SLE", "SGP", "SVK", "SVN", "SGS", "SSD",
"ESP", "SDN", "SJM", "SWE", "CHE", "TGO", "TUN", "TUR", "UKR", "GBR",
"UMI", "ESH", "ZMB", "ALA",
],
}
# fmt: on
IMPF_ID = {
"NA1": 1,
"NA2": 2,
"NI": 3,
"OC": 4,
"SI": 5,
"WP1": 6,
"WP2": 7,
"WP3": 8,
"WP4": 9,
"ROW": 10,
}

REGION_NAME = {
"NA1": "Caribbean and Mexico",
"NA2": "USA and Canada",
"NI": "North Indian",
"OC": "Oceania",
"SI": "South Indian",
"WP1": "South East Asia",
"WP2": "Philippines",
"WP3": "China Mainland",
"WP4": "North West Pacific",
"ROW": "Rest of The World",
}


class ImpfTropCyclone(ImpactFunc):
"""Impact functions for tropical cyclones."""

Expand Down Expand Up @@ -311,118 +441,74 @@
"""
if not region:
region = "all"
# fmt: off
iso3n = {
"NA1": [
660, 28, 32, 533, 44, 52, 84, 60, 68, 132,
136, 152, 170, 188, 192, 212, 214, 218, 222, 238,
254, 308, 312, 320, 328, 332, 340, 388, 474, 484,
500, 558, 591, 600, 604, 630, 654, 659, 662, 670,
534, 740, 780, 796, 858, 862, 92, 850,
],
"NA2": [124, 840],
"NI": [
4, 51, 31, 48, 50, 64, 262, 232, 231, 268,
356, 364, 368, 376, 400, 398, 414, 417, 422, 462,
496, 104, 524, 512, 586, 634, 682, 706, 144, 760,
762, 795, 800, 784, 860, 887,
],
"OC": [
16, 36, 184, 242, 258, 316, 296, 584, 583, 520,
540, 554, 570, 574, 580, 585, 598, 612, 882, 90,
626, 772, 776, 798, 548, 876,
],
"SI": [174, 180, 748, 450, 454, 466, 480, 508, 710, 834, 716],
"WP1": [116, 360, 418, 458, 764, 704],
"WP2": [608],
"WP3": [156],
"WP4": [344, 392, 410, 446, 158],
"ROW": [
8, 12, 20, 24, 10, 40, 112, 56, 204, 535,
70, 72, 74, 76, 86, 96, 100, 854, 108, 120,
140, 148, 162, 166, 178, 191, 531, 196, 203, 384,
208, 818, 226, 233, 234, 246, 250, 260, 266, 270,
276, 288, 292, 300, 304, 831, 324, 624, 334, 336,
348, 352, 372, 833, 380, 832, 404, 408, 983, 428,
426, 430, 434, 438, 440, 442, 470, 478, 175, 498,
492, 499, 504, 516, 528, 562, 566, 807, 578, 275,
616, 620, 642, 643, 646, 638, 652, 663, 666, 674,
678, 686, 688, 690, 694, 702, 703, 705, 239, 728,
724, 729, 744, 752, 756, 768, 788, 792, 804, 826,
581, 732, 894, 248,
],
}
iso3a = {
"NA1": [
"AIA", "ATG", "ARG", "ABW", "BHS", "BRB", "BLZ", "BMU", "BOL", "CPV",
"CYM", "CHL", "COL", "CRI", "CUB", "DMA", "DOM", "ECU", "SLV", "FLK",
"GUF", "GRD", "GLP", "GTM", "GUY", "HTI", "HND", "JAM", "MTQ", "MEX",
"MSR", "NIC", "PAN", "PRY", "PER", "PRI", "SHN", "KNA", "LCA", "VCT",
"SXM", "SUR", "TTO", "TCA", "URY", "VEN", "VGB", "VIR",
],
"NA2": ["CAN", "USA"],
"NI": [
"AFG", "ARM", "AZE", "BHR", "BGD", "BTN", "DJI", "ERI", "ETH", "GEO",
"IND", "IRN", "IRQ", "ISR", "JOR", "KAZ", "KWT", "KGZ", "LBN", "MDV",
"MNG", "MMR", "NPL", "OMN", "PAK", "QAT", "SAU", "SOM", "LKA", "SYR",
"TJK", "TKM", "UGA", "ARE", "UZB", "YEM",
],
"OC": [
"ASM", "AUS", "COK", "FJI", "PYF", "GUM", "KIR", "MHL", "FSM", "NRU",
"NCL", "NZL", "NIU", "NFK", "MNP", "PLW", "PNG", "PCN", "WSM", "SLB",
"TLS", "TKL", "TON", "TUV", "VUT", "WLF",
],
"SI": [
"COM", "COD", "SWZ", "MDG", "MWI", "MLI", "MUS", "MOZ", "ZAF", "TZA",
"ZWE",
],
"WP1": ["KHM", "IDN", "LAO", "MYS", "THA", "VNM"],
"WP2": ["PHL"],
"WP3": ["CHN"],
"WP4": ["HKG", "JPN", "KOR", "MAC", "TWN"],
"ROW": [
"ALB", "DZA", "AND", "AGO", "ATA", "AUT", "BLR", "BEL", "BEN", "BES",
"BIH", "BWA", "BVT", "BRA", "IOT", "BRN", "BGR", "BFA", "BDI", "CMR",
"CAF", "TCD", "CXR", "CCK", "COG", "HRV", "CUW", "CYP", "CZE", "CIV",
"DNK", "EGY", "GNQ", "EST", "FRO", "FIN", "FRA", "ATF", "GAB", "GMB",
"DEU", "GHA", "GIB", "GRC", "GRL", "GGY", "GIN", "GNB", "HMD", "VAT",
"HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "KEN", "PRK", "XKX", "LVA",
"LSO", "LBR", "LBY", "LIE", "LTU", "LUX", "MLT", "MRT", "MYT", "MDA",
"MCO", "MNE", "MAR", "NAM", "NLD", "NER", "NGA", "MKD", "NOR", "PSE",
"POL", "PRT", "ROU", "RUS", "RWA", "REU", "BLM", "MAF", "SPM", "SMR",
"STP", "SEN", "SRB", "SYC", "SLE", "SGP", "SVK", "SVN", "SGS", "SSD",
"ESP", "SDN", "SJM", "SWE", "CHE", "TGO", "TUN", "TUR", "UKR", "GBR",
"UMI", "ESH", "ZMB", "ALA",
],
}
# fmt: on
impf_id = {
"NA1": 1,
"NA2": 2,
"NI": 3,
"OC": 4,
"SI": 5,
"WP1": 6,
"WP2": 7,
"WP3": 8,
"WP4": 9,
"ROW": 10,
}
region_name = dict()
region_name["NA1"] = "Caribbean and Mexico"
region_name["NA2"] = "USA and Canada"
region_name["NI"] = "North Indian"
region_name["OC"] = "Oceania"
region_name["SI"] = "South Indian"
region_name["WP1"] = "South East Asia"
region_name["WP2"] = "Philippines"
region_name["WP3"] = "China Mainland"
region_name["WP4"] = "North West Pacific"

if region == "all":
return region_name, impf_id, iso3n, iso3a
return (

Check warning on line 446 in climada/entity/impact_funcs/trop_cyclone.py

View check run for this annotation

Jenkins - WCR / Code Coverage

Not covered line

Line 446 is not covered by tests
CountryCode.REGION_NAME.value,
CountryCode.IMPF_ID.value,
CountryCode.ISO3N.value,
CountryCode.ISO3A.value,
)

return (
CountryCode.REGION_NAME.value[region],
CountryCode.IMPF_ID.value[region],
CountryCode.ISO3N.value[region],
CountryCode.ISO3A.value[region],
)

@staticmethod
def get_impf_id_regions_per_countries(

Check warning on line 461 in climada/entity/impact_funcs/trop_cyclone.py

View check run for this annotation

Jenkins - WCR / Pylint

invalid-name

LOW: Method name "get_impf_id_regions_per_countries" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
Raw output
Used when the name doesn't match the regular expression associated to its type(constant, variable, class...).
countries: list = None, code_type: str = "ISO3A"
) -> tuple:
"""Return the impact function id and the region corresponding to a list of countries,
or a single country.

Parameters:
-----------
countries : list
List containing the ISO codes of the country, which should be either
in string format if the code is "ISO3A" or an integer if "ISO3N", see code_type below.
code_type : str
Either "ISO3A" or "ISO3N". "ISO3A" stands for "ISO 3166-1 alpha-3" which is a
three-letter country code, "ISO3N" stands for "ISO 3166-1 numeric" which is a
three-digit country code, the numeric version of "ISO3A". For example, for Switzerland:
the "ISO3A" code is "CHE" and the "ISO3N" is 756.
Comment on lines +472 to +476
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inconsistent with country_to_iso, where the two types are called numeric and alpha-3


Returns:
--------
impf_ids : list
List of impact function ids matching the countries.
Comment on lines +480 to +481
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not immediately return the appropriate impact function (set)s?

regions_ids : list
List of the region ids. Regions are a container of countries as defined in:
https://nhess.copernicus.org/articles/21/393/2021/nhess-21-393-2021.pdf, and implemented
in the CountryCode Enum Class. Example: "NA1", "NA2", ...
regions_names : list
List of the regions names. Example: "Caribbean and Mexico", "USA and Canada", ...
"""

return region_name[region], impf_id[region], iso3n[region], iso3a[region]
if code_type not in {"ISO3A", "ISO3N"}:

Check warning on line 490 in climada/entity/impact_funcs/trop_cyclone.py

View check run for this annotation

Jenkins - WCR / Pylint

no-else-raise

LOW: Unnecessary "elif" after "raise", remove the leading "el" from "elif"
Raw output
no description found
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is code_type a parameter at all? If any item in the list is a str, it must be an alpha-3 code. If it is int, it must be a numerical code.

raise ValueError("code_type must be either 'iso3a' or 'iso3n'")
elif not all(isinstance(country, type(countries[0])) for country in countries):
raise ValueError("All elements in the list must be of the same type.")
Comment on lines +492 to +493
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? It's easy to decide on a per-element basis if a numerical or an alpha-3 code is given.

elif code_type == "ISO3A" and isinstance((countries[0]), int):
raise ValueError("ISO3A code type cannot have integer values.")
elif code_type == "ISO3N" and isinstance((countries[0]), str):
raise ValueError("ISO3N code type cannot have string values.")
Comment on lines +491 to +497
Copy link
Member

@chahank chahank Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please choose clear words / variable names. Here it is neither consistent (e.e. iso3a and ISO3A), nor standard.


region_country_dict = getattr(CountryCode, code_type).value
# Find region
regions_ids = [
region_id
for country in countries
for region_id, countr_in_region_id in region_country_dict.items()
if country in countr_in_region_id
]
# Find impact function id
impf_ids = [CountryCode.IMPF_ID.value[region] for region in regions_ids]
regions_name = [CountryCode.REGION_NAME.value[region] for region in regions_ids]

return impf_ids, regions_ids, regions_name


@deprecated(
Expand Down