TiMBA-Trade 0.1.0__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.
- timba_trade/__init__.py +0 -0
- timba_trade/core/BACIDataProcessor.py +111 -0
- timba_trade/core/InputManager.py +35 -0
- timba_trade/core/OutputManager.py +30 -0
- timba_trade/core/ProcessManager.py +107 -0
- timba_trade/core/TiMBADataProcessor.py +217 -0
- timba_trade/core/__init__.py +0 -0
- timba_trade/logic/CalculationManager.py +52 -0
- timba_trade/logic/RAS.py +306 -0
- timba_trade/logic/__init__.py +0 -0
- timba_trade/main.py +15 -0
- timba_trade/paramters/__init__.py +0 -0
- timba_trade/paramters/country_info.py +182 -0
- timba_trade/paramters/hscodes.py +374 -0
- timba_trade/paramters/paramters.py +102 -0
- timba_trade/paramters/paths.py +22 -0
- timba_trade/utils/__init__.py +0 -0
- timba_trade/utils/helper.py +23 -0
- timba_trade/utils/logger.py +23 -0
- timba_trade-0.1.0.dist-info/METADATA +55 -0
- timba_trade-0.1.0.dist-info/RECORD +24 -0
- timba_trade-0.1.0.dist-info/WHEEL +5 -0
- timba_trade-0.1.0.dist-info/licenses/COPYING +661 -0
- timba_trade-0.1.0.dist-info/top_level.txt +1 -0
timba_trade/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from src.timba_trade.core.InputManager import InputManager
|
|
2
|
+
from src.timba_trade.paramters.hscodes import timba_commodity_list, timba_comcode
|
|
3
|
+
import src.timba_trade.paramters.paramters as p
|
|
4
|
+
from src.timba_trade.utils.helper import hs_to_fao_dict
|
|
5
|
+
from src.timba_trade.utils.logger import setup_logging
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
setup_logging()
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BACIProcessor:
|
|
15
|
+
def __init__(self, df:pd.DataFrame, year: int = 2023,):
|
|
16
|
+
self.im = InputManager()
|
|
17
|
+
self.commodity_list = timba_commodity_list
|
|
18
|
+
self.year = year
|
|
19
|
+
self.df = df
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _groupby_sum(df: pd.DataFrame, id_list: list, value_list: list) -> pd.DataFrame:
|
|
23
|
+
return df.groupby(id_list)[value_list].sum().reset_index()
|
|
24
|
+
|
|
25
|
+
def prepare_baci_data(self):
|
|
26
|
+
hs_to_fao_map = hs_to_fao_dict(self.commodity_list)
|
|
27
|
+
|
|
28
|
+
df = self.df.copy()
|
|
29
|
+
df["Commodity"] = df[p.COM_COLUMN].map(hs_to_fao_map)
|
|
30
|
+
|
|
31
|
+
return df[df[p.TIME_COLUMN] == self.year].dropna(subset=["Commodity"]).reset_index(drop=True)
|
|
32
|
+
|
|
33
|
+
def create_bilateral_trade(self,df:pd.DataFrame)->pd.DataFrame:
|
|
34
|
+
counterflow = df.rename(
|
|
35
|
+
columns={
|
|
36
|
+
p.REPORTER_COLUMN: p.PARTNER_COLUMN,
|
|
37
|
+
p.PARTNER_COLUMN: p.REPORTER_COLUMN,
|
|
38
|
+
p.QUANTITY_COLUMN: "Import",
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
result = df.merge(counterflow,on=p.BACIID,how="outer")
|
|
42
|
+
result = result.rename(columns={p.QUANTITY_COLUMN: "Export"})
|
|
43
|
+
|
|
44
|
+
return self._groupby_sum(df=result,id_list=p.BACIID,value_list=p.X_M_NAMES)
|
|
45
|
+
|
|
46
|
+
def _merge_trade_totals(self,df)->pd.DataFrame:
|
|
47
|
+
exporter_totals = self._groupby_sum(df=df,id_list=p.TOTALSID,value_list=p.X_M_NAMES)
|
|
48
|
+
importer_totals = exporter_totals.rename(columns={p.REPORTER_COLUMN: p.PARTNER_COLUMN})
|
|
49
|
+
|
|
50
|
+
total = df.merge(
|
|
51
|
+
right=exporter_totals,
|
|
52
|
+
how="outer",
|
|
53
|
+
on=p.TOTALSID,
|
|
54
|
+
suffixes=(
|
|
55
|
+
"_bilateral",
|
|
56
|
+
"_total_exporter",
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
total = total.merge(
|
|
61
|
+
right=importer_totals,
|
|
62
|
+
how="outer",
|
|
63
|
+
on=p.TOTALSIMPORTID,
|
|
64
|
+
suffixes=("", "_total_importer"),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return total
|
|
68
|
+
|
|
69
|
+
def add_trade_totals(self,df:pd.DataFrame,map:dict)->pd.DataFrame:
|
|
70
|
+
total = self._merge_trade_totals(df)
|
|
71
|
+
|
|
72
|
+
total = total[
|
|
73
|
+
[
|
|
74
|
+
p.TIME_COLUMN,
|
|
75
|
+
p.REPORTER_COLUMN,
|
|
76
|
+
p.PARTNER_COLUMN,
|
|
77
|
+
"Commodity",
|
|
78
|
+
"Export_bilateral",
|
|
79
|
+
"Import_bilateral",
|
|
80
|
+
"Export_total_exporter",
|
|
81
|
+
"Import_total_exporter",
|
|
82
|
+
"Export",
|
|
83
|
+
"Import",
|
|
84
|
+
]
|
|
85
|
+
].rename(
|
|
86
|
+
columns={
|
|
87
|
+
p.REPORTER_COLUMN: "Reporter",
|
|
88
|
+
p.PARTNER_COLUMN: "Partner",
|
|
89
|
+
"Export": "Export_total_importer",
|
|
90
|
+
"Import": "Import_total_importer",
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
total["Period"] = 0
|
|
95
|
+
total["Commodity"] = total["Commodity"].map(map)
|
|
96
|
+
|
|
97
|
+
return total
|
|
98
|
+
|
|
99
|
+
def main(self)->pd.DataFrame:
|
|
100
|
+
baci_data = self.prepare_baci_data()
|
|
101
|
+
baci_data = self._groupby_sum(
|
|
102
|
+
df=baci_data,
|
|
103
|
+
id_list=p.BACIID,
|
|
104
|
+
value_list=["Quantity"]
|
|
105
|
+
)
|
|
106
|
+
bilateral_trade = self.add_trade_totals(
|
|
107
|
+
df=self.create_bilateral_trade(df=baci_data),
|
|
108
|
+
map=timba_comcode,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return bilateral_trade
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from src.timba_trade.paramters.paths import INPUT_PATH, BITRADEPATH
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pickle
|
|
5
|
+
import gzip
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InputManager:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def load_timba_results():
|
|
17
|
+
path = INPUT_PATH
|
|
18
|
+
pkl_files = list(path.glob("*.pkl"))
|
|
19
|
+
|
|
20
|
+
if not pkl_files:
|
|
21
|
+
raise FileNotFoundError(
|
|
22
|
+
f" No .pkl file found at: {path.resolve()}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
file = max(pkl_files, key=lambda f: f.stat().st_mtime)
|
|
26
|
+
|
|
27
|
+
with gzip.open(file, "rb") as pkl_file:
|
|
28
|
+
obj = pickle.load(pkl_file)
|
|
29
|
+
|
|
30
|
+
return obj["data_periods"]
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def load_baci_data():
|
|
34
|
+
return pd.read_parquet(BITRADEPATH)
|
|
35
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import pyarrow
|
|
4
|
+
|
|
5
|
+
from src.timba_trade.paramters.paths import OUTPUT_PATH
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OutputManager:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def save_df(self, df, file_name, max_attempts=10):
|
|
16
|
+
for i in range(max_attempts):
|
|
17
|
+
file = (file_name + ".csv") if i == 0 else f"{file_name}_({i}).csv"
|
|
18
|
+
try:
|
|
19
|
+
df.to_csv(OUTPUT_PATH / Path(file), index=False)
|
|
20
|
+
logger.info(f"CSV file saved at: {OUTPUT_PATH / Path(file)}")
|
|
21
|
+
return file
|
|
22
|
+
except PermissionError:
|
|
23
|
+
if i == max_attempts - 1:
|
|
24
|
+
raise
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
def save_parquet(self, df, file_name):
|
|
28
|
+
file = Path(file_name + ".parquet")
|
|
29
|
+
df.to_parquet(OUTPUT_PATH / file)
|
|
30
|
+
logger.info(f"Parquet saved at: {OUTPUT_PATH / Path(file)}")
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from src.timba_trade.core.TiMBADataProcessor import TiMBADataProcessor
|
|
2
|
+
from src.timba_trade.core.BACIDataProcessor import BACIProcessor
|
|
3
|
+
from src.timba_trade.core.InputManager import InputManager
|
|
4
|
+
from src.timba_trade.logic.CalculationManager import CalcManager
|
|
5
|
+
from src.timba_trade.logic.RAS import RAS
|
|
6
|
+
from src.timba_trade.core.OutputManager import OutputManager
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import logging
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProcessManager:
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.data = None
|
|
18
|
+
self.country_data = None
|
|
19
|
+
self.partner_data = None
|
|
20
|
+
self.countries = None
|
|
21
|
+
self.commodities = None
|
|
22
|
+
self.periods = None
|
|
23
|
+
self.dataset = None
|
|
24
|
+
self.result = None
|
|
25
|
+
self.start_time = None
|
|
26
|
+
self.im = InputManager()
|
|
27
|
+
self.om = OutputManager()
|
|
28
|
+
|
|
29
|
+
def start_process(self, process_description:str):
|
|
30
|
+
self.start_time = time.time()
|
|
31
|
+
logger.info(process_description)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def end_process(self, process_description):
|
|
35
|
+
if self.start_time is None:
|
|
36
|
+
raise ValueError("The process has not been started. Use `start_process` first.")
|
|
37
|
+
|
|
38
|
+
end_time = time.time()
|
|
39
|
+
elapsed_time = round(end_time - self.start_time, 2)
|
|
40
|
+
logger.info(
|
|
41
|
+
f"{process_description} in {elapsed_time:.1f} seconds"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def load_timba_process(self, data:pd.DataFrame|None)->pd.DataFrame:
|
|
45
|
+
self.start_process("Beginn to load TiMBA data")
|
|
46
|
+
if data is None:
|
|
47
|
+
data = self.im.load_timba_results()
|
|
48
|
+
tdp = TiMBADataProcessor(data)
|
|
49
|
+
timba_data = tdp.main()
|
|
50
|
+
self.end_process("TiMBA data loaded")
|
|
51
|
+
|
|
52
|
+
return timba_data
|
|
53
|
+
|
|
54
|
+
def load_baci_process(self)->pd.DataFrame:
|
|
55
|
+
self.start_process("Beginn to load BACI data")
|
|
56
|
+
baci_data = self.im.load_baci_data()
|
|
57
|
+
bdp = BACIProcessor(df=baci_data)
|
|
58
|
+
baci_data = bdp.main()
|
|
59
|
+
self.end_process("BACI data loaded")
|
|
60
|
+
|
|
61
|
+
return baci_data
|
|
62
|
+
|
|
63
|
+
def main_process(self, td:pd.DataFrame|None)->pd.DataFrame:
|
|
64
|
+
timba_data = self.load_timba_process(data=td)
|
|
65
|
+
fao_df = CalcManager(data=timba_data).main().copy()
|
|
66
|
+
fao_df = fao_df[["Reporter","Partner","Commodity","Period","X_q_o","M_q_p","P_share_growth"]]
|
|
67
|
+
|
|
68
|
+
bt_df = self.load_baci_process()[["Reporter","Partner","Commodity","Period","Export_bilateral"]]
|
|
69
|
+
bt_df["Export_bilateral"] /= 1000
|
|
70
|
+
|
|
71
|
+
self.start_process(f"Beginn the RAS process")
|
|
72
|
+
|
|
73
|
+
fao_df = fao_df.loc[~fao_df["Commodity"].isin([89, 90, 93])]
|
|
74
|
+
bt_df = bt_df.loc[~bt_df["Commodity"].isin([89, 90, 93])]
|
|
75
|
+
|
|
76
|
+
merge_keys = ["Reporter","Partner", "Commodity", "Period"]
|
|
77
|
+
bt_result = pd.DataFrame()
|
|
78
|
+
for period in fao_df.Period.unique():
|
|
79
|
+
logger.info(f" Period: {period}")
|
|
80
|
+
fp = fao_df[fao_df.Period==period]
|
|
81
|
+
|
|
82
|
+
bt_df = bt_df[["Reporter","Partner","Commodity","Export_bilateral"]]
|
|
83
|
+
bt_df["Period"]=period
|
|
84
|
+
bt_df = bt_df.drop_duplicates()
|
|
85
|
+
bt_df = bt_df.dropna().reset_index(drop=True)
|
|
86
|
+
bt_df = bt_df[bt_df['Reporter'] != 'RoW']
|
|
87
|
+
bt_df = bt_df[bt_df['Partner'] != 'RoW']
|
|
88
|
+
|
|
89
|
+
ras_df = fp.merge(
|
|
90
|
+
bt_df,
|
|
91
|
+
on=merge_keys,
|
|
92
|
+
how="left"
|
|
93
|
+
)
|
|
94
|
+
ras_df["Export_bilateral"] *= ras_df["P_share_growth"]
|
|
95
|
+
|
|
96
|
+
bt_df = RAS().fit(ras_df)
|
|
97
|
+
bt_result = pd.concat([bt_result,bt_df]).reset_index(drop=True)
|
|
98
|
+
|
|
99
|
+
self.om.save_parquet(
|
|
100
|
+
df=bt_result,
|
|
101
|
+
file_name="TiMBA_bilateral_results"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
self.end_process(f"End of RAS process")
|
|
105
|
+
|
|
106
|
+
return bt_result
|
|
107
|
+
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from src.timba_trade.paramters.country_info import country_dict
|
|
2
|
+
import src.timba_trade.paramters.paramters as params
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
import xarray as xr
|
|
7
|
+
import logging
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TiMBADataProcessor:
|
|
12
|
+
def __init__(self, data):
|
|
13
|
+
self.data = data
|
|
14
|
+
|
|
15
|
+
def _rename_columns(self):
|
|
16
|
+
self.data = self.data.rename(columns=params.COLUMN_MAPPING)
|
|
17
|
+
|
|
18
|
+
def _add_iso_codes(self):
|
|
19
|
+
self.data["Country"] = self.data["RegionCode"].map(country_dict)
|
|
20
|
+
|
|
21
|
+
self.data = (
|
|
22
|
+
self.data
|
|
23
|
+
.dropna(subset=["Country"])
|
|
24
|
+
.reset_index(drop=True)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
self.data = self.data[
|
|
28
|
+
[
|
|
29
|
+
"Country",
|
|
30
|
+
"Commodity",
|
|
31
|
+
"domain",
|
|
32
|
+
"Period",
|
|
33
|
+
"p",
|
|
34
|
+
"q",
|
|
35
|
+
]
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def _aggregate_domains(self):
|
|
39
|
+
self.data = self.data[
|
|
40
|
+
~self.data["domain"].isin(params.DOMAINS_TO_REMOVE)
|
|
41
|
+
].copy()
|
|
42
|
+
|
|
43
|
+
self.data["domain"] = self.data["domain"].replace(
|
|
44
|
+
params.DOMAIN_MAPPING
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
self.data = (
|
|
48
|
+
self.data
|
|
49
|
+
.groupby(
|
|
50
|
+
params.GROUP_COLUMNS,
|
|
51
|
+
as_index=False,
|
|
52
|
+
observed=True
|
|
53
|
+
)
|
|
54
|
+
.agg(
|
|
55
|
+
p=("p", "max"),
|
|
56
|
+
q=("q", "max"),
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def _downcasting(self):
|
|
61
|
+
self.data["Country"] = self.data["Country"].astype("category")
|
|
62
|
+
self.data["Commodity"] = self.data["Commodity"].astype("category")
|
|
63
|
+
self.data["domain"] = self.data["domain"].astype("category")
|
|
64
|
+
self.data["p"] = self.data["p"].astype("float32")
|
|
65
|
+
self.data["q"] = self.data["q"].astype("float32")
|
|
66
|
+
self.data["Period"] = self.data["Period"].astype("int16")
|
|
67
|
+
|
|
68
|
+
def _pivot_domains(self):
|
|
69
|
+
self.data = self.data.pivot(
|
|
70
|
+
index=params.INDEX_COLUMNS,
|
|
71
|
+
columns="domain",
|
|
72
|
+
values=params.VALUE_COLUMNS,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.data.columns = [
|
|
76
|
+
f"{domain}_{variable}"
|
|
77
|
+
for variable, domain in self.data.columns
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
self.data = self.data.reset_index()
|
|
81
|
+
|
|
82
|
+
def _sort_data(self):
|
|
83
|
+
self.data = (
|
|
84
|
+
self.data
|
|
85
|
+
.sort_values(params.GROUP_COLUMNS)
|
|
86
|
+
.reset_index(drop=True)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def _prepare_timba_for_biltrade(self):
|
|
90
|
+
self.country_data = self.data[params.COUNTRY_COLUMNS].copy()
|
|
91
|
+
|
|
92
|
+
self.partner_data = self.data[params.PARTNER_COLUMNS].rename(
|
|
93
|
+
columns={"Country": "Partner"}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.countries = np.sort(self.data["Country"].unique())
|
|
97
|
+
self.commodities = np.sort(self.data["Commodity"].unique())
|
|
98
|
+
self.periods = np.sort(self.data["Period"].unique())
|
|
99
|
+
|
|
100
|
+
def _to_dense_array(self, df: pd.DataFrame, dim_name: str, value_col: str) -> xr.DataArray:
|
|
101
|
+
series = (
|
|
102
|
+
df[[dim_name, "Commodity", "Period", value_col]]
|
|
103
|
+
.set_index([dim_name, "Commodity", "Period"])[value_col]
|
|
104
|
+
.astype(np.float32)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
array = xr.DataArray.from_series(series)
|
|
108
|
+
array = array.reindex(
|
|
109
|
+
{
|
|
110
|
+
dim_name: self.countries,
|
|
111
|
+
"Commodity": self.commodities,
|
|
112
|
+
"Period": self.periods,
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return array
|
|
117
|
+
|
|
118
|
+
def _to_dense_array(self, df: pd.DataFrame, dim_name: str, value_col: str) -> xr.DataArray:
|
|
119
|
+
series = (
|
|
120
|
+
df[[dim_name, "Commodity", "Period", value_col]]
|
|
121
|
+
.set_index([dim_name, "Commodity", "Period"])[value_col]
|
|
122
|
+
.astype(np.float32)
|
|
123
|
+
)
|
|
124
|
+
array = xr.DataArray.from_series(series)
|
|
125
|
+
array = array.reindex(
|
|
126
|
+
{
|
|
127
|
+
dim_name: self.countries,
|
|
128
|
+
"Commodity": self.commodities,
|
|
129
|
+
"Period": self.periods,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
return array
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def _log_of_price_share(data):
|
|
136
|
+
with np.errstate(divide="ignore"):
|
|
137
|
+
data["P_share"] = np.log(data["P_p_o"] / data["P_p_p"])
|
|
138
|
+
return data
|
|
139
|
+
|
|
140
|
+
def _build_dataset(self):
|
|
141
|
+
self.dataset = xr.Dataset(
|
|
142
|
+
{
|
|
143
|
+
"P_p_o": self._to_dense_array(self.country_data, "Country", "P_p"),
|
|
144
|
+
"P_q_o": self._to_dense_array(self.country_data, "Country", "P_q"),
|
|
145
|
+
"M_p_o": self._to_dense_array(self.country_data, "Country", "M_p"),
|
|
146
|
+
"M_q_o": self._to_dense_array(self.country_data, "Country", "M_q"),
|
|
147
|
+
"X_p_o": self._to_dense_array(self.country_data, "Country", "X_p"),
|
|
148
|
+
"X_q_o": self._to_dense_array(self.country_data, "Country", "X_q"),
|
|
149
|
+
"P_p_p": self._to_dense_array(self.partner_data, "Partner", "P_p"),
|
|
150
|
+
"P_q_p": self._to_dense_array(self.partner_data, "Partner", "P_q"),
|
|
151
|
+
"X_p_p": self._to_dense_array(self.partner_data, "Partner", "X_p"),
|
|
152
|
+
"X_q_p": self._to_dense_array(self.partner_data, "Partner", "X_q"),
|
|
153
|
+
"M_q_p": self._to_dense_array(self.partner_data, "Partner", "M_q"),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
self._log_of_price_share(data=self.dataset)
|
|
158
|
+
self._add_lags()
|
|
159
|
+
|
|
160
|
+
def _add_lags(self):
|
|
161
|
+
lagged = self.dataset.shift(Period=1)
|
|
162
|
+
lagged = lagged.rename(
|
|
163
|
+
{var: f"{var}_lag" for var in lagged.data_vars}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
self.dataset = xr.merge([self.dataset, lagged])
|
|
167
|
+
|
|
168
|
+
def _process_all(self):
|
|
169
|
+
chunk_size = params.PROCESS_CHUNK_SIZE
|
|
170
|
+
n_countries = len(self.countries)
|
|
171
|
+
|
|
172
|
+
chunks = []
|
|
173
|
+
|
|
174
|
+
for start in range(0, n_countries, chunk_size):
|
|
175
|
+
end = min(start + chunk_size, n_countries)
|
|
176
|
+
|
|
177
|
+
chunk_ds = self.dataset.isel(Country=slice(start, end))
|
|
178
|
+
|
|
179
|
+
chunk_df = (
|
|
180
|
+
chunk_ds.to_dataframe()
|
|
181
|
+
.reset_index()
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
chunks.append(chunk_df)
|
|
185
|
+
|
|
186
|
+
self.result = pd.concat(chunks, ignore_index=True)
|
|
187
|
+
|
|
188
|
+
self.result = self.result[
|
|
189
|
+
[
|
|
190
|
+
"Country", "Partner", "Commodity", "Period",
|
|
191
|
+
"P_p_o", "P_p_o_lag",
|
|
192
|
+
"P_q_o", "P_q_o_lag",
|
|
193
|
+
"P_p_p", "P_p_p_lag",
|
|
194
|
+
"P_q_p", "P_q_p_lag",
|
|
195
|
+
"M_p_o", "M_p_o_lag",
|
|
196
|
+
"M_q_o", "M_q_o_lag",
|
|
197
|
+
"M_q_p", "M_q_p_lag",
|
|
198
|
+
"X_p_o", "X_p_o_lag",
|
|
199
|
+
"X_q_o", "X_q_o_lag",
|
|
200
|
+
"X_p_p", "X_p_p_lag",
|
|
201
|
+
"X_q_p", "X_q_p_lag",
|
|
202
|
+
"P_share", "P_share_lag",
|
|
203
|
+
]
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
def main(self) -> pd.DataFrame:
|
|
207
|
+
self._rename_columns()
|
|
208
|
+
self._add_iso_codes()
|
|
209
|
+
self._aggregate_domains()
|
|
210
|
+
self._sort_data()
|
|
211
|
+
self._downcasting()
|
|
212
|
+
self._pivot_domains()
|
|
213
|
+
self._prepare_timba_for_biltrade()
|
|
214
|
+
self._build_dataset()
|
|
215
|
+
self._process_all()
|
|
216
|
+
|
|
217
|
+
return self.result
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CalcManager:
|
|
6
|
+
def __init__(self, data:pd.DataFrame):
|
|
7
|
+
self.AE = 1
|
|
8
|
+
self.data = data
|
|
9
|
+
|
|
10
|
+
def _calc_price_share(self):
|
|
11
|
+
df = self.data
|
|
12
|
+
df["P_share_growth"] = df.P_share - df.P_share_lag + 1
|
|
13
|
+
df["LD"] = df.P_q_o - df.X_q_o + df.M_q_o
|
|
14
|
+
df["LD_lag"] = df.P_q_o_lag - df.X_q_o_lag + df.M_q_o_lag
|
|
15
|
+
df["LD_growth"] = df.LD / df.LD_lag
|
|
16
|
+
|
|
17
|
+
df.rename(
|
|
18
|
+
columns={
|
|
19
|
+
"Country":"Reporter"
|
|
20
|
+
},
|
|
21
|
+
inplace=True
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
self.results = df[[
|
|
25
|
+
"Reporter", "Partner", "Commodity", "Period",
|
|
26
|
+
"X_q_o", "M_q_o", "X_q_p", "M_q_p",
|
|
27
|
+
"P_share_growth","LD_growth"
|
|
28
|
+
]]
|
|
29
|
+
|
|
30
|
+
def clip_shares(self,cols:list,lb_fix:float=None,ub_fix:float=None):
|
|
31
|
+
df = self.results
|
|
32
|
+
for col in cols:
|
|
33
|
+
df[col] = df[col].replace([np.inf, -np.inf, np.nan], 1)
|
|
34
|
+
q1 = df[col].quantile(0.25)
|
|
35
|
+
q3 = df[col].quantile(0.75)
|
|
36
|
+
iqr = (q3 - q1)
|
|
37
|
+
|
|
38
|
+
lb = lb_fix if lb_fix is not None else q1 - iqr * 5
|
|
39
|
+
ub = ub_fix if ub_fix is not None else q3 + iqr * 5
|
|
40
|
+
|
|
41
|
+
df[col] = df[col].clip(lower=lb, upper=ub)
|
|
42
|
+
|
|
43
|
+
def _combined_effect(self):
|
|
44
|
+
self.results["M_growth"] = self.results["LD_growth"] * self.results["P_share_growth"] ** (self.AE)
|
|
45
|
+
|
|
46
|
+
def main(self)->pd.DataFrame:
|
|
47
|
+
self._calc_price_share()
|
|
48
|
+
self.clip_shares(cols=["P_share_growth"],lb_fix=0.5,ub_fix=2)
|
|
49
|
+
self.clip_shares(cols=["LD_growth"])
|
|
50
|
+
self._combined_effect()
|
|
51
|
+
#self.clip_shares(cols=["M_growth"])
|
|
52
|
+
return self.results
|