{"id":32200334,"url":"https://github.com/jacob-long/dpm","last_synced_at":"2025-10-22T03:46:03.224Z","repository":{"id":93154088,"uuid":"105382663","full_name":"jacob-long/dpm","owner":"jacob-long","description":"R package for fitting dynamic panel models with maximum likelihood","archived":false,"fork":false,"pushed_at":"2024-01-22T20:25:38.000Z","size":319,"stargazers_count":16,"open_issues_count":5,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-22T03:46:00.742Z","etag":null,"topics":["r","r-package","social-science","statistics"],"latest_commit_sha":null,"homepage":"https://dpm.jacob-long.com","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacob-long.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-09-30T16:16:24.000Z","updated_at":"2024-11-30T12:53:09.000Z","dependencies_parsed_at":"2024-01-12T23:40:59.848Z","dependency_job_id":"91c62cf6-553e-48e7-9b5d-5fe980a754f7","html_url":"https://github.com/jacob-long/dpm","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/jacob-long/dpm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacob-long%2Fdpm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacob-long%2Fdpm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacob-long%2Fdpm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacob-long%2Fdpm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacob-long","download_url":"https://codeload.github.com/jacob-long/dpm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacob-long%2Fdpm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280376534,"owners_count":26320276,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-22T02:00:06.515Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["r","r-package","social-science","statistics"],"created_at":"2025-10-22T03:45:57.519Z","updated_at":"2025-10-22T03:46:03.219Z","avatar_url":"https://github.com/jacob-long.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n# dpm\n\n[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version-ago/dpm)](https://cran.r-project.org/package=dpm) [![GitHub tag](https://img.shields.io/github/tag/jacob-long/dpm.svg?label=Github)](https://github.com/jacob-long/dpm) [![Total Downloads](https://cranlogs.r-pkg.org/badges/grand-total/dpm)](https://cran.r-project.org/package=dpm)\n[![Build Status](https://github.com/jacob-long/dpm/workflows/R-CMD-check/badge.svg)](https://github.com/jacob-long/interactions/actions) [![codecov](https://codecov.io/gh/jacob-long/dpm/branch/master/graph/badge.svg)](https://app.codecov.io/gh/jacob-long/dpm) \n[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://opensource.org/license/mit/)\n\n\n```{r, echo = FALSE}\nknitr::opts_chunk$set(\n  collapse = F,\n  comment = \"\",\n  fig.path = \"README-\"\n)\n```\n\nThis R package implements the dynamic panel data modeling framework\ndescribed by Allison, Williams, and Moral-Benito (2017). This approach allows\nfitting models with fixed effects that do not assume strict exogeneity of\npredictors. That means you can simultaneously get the robustness to confounding\noffered by fixed effects models and account for reciprocal causation between the \npredictors and the outcome variable. The estimating approach from Allison et al.\nprovides better finite sample performance in terms of both bias and efficiency\nthan other popular methods (e.g., the Arellano-Bond estimator).\n\nThese models are fit using structural equation models, using maximum\nlikelihood estimation and offering the missing data handling and flexibility\nafforded by SEM. This package will reshape your data, specify the model \nproperly, and fit it with `lavaan`.\n\nIf a result doesn't seem right, it would be a good idea to cross-reference it\nwith `xtdpdml` for Stata. Go to\n[https://www3.nd.edu/~rwilliam/dynamic/](https://www3.nd.edu/~rwilliam/dynamic/)\nto learn about `xtdpdml` and the underlying method. You may also be interested\nin the article by Paul Allison, Richard Williams, and Enrique Moral-Benito in\n**Socius**, accessible\n[here](http://journals.sagepub.com/doi/full/10.1177/2378023117710578).\n\n# Installation\n\n`dpm` is now on CRAN and can be installed like other R packages.\n\n```{r eval = F}\ninstall.packages(\"dpm\")\n```\n\n# Usage\n\nThis package assumes your data are in *long* format, with each row representing\na single observation of a single participant. Contrast this with *wide* format\nin which each row contains all observations of a single participant. \nFor help on converting data from wide to long format, check out \n[the tutorial](https://panelr.jacob-long.com/articles/reshape.html)\nthat accompanies the `panelr` package.\n\nFirst we load the package and the `WageData` from `panelr`.\n\n```{r message = F}\nlibrary(dpm)\ndata(\"WageData\", package = \"panelr\")\n```\n\nThis next line of code converts the data to class `panel_data`, which is a\nclass specific to the [`panelr`](https://github.com/jacob-long/panelr) that\nhelps to simplify the treatment of the long-form\npanel data. You don't have to do this, but it saves you from providing \n`id` and `wave` arguments to the model fitting function each time you use it.\n\n```{r}\nwages \u003c- panel_data(WageData, id = id, wave = t)\n```\n\n## Basic formula syntax\n\nThe formula syntax used in this package is meant to be as similar to a typical\nregression model as possible. \n\nThe most basic model can be specified like any other: `y ~ x`, where `y` is\nthe dependent variable and `x` is a time-varying predictor. If you would like \nto include time-invariant predictors, you will make the formula consist of two \nparts, separated with a bar (`|`) like so: `y ~ x | z` where z is a time \ninvariant predictor, like ethnicity. \n\nOne of the innovations of the method, however, is the notion of pre-determined,\nor sequentially exogenous, predictors. To specify a model with a pre-determined\nvariable, put the variable within a `pre` function, `y ~ pre(x1) + x2 | z`. \nThis tells the function that `x1` is pre-determined while `x2` is strictly \nexogenous by assumption. You could have multiple pre-determined predictors as \nwell (e.g., `y ~ pre(x1) + pre(x2) | z`). \n\nYou may also fit models with lagged predictors. Simply apply the lag function \nto the lagged predictors in the formula: `y ~ pre(lag(x1)) + lag(x2) | z`. \nTo specify more than 1 lag, just provide it as an argument. For instance,\n`y ~ pre(lag(x1, 2)) + lag(x2) | z` will use 2 lags of the `x1` variable.\n\n## *Socius* article example\n\nThis will replicate the analysis of the wages data in the *Socius* article that\ndescribes these models.\n\nNote that to get matching standard errors, set `information = \"observed\"` to \noverride `lavaan`'s default, `information = \"expected\"`.\n\n```{r}\nfit \u003c- dpm(wks ~ pre(lag(union)) + lag(lwage) | ed, data = wages,\n           error.inv = TRUE, information = \"observed\")\nsummary(fit)\n```\n\nAny arguments supplied other than those that are documented within the \n`dpm` function are passed on to `sem` from the `lavaan` package.\n\n## Model specification options\n\nThe following arguments allow you to make changes to the default model\nspecification:\n\n* `y.lag`: By default the lag 1 value of the DV is included as a predictor \n(this is why they are dynamic models). You may choose a different value or\nmultiple values instead, including 0 (no lagged DV at all).\n* `fixed.effects`: By default, the model is specified as a fixed effects model.\nIf you set this to FALSE, you get a random effects specification instead.\n* `error.inv`: This constrains error variances to be equal in each wave. It \nis FALSE by default.\n* `const.inv`: This constrains the constants to be equal in each wave. \nIt is FALSE by default, but if TRUE it eliminates cross-sectional dependence.\n* `y.free`: This allows the regression coefficient of the lagged DV to vary\nacross time. It is FALSE by default and you can either set it to TRUE or\nto the specific lag number(s).\n* `x.free`: This allows the regression coefficients for the predictors to \nvary across time. It is FALSE by default and you can either set it to TRUE\nto set all predictors' coefficients free over time or else pass a vector\nof strings of the predictors whose coefficients should be set free over time.\n* `alpha.free`: If TRUE, relaxes the constraint that the fixed effects are \nequal across time. Default is FALSE to be consistent with how fixed effects\nmodels normally work.\n* `partial.pre`: If TRUE (FALSE by default), predetermined lagged predictors \nwill also be allowed to correlate with the contemporaneous error term as \n[suggested by Paul Allison](https://statisticalhorizons.com/getting-the-lags-right-a-new-solution/)\nfor scenarios when it's not clear whether you have chosen the right lag structure.\n\n## Summary options\n\nYou have most of the options available to you via `lavaan`'s summary method.\n\nYou can choose to omit any of: the *z* statistics (`zstat = FALSE`),\nthe standard errors (`se = FALSE`), or the p values (`pvalue = FALSE`). You\nmay also add confidence intervals (`ci = TRUE`) at any specified level \n(`ci.level = .95`). If you used bootstrapping for uncertainty intervals,\nyou can also specify the method (`boot.ci.type = \"perc\"`).\n\nThe number of digits to print can be set via `digits` or with the option\n`dpm-digits`. You may also standardize coefficients via `lavaan`'s method\nusing `standardize = TRUE`.\n\n## Other features\n\n### Lavaan syntax only\n\nIf you just want the `lavaan` model specification and don't want this package\nto fit the model for you, you can set `print.only = TRUE`. To reduce the\namount of output, I'm condensing `wages` to 4 waves here.\n\n```{r}\ndpm(wks ~ pre(lag(union)) + lag(lwage) | ed, data = wages[wages$t \u003c 5,],\n    print.only = TRUE)\n```\n\n### Extract components\n\nAlternately, you can extract the `lavaan` model syntax and wide-formatted\ndata from the fitted model object to do your own fitting and tweaking.\n\n```{r eval = FALSE}\nget_wide_data(fit)\nget_syntax(fit)\n```\n\nThe model is a special type of `lavaan` object. This means most methods \nimplemented for `lavaan` objects will work on these. You can also convert\nthe fitted model into a typical `lavaan` object:\n\n```{r eval = FALSE}\nas(fit, \"lavaan\")\n```\n\n### Get full `lavaan` summary\n\nWhile you could convert the model to `lavaan` model and apply any of\n`lavaan`'s  functions to it (and you should!), as a convenience you can use \n`lav_summary()` to get `lavaan`'s summary of the model.\n\n### Missing data\n\nTake advantage of `lavaan`'s missing data handling by using the\n`missing = \"fiml\"` argument as well as any other arguments accepted by \n`lavaan::sem()`.\n\n# Feature comparison and roadmap\n\n* CFI/TLI fit measures are much different than Stata's and consistently\nmore optimistic. For now, they are not printed with the summary because they\nare probably misleading.\n* ~~You cannot use multiple lags of the same predictor (e.g.,\n`y ~ x + lag(x)`).~~ (Fixed in `1.0.0`)\n* The function does not yet support input data that is already in wide format.\n(Not planning to fix)\n* ~~You cannot apply arbitrary functions to variables in the formula like you \ncan with regression models. For instance, a specification like `y ~ scale(x)`\nwill cause an error.~~ (Works as of `1.1.0`)\n\nFeature parity with `xtdpdml` (Stata) is a goal. Here's how we are doing\nin terms of matching relevant `xtdpdml` options:\n\n- [x] `alphafree` (as `alpha.free`)\n- [x] `xfree` (as `x.free`)\n- [x] `xfree(varlist)` (as `x.free`)\n- [x] `yfree` (added as `y.free` argument in `1.0.0`)\n- [ ] `yfree(numlist)` \n- [x] `re` (added via `fixed.effects` argument in `1.0.0`)\n- [x] `errorinv` (as `error.inv`)\n- [x] `nocsd`/`constinv` (as `const.inv`)\n- [x] `ylag(numlist)` (added as `y.lag` argument in `1.0.0`; option to\nspecify as 0 — no lagged DV — added in `1.1.0`)\n- [ ] `std` (but `standardize` argument of `summary` may suffice)\n- [x] `dryrun` (as `print.only`)\n\nMany and perhaps more SEM fitting options are implemented by virtue of \naccepting any `lavaan::sem()` argument.\n\n## Roadmap\n\n- [ ] Get proper CFI/TLI statistics --- this is a `lavaan` problem.\n- [x] Allow full use of formula syntax, e.g. `y ~ scale(x)` (fixed in `1.1.0`)\n- [x] Add `broom` methods (`tidy`, `glance`) (added `tidy` in `1.1.0`)\n- [ ] Create a `predict` method and perhaps some ability to plot predictions\n- [x] Add `x.free` option to allow the coefficients of all predictors to\nvary across periods. This will make the `summary` output a pain, so it will\ntake some time to implement. (added in `1.1.1`)\n\n# References\n\nAllison, P. (2022, October 24). Getting the lags right – a new solution. \n*Statistical Horizons*. \nhttps://statisticalhorizons.com/getting-the-lags-right-a-new-solution/\n\nAllison, P. D., Williams, R., \u0026 Moral-Benito, E. (2017). Maximum likelihood for\ncross-lagged panel models with fixed effects. *Socius*, *3*, 1–17.\nhttps://doi.org/10.1177/2378023117710578\n\nLeszczensky, L., \u0026 Wolbring, T. (2022). How to deal with reverse causality using\npanel data? Recommendations for researchers based on a simulation study. \n*Sociological Methods \u0026 Research*, *51*(2), 837–865. \nhttps://doi.org/10.1177/0049124119882473\n\nMoral-Benito, E., Allison, P., \u0026 Williams, R. (2019). Dynamic panel data\nmodelling using maximum likelihood: An alternative to Arellano-Bond. \n*Applied Economics*, *51*, 2221–2232. \nhttps://doi.org/10.1080/00036846.2018.1540854\n\nWilliams, R., Allison, P. D., \u0026 Moral-Benito, E. (2018). Linear dynamic \npanel-data estimation using maximum likelihood and structural equation modeling.\n*The Stata Journal*, *18*, 293–326. https://doi.org/10.1177/1536867X1801800201\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacob-long%2Fdpm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacob-long%2Fdpm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacob-long%2Fdpm/lists"}