Blog

Articles, tutorials, and real-world use cases for Hayashi.

  • Hayashi 0.2.10-1 is out

    A stable release that moves Hayashi off the Greeners dev branch and onto the published Greeners 2.0.1 on crates.io, plus a tighter validation policy and clearer documentation of known gaps.

    Version 0.2.10-1 is a focused maintenance release. The language, parser, and 219 validation cases are unchanged; the work is in how Hayashi consumes and documents its numerical core.

    Greeners 2.0.1 from crates.io

    The main blocker for a stable release was the [patch.crates-io] override that pointed Hayashi to the Greeners dev branch. Greeners 2.0.1 was published to crates.io for all 13 sub-crates, and Hayashi now consumes the published facade directly.

    • Greeners is pinned with greeners = "=2.0.1".
    • The [patch.crates-io] block was removed from the release branch.
    • Cargo.lock is committed, and CI runs --locked to avoid silent numerical drift.

    Validation reference policy

    The validation documentation now states the reference policy explicitly: a case is pass with at least one deterministic reference (R, Python, Stata, or an independent hand-coded comparison). Cases without a stable reference are not-supported with the reason recorded in the case metadata.

    Known gaps and cffilter

    A confirmed numerical bug in the upstream cffilter implementation is now documented in KNOWN_GAPS.md and the validation notes. This is an example of how the project treats unvalidated estimators: they remain callable, but any confirmed defect is recorded visibly.

    What did not change

    • 219 passing validation cases, 15 not-supported.
    • 133 fully validated estimator families in the standard build.
    • All smoke, DAP, and numerical-golden tests continue to pass on Linux.

    How to update

    cargo install --path .  # or download the release binary
    hay --version           # should show 0.2.10-1

    The next development cycle bumps Hayashi to 0.2.11-dev and restores the [patch.crates-io] override against the Greeners dev branch for continued R&D.

  • Descriptive statistics with Hayashi: a first walk through wooldridge wage1

    Learn how to install the Wooldridge datasets plugin, load a built-in dataset, and inspect it with summary statistics, percentiles, frequency tables, and correlations.

    If you are used to Stata, R, or Python, the first thing you want from a new language is a fast way to look at your data. This tutorial uses the Wooldridge datasets plugin to load wage1, a small labour-economics dataset with 526 observations.

    1. Install the plugin

    The Wooldridge datasets live in a separate Hayashi plugin. Install it once from the command line:

    hay install sheep-farm/haywooldridge

    After this, any Hayashi script can import the plugin. To list the available datasets, call wool::wooldridge_datasets().

    2. Import and load the data

    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")

    The import() command loads the plugin under the wool namespace. The wooldridge_data() function returns the requested dataset as a Hayashi DataFrame.

    3. Numeric summary

    summarize(wage1, wage, educ, exper, tenure)

    In Hayashi, the dataset is the first argument. summarize reports observations, mean, standard deviation, minimum, and maximum for one or more variables. For wage, you will see 526 observations, an average hourly wage near 5.9, and a long right tail.

    4. Detailed codebook

    codebook(wage1, wage)

    Use codebook when you need the type of a variable, the number of missing values, and the range in one place.

    5. Percentiles

    centile(wage1, wage, percentiles=[10, 25, 50, 75, 90])

    The centile command lets you pick arbitrary percentiles with the percentiles= option. The command above shows the 10th, 25th, median, 75th, and 90th percentiles, which is a quick way to check skewness.

    6. Frequency table

    tabulate(wage1, female)

    For categorical or dummy variables, tabulate gives counts and percentages. You can also cross-tabulate with a second variable, for example tabulate(wage1, female, married).

    7. Correlations

    pwcorr(wage1, wage, educ, exper, tenure)

    pwcorr prints a pairwise correlation matrix. This is often the first check before running an OLS regression: do wage and educ move together? Do exper and tenure share too much variation?

    Where to go next

    These commands prepare the ground for regression. The next tutorial in this series estimates a Mincer-style log-wage equation on the same dataset and interprets returns to schooling and experience.

    # Run this in the terminal first:
    # hay install sheep-farm/haywooldridge
    
    # Then run the script:
    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")
    
    summarize(wage1, wage, educ, exper, tenure)
    
    codebook(wage1, wage)
    
    centile(wage1, wage, percentiles=[10, 25, 50, 75, 90])
    
    tabulate(wage1, female)
    
    pwcorr(wage1, wage, educ, exper, tenure)
  • A Mincer wage equation in Hayashi: returns to schooling and experience

    Estimate the classic log-wage regression on the wooldridge wage1 data, add the experience-quadratic, use robust standard errors, and interpret the coefficients as percentage returns.

    This is the second tutorial in the wage1 series. After describing the data, we run a Mincer-style wage equation: log hourly wage on years of schooling, experience, and experience squared. This is the standard starting point for discussing returns to education in labour econometrics.

    1. Load the data

    # Run in the terminal first:
    # hay install sheep-farm/haywooldridge
    
    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")

    2. Why log wage?

    Wage distributions are right-skewed, so the log transformation makes the relationship with schooling and experience closer to additive. Interpreting the coefficient on log wage as a percentage change is also simpler: a coefficient of 0.09 on educ means each additional year of schooling is associated with about a 9% increase in earnings, holding experience constant.

    3. Fit the Mincer equation

    let m = ols(lwage ~ educ + exper + expersq, wage1)

    The wage1 dataset already has lwage (the natural log of wage) and expersq (experience squared), so the regression is a one-liner. The intercept is included by default.

    4. Read the coefficient table

    tidy(m)
    glance(m)

    From this specification you should see roughly the following pattern: an educ coefficient near 0.09, an exper coefficient near 0.04 and a small negative expersq coefficient. With 526 observations the model explains about 30% of the variation in log wages. R-squared, adjusted R-squared, AIC, BIC and the F-statistic are available in glance(m).

    5. Robust standard errors

    let m_hc3 = ols(lwage ~ educ + exper + expersq, wage1, cov="HC3")
    tidy(m_hc3)

    Use quoted option values to avoid shadowing. "HC3" is a MacKinnon-White heteroskedasticity-consistent estimator. The alternative cov="HC1" through cov="HC4" are also accepted, and cov="robust" is an alias for HC1.

    6. Interpret the returns

    The schooling coefficient is the percentage return to one more year of education, ceteris paribus. The experience terms work together: the marginal effect of an extra year of experience is exper + 2 * expersq * exper, or about 0.04 - 0.0014 * exper in this sample. With those numbers, returns to experience rise early and then fall, turning negative after about 28 years on the job.

    7. Joint test of the experience terms

    test(m, "exper", "expersq")

    Even though the linear and squared terms are individually significant, a joint F-test checks whether experience as a whole belongs in the model.

    8. Store and export a table

    eststo(m)
    esttab()

    eststo saves the model and esttab prints a formatted coefficient table with standard errors in parentheses and significance stars. This sets up the next tutorial, where we will compare multiple specifications side by side.

    Where to go next

    The next tutorial in this series adds gender and marriage dummies, an interaction, and a side-by-side table with esttab, turning the simple Mincer model into a small wage-gap analysis.

    # Full Mincer script
    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")
    
    let m = ols(lwage ~ educ + exper + expersq, wage1)
    tidy(m)
    glance(m)
    
    let m_hc3 = ols(lwage ~ educ + exper + expersq, wage1, cov="HC3")
    tidy(m_hc3)
    
    test(m, "exper", "expersq")
    
    eststo(m)
    esttab()
  • Wage gaps and interactions in Hayashi: comparing OLS specifications

    Add gender and marriage dummies, an interaction term, and build a side-by-side regression table using eststo and esttab on the wooldridge wage1 data.

    This is the third tutorial in the wage1 series. We start from the Mincer model and ask whether log wages differ by gender and marital status. Adding dummies and an interaction turns a purely descriptive model into a small wage-gap analysis.

    1. Load the data

    # Run in the terminal first:
    # hay install sheep-farm/haywooldridge
    
    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")

    2. The baseline Mincer model

    let m_base = ols(lwage ~ educ + exper + expersq, wage1)

    We keep the same baseline as the previous tutorial: log wage on schooling, experience, and experience squared. This is the reference we want to compare against.

    3. Add dummies and an interaction

    let m_ext = ols(lwage ~ educ + exper + expersq + female + married + female:married, wage1)

    female and married are 0/1 dummies. The female:married term creates the product of the two, capturing whether the marriage premium differs for women. In the output the column is literally named female:married, making it easy to identify.

    4. Inspect the extended model

    tidy(m_ext)
    glance(m_ext)

    In this sample, the results should look like this: the coefficient on female is negative and the coefficient on married is positive, but the interaction female:married is also negative and sizable. That means being married raises wages for men more than for women. Model fit improves substantially: R-squared moves from about 0.30 to about 0.43.

    5. Test the joint contribution of the new terms

    test(m_ext, "female", "married", "female:married")

    An F-test checks whether all three demographic terms are jointly zero. If the p-value is below 0.01, the group of variables belongs in the model.

    6. Build a side-by-side table

    eststo(m_base)
    eststo(m_ext)
    esttab()

    eststo stores each model, and esttab prints a compact comparison table with coefficients, standard errors in parentheses, and significance stars. Empty cells appear for variables that are not in the first model.

    7. Where to go next

    The next tutorial will use this same framework to add robust standard errors to both columns and test whether the gender gap differs after controlling for industry and occupation.

    # Full wage-gap script
    import("sheep-farm/haywooldridge", as=wool)
    let wage1 = wool::wooldridge_data("wage1")
    
    let m_base = ols(lwage ~ educ + exper + expersq, wage1)
    let m_ext = ols(lwage ~ educ + exper + expersq + female + married + female:married, wage1)
    
    tidy(m_ext)
    glance(m_ext)
    
    test(m_ext, "female", "married", "female:married")
    
    eststo(m_base)
    eststo(m_ext)
    esttab()
  • Hayashi 0.2.10 is out

    115 estimators, 234 validation cases, a rebuilt WebAssembly playground, and a fully native Rust implementation. What changed, why it matters, and where the project goes next.

    The 0.2.10 release is the most comprehensive version of Hayashi so far. It is also a turning point: after this cycle, the focus shifts from adding estimators to showing what the language can already do.

    The numbers

    • 115 implemented estimators, from OLS and IV to spatial SAR/SEM, panel GEE, non-linear least squares, stochastic frontiers, machine-learning baselines, and copula models.
    • 234 empirical validation cases: 219 pass, 15 not-supported, 0 fail.
    • 100% Rust: no C, no Fortran, no system BLAS/LAPACK.
    • 9.6 MB WebAssembly bundle that runs the same interpreter in the browser.

    What is new in 0.2.10

    The biggest change is the move to greeners = "2.0.0" on crates.io. The numerical facade is now a single published crate, which removes the external Greeners checkout from CI and makes the project easier to build from source.

    The release also brings a wave of new commands and diagnostics:

    • Spatial econometrics: spatial_sar, spatial_sem, spatial_durbin, and panel variants.
    • Panel GEE: xtlogit, xtprobit, xtpoisson.
    • Non-linear least squares: nls_exp, nls_power, nls_logistic, nls_cobb_douglas, nls_ces.
    • Causal inference: did, synthdid, eventstudy, double_ml.
    • Time series: modwt, tvp, setar, tvp_var, msvar, bvar.
    • ML baselines: rf, gbm, mlp, qrf.
    • Diagnostics: cusumtest, lrtest, akaike_weights, estat_overid, estat_endog, lroc, estat_gof, linktest, and marginsplot.

    Validation, not just features

    A language for econometrics is only useful if the numbers are right. The validation programme runs Hayashi scripts against reference implementations in R, Python, and Stata, then checks coefficients, standard errors, and fit statistics within tolerance.

    The 0.2.10 audit covers 234 cases. The previous 9 fail and 2 blocked cases are now passing. New cases include decompose, ucm, cancorr, bplm, chamberlain, NLS variants, spatial models, and several panel estimators.

    Try it in the browser

    The playground now ships a fresh WebAssembly build of the full interpreter. You can run small scripts, test syntax, or prototype a regression without installing anything.

    let d = {"x": [1.0, 2.0, 3.0, 4.0, 5.0], "y": [2.0, 4.0, 5.0, 4.0, 5.0]}
    let df = dataframe(d)
    ols(y ~ x, df)

    What comes next

    With the numerical core validated and the language stabilized, the next months will focus on content: tutorials, reproductions of published papers, migration guides, and case studies. If you have a dataset or a paper you would like to see ported to Hayashi, open an issue or start a discussion on GitHub.

    The 1.0.0 roadmap still includes an LSP, a formatter, and a debugger, but the priority now is to make 0.2.10 useful for real work.

    Get the release

    Install Hayashi 0.2.10 with the installer:

    curl -sSL https://raw.githubusercontent.com/sheep-farm/hayashi/master/install.sh | bash -s v0.2.10

    Or run it from the playground.