Skip to content

Commit e2a3f88

Browse files
committed
Add regional economy module
1 parent dbd814d commit e2a3f88

4 files changed

Lines changed: 308 additions & 0 deletions

File tree

bcb/sgs/__init__.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
from io import StringIO
2+
3+
import requests
4+
import pandas as pd
5+
6+
from bcb.utils import Date
7+
8+
"""
9+
Sistema Gerenciador de Séries Temporais (SGS)
10+
11+
O módulo ``sgs`` obtem os dados do webservice do Banco Central,
12+
interface json do serviço BCData/SGS -
13+
`Sistema Gerenciador de Séries Temporais (SGS)
14+
<https://www3.bcb.gov.br/sgspub/localizarseries/localizarSeries.do?method=prepararTelaLocalizarSeries>`_.
15+
"""
16+
17+
18+
class SGSCode:
19+
def __init__(self, code, name=None):
20+
if name is None:
21+
if isinstance(code, int) or isinstance(code, str):
22+
self.name = str(code)
23+
self.value = int(code)
24+
else:
25+
self.name = str(name)
26+
self.value = int(code)
27+
28+
29+
def _codes(codes):
30+
if isinstance(codes, int) or isinstance(codes, str):
31+
yield SGSCode(codes)
32+
elif isinstance(codes, tuple):
33+
yield SGSCode(codes[1], codes[0])
34+
elif isinstance(codes, list):
35+
for cd in codes:
36+
_ist = isinstance(cd, tuple)
37+
yield SGSCode(cd[1], cd[0]) if _ist else SGSCode(cd)
38+
elif isinstance(codes, dict):
39+
for cd in codes:
40+
yield SGSCode(codes[cd], cd)
41+
42+
43+
def _get_url_and_payload(code, start_date, end_date, last):
44+
payload = {"formato": "json"}
45+
if last == 0:
46+
if start_date is not None or end_date is not None:
47+
payload["dataInicial"] = Date(start_date).date.strftime("%d/%m/%Y")
48+
end_date = end_date if end_date else "today"
49+
payload["dataFinal"] = Date(end_date).date.strftime("%d/%m/%Y")
50+
url = "http://api.bcb.gov.br/dados/serie/bcdata.sgs.{}/dados".format(code)
51+
else:
52+
url = (
53+
"http://api.bcb.gov.br/dados/serie/bcdata.sgs.{}/dados" "/ultimos/{}"
54+
).format(code, last)
55+
56+
return {"payload": payload, "url": url}
57+
58+
59+
def _format_df(df, code, freq):
60+
cns = {"data": "Date", "valor": code.name, "datafim": "enddate"}
61+
df = df.rename(columns=cns)
62+
if "Date" in df:
63+
df["Date"] = pd.to_datetime(df["Date"], format="%d/%m/%Y")
64+
if "enddate" in df:
65+
df["enddate"] = pd.to_datetime(df["enddate"], format="%d/%m/%Y")
66+
df = df.set_index("Date")
67+
if freq:
68+
df.index = df.index.to_period(freq)
69+
return df
70+
71+
72+
def get(codes, start=None, end=None, last=0, multi=True, freq=None):
73+
"""
74+
Retorna um DataFrame pandas com séries temporais obtidas do SGS.
75+
76+
Parameters
77+
----------
78+
79+
codes : {int, List[int], List[str], Dict[str:int]}
80+
Este argumento pode ser uma das opções:
81+
82+
* ``int`` : código da série temporal
83+
* ``list`` ou ``tuple`` : lista ou tupla com códigos
84+
* ``list`` ou ``tuple`` : lista ou tupla com pares ``('nome', código)``
85+
* ``dict`` : dicionário com pares ``{'nome': código}``
86+
87+
Com códigos numéricos é interessante utilizar os nomes com os códigos
88+
para definir os nomes nas colunas das séries temporais.
89+
start : str, int, date, datetime, Timestamp
90+
Data de início da série.
91+
Interpreta diferentes tipos e formatos de datas.
92+
end : string, int, date, datetime, Timestamp
93+
Data de início da série.
94+
Interpreta diferentes tipos e formatos de datas.
95+
last : int
96+
Retorna os últimos ``last`` elementos disponíveis da série temporal
97+
solicitada. Se ``last`` for maior que 0 (zero) os argumentos ``start``
98+
e ``end`` são ignorados.
99+
multi : bool
100+
Define se, quando mais de 1 série for solicitada, a função retorna uma
101+
série multivariada ou uma lista com séries univariadas.
102+
freq : str
103+
Define a frequência a ser utilizada na série temporal
104+
105+
Returns
106+
-------
107+
108+
``DataFrame`` :
109+
série temporal univariada ou multivariada,
110+
quando solicitado mais de uma série (parâmetro ``multi=True``).
111+
112+
``list`` :
113+
lista com séries temporais univariadas,
114+
quando solicitado mais de uma série (parâmetro ``multi=False``).
115+
"""
116+
dfs = []
117+
for code in _codes(codes):
118+
urd = _get_url_and_payload(code.value, start, end, last)
119+
res = requests.get(urd["url"], params=urd["payload"])
120+
if res.status_code != 200:
121+
raise Exception("Download error: code = {}".format(code.value))
122+
df = pd.read_json(StringIO(res.text))
123+
df = _format_df(df, code, freq)
124+
dfs.append(df)
125+
if len(dfs) == 1:
126+
return dfs[0]
127+
else:
128+
if multi:
129+
return pd.concat(dfs, axis=1)
130+
else:
131+
return dfs

bcb/sgs/regional_economy.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from bcb.sgs import get
2+
import pandas as pd
3+
4+
NON_PERFORMING_LOANS_BY_REGION_PF = {
5+
"N": "",
6+
"NE": "",
7+
"CO": "",
8+
"SE": "",
9+
"S": "",
10+
}
11+
NON_PERFORMING_LOANS_BY_STATE_PF = {
12+
"AC": "15861",
13+
"AL": "",
14+
"AP": "15863",
15+
"AM": "15864",
16+
"BA": "15865",
17+
"CE": "",
18+
"DF": "",
19+
"ES": "",
20+
"GO": "",
21+
"MA": "",
22+
"MT": "",
23+
"MS": "",
24+
"MG": "",
25+
"PA": "15874",
26+
"PB": "",
27+
"PR": "",
28+
"PE": "",
29+
"PI": "",
30+
"RJ": "",
31+
"RN": "",
32+
"RS": "",
33+
"RO": "",
34+
"RR": "",
35+
"SC": "",
36+
"SP": "",
37+
"SE": "",
38+
"TO": "",
39+
}
40+
NON_PERFORMING_LOANS_BY_REGION_PJ = {
41+
"N": "",
42+
"NE": "",
43+
"CO": "",
44+
"SE": "",
45+
"S": "",
46+
}
47+
NON_PERFORMING_LOANS_BY_STATE_PJ = {
48+
"AC": "15861",
49+
"AL": "",
50+
"AP": "15863",
51+
"AM": "15864",
52+
"BA": "15865",
53+
"CE": "",
54+
"DF": "",
55+
"ES": "",
56+
"GO": "",
57+
"MA": "",
58+
"MT": "",
59+
"MS": "",
60+
"MG": "",
61+
"PA": "15874",
62+
"PB": "",
63+
"PR": "",
64+
"PE": "",
65+
"PI": "",
66+
"RJ": "",
67+
"RN": "",
68+
"RS": "",
69+
"RO": "",
70+
"RR": "",
71+
"SC": "",
72+
"SP": "",
73+
"SE": "",
74+
"TO": ""
75+
}
76+
NON_PERFORMING_LOANS_BY_REGION_TOTAL = {
77+
"N": "",
78+
"NE": "",
79+
"CO": "",
80+
"SE": "",
81+
"S": "",
82+
}
83+
NON_PERFORMING_LOANS_BY_STATE_TOTAL = {
84+
"AC": "15861",
85+
"AL": "",
86+
"AP": "15863",
87+
"AM": "15864",
88+
"BA": "15865",
89+
"CE": "",
90+
"DF": "",
91+
"ES": "",
92+
"GO": "",
93+
"MA": "",
94+
"MT": "",
95+
"MS": "",
96+
"MG": "",
97+
"PA": "15874",
98+
"PB": "",
99+
"PR": "",
100+
"PE": "",
101+
"PI": "",
102+
"RJ": "",
103+
"RN": "",
104+
"RS": "",
105+
"RO": "",
106+
"RR": "",
107+
"SC": "",
108+
"SP": "",
109+
"SE": "",
110+
"TO": ""
111+
}
112+
113+
114+
def get_non_performing_loans_codes(states_or_region, mode="total"):
115+
"""SGS da Inadimplência das operações de crédito.
116+
117+
Pode ser total, pessoas físicas (PF) ou jurídicas (PJ)."""
118+
non_performing_loans_by_state = NON_PERFORMING_LOANS_BY_STATE_TOTAL
119+
non_performing_loans_by_region = NON_PERFORMING_LOANS_BY_REGION_TOTAL
120+
121+
is_state = False
122+
is_region = False
123+
states_or_region = [states_or_region] if isinstance(states_or_region, str) else states_or_region
124+
states_or_region = [location.upper() for location in states_or_region]
125+
if any(location in list(non_performing_loans_by_state.keys()) for location in states_or_region):
126+
is_state = True
127+
elif any(location in list(non_performing_loans_by_region.keys()) for location in states_or_region):
128+
is_region = True
129+
130+
if not is_state and not is_region:
131+
raise Exception(f"Not a valid state or region: {states_or_region}")
132+
133+
codes = []
134+
if is_state:
135+
if mode.upper() == "PF":
136+
non_performing_loans_by_state = NON_PERFORMING_LOANS_BY_STATE_PF
137+
elif mode.upper() == "PJ":
138+
non_performing_loans_by_state = NON_PERFORMING_LOANS_BY_STATE_PJ
139+
elif is_region:
140+
if mode.upper() == "PF":
141+
non_performing_loans_by_state = NON_PERFORMING_LOANS_BY_REGION_PF
142+
elif mode.upper() == "PJ":
143+
non_performing_loans_by_state = NON_PERFORMING_LOANS_BY_REGION_PJ
144+
145+
for location in states_or_region:
146+
codes.append(non_performing_loans_by_state.get(location))
147+
return codes
148+
149+
150+
def get_non_performing_loans(states_or_region, mode="total", start=None, end=None, last=0, freq=None):
151+
codes = get_non_performing_loans_codes(states_or_region, mode=mode)
152+
return get(codes, start=start, end=end, last=last, multi=True, freq=freq)

tests/sgs/__init__.py

Whitespace-only changes.

tests/sgs/test_regional_economy.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import pandas as pd
2+
import pytest
3+
from bcb.sgs.regional_economy import get_non_performing_loans, get_non_performing_loans_codes
4+
5+
6+
class TestGetNonPerformingLoansCodes:
7+
@pytest.mark.parametrize("states,expected_codes", [
8+
(["ba", "pa"], ["15865", "15874"]),
9+
(["BA"], ["15865"]),
10+
])
11+
def test_get_non_performing_loans_codes_by_state_total(self, states, expected_codes):
12+
assert get_non_performing_loans_codes(states) == expected_codes
13+
14+
15+
class TestGetNonPerformingLoans:
16+
@pytest.mark.parametrize("states,expected_columns", [
17+
(["BA"], ["15865"]),
18+
(["ba"], ["15865"]),
19+
#(["ba", "se", "al"], ["1"])
20+
])
21+
def test_get_series_by_states(self, states, expected_columns):
22+
series = get_non_performing_loans(states, last=10)
23+
assert isinstance(series, pd.DataFrame)
24+
assert series.columns == expected_columns
25+
assert len(series) == 10

0 commit comments

Comments
 (0)