modelflowib 2.73__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.
- modelBLfunk.py +180 -0
- model_Excel.py +332 -0
- model_cvx.py +139 -0
- model_dynare.py +173 -0
- model_financial_stability.py +88 -0
- model_latex.py +497 -0
- model_latex_class.py +808 -0
- model_parquet_mixin.py +424 -0
- modelclass.py +9828 -0
- modelconstruct.py +1496 -0
- modelconstruct_estimation.py +2872 -0
- modeldash.py +265 -0
- modeldashboot.py +202 -0
- modeldashsidebar.py +456 -0
- modeldekom.py +651 -0
- modeldiff.py +561 -0
- modeldisplay.py +550 -0
- modelestimation.py +1776 -0
- modelestimator_new.py +2613 -0
- modelflowib-2.73.dist-info/METADATA +156 -0
- modelflowib-2.73.dist-info/RECORD +44 -0
- modelflowib-2.73.dist-info/WHEEL +5 -0
- modelflowib-2.73.dist-info/licenses/license.md +10 -0
- modelflowib-2.73.dist-info/top_level.txt +39 -0
- modelgrab.py +318 -0
- modelgrabgdx.py +584 -0
- modelgrabwf2.py +1107 -0
- modelhelp.py +543 -0
- modelhtml.py +606 -0
- modelinvert.py +250 -0
- modeljupyter.py +824 -0
- modeljupytermagic.py +813 -0
- modelmacrograb.py +98 -0
- modelmanipulation.py +1461 -0
- modelmf.py +349 -0
- modelnet.py +114 -0
- modelnewton.py +2178 -0
- modelnormalize.py +430 -0
- modelpattern.py +428 -0
- modelreport.py +2187 -0
- modeluserfunk.py +97 -0
- modelvis.py +1038 -0
- modelwidget.py +718 -0
- modelwidget_input.py +1933 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modelflowib
|
|
3
|
+
Version: 2.73
|
|
4
|
+
Summary: A tool to solve and manage dynamic economic and financial models
|
|
5
|
+
Author-email: Ib Hansen <Ib.Hansen.Iv@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/IbHansen/ModelFlow2
|
|
8
|
+
Project-URL: Repository, https://github.com/IbHansen/ModelFlow2
|
|
9
|
+
Keywords: economics,dynamic models,BLL,numba,modelling
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: license.md
|
|
15
|
+
Requires-Dist: pandas
|
|
16
|
+
Requires-Dist: matplotlib
|
|
17
|
+
Requires-Dist: seaborn
|
|
18
|
+
Requires-Dist: sympy
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: networkx
|
|
21
|
+
Requires-Dist: tqdm
|
|
22
|
+
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: numexpr
|
|
24
|
+
Requires-Dist: bottleneck
|
|
25
|
+
Requires-Dist: XlsxWriter
|
|
26
|
+
Requires-Dist: pyarrow
|
|
27
|
+
Requires-Dist: tabulate
|
|
28
|
+
Requires-Dist: openpyxl
|
|
29
|
+
Requires-Dist: xarray
|
|
30
|
+
Requires-Dist: cvxopt
|
|
31
|
+
Requires-Dist: numba
|
|
32
|
+
Requires-Dist: dash
|
|
33
|
+
Requires-Dist: dash-bootstrap-components
|
|
34
|
+
Requires-Dist: dash-interactive-graphviz
|
|
35
|
+
Requires-Dist: lmfit
|
|
36
|
+
Requires-Dist: jupyter
|
|
37
|
+
Requires-Dist: ipywidgets
|
|
38
|
+
Requires-Dist: ipydatagrid
|
|
39
|
+
Provides-Extra: shiny
|
|
40
|
+
Requires-Dist: shiny; extra == "shiny"
|
|
41
|
+
Requires-Dist: shinywidgets; extra == "shiny"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# ModelFlow
|
|
45
|
+
A Python toolkit to manage models
|
|
46
|
+
|
|
47
|
+
The easy way to start is here [](https://mybinder.org/v2/gh/IbHansen/modelflow/master).
|
|
48
|
+
|
|
49
|
+
This will start **ModelFlow** as an online Jupyter notebook. Select one of the notebooks (files with the extension .ipynb). Some of the
|
|
50
|
+
notebooks are ModelFlow related. Other implement different model
|
|
51
|
+
|
|
52
|
+
- FRBUS will start the FRB/US model from the Federal reserve board.
|
|
53
|
+
- Q-JEM will start the Quarterly Japanese Economic Model from Bank of Japan
|
|
54
|
+
- ADAM will start the Anual Danish Aggregate Model for the Danish Statistical agency.
|
|
55
|
+
- Solow will implement a simple textybook Solow model.
|
|
56
|
+
|
|
57
|
+
You can also look at the python files of the system. Located at the **modelflow/** folder
|
|
58
|
+
|
|
59
|
+
Alternative look at the *getting started* section later in this file. It explains how to run ModelFlow localy.
|
|
60
|
+
|
|
61
|
+
The **Pandas** library is a great library to handle all kinds of datamanipulation and transformations.
|
|
62
|
+
|
|
63
|
+
However when it comes to models which contains lags or models which requires solving simultanous equations, Pandas is not quite helpful.
|
|
64
|
+
|
|
65
|
+
ModelFlow extends Pandas to handle a range of such models. And they can be large.
|
|
66
|
+
|
|
67
|
+
It requires you to specify the *model* specified as equations (the **business logic**) and place the *data* in a Pandas **DataFrame**. ModelFlow allows the model to meet the data and return the result as a new DataFrame.
|
|
68
|
+
|
|
69
|
+
A number of **analytical tools for model and result analytic** helps to understand the model and its results.
|
|
70
|
+
|
|
71
|
+
The user can **extend and modify the tools** to her or his needs.
|
|
72
|
+
|
|
73
|
+
**Onboarding models and combining models from different sources**. Creating a Macro prudential model often entails recycling several models specified in different ways: Excel, Latex, Dynare, Python or other languages. Python's ecosystem makes it possible to transform many different models into ModelFlow models or to wrap them into functions which can be called from ModelFlow models.
|
|
74
|
+
|
|
75
|
+
**Models can be specified in a high level Business logic language (a Domain Specific language)**. This allows the formulation of a model in a concise and expressive language which is close to the economic of the model. The user can concentrate on the economic or financial content - not the coding of the solution. The code for solving the model is generated by the tool. Then you can *solve the
|
|
76
|
+
simultaneous* (or *non-simultaneous* model) in an efficient way.
|
|
77
|
+
|
|
78
|
+
## Introduction
|
|
79
|
+
|
|
80
|
+
**ModelFlow is written in Python**. Python comes "batteries included" and is
|
|
81
|
+
the basis of a very rich ecosystem, which consists of a wide array of
|
|
82
|
+
libraries. ModelFlow is just another library. It supplements the existing
|
|
83
|
+
libraries regarding modeling language and solving and allows the use of
|
|
84
|
+
Python as a model management framework.
|
|
85
|
+
|
|
86
|
+
**Data handling and wrangling is done in the Pandas library**. This
|
|
87
|
+
library is the Swiss army knife of data science in Python. It can import and export data to most systems and it is very powerful in manipulating and transforming data.
|
|
88
|
+
The core
|
|
89
|
+
element of Pandas is the *Dataframe*. A Dataframe is a two-dimensional
|
|
90
|
+
tabular data structure. Each *column* consists of cells of the same type
|
|
91
|
+
-- it can be a number, a string, a matrix or another Python data object.This includes matrices and other dataframes. Each *row is indexed.* The index can basically be any type of variable
|
|
92
|
+
including dates, which is especially relevant for economic and financial models.
|
|
93
|
+
|
|
94
|
+
**ModelFlow gives the user tools for more than solving models**. This
|
|
95
|
+
includes:
|
|
96
|
+
|
|
97
|
+
- *Visualization* and comparison of results
|
|
98
|
+
|
|
99
|
+
- *Integration* of models from different sources
|
|
100
|
+
|
|
101
|
+
- *Analyze the logical structure of a model*. By applying graph theory,
|
|
102
|
+
ModelFlow can find data lineage, find a suitable calculating sequence and trace
|
|
103
|
+
causes of changes through the calculations.
|
|
104
|
+
|
|
105
|
+
- *Inverting* the model to calculating the necessary instruments to
|
|
106
|
+
achieve a desired target.
|
|
107
|
+
|
|
108
|
+
- Calculating the *attributions* from input to the results of a model.
|
|
109
|
+
|
|
110
|
+
- Calculating the *attribution* from input to the result of each
|
|
111
|
+
formula.
|
|
112
|
+
|
|
113
|
+
- Finding and calculating partial *derivatives* of formulas
|
|
114
|
+
|
|
115
|
+
- *Integrating user defined python functions* in the Business logic
|
|
116
|
+
language (like optimization, calculating risk weights or to make a matrices consistent with the RAS algorithm )
|
|
117
|
+
|
|
118
|
+
- *Wrap matlab* models so they can be used in the Business logic
|
|
119
|
+
language.
|
|
120
|
+
|
|
121
|
+
- *Speed up* solving using "Just in time compilation"
|
|
122
|
+
|
|
123
|
+
- Analyze the model structure through tools from graph theory
|
|
124
|
+
|
|
125
|
+
- Handle *large models.* 1,000,000 formulas is not a problem.
|
|
126
|
+
|
|
127
|
+
- Integrate model management in Jupyter notebooks for *agile and user
|
|
128
|
+
friendly model use*
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
**The core code of ModelFlow is small and
|
|
132
|
+
documented.** Thus it can easily be modified and expanded to the specific need of the user. *ModelFlow is a toolset*. It can handle models, which conform to the tools.
|
|
133
|
+
|
|
134
|
+
If you need a feature or have a model which can't be handled in ModelFlow,
|
|
135
|
+
you are encouraged to improve ModelFlow. Please share the
|
|
136
|
+
improvement, other users may have the same need, or can be inspired by
|
|
137
|
+
your work.
|
|
138
|
+
|
|
139
|
+
Also bear in mind that ModelFlow is experimental. It is provided ”as is”, without any representation or warranty of any kind either express or implied.
|
|
140
|
+
|
|
141
|
+
## Getting started
|
|
142
|
+
|
|
143
|
+
You need Python 3.7+ with asssociated libraries. The easy way is to install Anaconda Python is to download the distribution from [https://www.anaconda.com/distribution](https://www.anaconda.com/distribution)
|
|
144
|
+
|
|
145
|
+
Now you can install ModelFlow by running a command window from the Anaconda prompt and execute theese commands
|
|
146
|
+
```
|
|
147
|
+
conda install modelflow -c ibh -c defaults -c conda-forge
|
|
148
|
+
```
|
|
149
|
+
You will find the anaconda prompt by searching "anaconda" in the start menu search field
|
|
150
|
+
|
|
151
|
+
Try out one of the workbooks. to do this you:
|
|
152
|
+
```
|
|
153
|
+
click on the Adaconda prompth in the Anaconda folder
|
|
154
|
+
cd <The location you have downloaded the workbooks>
|
|
155
|
+
jupyter notebook
|
|
156
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
modelBLfunk.py,sha256=2behWirmYQ7XMcp2E8plH5wjQ3M2nnqL-mX0SgaoHp4,4769
|
|
2
|
+
model_Excel.py,sha256=RiqfAWPrzmVT3eRLTNAy0ASHdx5pQsRABe5XlW_Kye4,11840
|
|
3
|
+
model_cvx.py,sha256=PKBJ2gv6Iryl4N5YrWi6R5XTenxIeOnlR57HQ2EhPYk,6102
|
|
4
|
+
model_dynare.py,sha256=WLW4m1SrfVlv0c6gdYhLCn9S8W8nLYkZst0etPe9JnQ,8037
|
|
5
|
+
model_financial_stability.py,sha256=3Wl20LV0T7EDcbf7THu2XXun-ParERaFatEGWG2EyKM,3084
|
|
6
|
+
model_latex.py,sha256=PKC0EWY37e4odM0he_sFLM9Ywbc9ydAPTFp6bZfbruI,18287
|
|
7
|
+
model_latex_class.py,sha256=rgVrG5rEozrlWndrsSfRkporn2Fwy0L9ktNov26ZxE0,28471
|
|
8
|
+
model_parquet_mixin.py,sha256=y75nrLIBovJYg5Ecq1cUqo3VRRCkM4Gg8incvkolMW0,16699
|
|
9
|
+
modelclass.py,sha256=Sa8LsLW8VTEUxkjPsOaPVmTA011Afil8Ho1e6FTY3So,444613
|
|
10
|
+
modelconstruct.py,sha256=Iv0C36egeinys5ppgXnl1_6W2VUCpt7w-sYSH26a4SE,51054
|
|
11
|
+
modelconstruct_estimation.py,sha256=0Yzm6ZqEKWmD-kIElT66Om8Hcu6Sl7OM6DZsWkMXlJg,106664
|
|
12
|
+
modeldash.py,sha256=Nns5JqAP5xjrdkD5752w2m9hwdm0F1mSi9RUNR0ZZRY,10641
|
|
13
|
+
modeldashboot.py,sha256=rSyg5aYgRjVeJNHTGbE2X_1bvUmpW_sK-oYcDCrb_gU,7516
|
|
14
|
+
modeldashsidebar.py,sha256=mUPCPFeYb6YPF1SFm4NALhbTZuYTBFnPq8RA1eeNhdA,18547
|
|
15
|
+
modeldekom.py,sha256=rnc8wBEZ65KRq44ICvbX2aviC7XPM7bATKeCrXthoK8,30360
|
|
16
|
+
modeldiff.py,sha256=erNGFxi2jy-59yxfkMv5D9lBSPauuljXlPNU2twrQyM,22500
|
|
17
|
+
modeldisplay.py,sha256=Ock9hJuOYh5onqyzT21VTIKR8_cqyoBEbMgQN5bCaLc,21301
|
|
18
|
+
modelestimation.py,sha256=bhEV2FsIINIad2oo-dAphdigmKaxZSyg4sHjZbcTb7I,70482
|
|
19
|
+
modelestimator_new.py,sha256=JEAYhwtjoPAm6tleR2xa0AqIiMm0jjyOwccMGgD5610,107136
|
|
20
|
+
modelgrab.py,sha256=3bJJRzfGChNExTOGfxzCfkq8aPiXRnBNRfLBeRB-GMc,14769
|
|
21
|
+
modelgrabgdx.py,sha256=pdi3HOM2NP7BvpC0Stg6PxUsOMlQMjrRh7oT9ZOheFk,21644
|
|
22
|
+
modelgrabwf2.py,sha256=6N62awkP8qx0yS4Tp-4Gz54QTKP_d5TYgMEECScXdpI,45239
|
|
23
|
+
modelhelp.py,sha256=47Lq9N-orlhry2ldfWzI1jICRRzT6jZ_TfOBcsl25Vs,21135
|
|
24
|
+
modelhtml.py,sha256=hOpczHInDqcwHLDo_WynBXfb4vvlMabk7yTsITlaYRg,22083
|
|
25
|
+
modelinvert.py,sha256=sbPPk29RlpMMnzB15g5FyE4SZvI9hxtGDkUbvK4_hIc,11217
|
|
26
|
+
modeljupyter.py,sha256=onKYvNUI4YfX8PjO8pJPe5vcQBwVa1Uua6mxLueyFoM,34218
|
|
27
|
+
modeljupytermagic.py,sha256=il7CXYQ6RiaOISu3ZJMBsZrzqGvLCKewIXCCkHfJVQA,27271
|
|
28
|
+
modelmacrograb.py,sha256=tBoISMYPkuckiop0HaGSKb3OwMGbJejXKmyJNXAQGAo,3771
|
|
29
|
+
modelmanipulation.py,sha256=olHIxKueVuspSGI47gmgQLoha_aaQq5jeaA7ot9yNX0,60771
|
|
30
|
+
modelmf.py,sha256=fNHB3KcoFYOhiIpX2X8xevnBkK8xD1Z5I8ehdwT5xgU,12409
|
|
31
|
+
modelnet.py,sha256=dMvJ0mdY9OCVD-VvmVdis-9TgQbediHfsKLHGCKO4b4,4960
|
|
32
|
+
modelnewton.py,sha256=3YbE7yGhOSuzL5Y-YtLSERkEKGFNXA40yDHtJT1vcto,104652
|
|
33
|
+
modelnormalize.py,sha256=0JwQ4XLVMXgZvdSiHiDutnyl6p_AIXde146mqOddg_E,18053
|
|
34
|
+
modelpattern.py,sha256=yw5ivHReI_uuEw3R6Grc7ehwrwDVCSBRQ5g75jByBkg,16809
|
|
35
|
+
modelreport.py,sha256=WRolsT4qInCLULRXSa4SYQNB40xUAmga02OaJAckxFI,88391
|
|
36
|
+
modeluserfunk.py,sha256=ro3C4bRFR0c-jxNwbmGJF5Fs9021ol1a5HrPqZfe1gE,3349
|
|
37
|
+
modelvis.py,sha256=hPPlPwSlhZ7tY4B_58QsH2olvM1zA0-zDdcFH-RaIYE,43430
|
|
38
|
+
modelwidget.py,sha256=tUy4aJgggSJdVH4kz5SSNnGNINjx2eg7Qxlr9L3I8Vg,27515
|
|
39
|
+
modelwidget_input.py,sha256=Tn_uBPGsNlMrU3Ch0y4assLqegyKdfRyfFInJ7EfbO0,69446
|
|
40
|
+
modelflowib-2.73.dist-info/licenses/license.md,sha256=WHoD6zNrj781B-IpjmCnu4gPDhkzexjJkVtnYfWFy3Y,1353
|
|
41
|
+
modelflowib-2.73.dist-info/METADATA,sha256=FAE2UIa2GxeHec5hljk4sTk_8dqpKdwxF3bL2X-UgH4,7440
|
|
42
|
+
modelflowib-2.73.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
43
|
+
modelflowib-2.73.dist-info/top_level.txt,sha256=GIAiCmVrEXo48UyogZXtVa4JeKSTgAd9dP_s8weM4QA,539
|
|
44
|
+
modelflowib-2.73.dist-info/RECORD,,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Copyright © 2019 Danmarks Nationalbank and ECB
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Soft-ware"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
7
|
+
|
|
8
|
+
Except as contained in this notice, the name of Danmarks Nationalbank and ECB shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Danmarks Nationalbank and ECB.
|
|
9
|
+
|
|
10
|
+
Based on MIT License.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
modelBLfunk
|
|
2
|
+
model_Excel
|
|
3
|
+
model_cvx
|
|
4
|
+
model_dynare
|
|
5
|
+
model_financial_stability
|
|
6
|
+
model_latex
|
|
7
|
+
model_latex_class
|
|
8
|
+
model_parquet_mixin
|
|
9
|
+
modelclass
|
|
10
|
+
modelconstruct
|
|
11
|
+
modelconstruct_estimation
|
|
12
|
+
modeldash
|
|
13
|
+
modeldashboot
|
|
14
|
+
modeldashsidebar
|
|
15
|
+
modeldekom
|
|
16
|
+
modeldiff
|
|
17
|
+
modeldisplay
|
|
18
|
+
modelestimation
|
|
19
|
+
modelestimator_new
|
|
20
|
+
modelgrab
|
|
21
|
+
modelgrabgdx
|
|
22
|
+
modelgrabwf2
|
|
23
|
+
modelhelp
|
|
24
|
+
modelhtml
|
|
25
|
+
modelinvert
|
|
26
|
+
modeljupyter
|
|
27
|
+
modeljupytermagic
|
|
28
|
+
modelmacrograb
|
|
29
|
+
modelmanipulation
|
|
30
|
+
modelmf
|
|
31
|
+
modelnet
|
|
32
|
+
modelnewton
|
|
33
|
+
modelnormalize
|
|
34
|
+
modelpattern
|
|
35
|
+
modelreport
|
|
36
|
+
modeluserfunk
|
|
37
|
+
modelvis
|
|
38
|
+
modelwidget
|
|
39
|
+
modelwidget_input
|
modelgrab.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Mon Jun 10 21:11:08 2019
|
|
4
|
+
|
|
5
|
+
@author: hanseni
|
|
6
|
+
|
|
7
|
+
modules to grab models with different specifications and make them ModelFlow conforme
|
|
8
|
+
|
|
9
|
+
**GrabWbModel** will take a eviews model and transform it to Business logic
|
|
10
|
+
- Create a normalized model, add dampning for the stocastic equations
|
|
11
|
+
- Add add-factors to the stocastic equations
|
|
12
|
+
- Generate BL for a model which calculates add-factors so a solution will match teh existing values
|
|
13
|
+
- Generate BL for the model
|
|
14
|
+
|
|
15
|
+
-grap data from excel sheet
|
|
16
|
+
|
|
17
|
+
- Make model instance for model and add-factor model
|
|
18
|
+
|
|
19
|
+
- Run the model, check that the results match.
|
|
20
|
+
|
|
21
|
+
For debuggging valuesthe last part checs value in the order, in which they are calculated,
|
|
22
|
+
and then displays the input to off mark equations
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import pandas as pd
|
|
26
|
+
import re
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
import functools
|
|
29
|
+
from tqdm import tqdm
|
|
30
|
+
import json
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
from modelclass import model
|
|
35
|
+
import modelmf
|
|
36
|
+
import modelmanipulation as mp
|
|
37
|
+
|
|
38
|
+
import modelnormalize as nz
|
|
39
|
+
|
|
40
|
+
assert 1==1
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class GrapWbModel():
|
|
45
|
+
'''This class takes a world bank model specification, variable data and variable description
|
|
46
|
+
and transform it to ModelFlow business language'''
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
frml : str = '' # path to model
|
|
50
|
+
data : str = '' # path to data
|
|
51
|
+
des : any = '' # path to descriptions
|
|
52
|
+
scalars : str = '' # path to scalars
|
|
53
|
+
modelname : str = 'No Name' # modelname
|
|
54
|
+
start : int = 2017
|
|
55
|
+
end : int = 2040
|
|
56
|
+
country_trans : any = lambda x:x[:] # function which transform model specification
|
|
57
|
+
country_df_trans : any = lambda x:x # function which transforms initial dataframe
|
|
58
|
+
from_wf2 : bool = False
|
|
59
|
+
make_fitted : bool = False # if True, a clean equation for fittet variables is created
|
|
60
|
+
fit_start : int =2000 # start of fittet model unless overruled by mfmsa
|
|
61
|
+
fit_end : int = 2100 # end of fittet model unless overruled by mfmsa
|
|
62
|
+
do_add_factor_calc : bool = True # calculate the add factors
|
|
63
|
+
mfmsa : str = '' # path to mfsa options
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __post_init__(self):
|
|
67
|
+
# breakpoint()
|
|
68
|
+
|
|
69
|
+
print(f'\nProcessing the model:{self.modelname}',flush=True)
|
|
70
|
+
self.rawmodel_org = open(self.frml).read()
|
|
71
|
+
eviewsline = [l for l in self.rawmodel_org.split('\n') if len(l.strip()) >=2
|
|
72
|
+
and not ('*******' in l or '----------' in l)]
|
|
73
|
+
# breakpoint()
|
|
74
|
+
self.rawmodel = self.country_trans(self.rawmodel_org)
|
|
75
|
+
rawmodel6 = self.trans_eviews(self.rawmodel)
|
|
76
|
+
# breakpoint()
|
|
77
|
+
bars = '{desc}: {percentage:3.0f}%|{bar}|{n_fmt}/{total_fmt}'
|
|
78
|
+
if self.from_wf2:
|
|
79
|
+
|
|
80
|
+
orgline = [l for l in rawmodel6.split('\n')]
|
|
81
|
+
line_type = ['ident' if l.startswith('@IDENTITY') else 'stoc' for l in orgline]
|
|
82
|
+
line = [l.replace('@IDENTITY ','').replace(' ','') for l in orgline]
|
|
83
|
+
else:
|
|
84
|
+
line_type = []
|
|
85
|
+
line =[]
|
|
86
|
+
with tqdm(total=len(rawmodel6.split('\n')),desc='Reading original model',bar_format=bars) as pbar:
|
|
87
|
+
for l in rawmodel6.split('\n'):
|
|
88
|
+
if ('*******' in l or '----------' in l) and 'IDEN' in l.upper():
|
|
89
|
+
sec='iden'
|
|
90
|
+
#print(l)
|
|
91
|
+
elif '*******' in l and 'STOC' in l:
|
|
92
|
+
sec='stoc'
|
|
93
|
+
#print(l)
|
|
94
|
+
else:
|
|
95
|
+
line_type.append(sec)
|
|
96
|
+
line.append(l)
|
|
97
|
+
# print(f' {sec} {l[:30]} ....')
|
|
98
|
+
pbar.update(1)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
errline1 = [(l,o,e) for l,o,e in zip(line,orgline,eviewsline) if '@' in l or '.coef' in l ]
|
|
102
|
+
if len(errline1):
|
|
103
|
+
print('Probably errors as @ in lines:')
|
|
104
|
+
for l,o,e in errline1:
|
|
105
|
+
print(f'\nEviews line :{e}')
|
|
106
|
+
print(f'Original line :{o}')
|
|
107
|
+
print(f'New modelflow line:{l}')
|
|
108
|
+
raise Exception('@ in lines ')
|
|
109
|
+
|
|
110
|
+
self.all_frml = [nz.normal(l,add_add_factor=(typ=='stoc'),make_fitted=(typ=='stoc'),make_fixable =(typ=='stoc')) for l,typ in tqdm(zip(line,line_type),desc='Normalizing model',total=len(line),bar_format=bars)]
|
|
111
|
+
self.all_frml_dict = {f.endo_var: f for f in self.all_frml}
|
|
112
|
+
lfname = ["<Z,EXO> " if typ == 'stoc' else '' for typ in line_type ]
|
|
113
|
+
self.rorg = [fname + f.normalized for f,fname in zip(self.all_frml,lfname) ]
|
|
114
|
+
|
|
115
|
+
if self.make_fitted:
|
|
116
|
+
self.rfitmodel = ['<FIT> ' + f.fitted for f in self.all_frml if len(f.fitted)]
|
|
117
|
+
self.mfitmodel = model('\n'.join(self.rfitmodel))
|
|
118
|
+
self.mfitmodel.modelname = self.modelname + ' calc fittet values'
|
|
119
|
+
else:
|
|
120
|
+
self.rfitmodel = []
|
|
121
|
+
|
|
122
|
+
self.rres = [f'{f.calc_add_factor}' for f in self.all_frml if len(f.calc_add_factor)]
|
|
123
|
+
self.rres_tomodel ='\n'.join([f'FRML <CALC_ADD_FACTOR> {f.calc_add_factor}$' for f in self.all_frml if len(f.calc_add_factor)])
|
|
124
|
+
# self.fmodel = mp.exounroll(mp.tofrml ('\n'.join(self.rorg+self.rfitmodel)))+self.rres_tomodel
|
|
125
|
+
self.fmodel = mp.tofrml ('\n'.join(self.rorg+self.rfitmodel))+self.rres_tomodel
|
|
126
|
+
# breakpoint()
|
|
127
|
+
self.fres = ('\n'.join(self.rres))
|
|
128
|
+
self.mmodel = model(self.fmodel,modelname = self.modelname)
|
|
129
|
+
self.mmodel.set_var_description(self.var_description)
|
|
130
|
+
self.mmodel.wb_MFMSAOPTIONS = self.mfmsa_options
|
|
131
|
+
self.mres = model(self.fres,modelname = f'Calculation of add factors for {self.modelname}')
|
|
132
|
+
# breakpoint()
|
|
133
|
+
self.start,self.end = self.mfmsa_start_end
|
|
134
|
+
if self.do_add_factor_calc:
|
|
135
|
+
self.base_input = self.mres.res(self.dfmodel,self.start,self.end)
|
|
136
|
+
else:
|
|
137
|
+
self.base_input = self.dfmodel
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def trans_eviews(rawmodel):
|
|
141
|
+
rawmodel0 = '\n'.join(l for l in rawmodel.upper().split('\n') if len(l.strip()) >=2)
|
|
142
|
+
# trailing and leading "
|
|
143
|
+
rawmodel1 = '\n'.join(l[1:-1] if l.startswith('"') else l for l in rawmodel0.split('\n'))
|
|
144
|
+
# powers
|
|
145
|
+
rawmodel2 = rawmodel1.replace('^','**').replace('""',' ').replace('"',' ').\
|
|
146
|
+
replace('@EXP','exp').replace('@RECODE','recode').replace('@MOVAV','movavg').replace('@LOGIT','logit_inverse') \
|
|
147
|
+
.replace('@MEAN(@PC(','@AVERAGE_GROWTH((').replace('@PCY','PCT_GROWTH').replace('@PC','PCT_GROWTH')\
|
|
148
|
+
.replace('@PMAX','MAX').replace('@TREND','EVIEWS_TREND')
|
|
149
|
+
# @ELEM and @DURING
|
|
150
|
+
# @ELEM and @DURING
|
|
151
|
+
rawmodel3 = nz.elem_trans(rawmodel2)
|
|
152
|
+
rawmodel4 = re.sub(r'@DURING\( *([0-9]+) *\)', r'during_\1',rawmodel3)
|
|
153
|
+
rawmodel5 = re.sub(r'@DURING\( *([0-9]+) *([0-9]+) *\)', r'during_\1_\2',rawmodel4)
|
|
154
|
+
|
|
155
|
+
# during check
|
|
156
|
+
ldur = '\n'.join(l for l in rawmodel5.split('\n') if '@DURING' in l)
|
|
157
|
+
ldur2 = '\n'.join(l for l in rawmodel5.split('\n') if 'during' in l)
|
|
158
|
+
|
|
159
|
+
# check D(
|
|
160
|
+
ld = '\n'.join(l for l in rawmodel5.split('\n') if re.search(r'([^A-Z]|^)D\(',l) )
|
|
161
|
+
ld1 = '\n'.join(l for l in rawmodel5.split('\n') if re.search(r'([^A-Z0-9_]|^)D\(',l) )
|
|
162
|
+
# breakpoint()
|
|
163
|
+
rawmodel6 = nz.funk_replace('D','DIFF',rawmodel5)
|
|
164
|
+
# did we get all the lines
|
|
165
|
+
ldif = '\n'.join(l for l in rawmodel6.split('\n') if 'DIFF(' in l )
|
|
166
|
+
return rawmodel6
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def var_description(self):
|
|
170
|
+
'''
|
|
171
|
+
Adds var descriptions for add factors, exogenizing dummies and exoggenizing values
|
|
172
|
+
'''
|
|
173
|
+
|
|
174
|
+
if isinstance(self.des,dict):
|
|
175
|
+
return self.des
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
# breakpoint()
|
|
179
|
+
des_file = Path(self.des)
|
|
180
|
+
if des_file.suffix == '.xlsx':
|
|
181
|
+
trans0 = pd.read_excel(self.des).loc[:,['mnem','Excel']].set_index('mnem').to_dict(orient = 'dict')['Excel']
|
|
182
|
+
var_description = {str(k) : str(v) for k,v in trans0.items() if 'nan' != str(v)}
|
|
183
|
+
else:
|
|
184
|
+
with open(des_file,'rt') as f:
|
|
185
|
+
var_description = json.load(f)
|
|
186
|
+
add_d = { newname : 'Add factor:'+ var_description.get(v,v) for v in self.mmodel.endogene if (newname := v+'_A') in self.mmodel.exogene }
|
|
187
|
+
dummy_d = { newname : 'Exo dummy:'+ var_description.get(v,v) for v in self.mmodel.endogene if (newname := v+'_D') in self.mmodel.exogene }
|
|
188
|
+
exo_d = { newname : 'Exo value:'+ var_description.get(v,v) for v in self.mmodel.endogene if (newname := v+'_X') in self.mmodel.exogene }
|
|
189
|
+
fitted_d = { newname : 'Fitted value:'+ var_description.get(v,v) for v in self.mmodel.endogene if (newname := v+'_FITTED') in self.mmodel.endogene }
|
|
190
|
+
var_description = {**var_description,**add_d,**dummy_d,**exo_d,**fitted_d}
|
|
191
|
+
self.mmodel.set_var_description(var_description)
|
|
192
|
+
except:
|
|
193
|
+
print('*** No variable description',flush=True)
|
|
194
|
+
var_description = {}
|
|
195
|
+
return var_description
|
|
196
|
+
|
|
197
|
+
@functools.cached_property
|
|
198
|
+
def mfmsa_options(self):
|
|
199
|
+
'''Grab the mfmsa options, a world bank speciality'''
|
|
200
|
+
if self.mfmsa:
|
|
201
|
+
with open(self.mfmsa,'rt') as f:
|
|
202
|
+
options = f.read()
|
|
203
|
+
else:
|
|
204
|
+
options = ''
|
|
205
|
+
return options
|
|
206
|
+
|
|
207
|
+
@functools.cached_property
|
|
208
|
+
def mfmsa_start_end(self):
|
|
209
|
+
import xml
|
|
210
|
+
root = xml.etree.ElementTree.fromstring(self.mfmsa_options)
|
|
211
|
+
start = int(root.find('iFace').find('SolveStart').text)
|
|
212
|
+
end = int(root.find('iFace').find('SolveEnd').text)
|
|
213
|
+
return start,end
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@functools.cached_property
|
|
217
|
+
def dfmodel(self):
|
|
218
|
+
'''The original input data enriched with during variablees, variables containing
|
|
219
|
+
values for specific historic years and model specific transformation '''
|
|
220
|
+
# Now the data
|
|
221
|
+
if self.from_wf2:
|
|
222
|
+
df = pd.read_excel(self.data,index_col=0)
|
|
223
|
+
else:
|
|
224
|
+
df = (pd.read_excel(self.data).
|
|
225
|
+
pipe( lambda df : df.rename(columns={c:c.upper() for c in df.columns})).
|
|
226
|
+
pipe( lambda df : df.rename(columns={'_DATE_':'DATEID'})).
|
|
227
|
+
pipe( lambda df : df.set_index('DATEID'))
|
|
228
|
+
)
|
|
229
|
+
df.index = [int(i.year) for i in df.index]
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
sca = pd.read_excel(self.scalars ,index_col=0,header=None).T.pipe(
|
|
233
|
+
lambda _df : _df.loc[_df.index.repeat(len(df.index)),:]).\
|
|
234
|
+
set_index(df.index)
|
|
235
|
+
df= pd.concat([df,sca],axis=1)
|
|
236
|
+
except:
|
|
237
|
+
print(f'{self.modelname} no Scalars prowided ')
|
|
238
|
+
# breakpoint()
|
|
239
|
+
if self.make_fitted:
|
|
240
|
+
df = self.mfitmodel.res(df,self.fit_start,self.fit_end)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
#% Now set the vars with fixedvalues
|
|
244
|
+
value_vars = self.mmodel.vlist('*_value_*')
|
|
245
|
+
for var,val,year in (v.rsplit('_',2) for v in value_vars) :
|
|
246
|
+
df.loc[:,f'{var}_{val}_{year}'] = df.loc[int(year),var]
|
|
247
|
+
self.showvaluevars = df[value_vars]
|
|
248
|
+
|
|
249
|
+
#% now set the values of the dummies
|
|
250
|
+
# breakpoint()
|
|
251
|
+
during_vars = self.mmodel.vlist('*during_*')
|
|
252
|
+
for varname,(dur,per) in ((v,v.split('_',1)) for v in during_vars):
|
|
253
|
+
df.loc[:,varname]=0
|
|
254
|
+
# print(varname,dur,per)
|
|
255
|
+
pers = per.split('_')
|
|
256
|
+
if len(pers) == 1:
|
|
257
|
+
df.loc[int(pers[0]),varname] = 1
|
|
258
|
+
else:
|
|
259
|
+
df.loc[int(pers[0]):int(pers[1]),varname]=1.
|
|
260
|
+
self.showduringvars = df[during_vars]
|
|
261
|
+
# breakpoint()
|
|
262
|
+
df_out = self.mmodel.insertModelVar(df).pipe(self.country_df_trans).fillna(0.0)
|
|
263
|
+
return df_out
|
|
264
|
+
|
|
265
|
+
def __call__(self):
|
|
266
|
+
|
|
267
|
+
return self.mmodel,self.base_input
|
|
268
|
+
|
|
269
|
+
def test_model(self,start=None,end=None,maxvar=1_000_000, maxerr=100,tol=0.0001,showall=False):
|
|
270
|
+
'''
|
|
271
|
+
Compares a straight calculation with the input dataframe.
|
|
272
|
+
|
|
273
|
+
shows which variables dont have the same value
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
df (TYPE): dataframe to run.
|
|
277
|
+
start (TYPE, optional): start period. Defaults to None.
|
|
278
|
+
end (TYPE, optional): end period. Defaults to None.
|
|
279
|
+
maxvar (TYPE, optional): how many variables are to be chekked. Defaults to 1_000_000.
|
|
280
|
+
maxerr (TYPE, optional): how many errors to check Defaults to 100.
|
|
281
|
+
tol (TYPE, optional): check for absolute value of difference. Defaults to 0.0001.
|
|
282
|
+
showall (TYPE, optional): show more . Defaults to False.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
None.
|
|
286
|
+
|
|
287
|
+
'''
|
|
288
|
+
_start = start if start else self.start
|
|
289
|
+
_end = end if end else self.end
|
|
290
|
+
# breakpoint()
|
|
291
|
+
|
|
292
|
+
resresult = self.mmodel(self.base_input,_start,_end,reset_options=True,silent=0,solver='base_res')
|
|
293
|
+
self.mmodel.basedf = self.dfmodel
|
|
294
|
+
pd.options.display.float_format = '{:.10f}'.format
|
|
295
|
+
err=0
|
|
296
|
+
print(f'\nChekking residuals for {self.mmodel.name} {_start} to {_end}')
|
|
297
|
+
for i,v in enumerate(self.mmodel.solveorder):
|
|
298
|
+
# if v.endswith('_FITTED'): continue
|
|
299
|
+
if i > maxvar : break
|
|
300
|
+
if err > maxerr : break
|
|
301
|
+
check = self.mmodel.get_values(v,pct=True).T
|
|
302
|
+
check.columns = ['Before check','After calculation','Difference','Pct']
|
|
303
|
+
# breakpoint()
|
|
304
|
+
if (check.Difference.abs() >= tol).any():
|
|
305
|
+
err=err+1
|
|
306
|
+
maxdiff = check.Difference.abs().max()
|
|
307
|
+
maxpct = check.Pct.abs().max()
|
|
308
|
+
# breakpoint()
|
|
309
|
+
print('\nVariable with residuals above threshold')
|
|
310
|
+
print(f"{v}, Max difference:{maxdiff:15.8f} Max Pct {maxpct:15.10f}% It is number {i} in the solveorder and error number {err}")
|
|
311
|
+
if showall:
|
|
312
|
+
print(f'\n{self.mmodel.allvar[v]["frml"]}')
|
|
313
|
+
print(f'\nResult of equation \n {check}')
|
|
314
|
+
print(f'\nEquation values before calculations: \n {self.mmodel.get_eq_values(v,last=False,showvar=1)} \n')
|
|
315
|
+
self.mmodel.oldkwargs = {}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|