Learn R Programming

sfcr

The goal of the sfcr package is to provide an intuitive and tidy way to estimate stock-flow consistent (SFC) models with R.

Installation

sfcr is on CRAN and can be installed with:

install.packages("sfcr")

For the development version available on GitHub, use the devtools package for installation:

# install.packages("devtools")
devtools::install_github("joaomacalos/sfcr")

Example

This is a basic example which shows how to simulate the “SIM” model from Godley and Lavoie (2007ch. 3), as well as how to add scenarios to this baseline model.

The sfcr_set() function is used to create define the equations and external variables of the model.

These sets are used to simulate the baseline scenario of the model with the sfcr_baseline() function:

library(sfcr)

eqs <- sfcr_set(
  TXs ~ TXd,
  YD ~ W * Ns - TXs,
  Cd ~ alpha1 * YD + alpha2 * Hh[-1],
  Hh ~ YD - Cd + Hh[-1],
  Ns ~ Nd,
  Nd ~ Y / W,
  Cs ~ Cd,
  Gs ~ Gd,
  Y ~ Cs + Gs,
  TXd ~ theta * W * Ns,
  Hs ~ Gd - TXd + Hs[-1]
)

external <- sfcr_set(
  Gd ~ 20, 
  W ~ 1,
  alpha1 ~ 0.6,
  alpha2 ~ 0.4,
  theta ~ 0.2
  )

sim <- sfcr_baseline(
  equations = eqs, 
  external = external,
  periods = 60, 
  
  )

sim
#> # A tibble: 60 x 17
#>    period      TXs       YD       Cd       Hh       Ns       Nd       Cs
#>  *  <int>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
#>  1      1 1.00e-15 1.00e-15 1.00e-15 1.00e-15 1.00e-15 1.00e-15 1.00e-15
#>  2      2 7.69e+ 0 3.08e+ 1 1.85e+ 1 1.23e+ 1 3.85e+ 1 3.85e+ 1 1.85e+ 1
#>  3      3 9.59e+ 0 3.83e+ 1 2.79e+ 1 2.27e+ 1 4.79e+ 1 4.79e+ 1 2.79e+ 1
#>  4      4 1.12e+ 1 4.48e+ 1 3.59e+ 1 3.15e+ 1 5.59e+ 1 5.59e+ 1 3.59e+ 1
#>  5      5 1.25e+ 1 5.02e+ 1 4.27e+ 1 3.90e+ 1 6.27e+ 1 6.27e+ 1 4.27e+ 1
#>  6      6 1.37e+ 1 5.48e+ 1 4.85e+ 1 4.53e+ 1 6.85e+ 1 6.85e+ 1 4.85e+ 1
#>  7      7 1.47e+ 1 5.86e+ 1 5.33e+ 1 5.06e+ 1 7.33e+ 1 7.33e+ 1 5.33e+ 1
#>  8      8 1.55e+ 1 6.19e+ 1 5.74e+ 1 5.52e+ 1 7.74e+ 1 7.74e+ 1 5.74e+ 1
#>  9      9 1.62e+ 1 6.47e+ 1 6.09e+ 1 5.90e+ 1 8.09e+ 1 8.09e+ 1 6.09e+ 1
#> 10     10 1.68e+ 1 6.71e+ 1 6.38e+ 1 6.22e+ 1 8.38e+ 1 8.38e+ 1 6.38e+ 1
#> # ... with 50 more rows, and 9 more variables: Gs <dbl>, Y <dbl>, TXd <dbl>,
#> #   Hs <dbl>, Gd <dbl>, W <dbl>, alpha1 <dbl>, alpha2 <dbl>, theta <dbl>

With the steady state values at hand, we can use the sfcr_scenario() function to see what happens if we increase government expenditures Gd from 20 to 30:

shock <- sfcr_shock(
  variables = sfcr_set(
    Gd ~ 30
  ),
  start = 5,
  end = 60
)

sim2 <- sfcr_scenario(
  baseline = sim,
  scenario = shock,
  periods = 60
  )

sim2
#> # A tibble: 60 x 17
#>    period   TXs    YD    Cd    Hh    Ns    Nd    Cs    Gs     Y   TXd    Hs
#>  *  <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1      1  20.0  80.0  80.0  80.0  100.  100.  80.0    20  100.  20.0  80.0
#>  2      2  20.0  80.0  80.0  80.0  100.  100.  80.0    20  100.  20.0  80.0
#>  3      3  20.0  80.0  80.0  80.0  100.  100.  80.0    20  100.  20.0  80.0
#>  4      4  20.0  80.0  80.0  80.0  100.  100.  80.0    20  100.  20.0  80.0
#>  5      5  23.8  95.4  89.2  86.2  119.  119.  89.2    30  119.  23.8  86.2
#>  6      6  24.8  99.2  94.0  91.4  124.  124.  94.0    30  124.  24.8  91.4
#>  7      7  25.6 102.   98.0  95.8  128.  128.  98.0    30  128.  25.6  95.8
#>  8      8  26.3 105.  101.   99.5  131.  131. 101.     30  131.  26.3  99.5
#>  9      9  26.8 107.  104.  103.   134.  134. 104.     30  134.  26.8 103. 
#> 10     10  27.3 109.  107.  105.   137.  137. 107.     30  137.  27.3 105. 
#> # ... with 50 more rows, and 5 more variables: Gd <dbl>, W <dbl>, alpha1 <dbl>,
#> #   alpha2 <dbl>, theta <dbl>

With sfcr, the models are written entirely within R and use the standard R syntax. Furthermore, their output is a tibble, meaning that it can be easily manipulated with dplyr and other tidyverse tools and plotted with ggplot2.

Check the notebooks that replicate the models in Godley and Lavoie (2007) for more detailed examples on the usage of the package.

Frequently Asked Questions

Q: Can you add exogenous series to a sfcr model?

A: Since version 0.2, the sfcr package recommends the utilization of exogenous variables only in the sfcr_scenario() function. This functionality is going to be excluded from sfcr_baseline() function in the future because it led to unexpected behavior when calculating scenarios on the top of those baseline models.

The exogenous series can be added to the model with the help of sfcr_shock() and sfcr_set() functions. It is further required that the length of the exogenous time series being supplied be either 1 or exactly equal to length of the shock.

For example, the code supplied above can be modified to make Gd increase from 30 to 40 between periods 1 and 60 of the scenario:

library(dplyr) # for select() and everything() functions

shock <- sfcr_shock(
  variables = sfcr_set(
    Gd ~ seq(30, 40, length.out=60)
  ),
  start = 1,
  end = 60
)

sim2 <- sfcr_scenario(
  baseline = sim,
  scenario = shock,
  periods = 60
  )
#> Warning: Passing exogenous series with a shock can lead to unexpected behavior if the length of the series is smaller than the periods to the end of the scenario. Be cautious when using this functionality.
#> This warning is displayed once per session.

select(sim2, period, Gd, everything())
#> # A tibble: 60 x 17
#>    period    Gd   TXs    YD    Cd    Hh    Ns    Nd    Cs    Gs     Y   TXd
#>     <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1      1  30    20.0  80.0  80.0  80.0  100.  100.  80.0  20    100.  20.0
#>  2      2  30.2  23.9  95.6  89.4  86.3  120.  120.  89.4  30.2  120.  23.9
#>  3      3  30.3  24.9  99.8  94.4  91.7  125.  125.  94.4  30.3  125.  24.9
#>  4      4  30.5  25.8 103.   98.7  96.3  129.  129.  98.7  30.5  129.  25.8
#>  5      5  30.7  26.6 106.  102.  100.   133.  133. 102.   30.7  133.  26.6
#>  6      6  30.8  27.3 109.  106.  104.   137.  137. 106.   30.8  137.  27.3
#>  7      7  31.0  27.9 112.  109.  107.   140.  140. 109.   31.0  140.  27.9
#>  8      8  31.2  28.5 114.  111.  110.   142.  142. 111.   31.2  142.  28.5
#>  9      9  31.4  28.9 116.  113.  112.   145.  145. 113.   31.4  145.  28.9
#> 10     10  31.5  29.4 118.  115.  114.   147.  147. 115.   31.5  147.  29.4
#> # ... with 50 more rows, and 5 more variables: Hs <dbl>, W <dbl>, alpha1 <dbl>,
#> #   alpha2 <dbl>, theta <dbl>

Q: How to add random variation to endogenous variables?

A: The recommended way to add random variation to endogenous variables is with the sfcr_random() function. This function can only be used inside sfcr_set(), be it when you’re creating a set of exogenous variables or when defining the variables inside a sfcr_shock(). The advantage of utilizing this function is that it smartly guesses the length of the models, avoiding any unwanted mistake.

The sfcr_random() function can accept three arguments as its first .f argument: "rnorm", "rbinom", and "runif". These arguments implement wrappers around the built-in functions rnorm(), rbinom(), and runif() – random series generator function – but guessing the correct length of the sfcr_baseline(), sfcr_scenario(), or sfcr_shock() from where they are called. The sfcr_random() function also accepts any extra argument that can be passed to these functions.

Snippet:

sfcr_set(
  Ra ~ sfcr_random("rnorm", sd=0.05)
)
#> [[1]]
#> Ra ~ sfcr_random("rnorm", sd = 0.05)
#> 
#> attr(,"class")
#> [1] "sfcr_set" "list"

An utilization of this functionality in practice is provided in the article replicating the Portfolio Choice model from Godley and Lavoie (2007ch. 4).

Alternatively, the direct utilization of the random generator functions from stats are still allowed to ensure the compatibility with the v0.1.1 of the package. Nonetheless, the user must be careful when using this functionality at the sfcr_baseline() level since this expression is going to be evaluated again at the sfcr_scenario() level. The safest way to use these functions is by passing periods instead of an integer as their first argument.

Snippet:

# Not recommended but work:
sfcr_set(
  Ra ~ rnorm(periods, sd=0.05)
)
#> [[1]]
#> Ra ~ rnorm(periods, sd = 0.05)
#> 
#> attr(,"class")
#> [1] "sfcr_set" "list"

# NOT RECOMMENDED!
sfcr_set(
  Ra ~ rnorm(60, sd=0.05)
)
#> [[1]]
#> Ra ~ rnorm(60, sd = 0.05)
#> 
#> attr(,"class")
#> [1] "sfcr_set" "list"

Q: Can you add endogenous variables with more than one lag?

A: Yes, you can, but you need to use auxiliary variables.

For example, say that you want to modify model SIM to have Consumption Cd in period t defined as function of the moving average of disposable income. In this situation, you would have to code the variables as:

eqs <- sfcr_set(
  TXs ~ TXd,
  YD ~ W * Ns - TXs,
  YDlag1 ~ YD[-1],
  YDlag2 ~ YDlag1[-1],
  YDlag3 ~ YDlag2[-1],
  YDmav ~ (YD + YDlag1 + YDlag2 + YDlag3) / 4,
  Cd ~ alpha1 * YDmav + alpha2 * Hh[-1],
  Hh ~ YD - Cd + Hh[-1],
  Ns ~ Nd,
  Nd ~ Y / W,
  Cs ~ Cd,
  Gs ~ Gd,
  Y ~ Cs + Gs,
  TXd ~ theta * W * Ns,
  Hs ~ Gd - TXd + Hs[-1]
)

Submitting your code to the package repository

Everyone is invited to submit your published SFC models developed with the sfcr package to the package repository to be displayed together with the models of Godley and Lavoie (2007).

To do so, please submit a pull request or send me an email.

Acknowledgments

I’m grateful to Severin Reissl for his very useful comments and for always pointing me in the right direction, to Marc Lavoie for answering all my questions about SFC modeling, and to Italo Pedrosa for our discussions about the state of the SFC field.

I’d also like to acknowledge all the developers and academics that share their code and make the SFC field alive. In particular, many thanks to Antoine Godin for answering all my queries about the PKSFC package, from which I draw much inspiration, specially in the DAGs section of the package, to Gabriel Petrini da Silveira and Kenn Takara for their pysolve3 package, from which I found the references to implement the Broyden solver in R, and to Gennaro Zezza for his invaluable macros to simulate the models in Godley and Lavoie (2007).

References

Godley, Wynne, and Marc Lavoie. 2007. Monetary Economics: An Integrated Approach To Credit, Money, Income, Production and Wealth. Palgrave Macmillan.

Copy Link

Version

Install

install.packages('sfcr')

Monthly Downloads

220

Version

0.2.1

License

MIT + file LICENSE

Issues

Pull Requests

Stars

Forks

Maintainer

Joao Macalos

Last Published

October 11th, 2021

Functions in sfcr (0.2.1)

.pvarlag

Pattern replacement lag
.add_time_stamps

Find dependencies and order the equations
.scan_rows

Scan rows to fill whenever there's only one value missing.
.scan_cols

Scan columns to fill whenever there's only one value missing.
.to_latex_style

Transform entries into latex style
.sfcr_newton

Newton-Raphson solver implemented with rootSolve::multiroot()
sfcr_dag_cycles_plot

Plot the DAG with cycles information
sfcr_expand

Expand variables to implement sensitivity analysis
.return_loops

Find cyclical nodes
.add_time2

Find dependencies and order the equations
.abort_water_leakr

Abort if row validation is not fulfilled
.find_blocks

Find blocks of independent equations (wrapper around igraph functions)
.all_equal

Check if all values in x are equal
.broyden_solver

Broyden solver algorithm
.args_to_row

Take arguments and make them a row of a tibble
.sfcr_gauss_seidel

Gauss Seidel algorithm
.sfcr_make_scenario_matrix

Make matrix for scenario calculations
.find_names

Find names for display matrix
.extend_baseline_matrix

Extend a baseline matrix
.make_matrix

Make the underlying matrix that will be modified in place by the solvers
sfcr_dag_blocks_plot

Plot the DAG with blocks and cycles information
sfcr_set

Define the formulas of the model
sfcr_scenario

Add scenarios to a sfcr model.
.fill_rows_and_cols

Fill all possible rows and columns and validate
sfcr_dag_cycles

Create a tbl_graph object with cycles information
.check_symmetry

Check that symmetry condition is valid and fulfill missing entries
.eq_as_tb

Split the formulae into a tibble with the left-hand side and right-hand side as columns
.prep_equations

Re-wrote the equations with the correct matrix syntax that will be used to evaluate the expressions inside the Gauss Seidel algorithm
.get_matrix

Get numeric matrix for evaluation from balance-sheet or transactions-flow matrices
.abort_typo_code

Abort if typo on the codes of columns
.is_equal

Check if two values are equal
.abort_water_leakc

Abort if column validation is not fulfilled
sfcr_sankey

Plot Sankey's diagram representation of transactions-flow matrix
sfcr_random

Generate random sequences inside sfcr_set()
.prep_broyden

Prep equations for Broyden and Newton solvers
.sfcr_broyden

Broyden solver wrapper
new_sfcr_mlt_shock

sfcr_mlt_shock constructor
.validate_scan

Check that the portfolio matrix respect the horizontal and vertical adding-up constraints
sfcr_get_matrix

Get Matrix form of sfcr_tbl object
sfcr_get_blocks

Get block structure of a sfcr_tbl object
sfcr_matrix

Create balance-sheet or transactions-flow matrices
sfcr_matrix_display

Print matrix to screen
new_sfcr_tbl

new_sfcr_tbl constructor
new_sfcr_set

sfcr_set constructor
%>%

Pipe operator
.validate_matrix

Validate a balance-sheet or transactions-flow matrix on the simulated data
.pvar

Pattern replacement var
sfcr_set_index

Get names of endogenous vars and their index
.sfcr_find_order

Place the equations in the correct order for estimation
sfcr_shock

Create shock(s) to add to a sfcr_scenario().
.sfcr_find_adjacency

Find adjacency matrix for a system of equations
sfcr_multis

Simulate multiple SFC models at the same time
sfcr_portfolio

Find a valid matrix of portfolio parameters
new_sfcr_mlt_set

sfcr_mlt_set constructor
sfcr_dag_blocks

Create a tbl_graph object blocks and cycles information
sfcr_baseline

Simulate the baseline scenario of a stock-flow consistent model
.sfcr_eqs_check

Check for missing endogenous variables
new_sfcr_mlt

sfcr_mlt constructor
sfcr_validate

Validate a transactions-flow or balance-sheet matrix
.abort_if_dup

Abort if duplicated variables
abortifnot

Abort if not
.check_external_consistency

Check shocks for length consistency and warn about risks of using exogenous series
.check_shock_consistency

Check shocks for length consistency and warn about risks of using exogenous series