notoecd 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

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.
notoecd/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.4"
1
+ __version__ = "0.1.5"
2
2
 
3
3
  from .calls import get_df
4
4
  from .structure import get_structure
notoecd/calls.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import pandas as pd
2
- from typing import Union
3
2
  from functools import lru_cache
3
+ from typing import Union, Optional
4
4
  from .structure import get_structure
5
5
 
6
6
  @lru_cache(maxsize=64)
@@ -12,26 +12,58 @@ def _clean(s: str) -> str:
12
12
  return str(s).strip().lower()
13
13
 
14
14
 
15
+ def _clean_dict(d: dict) -> dict:
16
+ out = {}
17
+ for k, v in d.items():
18
+ k = _clean(k)
19
+ if isinstance(v, (list, tuple, set)):
20
+ out[k] = [_clean(x) for x in v]
21
+ else:
22
+ out[k] = _clean(v)
23
+ return out
24
+
25
+
15
26
  def _build_filter_expression(
16
27
  agencyID: str,
17
28
  dataflowID: str,
18
29
  filters: dict,
19
30
  ) -> str:
31
+ """
32
+ Builds a valid OECD SDMX filter expression from a dictionary.
20
33
 
21
- s = get_structure(agencyID, dataflowID)
22
- filters = {_clean(k): v for k, v in filters.items()}
34
+ Args:
35
+ agencyID (str): The data provider agency identifier.
36
+ dataflowID (str): The dataflow identifier within the agency.
37
+ filters (dict): Dictionary with dimension names as keys and
38
+ either codes or labels as values.
23
39
 
40
+ Returns
41
+ str: A valid OECD SDMX filter expression.
42
+ """
43
+ s = get_structure(agencyID, dataflowID)
44
+ filters = _clean_dict(filters)
45
+
24
46
  parts = []
25
47
  for dim in s.toc.title:
26
48
  dim_key = _clean(dim)
49
+
27
50
  if dim_key in filters:
28
51
  val = filters[dim_key]
29
- if isinstance(val, str):
52
+ concepts = _clean_dict(s.explain_vals(dim_key))
53
+ rev = {v: k for k, v in concepts.items()}
54
+
55
+ if isinstance(val, str):
30
56
  val = [val]
31
- parts.append("+".join(_clean(v) for v in val))
57
+ val = [_clean(v) for v in val]
58
+
59
+ for i, v in enumerate(val):
60
+ if v in concepts: continue
61
+ if v in rev: val[i] = rev[v]
62
+ else: raise ValueError(f"Invalid value '{v}' for dimension '{dim_key}'. ")
63
+
64
+ parts.append("+".join(val))
32
65
  else:
33
66
  parts.append("")
34
-
35
67
  return ".".join(parts).upper()
36
68
 
37
69
 
@@ -40,8 +72,26 @@ def get_df(
40
72
  dataflowID: str,
41
73
  filters: Union[str, dict] = "",
42
74
  version: str = "",
75
+ startYear: Optional[int] = None,
76
+ endYear: Optional[int] = None,
43
77
  ) -> pd.DataFrame:
44
-
78
+ """
79
+ Fetch data from the OECD SDMX API and return it as a pandas DataFrame.
80
+
81
+ Args:
82
+ agencyID (str): The data provider agency identifier.
83
+ dataflowID (str): The dataflow identifier within the agency.
84
+ filters (Union[str, dict], optional): Either a preformatted SDMX filter
85
+ string or a dictionary of filters.
86
+ version (str, optional): The dataflow version. Use an empty string for
87
+ the latest version.
88
+ startYear (int, optional): Start year (inclusive).
89
+ endYear (int, optional): End year (inclusive).
90
+
91
+ Returns:
92
+ pd.DataFrame: The resulting dataset.
93
+ """
94
+
45
95
  if isinstance(filters, dict):
46
96
  filter_expression = _build_filter_expression(agencyID, dataflowID, filters)
47
97
  else:
@@ -50,8 +100,11 @@ def get_df(
50
100
  url = (
51
101
  f"https://sdmx.oecd.org/public/rest/data/"
52
102
  f"{agencyID},{dataflowID},{version}/{filter_expression}"
53
- "?dimensionAtObservation=AllDimensions&format=csvfile"
103
+ f"?dimensionAtObservation=AllDimensions&format=csvfile"
54
104
  )
55
105
 
106
+ if startYear is not None: url += f"&startPeriod={startYear}"
107
+ if endYear is not None: url += f"&endPeriod={endYear}"
108
+
56
109
  base_df = _fetch_df(url)
57
110
  return base_df.copy()
notoecd/datasets.py CHANGED
@@ -68,7 +68,7 @@ def search_keywords(*keywords: str) -> pd.DataFrame:
68
68
  Searches OECD datasets for a set of keywords.
69
69
 
70
70
  Args:
71
- keywords (str | list[str]): Single keyword or list of keywords. Acts as OR.
71
+ *keywords (str): One or more keywords. Acts as OR.
72
72
 
73
73
  Returns:
74
74
  pd.DataFrame: Matching rows.
@@ -89,13 +89,13 @@ def search_keywords(*keywords: str) -> pd.DataFrame:
89
89
  )
90
90
  )
91
91
 
92
- # Combined normalized text for each row
93
92
  text = (
94
93
  datasets["name"].fillna("").astype(str)
95
94
  + " "
96
95
  + datasets["description"].fillna("").astype(str)
97
96
  )
98
97
  text_norm = _normalize_series(text)
98
+ name_norm = _normalize_series(datasets["name"])
99
99
 
100
100
  def _normalize_kw(kw: str) -> str:
101
101
  kw = unicodedata.normalize("NFKD", kw.lower())
@@ -108,8 +108,9 @@ def search_keywords(*keywords: str) -> pd.DataFrame:
108
108
 
109
109
  for kw in norm_keywords:
110
110
  m = text_norm.str.contains(kw, na=False, regex=False)
111
+ mt = name_norm.str.contains(kw, na=False, regex=False)
111
112
  overall_mask |= m
112
- score = score.add(m.astype("int8"), fill_value=0)
113
+ score = score.add(m.astype("int8"), fill_value=0) + mt.astype("int8")
113
114
 
114
115
  result = datasets.loc[overall_mask].copy()
115
116
  result["_match_score"] = score.loc[overall_mask]
notoecd/structure.py CHANGED
@@ -138,7 +138,11 @@ class Structure():
138
138
  print(f"Could not find explanation for {unclean_values}")
139
139
 
140
140
  return {i:explanation[i] for i in clean_values}
141
+
142
+ def explain_columns(self, df:pd.DataFrame) -> dict:
143
+ dim_dict = self.concepts['DIMENSIONS']
144
+ return {i: dim_dict[i] for i in df.columns if i in dim_dict}
141
145
 
142
- @lru_cache(maxsize=128)
146
+ @lru_cache(maxsize=64)
143
147
  def get_structure(agencyID: str, dataflowID: str) -> Structure:
144
148
  return Structure(agencyID, dataflowID)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notoecd
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Unofficial library for interacting with the OECD Data Explorer through Python.
5
5
  Author-email: Daniel Vegara Balsa <daniel.vegarabalsa@oecd.org>
6
6
  License-Expression: MIT
@@ -68,7 +68,7 @@ This returns datasets that mention GDP and regional levels (TL2/TL3). It gives t
68
68
 
69
69
  Once a dataset is identified, load its SDMX structure:
70
70
 
71
- dataset = 'Gross domestic product - Regions'
71
+ # Gross domestic product - Regions
72
72
  agencyID = 'OECD.CFE.EDS'
73
73
  dataflowID = 'DSD_REG_ECO@DF_GDP'
74
74
 
@@ -103,7 +103,7 @@ Keys correspond to SDMX dimensions, values are strings or lists (for multiple va
103
103
 
104
104
  Fetch the filtered dataset:
105
105
 
106
- df = notoecd.get_df(agency, dataflow, filters)
106
+ df = notoecd.get_df(agencyID, dataflowID, filters)
107
107
  df.head()
108
108
 
109
109
  The returned object is a pandas DataFrame containing the requested subset of OECD SDMX data.
@@ -0,0 +1,9 @@
1
+ notoecd/__init__.py,sha256=v397Mb7CzA-dmcZYLK6AiEesjCf3ugEv0keEVFh8bz4,189
2
+ notoecd/calls.py,sha256=wy7ELlMLl6X5bEN7V4Pn-AcV8YYqAUPWzL-9uT-1NzM,3400
3
+ notoecd/datasets.py,sha256=BTDgdhOK6tJrU0fp_7GZQOoTpOf4CRQrqDVkXcvpAcU,3818
4
+ notoecd/structure.py,sha256=ur8kkdCL2zRjCVrw1grtyCRNZ-aqHKRgv9X_a_6qtDQ,7349
5
+ notoecd-0.1.5.dist-info/licenses/LICENSE,sha256=jb9xgeCKfW-VCXFQtYmiM_SZ9tC2zPGtOIVsE5G17W8,1076
6
+ notoecd-0.1.5.dist-info/METADATA,sha256=hnnWK8pgdyajFl71f3hLsS3-QP_vYdapjYnQK-E9YNM,2755
7
+ notoecd-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ notoecd-0.1.5.dist-info/top_level.txt,sha256=GrcbH10OAguGh5dkpzst216N_C-NtZ-QF1nlXiUpeLs,8
9
+ notoecd-0.1.5.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- notoecd/__init__.py,sha256=8_cXiMZN0gino3W2Wat06ncVcAR8XpCBgC3Q7vEbHjQ,189
2
- notoecd/calls.py,sha256=SFM4kerc-K43Yo6oDBCsnvCIpN2Bg0-sHKpRfAujS-o,1496
3
- notoecd/datasets.py,sha256=c8iz2HzWyCGGQINNnzlHG-kJMqsDKFbDObvK11QZU0Y,3751
4
- notoecd/structure.py,sha256=sq6HrjNLfK-UWr9Cuqxun_DhHLPdegX7j7pKYcEYcJM,7169
5
- notoecd-0.1.4.dist-info/licenses/LICENSE,sha256=jb9xgeCKfW-VCXFQtYmiM_SZ9tC2zPGtOIVsE5G17W8,1076
6
- notoecd-0.1.4.dist-info/METADATA,sha256=hDuKZgO-urzF1ZHaqFIco36OFN435-b8zFf3lkwhs9c,2761
7
- notoecd-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- notoecd-0.1.4.dist-info/top_level.txt,sha256=GrcbH10OAguGh5dkpzst216N_C-NtZ-QF1nlXiUpeLs,8
9
- notoecd-0.1.4.dist-info/RECORD,,