oda-data 2.8.0__tar.gz → 2.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oda_data-2.8.0 → oda_data-2.8.2}/PKG-INFO +1 -1
- {oda_data-2.8.0 → oda_data-2.8.2}/pyproject.toml +1 -1
- {oda_data-2.8.0 → oda_data-2.8.2}/pyproject.toml.orig +1 -1
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/api/constants.py +8 -1
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/api/sources.py +0 -15
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/crs_functions.py +11 -6
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/policy_markers.py +51 -33
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/sector_imputations.py +11 -2
- {oda_data-2.8.0 → oda_data-2.8.2}/README.md +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/api/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/api/oecd.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/api/representations.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/cache/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/cache/_migrate.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/cache/api.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/cache/config.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/cache/types.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/channel_share_proxies.csv +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/channels.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/common.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/crs_channel_mapping.csv +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/crs_channel_mapping_vintage.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/dtypes.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/multilateral_channel_crosswalk.csv +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/schema.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/validation.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/config.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/common.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/common.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/crs_indicators.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/oecd.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/crs/one.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/common.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/dac1_functions.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/dac1_indicators.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/oecd.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac1/one.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/common.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/dac2a_functions.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/dac2a_indicators.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/oecd.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/dac2a/one.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/indicator.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/eu.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/imputation_quality.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/indicators/research/imputation_shares.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/logger.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/oda_data_indicators.csv +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/crs_flow_types.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/crs_modalities.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/crs_policy_markers.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/crs_purpose.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/crs_type_of_finance.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/dac1_aid_flow_types.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/dac2a_aid_type.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/flow_types.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/indicators.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/key_columns.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/provider_groupings.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/recipient_groupings.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/settings/v1_indicators_mapping.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/cache.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/compatibility.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/gni.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/groupings.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/__init__.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/add.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/create_mapping.py +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/crs_names.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/dac1_names.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/names/dac2a_names.json +0 -0
- {oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/tools/sector_lists.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oda_data
|
|
3
|
-
Version: 2.8.
|
|
3
|
+
Version: 2.8.2
|
|
4
4
|
Summary: A python package to work with Official Development Assistance data from the OECD DAC.
|
|
5
5
|
Author: Jorge Rivera, Miguel Haro Ruiz
|
|
6
6
|
Author-email: Jorge Rivera <jorge.rivera@one.org>, Miguel Haro Ruiz <miguel.haroruiz@one.org>
|
|
@@ -13,7 +13,14 @@ MarkerScore = Literal[
|
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
PolicyMarker = Literal[
|
|
16
|
-
"gender",
|
|
16
|
+
"gender",
|
|
17
|
+
"environment",
|
|
18
|
+
"climate_adaptation",
|
|
19
|
+
"climate_mitigation",
|
|
20
|
+
"biodiversity",
|
|
21
|
+
"desertification",
|
|
22
|
+
"disability",
|
|
23
|
+
"nutrition",
|
|
17
24
|
]
|
|
18
25
|
|
|
19
26
|
CURRENCIES: dict[str, str] = {
|
|
@@ -18,7 +18,6 @@ from oda_data.clean_data.common import (
|
|
|
18
18
|
convert_dot_stat_to_data_explorer_codes,
|
|
19
19
|
)
|
|
20
20
|
from oda_data.clean_data.schema import (
|
|
21
|
-
CORE_CRS_MAPPING,
|
|
22
21
|
AidDataSchema,
|
|
23
22
|
ODASchema,
|
|
24
23
|
)
|
|
@@ -42,20 +41,6 @@ from oda_data.tools.cache import (
|
|
|
42
41
|
)
|
|
43
42
|
|
|
44
43
|
|
|
45
|
-
def translate_cols_and_filters_to_raw(
|
|
46
|
-
columns: list | None, filters: list[tuple] | None
|
|
47
|
-
) -> tuple[list | None, list[tuple] | None]:
|
|
48
|
-
crs_mapping = {v: k for k, v in CORE_CRS_MAPPING.items()}
|
|
49
|
-
cols = [crs_mapping.get(col, col) for col in columns] if columns else columns
|
|
50
|
-
filters = (
|
|
51
|
-
[(crs_mapping.get(col, col), op, val) for col, op, val in filters]
|
|
52
|
-
if filters
|
|
53
|
-
else None
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
return cols, filters
|
|
57
|
-
|
|
58
|
-
|
|
59
44
|
def _filters_to_query(filters: list[tuple[str, str, list]]) -> str:
|
|
60
45
|
"""Transform a list of filters into a query string.
|
|
61
46
|
|
|
@@ -36,15 +36,20 @@ def _group_by_mapped_channel(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
36
36
|
def _rolling_period_total(df: pd.DataFrame, period_length: int = 3) -> pd.DataFrame:
|
|
37
37
|
"""Calculate a rolling total of `period_length` years.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
(`oda_data.indicators.research.imputation_shares`)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
Wraps the shared, vectorised `rolling_window_total`
|
|
40
|
+
(`oda_data.indicators.research.imputation_shares`). A (grouper, year)
|
|
41
|
+
combination is emitted once its group has a full `period_length`-year
|
|
42
|
+
window within the years present in `df`, and only when some value in that
|
|
43
|
+
window is nonzero. `rolling_window_total` fills a window in which the group
|
|
44
|
+
has no rows with zeros, and those rows would otherwise become zero-share
|
|
45
|
+
rows for years before or after the group existed.
|
|
44
46
|
"""
|
|
45
47
|
values = list(crs_value_cols().values())
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
totals = rolling_window_total(df, period_length=period_length, value_cols=values)
|
|
50
|
+
has_value = totals[values].fillna(0).ne(0).any(axis=1)
|
|
51
|
+
|
|
52
|
+
return totals.loc[has_value].reset_index(drop=True)
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
def _purpose_share(value_row: pd.DataFrame, value_col: str) -> pd.Series:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from typing import get_args
|
|
2
|
+
|
|
1
3
|
import pandas as pd
|
|
2
|
-
from pyarrow import ArrowInvalid
|
|
3
4
|
|
|
4
5
|
from oda_data.api.constants import (
|
|
5
6
|
EXTENDED_PROVIDER_PURPOSE_GROUPER,
|
|
@@ -8,18 +9,35 @@ from oda_data.api.constants import (
|
|
|
8
9
|
Measure,
|
|
9
10
|
PolicyMarker,
|
|
10
11
|
)
|
|
11
|
-
from oda_data.api.sources import translate_cols_and_filters_to_raw
|
|
12
12
|
from oda_data.clean_data.common import convert_units
|
|
13
13
|
from oda_data.clean_data.schema import ODASchema
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
# CRS marker values kept by each score. Desertification also reports 3, a principal
|
|
16
|
+
# objective that supports an action programme to combat desertification. The
|
|
17
|
+
# not_screened and total_allocable scores have no values: not_screened keeps blank
|
|
18
|
+
# markers and total_allocable keeps every row.
|
|
19
|
+
_MARKER_SCORE_VALUES: dict[str, list[int] | None] = {
|
|
20
|
+
"significant": [1],
|
|
21
|
+
"principal": [2, 3],
|
|
22
|
+
"not_targeted": [0],
|
|
23
|
+
"total_targeted": [1, 2, 3],
|
|
24
|
+
"not_screened": None,
|
|
25
|
+
"total_allocable": None,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _marker_score_map(marker_score: MarkerScore | str) -> list[int] | None:
|
|
30
|
+
"""Return the CRS marker values a score keeps, or None if it filters no values.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If the score is not a MarkerScore.
|
|
34
|
+
"""
|
|
35
|
+
if marker_score not in _MARKER_SCORE_VALUES:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Unknown marker_score {marker_score!r}; "
|
|
38
|
+
f"expected one of {list(_MARKER_SCORE_VALUES)}"
|
|
39
|
+
)
|
|
40
|
+
return _MARKER_SCORE_VALUES[marker_score]
|
|
23
41
|
|
|
24
42
|
|
|
25
43
|
def _marker_modality_filter() -> list[tuple[str, str, list]]:
|
|
@@ -63,19 +81,30 @@ def bilateral_policy_marker(
|
|
|
63
81
|
providers (list | int | None, optional): Providers to filter the data. Defaults to None.
|
|
64
82
|
recipients (list | int | None, optional): Recipients to filter the data. Defaults to None.
|
|
65
83
|
measure (Measure | str, optional): Measure type. Defaults to "gross_disbursement".
|
|
66
|
-
marker (PolicyMarker): Policy marker. Options are: "gender", "environment",
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
marker (PolicyMarker): Policy marker. Options are: "gender", "environment",
|
|
85
|
+
"climate_adaptation", "climate_mitigation", "biodiversity", "desertification",
|
|
86
|
+
"disability", "nutrition".
|
|
87
|
+
marker_score (MarkerScore): Policy marker score. "principal", "significant",
|
|
88
|
+
"not_targeted" and "not_screened" (a blank marker) split allocable aid with no
|
|
89
|
+
overlap. "total_targeted" is principal plus significant, and "total_allocable"
|
|
90
|
+
is all allocable aid, screened or not.
|
|
91
|
+
oda_only (bool, optional): Whether to include only ODA flows. Defaults to True.
|
|
71
92
|
currency (str, optional): Target currency. Defaults to "USD".
|
|
72
93
|
base_year (int | None, optional): Base year for conversion. Defaults to None.
|
|
73
94
|
|
|
74
95
|
Returns:
|
|
75
96
|
pd.DataFrame: Dataframe with spending by purpose.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If the marker or the score is not one of the options above.
|
|
76
100
|
"""
|
|
77
101
|
from oda_data.api.sources import CRSData
|
|
78
102
|
|
|
103
|
+
if marker not in get_args(PolicyMarker):
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Unknown marker {marker!r}; expected one of {list(get_args(PolicyMarker))}"
|
|
106
|
+
)
|
|
107
|
+
|
|
79
108
|
# Get the relevant measure
|
|
80
109
|
measure = MEASURES["CRS"][measure]["column"]
|
|
81
110
|
|
|
@@ -108,25 +137,14 @@ def bilateral_policy_marker(
|
|
|
108
137
|
crs = CRSData(providers=providers, years=years, recipients=recipients)
|
|
109
138
|
|
|
110
139
|
# Read the data and group by provider and purpose
|
|
140
|
+
data = crs.read(
|
|
141
|
+
columns=[*grouper, measure, marker],
|
|
142
|
+
additional_filters=filters,
|
|
143
|
+
using_bulk_download=True,
|
|
144
|
+
)
|
|
111
145
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
try:
|
|
115
|
-
data = crs.read(
|
|
116
|
-
columns=cols,
|
|
117
|
-
additional_filters=filters,
|
|
118
|
-
using_bulk_download=True,
|
|
119
|
-
)
|
|
120
|
-
except ArrowInvalid:
|
|
121
|
-
c, f = translate_cols_and_filters_to_raw(cols, filters)
|
|
122
|
-
data = crs.read(
|
|
123
|
-
columns=c,
|
|
124
|
-
additional_filters=f,
|
|
125
|
-
using_bulk_download=True,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# if marker is not_screened, we need to filter out the screened data
|
|
129
|
-
if marker == "not_screened":
|
|
146
|
+
# Keep only unscreened activities (blank marker)
|
|
147
|
+
if marker_score == "not_screened":
|
|
130
148
|
data = data.loc[lambda d: d[marker].isna()]
|
|
131
149
|
|
|
132
150
|
data = (
|
|
@@ -208,15 +208,18 @@ def _read_core_contributions(
|
|
|
208
208
|
|
|
209
209
|
|
|
210
210
|
def _get_channel_share_proxies() -> pd.DataFrame:
|
|
211
|
-
"""Read the
|
|
211
|
+
"""Read the proxy table, keeping only rows marked `reviewed=true`.
|
|
212
212
|
|
|
213
213
|
Returns:
|
|
214
214
|
pd.DataFrame: columns channel_code, channel_name, proxy_channel_code,
|
|
215
215
|
proxy_type ("parent_fund" or "fixed_purpose"), fixed_purpose_code,
|
|
216
216
|
rationale, reviewed. `proxy_channel_code` is null for `fixed_purpose`
|
|
217
217
|
rows; `fixed_purpose_code` is null for `parent_fund` rows.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
ValueError: If the table has no `reviewed` column.
|
|
218
221
|
"""
|
|
219
|
-
|
|
222
|
+
proxies = pd.read_csv(
|
|
220
223
|
ODAPaths.cleaning / PROXY_TABLE_FILE,
|
|
221
224
|
dtype={
|
|
222
225
|
ODASchema.CHANNEL_CODE: "Int64",
|
|
@@ -224,6 +227,12 @@ def _get_channel_share_proxies() -> pd.DataFrame:
|
|
|
224
227
|
"fixed_purpose_code": "Int64",
|
|
225
228
|
},
|
|
226
229
|
)
|
|
230
|
+
if "reviewed" not in proxies.columns:
|
|
231
|
+
raise ValueError(
|
|
232
|
+
f"{PROXY_TABLE_FILE} has no 'reviewed' column, so unreviewed proxies "
|
|
233
|
+
"cannot be told apart from reviewed ones."
|
|
234
|
+
)
|
|
235
|
+
return proxies.loc[proxies["reviewed"].eq(True)].reset_index(drop=True)
|
|
227
236
|
|
|
228
237
|
|
|
229
238
|
def _apply_proxies(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oda_data-2.8.0 → oda_data-2.8.2}/src/oda_data/clean_data/multilateral_channel_crosswalk.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|