├── .github ├── .gitignore └── workflows │ ├── check-standard.yaml │ └── test-coverage.yaml ├── tests ├── testthat.R └── testthat │ ├── test-data.R │ ├── test-interpret.R │ └── test-btergm.R ├── .Rbuildignore ├── .gitignore ├── data ├── chemnet.rda ├── knecht.rda ├── alliances.RData └── ch_coauthor.RData ├── inst └── CITATION ├── man ├── getformula.Rd ├── edgeprob.Rd ├── handleMissings.Rd ├── createTbergm.Rd ├── tbergm.Rd ├── createBtergm.Rd ├── createMtergm.Rd ├── btergm-package.Rd ├── tbergm-class.Rd ├── mtergm-class.Rd ├── mtergm.Rd ├── alliances.Rd ├── adjust.Rd ├── marginalplot.Rd ├── checkdegeneracy.Rd ├── tergmprepare.Rd ├── simulate.btergm.Rd ├── btergm-class.Rd ├── tergm-terms.Rd ├── chemnet.Rd ├── knecht.Rd ├── coauthor.Rd ├── btergm.Rd └── gof-plot.Rd ├── R ├── getformula.R ├── alliances.R ├── tbergm.R ├── checkdegeneracy.R ├── timecov.R ├── chemnet.R ├── knecht.R └── ch_coauthor.R ├── DESCRIPTION ├── README.md └── NAMESPACE /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library("testthat") 2 | 3 | test_check("btergm") 4 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | README.md 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^\.github$ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.backup 3 | .Rproj.user 4 | btergm.Rproj 5 | .Rhistory 6 | -------------------------------------------------------------------------------- /data/chemnet.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leifeld/btergm/HEAD/data/chemnet.rda -------------------------------------------------------------------------------- /data/knecht.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leifeld/btergm/HEAD/data/knecht.rda -------------------------------------------------------------------------------- /data/alliances.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leifeld/btergm/HEAD/data/alliances.RData -------------------------------------------------------------------------------- /data/ch_coauthor.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leifeld/btergm/HEAD/data/ch_coauthor.RData -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | bibentry(bibtype = "Article", 2 | title = "Temporal Exponential Random Graph Models with {btergm}: Estimation and Bootstrap Confidence Intervals", 3 | author = c(person(given = "Philip", 4 | family = "Leifeld", 5 | email = "philip.leifeld@essex.ac.uk"), 6 | person(given = c("Skyler", "J."), 7 | family = "Cranmer", 8 | email = "cranmer.12@osu.edu"), 9 | person(given = c("Bruce", "A."), 10 | family = "Desmarais", 11 | email = "bdesmarais@psu.edu")), 12 | journal = "Journal of Statistical Software", 13 | year = "2018", 14 | volume = "83", 15 | number = "6", 16 | pages = "1--36", 17 | doi = "10.18637/jss.v083.i06", 18 | 19 | header = "To cite btergm in publications, use:" 20 | ) 21 | -------------------------------------------------------------------------------- /man/getformula.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getformula.R 3 | \docType{methods} 4 | \name{getformula} 5 | \alias{getformula} 6 | \alias{getformula-methods} 7 | \alias{getformula,ergm-method} 8 | \alias{getformula,btergm-method} 9 | \alias{getformula,mtergm-method} 10 | \alias{getformula,tbergm-method} 11 | \title{Extract the formula from a model} 12 | \usage{ 13 | getformula(x) 14 | 15 | \S4method{getformula}{ergm}(x) 16 | 17 | \S4method{getformula}{btergm}(x) 18 | 19 | \S4method{getformula}{mtergm}(x) 20 | 21 | \S4method{getformula}{tbergm}(x) 22 | } 23 | \arguments{ 24 | \item{x}{A fitted model.} 25 | } 26 | \description{ 27 | Extract the model formula from a fitted object. 28 | } 29 | \details{ 30 | The \code{getformula} function will extract the formula from a fitted model. 31 | } 32 | \section{Methods (by class)}{ 33 | \itemize{ 34 | \item \code{getformula(ergm)}: Extract the formula from an \code{ergm} object. 35 | 36 | \item \code{getformula(btergm)}: Extract the formula from a \code{btergm} object. 37 | 38 | \item \code{getformula(mtergm)}: Extract the formula from an \code{mtergm} object. 39 | 40 | \item \code{getformula(tbergm)}: Extract the formula from a \code{tbergm} object. 41 | 42 | }} 43 | -------------------------------------------------------------------------------- /R/getformula.R: -------------------------------------------------------------------------------- 1 | #' Extract the formula from a model 2 | #' 3 | #' Extract the model formula from a fitted object. 4 | #' 5 | #' The \code{getformula} function will extract the formula from a fitted model. 6 | #' 7 | #' @param x A fitted model. 8 | #' 9 | #' @docType methods 10 | #' @aliases getformula-methods 11 | #' @export 12 | setGeneric("getformula", function(x) standardGeneric("getformula"), 13 | package = "btergm") 14 | 15 | #' @describeIn getformula Extract the formula from an \code{ergm} object. 16 | #' @export 17 | setMethod("getformula", signature = className("ergm", "ergm"), 18 | definition = function(x) x$formula) 19 | 20 | #' @describeIn getformula Extract the formula from a \code{btergm} object. 21 | #' @export 22 | setMethod("getformula", signature = className("btergm", "btergm"), 23 | definition = function(x) x@formula) 24 | 25 | #' @describeIn getformula Extract the formula from an \code{mtergm} object. 26 | #' @export 27 | setMethod("getformula", signature = className("mtergm", "btergm"), 28 | definition = function(x) x@formula) 29 | 30 | #' @describeIn getformula Extract the formula from a \code{tbergm} object. 31 | #' @export 32 | setMethod("getformula", signature = className("tbergm", "btergm"), 33 | definition = function(x) x@formula) -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: btergm 2 | Version: 1.11.1 3 | Date: 2025-03-19 4 | Title: Temporal Exponential Random Graph Models by Bootstrapped Pseudolikelihood 5 | Authors@R: c(person(given = "Philip", family = "Leifeld", email = "philip.leifeld@manchester.ac.uk", role = c("aut", "cre")), person(given = c("Skyler", "J."), family = "Cranmer", email = "cranmer.12@osu.edu", role = "ctb"), person(given = c("Bruce", "A."), family = "Desmarais", email = "bdesmarais@psu.edu", role = "ctb")) 6 | Description: Temporal Exponential Random Graph Models (TERGM) estimated by maximum pseudolikelihood with bootstrapped confidence intervals or Markov Chain Monte Carlo maximum likelihood. Goodness of fit assessment for ERGMs, TERGMs, and SAOMs. Micro-level interpretation of ERGMs and TERGMs. The methods are described in Leifeld, Cranmer and Desmarais (2018), JStatSoft . 7 | URL: https://github.com/leifeld/btergm 8 | Encoding: UTF-8 9 | Imports: 10 | stats, 11 | utils, 12 | methods, 13 | graphics, 14 | network (>= 1.19.0), 15 | sna (>= 2.8), 16 | ergm (>= 4.8.1), 17 | parallel, 18 | Matrix (>= 1.3.2), 19 | boot (>= 1.3.17), 20 | coda (>= 0.18.1), 21 | ROCR (>= 1.0.7), 22 | igraph (>= 2.1.2), 23 | statnet.common (>= 4.11.0) 24 | Suggests: 25 | fastglm (>= 0.0.1), 26 | speedglm (>= 0.3.1), 27 | testthat, 28 | Bergm (>= 5.0.7), 29 | RSiena (>= 1.0.12.232), 30 | ggplot2 (>= 2.0.0) 31 | Depends: R (>= 3.5) 32 | License: GPL (>= 2) 33 | RoxygenNote: 7.3.2 34 | -------------------------------------------------------------------------------- /.github/workflows/check-standard.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: | 31 | covr::codecov( 32 | quiet = FALSE, 33 | clean = FALSE, 34 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 35 | ) 36 | shell: Rscript {0} 37 | 38 | - name: Show testthat output 39 | if: always() 40 | run: | 41 | ## -------------------------------------------------------------------- 42 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 43 | shell: bash 44 | 45 | - name: Upload test results 46 | if: failure() 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: coverage-test-failures 50 | path: ${{ runner.temp }}/package 51 | -------------------------------------------------------------------------------- /man/edgeprob.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interpretation.R 3 | \name{edgeprob} 4 | \alias{edgeprob} 5 | \title{Create all predicted tie probabilities using MPLE} 6 | \usage{ 7 | edgeprob(object, verbose = FALSE) 8 | } 9 | \arguments{ 10 | \item{object}{An \code{ergm}, \code{btergm}, or \code{mtergm} object.} 11 | 12 | \item{verbose}{Print details?} 13 | } 14 | \value{ 15 | The first variable in the resulting data frame contains the edge 16 | value (i.e., the dependent variable, which is usually binary). The next 17 | variables contain all the predictors from the ERGM or TERGM (i.e., the change 18 | statistics). The next five variables contain the indices of the sender (i), 19 | the receiver (j), the time step (t), the vertex id of i (i.name), and the 20 | vertex id of j (j.name). These five variables serve to identify the dyad. The 21 | last variable contains the computed edge probabilities. 22 | } 23 | \description{ 24 | Create all predicted tie probabilities using MPLE. 25 | } 26 | \details{ 27 | For a given (T)ERGM, return a data frame with all predicted edge 28 | probabilities along with the design matrix of the MPLE logit model, based 29 | on the estimated coefficients and the design matrix, for all time points, 30 | along with \code{i}, \code{j}, and \code{t} variables indicating where the 31 | respective dyad is located. 32 | 33 | \code{edgeprob} is a convenience function that creates a data frame with all 34 | dyads in the ERGM or TERGM along with their edge probabilities and their 35 | predictor values (i.e., change statistics). This is useful for creating 36 | marginal effects plots or contrasting multiple groups of dyads. This function 37 | works faster than the \code{\link{interpret}} function. 38 | } 39 | \seealso{ 40 | Other interpretation: 41 | \code{\link{interpret}()}, 42 | \code{\link{marginalplot}()} 43 | } 44 | \concept{interpretation} 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # btergm 2 | 3 | Temporal Exponential Random Graph Models by Bootstrapped Pseudolikelihood. 4 | 5 | Temporal Exponential Random Graph Models (TERGM) estimated by maximum pseudolikelihood with bootstrapped confidence intervals or Markov Chain Monte Carlo maximum likelihood (MCMC MLE). Goodness of fit assessment for ERGMs, TERGMs, and SAOMs. Micro-level interpretation of ERGMs and TERGMs. 6 | 7 | [![R-CMD-check](https://github.com/leifeld/btergm/actions/workflows/check-standard.yaml/badge.svg)](https://github.com/leifeld/btergm/actions/workflows/check-standard.yaml) 8 | [![test-coverage](https://github.com/leifeld/btergm/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/leifeld/btergm/actions/workflows/test-coverage.yaml) 9 | [![coverage status](https://codecov.io/gh/leifeld/btergm/branch/master/graph/badge.svg)](https://codecov.io/github/leifeld/btergm?branch=master) 10 | 11 | # Installation 12 | 13 | The last stable release can be installed from CRAN: 14 | ```r 15 | install.packages("btergm") 16 | ``` 17 | To install the latest development version from GitHub, use the remotes package: 18 | ```r 19 | remotes::install_github("leifeld/btergm") 20 | ``` 21 | 22 | [![cran version](http://www.r-pkg.org/badges/version/btergm)](https://cran.r-project.org/package=btergm) 23 | [![downloads](http://cranlogs.r-pkg.org/badges/btergm)](http://cranlogs.r-pkg.org/badges/btergm) 24 | [![total downloads](http://cranlogs.r-pkg.org/badges/grand-total/btergm)](http://cranlogs.r-pkg.org/badges/grand-total/btergm) 25 | [![research software impact](http://depsy.org/api/package/cran/btergm/badge.svg)](http://depsy.org/package/r/btergm) 26 | 27 | # Documentation 28 | Documentation of the package is available as a JStatSoft article: 29 | 30 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal Exponential Random Graph Models with btergm: Estimation and Bootstrap Confidence Intervals. _Journal of Statistical Software_ 83(6): 1-36. https://doi.org/10.18637/jss.v083.i06. 31 | -------------------------------------------------------------------------------- /man/handleMissings.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/preprocess.R 3 | \name{handleMissings} 4 | \alias{handleMissings} 5 | \title{Handle missing data in matrices} 6 | \usage{ 7 | handleMissings(mat, na = NA, method = "remove", logical = FALSE) 8 | } 9 | \arguments{ 10 | \item{mat}{A matrix object.} 11 | 12 | \item{na}{The value that missing data are coded as. Usually \code{NA}, 13 | sometimes \code{9} or \code{10}.} 14 | 15 | \item{method}{What should be done with the missing data? If 16 | \code{method = "remove"} is set, the function determines how many missing 17 | entries are in each row and column and iteratively removes rows or columns 18 | with the largest amount of missing data until no missing data are left in 19 | the matrix. If \code{method = "fillmode"} is set, the modal value of the 20 | matrix is identified (usually \code{0} in network matrices) and missing 21 | cells are imputed by filling in this modal value. \code{method = "zero"} 22 | replaces NAs by 0s.} 23 | 24 | \item{logical}{Return a matrix with logical values indicating which cells 25 | should be removed? By default the manipulated matrix is returned.} 26 | } 27 | \value{ 28 | Either a matrix in which missing data were taken care of or a matrix 29 | indicating where missing data are located. 30 | } 31 | \description{ 32 | Process \code{NA} values (= remove nodes with \code{NA}s iteratively). 33 | } 34 | \details{ 35 | This function deals with missing data in matrices or network objects used for 36 | inferential network analysis. It can either remove missing rows and/or 37 | columns iteratively (rows and columns with more \code{NA} values first, then 38 | successively rows and columns with fewer \code{NA} entries) or replace 39 | missing values by the modal value of the matrix or by \code{0}. The function 40 | can return either the manipulated matrix or a matrix with logical values 41 | indicating which of the cells should be removed. 42 | } 43 | \seealso{ 44 | \code{\link{adjust}} 45 | } 46 | -------------------------------------------------------------------------------- /man/createTbergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbergm.R 3 | \name{createTbergm} 4 | \alias{createTbergm} 5 | \title{Constructor for \linkS4class{tbergm} objects} 6 | \usage{ 7 | createTbergm( 8 | time.steps, 9 | formula, 10 | formula2, 11 | auto.adjust, 12 | offset, 13 | directed, 14 | bipartite, 15 | estimate, 16 | bergm, 17 | nvertices, 18 | data 19 | ) 20 | } 21 | \arguments{ 22 | \item{time.steps}{Object of class \code{"numeric"}. Number of time steps.} 23 | 24 | \item{formula}{Object of class \code{"formula"}. The original model formula 25 | (without indices for the time steps).} 26 | 27 | \item{formula2}{The revised formula with the object references after applying 28 | the \code{\link{tergmprepare}} function.} 29 | 30 | \item{auto.adjust}{Object of class \code{"logical"}. Indicates whether 31 | automatic adjustment of dimensions was done before estimation.} 32 | 33 | \item{offset}{Object of class \code{"logical"}. Indicates whether an offset 34 | matrix with structural zeros was used.} 35 | 36 | \item{directed}{Object of class \code{"logical"}. Are the dependent networks 37 | directed?} 38 | 39 | \item{bipartite}{Object of class \code{"logical"}. Are the dependent networks 40 | bipartite?} 41 | 42 | \item{estimate}{Estimate: \code{"bergm"} for Bayesian estimation.} 43 | 44 | \item{bergm}{The original \code{bergm} object as estimated by the 45 | \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package.} 46 | 47 | \item{nvertices}{Number of vertices.} 48 | 49 | \item{data}{The data after processing by the \code{\link{tergmprepare}} 50 | function.} 51 | } 52 | \description{ 53 | Constructor for \linkS4class{tbergm} objects. 54 | } 55 | \details{ 56 | Create an S4 \linkS4class{tbergm} object using this constructor function. 57 | } 58 | \seealso{ 59 | Other tergm-classes: 60 | \code{\link{btergm-class}}, 61 | \code{\link{createBtergm}()}, 62 | \code{\link{createMtergm}()}, 63 | \code{\link{mtergm-class}}, 64 | \code{\link{tbergm-class}} 65 | } 66 | \author{ 67 | Philip Leifeld 68 | } 69 | \concept{tergm-classes} 70 | -------------------------------------------------------------------------------- /man/tbergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbergm.R 3 | \name{tbergm} 4 | \alias{tbergm} 5 | \title{Estimate a TERGM using Bayesian estimation} 6 | \usage{ 7 | tbergm(formula, returndata = FALSE, verbose = TRUE, ...) 8 | } 9 | \arguments{ 10 | \item{formula}{Formula for the TERGM. Model construction works like in the 11 | \pkg{ergm} package with the same model terms etc. (for a list of terms, see 12 | \code{help("\link[ergm]{ergm-terms}")}). The networks to be modeled on the 13 | left-hand side of the equation must be given either as a list of network 14 | objects with more recent networks last (i.e., chronological order) or as a 15 | list of matrices with more recent matrices at the end. \code{dyadcov} and 16 | \code{edgecov} terms accept time-independent covariates (as \code{network} 17 | or \code{matrix} objects) or time-varying covariates (as a list of networks 18 | or matrices with the same length as the list of networks to be modeled).} 19 | 20 | \item{returndata}{Return the processed input data instead of estimating and 21 | returning the model? In the \code{btergm} case, this will return a data 22 | frame with the dyads of the dependent variable/network and the change 23 | statistics for all covariates. In the \code{mtergm} case, this will return 24 | a list object with the blockdiagonal network object for the dependent 25 | variable and blockdiagonal matrices for all dyadic covariates and the 26 | offset matrix for the structural zeros.} 27 | 28 | \item{verbose}{Print details about data preprocessing and estimation 29 | settings.} 30 | 31 | \item{...}{Further arguments to be handed over to the 32 | \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package.} 33 | } 34 | \description{ 35 | Estimate a TERGM using Bayesian estimation. 36 | } 37 | \details{ 38 | The \code{tbergm} function computes TERGMs by Bayesian estimation via 39 | blockdiagonal matrices and structural zeros. It acts as a wrapper for the 40 | \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package. 41 | } 42 | \references{ 43 | Caimo, Alberto and Nial Friel (2012): Bergm: Bayesian Exponential 44 | Random Graphs in R. \emph{Journal of Statistical Software} 61(2): 1-25. 45 | \doi{10.18637/jss.v061.i02}. 46 | } 47 | \seealso{ 48 | \code{\link{btergm}} \code{\link{mtergm}} 49 | } 50 | \author{ 51 | Philip Leifeld 52 | } 53 | -------------------------------------------------------------------------------- /man/createBtergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/btergm.R 3 | \name{createBtergm} 4 | \alias{createBtergm} 5 | \title{Constructor for \linkS4class{btergm} objects} 6 | \usage{ 7 | createBtergm( 8 | coef, 9 | boot, 10 | R, 11 | nobs, 12 | time.steps, 13 | formula, 14 | formula2, 15 | response, 16 | effects, 17 | weights, 18 | auto.adjust, 19 | offset, 20 | directed, 21 | bipartite, 22 | nvertices, 23 | data 24 | ) 25 | } 26 | \arguments{ 27 | \item{coef}{Object of class \code{"numeric"}. The coefficients.} 28 | 29 | \item{boot}{Object of class \code{"matrix"}. The bootstrapping sample.} 30 | 31 | \item{R}{Object of class \code{"numeric"}. Number of replications.} 32 | 33 | \item{nobs}{Object of class \code{"numeric"}. Number of observations.} 34 | 35 | \item{time.steps}{Object of class \code{"numeric"}. Number of time steps.} 36 | 37 | \item{formula}{Object of class \code{"formula"}. The original model formula 38 | (without indices for the time steps).} 39 | 40 | \item{formula2}{The revised formula with the object references after applying 41 | the \code{\link{tergmprepare}} function.} 42 | 43 | \item{response}{Object of class \code{"integer"}. The response variable.} 44 | 45 | \item{effects}{Object of class \code{"data.frame"}. The effects that went 46 | into the \code{glm} call.} 47 | 48 | \item{weights}{Object of class \code{"integer"}. The weights of the 49 | observations.} 50 | 51 | \item{auto.adjust}{Object of class \code{"logical"}. Indicates whether 52 | automatic adjustment of dimensions was done before estimation.} 53 | 54 | \item{offset}{Object of class \code{"logical"}. Indicates whether an offset 55 | matrix with structural zeros was used.} 56 | 57 | \item{directed}{Object of class \code{"logical"}. Are the dependent networks 58 | directed?} 59 | 60 | \item{bipartite}{Object of class \code{"logical"}. Are the dependent networks 61 | bipartite?} 62 | 63 | \item{nvertices}{Number of vertices.} 64 | 65 | \item{data}{The data after processing by the \code{\link{tergmprepare}} 66 | function.} 67 | } 68 | \description{ 69 | Constructor for \linkS4class{btergm} objects. 70 | } 71 | \details{ 72 | Create an S4 \linkS4class{btergm} object using this constructor function. 73 | } 74 | \seealso{ 75 | Other tergm-classes: 76 | \code{\link{btergm-class}}, 77 | \code{\link{createMtergm}()}, 78 | \code{\link{createTbergm}()}, 79 | \code{\link{mtergm-class}}, 80 | \code{\link{tbergm-class}} 81 | } 82 | \author{ 83 | Philip Leifeld 84 | } 85 | \concept{tergm-classes} 86 | -------------------------------------------------------------------------------- /man/createMtergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/mtergm.R 3 | \name{createMtergm} 4 | \alias{createMtergm} 5 | \title{Constructor for \linkS4class{mtergm} objects} 6 | \usage{ 7 | createMtergm( 8 | coef, 9 | se, 10 | pval, 11 | nobs, 12 | time.steps, 13 | formula, 14 | formula2, 15 | auto.adjust, 16 | offset, 17 | directed, 18 | bipartite, 19 | estimate, 20 | loglik, 21 | aic, 22 | bic, 23 | ergm, 24 | nvertices, 25 | data 26 | ) 27 | } 28 | \arguments{ 29 | \item{coef}{Object of class \code{"numeric"}. The coefficients.} 30 | 31 | \item{se}{Standard errors.} 32 | 33 | \item{pval}{The p-values.} 34 | 35 | \item{nobs}{Object of class \code{"numeric"}. Number of observations.} 36 | 37 | \item{time.steps}{Object of class \code{"numeric"}. Number of time steps.} 38 | 39 | \item{formula}{Object of class \code{"formula"}. The original model formula 40 | (without indices for the time steps).} 41 | 42 | \item{formula2}{The revised formula with the object references after applying 43 | the \code{\link{tergmprepare}} function.} 44 | 45 | \item{auto.adjust}{Object of class \code{"logical"}. Indicates whether 46 | automatic adjustment of dimensions was done before estimation.} 47 | 48 | \item{offset}{Object of class \code{"logical"}. Indicates whether an offset 49 | matrix with structural zeros was used.} 50 | 51 | \item{directed}{Object of class \code{"logical"}. Are the dependent networks 52 | directed?} 53 | 54 | \item{bipartite}{Object of class \code{"logical"}. Are the dependent networks 55 | bipartite?} 56 | 57 | \item{estimate}{Estimate: either MCMC MLE or MPLE.} 58 | 59 | \item{loglik}{Log likelihood of the MLE.} 60 | 61 | \item{aic}{Akaike's Information Criterion.} 62 | 63 | \item{bic}{Bayesian Information Criterion.} 64 | 65 | \item{ergm}{The original \code{ergm} object as estimated by the 66 | \code{\link[ergm]{ergm}} function in the \pkg{ergm} package.} 67 | 68 | \item{nvertices}{Number of vertices.} 69 | 70 | \item{data}{The data after processing by the \code{\link{tergmprepare}} 71 | function.} 72 | } 73 | \description{ 74 | Constructor for \linkS4class{mtergm} objects. 75 | } 76 | \details{ 77 | Create an S4 \linkS4class{mtergm} object using this constructor function. 78 | } 79 | \seealso{ 80 | Other tergm-classes: 81 | \code{\link{btergm-class}}, 82 | \code{\link{createBtergm}()}, 83 | \code{\link{createTbergm}()}, 84 | \code{\link{mtergm-class}}, 85 | \code{\link{tbergm-class}} 86 | } 87 | \author{ 88 | Philip Leifeld 89 | } 90 | \concept{tergm-classes} 91 | -------------------------------------------------------------------------------- /man/btergm-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/btergm.R 3 | \docType{package} 4 | \name{btergm-package} 5 | \alias{btergm-package} 6 | \title{Temporal Exponential Random Graph Models by Bootstrapped Pseudolikelihood} 7 | \description{ 8 | Temporal Exponential Random Graph Models by Bootstrapped Pseudolikelihood. 9 | } 10 | \details{ 11 | Temporal Exponential Random Graph Models (TERGM) estimated by maximum 12 | pseudolikelihood with bootstrapped confidence intervals, Markov Chain Monte 13 | Carlo maximum likelihood, or Bayesian estimation. Goodness of fit assessment 14 | for ERGMs, TERGMs, and SAOMs. Micro-level interpretation of ERGMs and TERGMs. 15 | 16 | The \pkg{btergm} package implements TERGMs with MPLE and bootstrapped 17 | confidence intervals (\code{\link{btergm}} function), MCMC MLE 18 | (\code{\link{mtergm}} function), or Bayesian estimation (\code{\link{tbergm}} 19 | function). Goodness of fit assessment for ERGMs, TERGMs, SAOMs, and dyadic 20 | independence models is possible with the generic \code{\link{gof}} function 21 | and its various methods defined here in the \pkg{btergm} package. New 22 | networks can be simulated from TERGMs using the \code{\link{simulate.btergm}} 23 | function. The package also implements micro-level interpretation for ERGMs 24 | and TERGMs using the \code{\link{interpret}} function. Furthermore, the 25 | package contains the \code{\link{chemnet}} and \code{\link{knecht}} (T)ERGM 26 | datasets. To display citation information, type \code{citation("btergm")}. 27 | } 28 | \references{ 29 | Cranmer, Skyler J., Tobias Heinrich and Bruce A. Desmarais (2014): 30 | Reciprocity and the Structural Determinants of the International Sanctions 31 | Network. \emph{Social Networks} 36(1): 5-22. 32 | \doi{10.1016/j.socnet.2013.01.001}. 33 | 34 | Desmarais, Bruce A. and Skyler J. Cranmer (2012): Statistical Mechanics of 35 | Networks: Estimation and Uncertainty. \emph{Physica A} 391: 1865--1876. 36 | \doi{10.1016/j.physa.2011.10.018}. 37 | 38 | Desmarais, Bruce A. and Skyler J. Cranmer (2010): Consistent Confidence 39 | Intervals for Maximum Pseudolikelihood Estimators. \emph{Neural Information 40 | Processing Systems 2010 Workshop on Computational Social Science and the 41 | Wisdom of Crowds}. 42 | 43 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 44 | Exponential Random Graph Models with btergm: Estimation and Bootstrap 45 | Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1--36. 46 | \doi{10.18637/jss.v083.i06}. 47 | } 48 | \seealso{ 49 | Useful links: 50 | \itemize{ 51 | \item \url{https://github.com/leifeld/btergm} 52 | } 53 | 54 | } 55 | \author{ 56 | Philip Leifeld, Skyler J. Cranmer, Bruce A. Desmarais 57 | } 58 | -------------------------------------------------------------------------------- /man/tbergm-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbergm.R 3 | \docType{class} 4 | \name{tbergm-class} 5 | \alias{tbergm-class} 6 | \alias{show,tbergm-method} 7 | \alias{nobs,tbergm-method} 8 | \alias{timesteps.tbergm} 9 | \alias{summary,tbergm-method} 10 | \title{An S4 class to represent a fitted TERGM using Bayesian estimation} 11 | \usage{ 12 | \S4method{show}{tbergm}(object) 13 | 14 | \S4method{nobs}{tbergm}(object) 15 | 16 | timesteps.tbergm(object) 17 | 18 | \S4method{summary}{tbergm}(object, ...) 19 | } 20 | \arguments{ 21 | \item{object}{A \code{tbergm} object.} 22 | 23 | \item{...}{Further arguments for the \code{summary} function in the 24 | \pkg{Bergm} package.} 25 | } 26 | \description{ 27 | An S4 class to represent a fitted TERGM using Bayesian estimation. 28 | } 29 | \details{ 30 | \code{tbergm} objects result from Bayesian estimation of a TERGM using the 31 | \code{\link{tbergm}} function. They contain the original \code{bergm} object 32 | and some additional information. 33 | } 34 | \section{Functions}{ 35 | \itemize{ 36 | \item \code{show(tbergm)}: Show the coefficients of a \code{tbergm} object. 37 | 38 | \item \code{nobs(tbergm)}: Return the number of observations saved in a 39 | \code{tbergm} object. 40 | 41 | \item \code{timesteps.tbergm()}: Return the number of time steps saved in a 42 | \code{tbergm} object. 43 | 44 | \item \code{summary(tbergm)}: Summary of a fitted \code{tbergm} object. 45 | 46 | }} 47 | \section{Slots}{ 48 | 49 | \describe{ 50 | \item{\code{time.steps}}{Object of class \code{"numeric"}. Number of time steps.} 51 | 52 | \item{\code{formula}}{Object of class \code{"formula"}. The original model formula 53 | (without indices for the time steps).} 54 | 55 | \item{\code{formula2}}{The revised formula with the object references after applying 56 | the \code{\link{tergmprepare}} function.} 57 | 58 | \item{\code{auto.adjust}}{Object of class \code{"logical"}. Indicates whether 59 | automatic adjustment of dimensions was done before estimation.} 60 | 61 | \item{\code{offset}}{Object of class \code{"logical"}. Indicates whether an offset 62 | matrix with structural zeros was used.} 63 | 64 | \item{\code{directed}}{Object of class \code{"logical"}. Are the dependent networks 65 | directed?} 66 | 67 | \item{\code{bipartite}}{Object of class \code{"logical"}. Are the dependent networks 68 | bipartite?} 69 | 70 | \item{\code{estimate}}{Estimate: \code{"bergm"} for Bayesian estimation.} 71 | 72 | \item{\code{bergm}}{The original \code{bergm} object as estimated by the 73 | \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package.} 74 | 75 | \item{\code{nvertices}}{Number of vertices.} 76 | 77 | \item{\code{data}}{The data after processing by the \code{\link{tergmprepare}} 78 | function.} 79 | }} 80 | 81 | \seealso{ 82 | Other tergm-classes: 83 | \code{\link{btergm-class}}, 84 | \code{\link{createBtergm}()}, 85 | \code{\link{createMtergm}()}, 86 | \code{\link{createTbergm}()}, 87 | \code{\link{mtergm-class}} 88 | } 89 | \author{ 90 | Philip Leifeld 91 | } 92 | \concept{tergm-classes} 93 | -------------------------------------------------------------------------------- /tests/testthat/test-data.R: -------------------------------------------------------------------------------- 1 | context("test chemnet replication") 2 | 3 | test_that("chemnet example can be replicated", { 4 | skip_on_cran() 5 | 6 | # preparatory steps 7 | library("network") 8 | library("sna") 9 | library("ergm") 10 | seed <- 12345 11 | set.seed(seed) 12 | data("chemnet") 13 | 14 | # create confirmed network relation 15 | sci <- scito * t(scifrom) # equation 1 in the AJPS paper 16 | prefsim <- dist(intpos, method = "euclidean") # equation 2 17 | prefsim <- max(prefsim) - prefsim # equation 3 18 | prefsim <- as.matrix(prefsim) 19 | committee <- committee %*% t(committee) # equation 4 20 | diag(committee) <- 0 # the diagonal has no meaning 21 | types <- types[, 1] # convert to vector 22 | 23 | # create network objects and store attributes 24 | nw.pol <- network(pol) # political/stratgic information exchange 25 | set.vertex.attribute(nw.pol, "orgtype", types) 26 | set.vertex.attribute(nw.pol, "betweenness", 27 | betweenness(nw.pol)) # centrality 28 | 29 | nw.sci <- network::network(sci) # technical/scientific information exchange 30 | set.vertex.attribute(nw.sci, "orgtype", types) 31 | set.vertex.attribute(nw.sci, "betweenness", 32 | betweenness(nw.sci)) # centrality 33 | 34 | # ERGM: model 1 in the AJPS paper; only preference similarity 35 | suppressMessages({ 36 | model1 <- ergm(nw.pol ~ edges + edgecov(prefsim), 37 | control = control.ergm(seed = seed)) 38 | }) 39 | expect_equal(class(model1), "ergm") 40 | expect_length(coef(model1), 2) 41 | 42 | # ERGM: model 2 in the AJPS paper; complete model 43 | suppressMessages({ 44 | model2 <- ergm(nw.pol ~ 45 | edges + 46 | edgecov(prefsim) + 47 | mutual + 48 | nodemix("orgtype", base = -7) + 49 | nodeifactor("orgtype", base = -1) + 50 | nodeofactor("orgtype", base = -5) + 51 | edgecov(committee) + 52 | edgecov(nw.sci) + 53 | edgecov(infrep) + 54 | gwesp(0.1, fixed = TRUE) + 55 | gwdsp(0.1, fixed = TRUE), 56 | control = control.ergm(seed = seed) 57 | ) 58 | }) 59 | expect_equal(class(model2), "ergm") 60 | expect_length(coef(model2), 11) 61 | 62 | # ERGM: model 3 in the AJPS paper; only preference similarity 63 | suppressMessages({ 64 | model3 <- ergm(nw.sci ~ edges + edgecov(prefsim), 65 | control = control.ergm(seed = seed)) 66 | }) 67 | expect_equal(class(model3), "ergm") 68 | expect_length(coef(model3), 2) 69 | 70 | # ERGM: model 4 in the AJPS paper; complete model 71 | suppressMessages({ 72 | model4 <- ergm(nw.sci ~ 73 | edges + 74 | edgecov(prefsim) + 75 | mutual + 76 | nodemix("orgtype", base = -7) + 77 | nodeifactor("orgtype", base = -1) + 78 | nodeofactor("orgtype", base = -5) + 79 | edgecov(committee) + 80 | edgecov(nw.pol) + 81 | edgecov(infrep) + 82 | gwesp(0.1, fixed = TRUE) + 83 | gwdsp(0.1, fixed = TRUE), 84 | control = control.ergm(seed = seed) 85 | ) 86 | }) 87 | expect_equal(class(model4), "ergm") 88 | expect_length(coef(model4), 11) 89 | 90 | # goodness of fit using the btergm package 91 | gof4 <- btergm::gof(model4, verbose = FALSE) 92 | expect_equal(length(gof4), 7) 93 | plot(gof4) 94 | }) -------------------------------------------------------------------------------- /man/mtergm-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/mtergm.R 3 | \docType{class} 4 | \name{mtergm-class} 5 | \alias{mtergm-class} 6 | \alias{show,mtergm-method} 7 | \alias{coef,mtergm-method} 8 | \alias{nobs,mtergm-method} 9 | \alias{timesteps.mtergm} 10 | \alias{summary,mtergm-method} 11 | \title{An S4 Class to represent a fitted TERGM by MCMC-MLE} 12 | \usage{ 13 | \S4method{show}{mtergm}(object) 14 | 15 | \S4method{coef}{mtergm}(object, invlogit = FALSE, ...) 16 | 17 | \S4method{nobs}{mtergm}(object) 18 | 19 | timesteps.mtergm(object) 20 | 21 | \S4method{summary}{mtergm}(object, ...) 22 | } 23 | \arguments{ 24 | \item{object}{An \code{mtergm} object.} 25 | 26 | \item{invlogit}{Apply inverse logit transformation to the estimates and/or 27 | confidence intervals? That is, \eqn{\frac{1}{1 + \exp(-x)}}, where \eqn{x} 28 | is the respective value.} 29 | 30 | \item{...}{Currently not in use.} 31 | } 32 | \description{ 33 | An S4 class to represent a fitted TERGM by MCMC-MLE. 34 | } 35 | \details{ 36 | \code{mtergm} objects result from MCMC-MLE-based estimation of a TERGM via 37 | the \code{\link{mtergm}} function. They contain the coefficients, standard 38 | errors, and p-values, among other details. 39 | } 40 | \section{Functions}{ 41 | \itemize{ 42 | \item \code{show(mtergm)}: Show the coefficients of an \code{mtergm} object. 43 | 44 | \item \code{coef(mtergm)}: Return the coefficients of an \code{mtergm} object. 45 | 46 | \item \code{nobs(mtergm)}: Return the coefficients of an \code{mtergm} object. 47 | 48 | \item \code{timesteps.mtergm()}: Return the number of time steps saved in an 49 | \code{mtergm} object. 50 | 51 | \item \code{summary(mtergm)}: Return the coefficients of an \code{mtergm} object. 52 | 53 | }} 54 | \section{Slots}{ 55 | 56 | \describe{ 57 | \item{\code{coef}}{Object of class \code{"numeric"}. The coefficients.} 58 | 59 | \item{\code{se}}{Object of class \code{"numeric"}. The standard errors.} 60 | 61 | \item{\code{pval}}{Object of class \code{"numeric"}. The p-values.} 62 | 63 | \item{\code{nobs}}{Object of class \code{"numeric"}. Number of observations.} 64 | 65 | \item{\code{time.steps}}{Object of class \code{"numeric"}. Number of time steps.} 66 | 67 | \item{\code{formula}}{Object of class \code{"formula"}. The original model formula 68 | (without indices for the time steps).} 69 | 70 | \item{\code{formula2}}{The revised formula with the object references after applying 71 | the \code{\link{tergmprepare}} function.} 72 | 73 | \item{\code{auto.adjust}}{Object of class \code{"logical"}. Indicates whether 74 | automatic adjustment of dimensions was done before estimation.} 75 | 76 | \item{\code{offset}}{Object of class \code{"logical"}. Indicates whether an offset 77 | matrix with structural zeros was used.} 78 | 79 | \item{\code{directed}}{Object of class \code{"logical"}. Are the dependent networks 80 | directed?} 81 | 82 | \item{\code{bipartite}}{Object of class \code{"logical"}. Are the dependent networks 83 | bipartite?} 84 | 85 | \item{\code{estimate}}{Estimate: either MLE or MPLE.} 86 | 87 | \item{\code{loglik}}{Log likelihood of the MLE.} 88 | 89 | \item{\code{aic}}{Akaike's Information Criterion.} 90 | 91 | \item{\code{bic}}{Bayesian Information Criterion.} 92 | 93 | \item{\code{ergm}}{The original \code{ergm} object as estimated by the 94 | \code{\link[ergm]{ergm}} function in the \pkg{ergm} package.} 95 | 96 | \item{\code{nvertices}}{Number of vertices.} 97 | 98 | \item{\code{data}}{The data after processing by the \code{\link{tergmprepare}} 99 | function.} 100 | }} 101 | 102 | \seealso{ 103 | Other tergm-classes: 104 | \code{\link{btergm-class}}, 105 | \code{\link{createBtergm}()}, 106 | \code{\link{createMtergm}()}, 107 | \code{\link{createTbergm}()}, 108 | \code{\link{tbergm-class}} 109 | } 110 | \author{ 111 | Philip Leifeld 112 | } 113 | \concept{tergm-classes} 114 | -------------------------------------------------------------------------------- /man/mtergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/mtergm.R 3 | \name{mtergm} 4 | \alias{mtergm} 5 | \title{Estimate a TERGM by MCMC-MLE} 6 | \usage{ 7 | mtergm(formula, constraints = ~., returndata = FALSE, verbose = TRUE, ...) 8 | } 9 | \arguments{ 10 | \item{formula}{Formula for the TERGM. Model construction works like in the 11 | \pkg{ergm} package with the same model terms etc. (for a list of terms, see 12 | \code{help("\link[ergm]{ergm-terms}")}). The networks to be modeled on the 13 | left-hand side of the equation must be given either as a list of network 14 | objects with more recent networks last (i.e., chronological order) or as a 15 | list of matrices with more recent matrices at the end. \code{dyadcov} and 16 | \code{edgecov} terms accept time-independent covariates (as \code{network} 17 | or \code{matrix} objects) or time-varying covariates (as a list of networks 18 | or matrices with the same length as the list of networks to be modeled).} 19 | 20 | \item{constraints}{Constraints of the ERGM. See \code{\link[ergm]{ergm}} for 21 | details.} 22 | 23 | \item{returndata}{Return the processed input data instead of estimating and 24 | returning the model? In the \code{btergm} case, this will return a data 25 | frame with the dyads of the dependent variable/network and the change 26 | statistics for all covariates. In the \code{mtergm} case, this will return 27 | a list object with the blockdiagonal network object for the dependent 28 | variable and blockdiagonal matrices for all dyadic covariates and the 29 | offset matrix for the structural zeros.} 30 | 31 | \item{verbose}{Print details about data preprocessing and estimation 32 | settings.} 33 | 34 | \item{...}{Further arguments to be handed over to the 35 | \code{\link[ergm]{ergm}} function.} 36 | } 37 | \description{ 38 | Estimate a TERGM by Markov Chain Monte Carlo Maximum Likelihood Estimation 39 | } 40 | \details{ 41 | The \code{mtergm} function computes TERGMs by MCMC MLE (or MPLE with 42 | uncorrected standard errors) via blockdiagonal matrices and structural zeros. 43 | It acts as a wrapper for the \pkg{ergm} package. The \code{btergm} function 44 | is faster than the \code{mtergm} function but is only asymptotically unbiased 45 | the longer the time series. The \code{mtergm} function yields unbiased 46 | estimates and standard errors but may suffer from degeneracy if the model is 47 | not specified in good keeping with the true data-generating process. 48 | } 49 | \examples{ 50 | library("network") 51 | set.seed(5) 52 | 53 | networks <- list() 54 | for (i in 1:10) { # create 10 random networks with 10 actors 55 | mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10) 56 | diag(mat) <- 0 # loops are excluded 57 | nw <- network::network(mat) # create network object 58 | networks[[i]] <- nw # add network to the list 59 | } 60 | 61 | covariates <- list() 62 | for (i in 1:10) { # create 10 matrices as covariate 63 | mat <- matrix(rnorm(100), nrow = 10, ncol = 10) 64 | covariates[[i]] <- mat # add matrix to the list 65 | } 66 | 67 | \dontrun{ 68 | fit2 <- mtergm(networks ~ edges + istar(2) + edgecov(covariates)) 69 | summary(fit2) 70 | } 71 | 72 | # For examples with real data, see help("knecht") or help("alliances"). 73 | 74 | } 75 | \references{ 76 | Leifeld, Philip and Skyler J. Cranmer (2019): A Theoretical and 77 | Empirical Comparison of the Temporal Exponential Random Graph Model and the 78 | Stochastic Actor-Oriented Model. \emph{Network Science} 7(1): 20-51. 79 | \doi{10.1017/nws.2018.26}. 80 | 81 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2017): 82 | Temporal Exponential Random Graph Models with btergm: Estimation and 83 | Bootstrap Confidence Intervals. \emph{Journal of Statistical Software} 84 | 83(6): 1-36. \doi{10.18637/jss.v083.i06}. 85 | } 86 | \seealso{ 87 | \code{\link{btergm}} \code{\link{tbergm}} 88 | } 89 | \author{ 90 | Philip Leifeld, Skyler J. Cranmer, Bruce A. Desmarais 91 | } 92 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(plot,boxplot) 4 | S3method(plot,gof) 5 | S3method(plot,pr) 6 | S3method(plot,roc) 7 | S3method(plot,rocpr) 8 | S3method(plot,univariate) 9 | S3method(print,boxplot) 10 | S3method(print,gof) 11 | S3method(print,pr) 12 | S3method(print,roc) 13 | S3method(print,rocpr) 14 | S3method(print,univariate) 15 | S3method(simulate,btergm) 16 | S3method(simulate,mtergm) 17 | export(adjust) 18 | export(b1deg) 19 | export(b1star) 20 | export(b2deg) 21 | export(b2star) 22 | export(btergm) 23 | export(btergm.se) 24 | export(checkdegeneracy) 25 | export(comemb) 26 | export(createGOF) 27 | export(deg) 28 | export(dsp) 29 | export(edgebetweenness.modularity) 30 | export(edgebetweenness.pr) 31 | export(edgebetweenness.roc) 32 | export(edgeprob) 33 | export(esp) 34 | export(fastgreedy.modularity) 35 | export(fastgreedy.pr) 36 | export(fastgreedy.roc) 37 | export(geodesic) 38 | export(getformula) 39 | export(gof) 40 | export(handleMissings) 41 | export(ideg) 42 | export(interpret) 43 | export(istar) 44 | export(kcycle) 45 | export(kstar) 46 | export(louvain.modularity) 47 | export(louvain.pr) 48 | export(louvain.roc) 49 | export(marginalplot) 50 | export(maxmod.modularity) 51 | export(maxmod.pr) 52 | export(maxmod.roc) 53 | export(mtergm) 54 | export(nsp) 55 | export(odeg) 56 | export(ostar) 57 | export(rocpr) 58 | export(spinglass.modularity) 59 | export(spinglass.pr) 60 | export(spinglass.roc) 61 | export(tbergm) 62 | export(tergmprepare) 63 | export(timecov) 64 | export(timesteps.btergm) 65 | export(timesteps.mtergm) 66 | export(timesteps.tbergm) 67 | export(triad.directed) 68 | export(triad.undirected) 69 | export(walktrap.modularity) 70 | export(walktrap.pr) 71 | export(walktrap.roc) 72 | exportClasses(btergm) 73 | exportClasses(mtergm) 74 | exportClasses(tbergm) 75 | exportMethods(coef) 76 | exportMethods(confint) 77 | exportMethods(getformula) 78 | exportMethods(gof) 79 | exportMethods(nobs) 80 | exportMethods(show) 81 | exportMethods(summary) 82 | import(stats) 83 | importFrom(Matrix,Matrix) 84 | importFrom(Matrix,t) 85 | importFrom(ROCR,performance) 86 | importFrom(ROCR,prediction) 87 | importFrom(boot,boot) 88 | importFrom(ergm,control.ergm) 89 | importFrom(ergm,control.simulate.formula) 90 | importFrom(ergm,ergm) 91 | importFrom(ergm,ergm.design) 92 | importFrom(ergm,ergm.geodistdist) 93 | importFrom(ergm,ergm.getnetwork) 94 | importFrom(ergm,ergm.pl) 95 | importFrom(ergm,ergmMPLE) 96 | importFrom(ergm,ergm_model) 97 | importFrom(ergm,mcmc.diagnostics) 98 | importFrom(ergm,simulate_formula) 99 | importFrom(ergm,summary_formula) 100 | importFrom(graphics,abline) 101 | importFrom(graphics,boxplot) 102 | importFrom(graphics,hist) 103 | importFrom(graphics,lines) 104 | importFrom(graphics,par) 105 | importFrom(graphics,plot) 106 | importFrom(graphics,points) 107 | importFrom(igraph,cluster_edge_betweenness) 108 | importFrom(igraph,cluster_fast_greedy) 109 | importFrom(igraph,cluster_louvain) 110 | importFrom(igraph,cluster_optimal) 111 | importFrom(igraph,cluster_spinglass) 112 | importFrom(igraph,cluster_walktrap) 113 | importFrom(igraph,graph_from_adjacency_matrix) 114 | importFrom(igraph,modularity) 115 | importFrom(methods,new) 116 | importFrom(methods,show) 117 | importFrom(network,get.network.attribute) 118 | importFrom(network,get.vertex.attribute) 119 | importFrom(network,is.bipartite) 120 | importFrom(network,is.directed) 121 | importFrom(network,is.network) 122 | importFrom(network,list.network.attributes) 123 | importFrom(network,list.vertex.attributes) 124 | importFrom(network,network) 125 | importFrom(network,network.size) 126 | importFrom(network,set.network.attribute) 127 | importFrom(network,set.vertex.attribute) 128 | importFrom(parallel,clusterEvalQ) 129 | importFrom(parallel,makeCluster) 130 | importFrom(parallel,mclapply) 131 | importFrom(parallel,parLapply) 132 | importFrom(parallel,parSapply) 133 | importFrom(parallel,stopCluster) 134 | importFrom(sna,gden) 135 | importFrom(sna,symmetrize) 136 | importFrom(sna,triad.census) 137 | importFrom(stats,pnorm) 138 | importFrom(stats,simulate) 139 | importFrom(stats,t.test) 140 | importFrom(utils,combn) 141 | importFrom(utils,packageDescription) 142 | -------------------------------------------------------------------------------- /R/alliances.R: -------------------------------------------------------------------------------- 1 | #' Longitudinal international defense alliance network, 1981--2000 2 | #' 3 | #' Longitudinal international defense alliance network, 1981--2000. 4 | #' 5 | #' The alliances dataset contains the international defense alliance network 6 | #' among 164 countries, covering the years 1981--2000. In addition to the 7 | #' yearly defense alliance network, it contains data on military capabilities, 8 | #' governing regime type, geographic contiguity and international conflict. 9 | #' This is an excerpt from a dataset that has been used in two published 10 | #' analyses. The full dataset (Cranmer, Desmarais and Menninga 2012; Cranmer, 11 | #' Desmarais and Kirkland 2012) contains a large number of countries and a much 12 | #' longer time series. 13 | #' 14 | #' @name alliances 15 | #' @aliases alliances allyNet contigMat lNet LSP warNet 16 | #' @docType data 17 | #' @format 18 | #' \describe{ 19 | #' \item{\code{allyNet}}{is a list of network objects at 20 time points, 20 | #' 1981--2000, containing undirected defense alliance networks. In addition to 21 | #' the alliance ties, each network object contains three vertex attributes. 22 | #' \code{cinc} is the "CINC" or Composite Index of National Capability score 23 | #' (see 24 | #' \url{https://correlatesofwar.org/data-sets/national-material-capabilities/}). 25 | #' \code{polity} is the "polity score" of each country in the respective year. 26 | #' Quoting the online description, "the Polity Score captures this regime 27 | #' authority spectrum on a 21-point scale ranging from -10 (hereditary 28 | #' monarchy) to +10 (consolidated democracy)," (see 29 | #' \url{https://www.systemicpeace.org/polityproject.html}). \code{year} is 30 | #' simply the year recorded as a vertex attribute.} 31 | #' 32 | #' \item{\code{contigMat}}{is a 164 x 164 binary matrix in which a 1 indicates 33 | #' that two countries share a border.} 34 | #' 35 | #' \item{\code{lNet}}{is a list of 20 matrices. Each element is the adjacency 36 | #' matrix from the previous year. This is used to model memory in the ties.} 37 | #' 38 | #' \item{\code{LSP}}{is a list of 20 matrices. Each element is a matrix 39 | #' recording the number of shared partners between countries in the alliance 40 | #' network from the previous year.} 41 | #' 42 | #' \item{\code{warNet}}{is a list of 20 matrices. Each element is a binary 43 | #' matrix that indicates whether two states were in a militarized interstate 44 | #' dispute in the respective year.} 45 | #' } 46 | #' 47 | #' @references Cranmer, Skyler J., Bruce A. Desmarais, and Justin H. Kirkland 48 | #' (2012): Toward a Network Theory of Alliance Formation. \emph{International 49 | #' Interactions} 38(3): 295--324. \doi{10.1080/03050629.2012.677741}. 50 | #' 51 | #' Cranmer, Skyler J., Bruce A. Desmarais, and Elizabeth Menninga (2012): 52 | #' Complex Dependencies in the Alliance Network. \emph{International 53 | #' Interactions} 29(3): 279--313. \doi{10.1177/0738894212443446}. 54 | #' 55 | #' @source The data were gathered by Skyler Cranmer and Bruce Desmarais in the 56 | #' process of writing Cranmer, Desmarais and Menninga (2012) and Cranmer, 57 | #' Desmarais and Kirkland (2012). 58 | #' 59 | #' Permission to redistribute this dataset along with this package was granted 60 | #' by Skyler Cranmer and Bruce Desmarais on December 15, 2015. Questions about 61 | #' the data should be directed to them. 62 | #' 63 | #' @keywords datasets 64 | #' 65 | #' @examples 66 | #' \dontrun{ 67 | #' data("alliances") 68 | #' 69 | #' # btergm formulas look very similar to ERGM formulas. 70 | #' # Note the R argument; usually want R > 1000. 71 | #' # Here it is set to 50 to limit computation time. 72 | #' # First, set the seed for replicability. 73 | #' set.seed(123) 74 | #' model <- btergm(allyNet ~ edges + gwesp(0, fixed = TRUE) 75 | #' + edgecov(lNet) + edgecov(LSP) + edgecov(warNet) 76 | #' + nodecov("polity") + nodecov("cinc") + absdiff("polity") 77 | #' + absdiff("cinc") + edgecov(contigMat) + nodecov("year"), 78 | #' R = 50) 79 | #' 80 | #' # View estimates and confidence intervals. 81 | #' summary(model) 82 | #' 83 | #' # Evaluate model fit. Simulate 100 networks for each time point. 84 | #' # Calculate edgewise shared partners, degree and geodesic distance 85 | #' # distance distributions. 86 | #' alliance_gof <- gof(model, statistics = c(deg, esp, geodesic)) 87 | #' 88 | #' # Plot goodness of fit. 89 | #' plot(alliance_gof) 90 | #' } 91 | NULL -------------------------------------------------------------------------------- /man/alliances.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/alliances.R 3 | \docType{data} 4 | \name{alliances} 5 | \alias{alliances} 6 | \alias{allyNet} 7 | \alias{contigMat} 8 | \alias{lNet} 9 | \alias{LSP} 10 | \alias{warNet} 11 | \title{Longitudinal international defense alliance network, 1981--2000} 12 | \format{ 13 | \describe{ 14 | \item{\code{allyNet}}{is a list of network objects at 20 time points, 15 | 1981--2000, containing undirected defense alliance networks. In addition to 16 | the alliance ties, each network object contains three vertex attributes. 17 | \code{cinc} is the "CINC" or Composite Index of National Capability score 18 | (see 19 | \url{https://correlatesofwar.org/data-sets/national-material-capabilities/}). 20 | \code{polity} is the "polity score" of each country in the respective year. 21 | Quoting the online description, "the Polity Score captures this regime 22 | authority spectrum on a 21-point scale ranging from -10 (hereditary 23 | monarchy) to +10 (consolidated democracy)," (see 24 | \url{https://www.systemicpeace.org/polityproject.html}). \code{year} is 25 | simply the year recorded as a vertex attribute.} 26 | 27 | \item{\code{contigMat}}{is a 164 x 164 binary matrix in which a 1 indicates 28 | that two countries share a border.} 29 | 30 | \item{\code{lNet}}{is a list of 20 matrices. Each element is the adjacency 31 | matrix from the previous year. This is used to model memory in the ties.} 32 | 33 | \item{\code{LSP}}{is a list of 20 matrices. Each element is a matrix 34 | recording the number of shared partners between countries in the alliance 35 | network from the previous year.} 36 | 37 | \item{\code{warNet}}{is a list of 20 matrices. Each element is a binary 38 | matrix that indicates whether two states were in a militarized interstate 39 | dispute in the respective year.} 40 | } 41 | } 42 | \source{ 43 | The data were gathered by Skyler Cranmer and Bruce Desmarais in the 44 | process of writing Cranmer, Desmarais and Menninga (2012) and Cranmer, 45 | Desmarais and Kirkland (2012). 46 | 47 | Permission to redistribute this dataset along with this package was granted 48 | by Skyler Cranmer and Bruce Desmarais on December 15, 2015. Questions about 49 | the data should be directed to them. 50 | } 51 | \description{ 52 | Longitudinal international defense alliance network, 1981--2000. 53 | } 54 | \details{ 55 | The alliances dataset contains the international defense alliance network 56 | among 164 countries, covering the years 1981--2000. In addition to the 57 | yearly defense alliance network, it contains data on military capabilities, 58 | governing regime type, geographic contiguity and international conflict. 59 | This is an excerpt from a dataset that has been used in two published 60 | analyses. The full dataset (Cranmer, Desmarais and Menninga 2012; Cranmer, 61 | Desmarais and Kirkland 2012) contains a large number of countries and a much 62 | longer time series. 63 | } 64 | \examples{ 65 | \dontrun{ 66 | data("alliances") 67 | 68 | # btergm formulas look very similar to ERGM formulas. 69 | # Note the R argument; usually want R > 1000. 70 | # Here it is set to 50 to limit computation time. 71 | # First, set the seed for replicability. 72 | set.seed(123) 73 | model <- btergm(allyNet ~ edges + gwesp(0, fixed = TRUE) 74 | + edgecov(lNet) + edgecov(LSP) + edgecov(warNet) 75 | + nodecov("polity") + nodecov("cinc") + absdiff("polity") 76 | + absdiff("cinc") + edgecov(contigMat) + nodecov("year"), 77 | R = 50) 78 | 79 | # View estimates and confidence intervals. 80 | summary(model) 81 | 82 | # Evaluate model fit. Simulate 100 networks for each time point. 83 | # Calculate edgewise shared partners, degree and geodesic distance 84 | # distance distributions. 85 | alliance_gof <- gof(model, statistics = c(deg, esp, geodesic)) 86 | 87 | # Plot goodness of fit. 88 | plot(alliance_gof) 89 | } 90 | } 91 | \references{ 92 | Cranmer, Skyler J., Bruce A. Desmarais, and Justin H. Kirkland 93 | (2012): Toward a Network Theory of Alliance Formation. \emph{International 94 | Interactions} 38(3): 295--324. \doi{10.1080/03050629.2012.677741}. 95 | 96 | Cranmer, Skyler J., Bruce A. Desmarais, and Elizabeth Menninga (2012): 97 | Complex Dependencies in the Alliance Network. \emph{International 98 | Interactions} 29(3): 279--313. \doi{10.1177/0738894212443446}. 99 | } 100 | \keyword{datasets} 101 | -------------------------------------------------------------------------------- /man/adjust.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/preprocess.R 3 | \name{adjust} 4 | \alias{adjust} 5 | \title{Adjust the dimensions of a source object to the dimensions of a target object} 6 | \usage{ 7 | adjust( 8 | source, 9 | target, 10 | remove = TRUE, 11 | add = TRUE, 12 | value = NA, 13 | returnlabels = FALSE 14 | ) 15 | } 16 | \arguments{ 17 | \item{source}{A matrix, network, list or data.frame object or a vector which 18 | should be adjusted.} 19 | 20 | \item{target}{A matrix, network, list or data.frame object or a vector to 21 | which the source object is compared with regard to its labels.} 22 | 23 | \item{remove}{Should rows and columns that are not present in the target 24 | object be removed?} 25 | 26 | \item{add}{Should rows and columns that are present in the target object but 27 | not in the source object be added to the source object?} 28 | 29 | \item{value}{The value to be inserted if a new row or column is added. By 30 | default, new cells are filled with \code{NA} values, but other sensible 31 | values may include \code{-Inf} or \code{0}.} 32 | 33 | \item{returnlabels}{Return a list of added and removed row and column labels 34 | rather than the actual matrix, vector, or network object?} 35 | } 36 | \description{ 37 | Adjust the dimensions of a source object to the dimensions of a target 38 | object. 39 | } 40 | \details{ 41 | An adjacency matrix (the \code{source} matrix) is compared to another 42 | adjacency matrix (the \code{target} matrix) by matching the row or column 43 | labels. If the target matrix contains rows/columns which are not present in 44 | the source matrix, new rows and columns with the corresponding labels and 45 | \code{NA} values in the cells are inserted into the source matrix. If the 46 | source matrix contains rows/columns which are not present in the target 47 | matrix, these rows and columns are removed from the source matrix. In 48 | addition to adjacency matrices, two-mode matrices, network objects (also with 49 | vertex attributes), and vectors are supported. 50 | 51 | Note that it is not necessary to use this function to preprocess any data 52 | before estimating a TERGM. The estimation functions in the \pkg{btergm} 53 | package call this function repeatedly to mutually adjust all data as needed. 54 | } 55 | \examples{ 56 | # create sociomatrix a with 13 vertices a to m 57 | vertices <- letters[1:13] 58 | a <- matrix(rbinom(length(vertices)^2, 1, 0.1), nrow = length(vertices)) 59 | rownames(a) <- colnames(a) <- vertices 60 | 61 | # create matrix b with the same vertices except f and k, but additional n 62 | vertices <- c(vertices[-c(6, 11)], "n") 63 | b <- matrix(rbinom(length(vertices)^2, 1, 0.1), nrow = length(vertices)) 64 | rownames(b) <- colnames(b) <- vertices 65 | 66 | # check dimensions 67 | dim(a) # 13 x 13 68 | dim(b) # 12 x 12 69 | 70 | # adjust a to b: add n and fill up with NAs; remove f and k 71 | adjust(a, b, add = TRUE, remove = TRUE) 72 | 73 | \dontrun{ 74 | # more complex example with additional attributes stored in the network 75 | # object; convert a to network object with additional vertex and network 76 | # attributes 77 | nw <- network(a) 78 | vertices <- letters[1:13] 79 | nwattrib1 <- matrix(rbinom(length(vertices)^2, 1, 0.1), 80 | nrow = length(vertices)) 81 | nwattrib2 <- nwattrib1 82 | rownames(nwattrib1) <- colnames(nwattrib1) <- vertices 83 | set.network.attribute(nw, "nwattrib1", nwattrib1) 84 | set.network.attribute(nw, "nwattrib2", nwattrib2) 85 | set.vertex.attribute(nw, "vattrib", 1:length(vertices)) 86 | 87 | # check presence of the two attributes 88 | list.network.attributes(nw) # nwattrib1 and nwattrib2 are listed 89 | get.network.attribute(nw, "nwattrib1") # returns sociomatrix with labels 90 | get.network.attribute(nw, "nwattrib2") # returns sociomatrix without labels 91 | list.vertex.attributes(nw) # vattrib is listed 92 | get.vertex.attribute(nw, "vattrib") # returns numeric vector 1:13 93 | 94 | # adjust the network including the two attributes 95 | nw.adjusted <- adjust(nw, b, add = TRUE, remove = TRUE) 96 | as.matrix(nw.adjusted) # note that the order of nodes may have changed 97 | get.network.attribute(nw.adjusted, "nwattrib1") # returns adjusted matrix 98 | get.network.attribute(nw.adjusted, "nwattrib2") # returns adjusted matrix 99 | get.vertex.attribute(nw.adjusted, "vattrib") # returns adjusted vector 100 | } 101 | 102 | } 103 | \seealso{ 104 | \code{\link{handleMissings}} 105 | } 106 | -------------------------------------------------------------------------------- /man/marginalplot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interpretation.R 3 | \name{marginalplot} 4 | \alias{marginalplot} 5 | \title{Plot marginal effects for two-way interactions in (T)ERGMs} 6 | \usage{ 7 | marginalplot( 8 | model, 9 | var1, 10 | var2, 11 | inter, 12 | ci = 0.95, 13 | rug = FALSE, 14 | point = FALSE, 15 | structzeromat = NULL, 16 | zeroline = TRUE, 17 | color = "black", 18 | xlab = NULL, 19 | ylab = NULL 20 | ) 21 | } 22 | \arguments{ 23 | \item{model}{An \code{ergm} object as generated by the \pkg{ergm} package. 24 | Note that marginal effects plots cannot be created for \code{btergm} 25 | objects because the variance-covariance matrix is not valid. However, it 26 | should be possible to apply the \code{marginalplot} function to 27 | MCMC-MLE-estimated TERGMs because the \code{ergm} object is stored in the 28 | \code{ergm} slot of an \code{mtergm} object. To do this, supply the 29 | \code{ergm} object instead of the \code{mtergm} object (e.g., 30 | \code{marginalplot(mtergmobject@ergm)}).} 31 | 32 | \item{var1}{Name of the first main variable. This is the focal variable.} 33 | 34 | \item{var2}{Name of the second main variable. This is the conditioning 35 | variable.} 36 | 37 | \item{inter}{Name of the interaction effect.} 38 | 39 | \item{ci}{Significance level.} 40 | 41 | \item{rug}{Display the distribution of the conditioning variable at the 42 | bottom of the plot?} 43 | 44 | \item{point}{Display error bars for the levels of the conditioning variable 45 | (instead of a continuous curve)?} 46 | 47 | \item{structzeromat}{An optional matrix object which indicates dyads that 48 | should be deleted prior to the calculation of the confidence interval for 49 | the marginal effect curve. This is useful when such a matrix was used to 50 | indicate structural zeros during estimation. In this event, the dyads 51 | characterized by structural zeros are not allowed to be tied, therefore 52 | they should be removed from the set of dyads used for the calculation of 53 | marginal effects. The matrix should contain ones for structural zeros and 54 | zeros for entries that should be used.} 55 | 56 | \item{zeroline}{Draw a horizontal line to indicate zero for the first main 57 | variable?} 58 | 59 | \item{color}{Color of the curve, confidence interval, and distribution.} 60 | 61 | \item{xlab}{Axis label for the second (conditioning) variable.} 62 | 63 | \item{ylab}{Axis label for the first (focal) variable.} 64 | } 65 | \description{ 66 | Plot marginal effects for two-way interactions in (T)ERGMs. 67 | } 68 | \details{ 69 | The \code{marginalplot} function creates marginal effects plots for ERGMs 70 | with interaction effects. The user has to supply the \code{ergm} object and 71 | the coefficient names of the first main variable, the second main variable, 72 | and the interaction term as stored in the coefficients vector inside the 73 | \code{ergm} object. It is possible to draw continuous curves or discrete 74 | error bars depending on the nature of the data (using the \code{point} 75 | argument). The distribution of the second (conditioning) variable can be 76 | plotted at the bottom of the viewport using the \code{rug} argument. 77 | 78 | The resulting marginal effects plot is a \code{ggplot2} plot. This means it 79 | can be extended by plotting additional elements and using themes. 80 | } 81 | \examples{ 82 | \dontrun{ 83 | # data preparation 84 | data("florentine") 85 | n <- network.size(flobusiness) 86 | wealth <- get.vertex.attribute(flobusiness, "wealth") 87 | priorates <- get.vertex.attribute(flobusiness, "priorates") 88 | wealth.icov <- matrix(rep(wealth, n), ncol = n, byrow = TRUE) 89 | priorates.icov <- matrix(rep(priorates, n), ncol = n, byrow = TRUE) 90 | interac <- wealth.icov * priorates.icov 91 | 92 | # estimate model with interaction effect 93 | model <- ergm(flobusiness ~ edges + esp(1) + edgecov(wealth.icov) 94 | + edgecov(priorates.icov) + edgecov(interac)) 95 | 96 | # plot the interaction (note the additional optional ggplot2 elements) 97 | marginalplot(model, var1 = "edgecov.wealth.icov", 98 | var2 = "edgecov.priorates.icov", inter = "edgecov.interac", 99 | color = "darkred", rug = TRUE, point = FALSE, 100 | xlab = "Priorates", ylab = "Wealth") + 101 | ggplot2::theme_bw() + 102 | ggplot2::ggtitle("Interaction effect") 103 | } 104 | 105 | } 106 | \seealso{ 107 | Other interpretation: 108 | \code{\link{edgeprob}()}, 109 | \code{\link{interpret}()} 110 | } 111 | \concept{interpretation} 112 | -------------------------------------------------------------------------------- /man/checkdegeneracy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/checkdegeneracy.R 3 | \docType{methods} 4 | \name{checkdegeneracy} 5 | \alias{checkdegeneracy} 6 | \alias{checkdegeneracy-methods} 7 | \alias{checkdegeneracy,mtergm-method} 8 | \alias{checkdegeneracy,btergm-method} 9 | \alias{print.degeneracy} 10 | \alias{plot.degeneracy} 11 | \title{Check for degeneracy in fitted TERGMs} 12 | \usage{ 13 | checkdegeneracy(object, ...) 14 | 15 | \S4method{checkdegeneracy}{mtergm}(object, ...) 16 | 17 | \S4method{checkdegeneracy}{btergm}( 18 | object, 19 | nsim = 1000, 20 | MCMC.interval = 1000, 21 | MCMC.burnin = 10000, 22 | verbose = FALSE 23 | ) 24 | 25 | \method{print}{degeneracy}( 26 | x, 27 | center = FALSE, 28 | t = 1:length(x$sim), 29 | terms = 1:length(x$target.stats[[1]]), 30 | ... 31 | ) 32 | 33 | \method{plot}{degeneracy}( 34 | x, 35 | center = TRUE, 36 | t = 1:length(x$sim), 37 | terms = 1:length(x$target.stats[[1]]), 38 | vbar = TRUE, 39 | main = NULL, 40 | xlab = NULL, 41 | target.col = "red", 42 | target.lwd = 3, 43 | ... 44 | ) 45 | } 46 | \arguments{ 47 | \item{object}{A \code{btergm} or \code{mtergm} object, as estimated using the 48 | \code{btergm} or \code{mtergm} function.} 49 | 50 | \item{...}{Arbitrary further arguments for subroutines.} 51 | 52 | \item{nsim}{The number of networks to be simulated at each time step. This 53 | number should be sufficiently large for a meaningful comparison. If 54 | possible, much more than 1,000 simulations.} 55 | 56 | \item{MCMC.interval}{Internally, this package uses the simulation facilities 57 | of the \pkg{ergm} package to create new networks against which to compare 58 | the original network(s) for goodness-of-fit assessment. This argument sets 59 | the MCMC interval to be passed over to the simulation command. The default 60 | value is \code{1000}, which means that every 1000th simulation outcome from 61 | the MCMC sequence is used. There is no general rule of thumb on the 62 | selection of this parameter, but if the results look suspicious (e.g., when 63 | the model fit is perfect), increasing this value may be helpful.} 64 | 65 | \item{MCMC.burnin}{Internally, this package uses the simulation facilities of 66 | the \pkg{ergm} package to create new networks against which to compare the 67 | original network(s) for goodness-of-fit assessment. This argument sets the 68 | MCMC burnin to be passed over to the simulation command. The default value 69 | is \code{10000}. There is no general rule of thumb on the selection of this 70 | parameter, but if the results look suspicious (e.g., when the model fit is 71 | perfect), increasing this value may be helpful.} 72 | 73 | \item{verbose}{Print details?} 74 | 75 | \item{x}{A \code{degeneracy} object created by the \code{checkdegeneracy} 76 | function.} 77 | 78 | \item{center}{If \code{TRUE}, print/plot the simulated minus the target 79 | statistics, with an expected value of 0 in a non-degenerate model. If 80 | \code{FALSE}, print/plot the distribution of simulated statistics and show 81 | the target statistic separately.} 82 | 83 | \item{t}{Time indices to include, e.g., \code{t = 2:4} for time steps 2 to 4.} 84 | 85 | \item{terms}{Indices of the model terms to include, e.g., \code{terms = 1:3} 86 | includes the first three statistics.} 87 | 88 | \item{vbar}{Show vertical bar for target statistic in histogram.} 89 | 90 | \item{main}{Main title of the plot.} 91 | 92 | \item{xlab}{Label on the x-axis. Defaults to the name of the statistic.} 93 | 94 | \item{target.col}{Color of the vertical bar for the target statistic. 95 | Defaults to red.} 96 | 97 | \item{target.lwd}{Line width of the vertical bar for the target statistic. 98 | Defaults to 3.} 99 | } 100 | \value{ 101 | A list with target statistics and simulations. 102 | } 103 | \description{ 104 | Check for degeneracy in fitted TERGMs. 105 | } 106 | \details{ 107 | The methods for the generic \code{degeneracy} function implement a degeneracy 108 | check for \code{btergm} and \code{mtergm} objects. For \code{btergm}, this 109 | works by comparing the global statistics of simulated networks to those of 110 | the observed networks at each observed time step. If the global statistics 111 | differ significantly, this is indicated by small p-values. If there are many 112 | significant results, this indicates degeneracy. For \code{mtergm}, the 113 | \code{mcmc.diagnostics} function from the \pkg{ergm} package is used. 114 | } 115 | \references{ 116 | Hanneke, Steve, Wenjie Fu and Eric P. Xing (2010): Discrete Temporal Models 117 | of Social Networks. \emph{Electronic Journal of Statistics} 4: 585--605. 118 | \doi{10.1214/09-EJS548}. 119 | 120 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 121 | Exponential Random Graph Models with btergm: Estimation and Bootstrap 122 | Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1-36. 123 | \doi{10.18637/jss.v083.i06}. 124 | } 125 | -------------------------------------------------------------------------------- /man/tergmprepare.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tergmprepare.R 3 | \name{tergmprepare} 4 | \alias{tergmprepare} 5 | \title{Prepare data structure for TERGM estimation, including composition change} 6 | \usage{ 7 | tergmprepare(formula, offset = TRUE, blockdiag = FALSE, verbose = TRUE) 8 | } 9 | \arguments{ 10 | \item{formula}{The original formula provided by the user, given the data 11 | structures in the workspace.} 12 | 13 | \item{offset}{Indicates whether absent nodes should be added where they are 14 | missing (\code{offset = TRUE}) or removed where they are not missing 15 | (\code{offset = FALSE}).} 16 | 17 | \item{blockdiag}{Should the time steps be arranged in a blockdiagonal matrix 18 | for use with MCMC-MLE or Bayesian estimation (\code{blockdiag = TRUE}), or 19 | should they be kept as items in a list for use with \code{\link{btergm}} 20 | (\code{blockdiag = FALSE})?} 21 | 22 | \item{verbose}{Report details about dimension adjustment?} 23 | } 24 | \value{ 25 | A list with the following slots: 26 | \describe{ 27 | \item{lhs.original}{A character object containing the original name of the 28 | object on the left-hand side of the formula provided by the user. This is 29 | saved here because the formula is manipulated such that the left-hand 30 | side of the formula contains a new item \code{networks[[i]]}.} 31 | \item{networks}{The list of networks on the left-hand side of the formula 32 | after dimension adjustment, or a blockdiagonal network representing the 33 | left-hand side of the formula after dimension adjustment if argument 34 | \code{blockdiag = TRUE} was used.} 35 | \item{num.vertices}{The maximum number of nodes of any time point after 36 | adjustment of dimensions.} 37 | \item{directed}{Are the networks directed?} 38 | \item{bipartite}{Are the networks bipartite?} 39 | \item{form}{The formula after manipulation and adjustment of the data, 40 | including \code{networks[[i]]} on the left-hand side and an added offset 41 | covariate on the right-hand side of the formula, in addition to added 42 | indices for the covariate terms.} 43 | \item{time.steps}{The number of time steps of the dataset.} 44 | \item{rhs.terms}{The right-hand side of the formula after adjustment, as 45 | a vector of \code{character} objects representing the terms.} 46 | \item{covnames}{A \code{character} vector containing the names of the 47 | objects in which the networks and covariates are stored, according to the 48 | manipulated formula. This includes \code{"networks"} (for the left-hand 49 | side of the formula) and all objects containing exogenous covariates on 50 | the right-hand side of the formula after manipulation.} 51 | \item{...}{Each of the covariates mentioned in the slot \code{covnames} is 52 | stored as an element of the list, either as a list of matrices or 53 | networks (if \code{blockdiag = FALSE}) or as a matrix or network object 54 | (if \code{blockdiag = TRUE}).} 55 | \item{auto.adjust}{Did the function have to adjust the dimensions of the 56 | networks or covariates at all?} 57 | \item{nvertices}{A matrix containing the number of nodes in the rows and 58 | columns of each object at each time step, after adjustment.} 59 | \item{offsmat}{A list of offset covariate matrices or a large blockdiagonal 60 | offset covariate matrix containing structural zeros. If 61 | \code{offset = FALSE}, this matrix or list of matrices will contain only 62 | zeros. If \code{offset = TRUE}, they will contain ones where nodes were 63 | absent in the original data.} 64 | } 65 | } 66 | \description{ 67 | Prepare data structure for TERGM estimation, including composition change. 68 | } 69 | \details{ 70 | This is a helper function that adjusts the dimensions of networks or 71 | covariates within a given time step to each other by removing nodes that are 72 | not present across all objects within a time step or by adding nodes where 73 | they are missing (and simultaneously adding entries to a list of structural 74 | zero matrices to indicate their absence). It is not necessary to have 75 | identical (numbers of) nodes across time steps as long as the dimensions of 76 | the matrices, networks, and vectors match cross-sectionally within time 77 | steps, given that temporal dependency terms like memory are interpreted as 78 | dyadic covariates in a given time step. This helper function also creates 79 | these dyadic covariate data structures for some of the custom temporal model 80 | terms, such as \code{memory} and \code{delrecip}. Leifeld, Cranmer and 81 | Desmarais (2018) contain additional details on composition change, dimension 82 | adjustment of matrices, and temporal dependencies. Note that this function 83 | should not normally be used by the end user. It is automatically called 84 | internally by the estimation functions to make the dimensions of all objects 85 | conformable to each other for estimation. Use this function only for 86 | diagnostic purposes! 87 | } 88 | \author{ 89 | Philip Leifeld 90 | } 91 | -------------------------------------------------------------------------------- /man/simulate.btergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/btergm.R 3 | \name{simulate.btergm} 4 | \alias{simulate.btergm} 5 | \alias{simulate.mtergm} 6 | \title{Simulate Networks from a \code{btergm} Object} 7 | \usage{ 8 | \method{simulate}{btergm}( 9 | object, 10 | nsim = 1, 11 | seed = NULL, 12 | index = NULL, 13 | formula = getformula(object), 14 | coef = object@coef, 15 | verbose = TRUE, 16 | ... 17 | ) 18 | 19 | \method{simulate}{mtergm}( 20 | object, 21 | nsim = 1, 22 | seed = NULL, 23 | index = NULL, 24 | formula = getformula(object), 25 | coef = object@coef, 26 | verbose = TRUE, 27 | ... 28 | ) 29 | } 30 | \arguments{ 31 | \item{object}{A \code{btergm} or \code{mtergm} object, resulting from a call 32 | of the \code{\link{btergm}} or \code{\link{mtergm}} function.} 33 | 34 | \item{nsim}{The number of networks to be simulated. Note that for values 35 | greater than one, a \code{network.list} object is returned, which can be 36 | indexed just like a \code{list} object, for example \code{mynetworks[[1]]} 37 | for the first simulated network in the object \code{mynetworks}.} 38 | 39 | \item{seed}{Random number integer seed. See \link[base:Random]{set.seed}.} 40 | 41 | \item{index}{Index of the network from which the new network(s) should be 42 | simulated. The index refers to the list of response networks on the 43 | left-hand side of the model formula. Note that more recent networks are 44 | located at the end of the list. By default, the last (= most recent) 45 | network is used.} 46 | 47 | \item{formula}{A model formula from which the new network(s) should be 48 | simulated. By default, the formula is taken from the \code{btergm} object.} 49 | 50 | \item{coef}{A vector of parameter estimates. By default, the coefficients are 51 | extracted from the given \code{btergm} object.} 52 | 53 | \item{verbose}{Print additional details while running the simulations?} 54 | 55 | \item{...}{Further arguments are handed over to the 56 | \code{\link[ergm]{simulate_formula}} function in the \pkg{ergm} package.} 57 | } 58 | \description{ 59 | Simulate networks from a \code{btergm} object using MCMC sampler. 60 | 61 | Simulate networks from an \code{mtergm} object using MCMC sampler. 62 | } 63 | \details{ 64 | The \code{simulate.btergm} function is a wrapper for the 65 | \code{\link[ergm]{simulate_formula}} function in the \pkg{ergm} package (see 66 | \code{help("simulate.ergm")}). It can be used to simulate new networks from a 67 | \code{btergm} object. The \code{index} argument specifies from which of the 68 | original networks the new network(s) should be simulated. For example, if 69 | \code{object} is an estimation based on cosponsorship networks from the 99th 70 | to the 107th Congress (as in Desmarais and Cranmer 2012), and the 71 | cosponsorship network in the 108th Congress should be predicted using the 72 | \code{simulate.btergm} function, then the argument \code{index = 9} should be 73 | passed to the function because the network should be based on the 9th network 74 | in the list (that is, the latest network, which is the cosponsorship network 75 | for the 107th Congress). Note that all relevant objects (the networks and the 76 | covariates) must be present in the workspace (as was the case during the 77 | estimation of the model). 78 | } 79 | \examples{ 80 | \dontrun{ 81 | # fit a TERGM to some toy data 82 | library("network") 83 | set.seed(5) 84 | networks <- list() 85 | for(i in 1:10){ # create 10 random networks with 10 actors 86 | mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10) 87 | diag(mat) <- 0 # loops are excluded 88 | nw <- network(mat) # create network object 89 | networks[[i]] <- nw # add network to the list 90 | } 91 | covariates <- list() 92 | for (i in 1:10) { # create 10 matrices as covariate 93 | mat <- matrix(rnorm(100), nrow = 10, ncol = 10) 94 | covariates[[i]] <- mat # add matrix to the list 95 | } 96 | fit <- btergm(networks ~ edges + istar(2) + 97 | edgecov(covariates), R = 100) 98 | 99 | # simulate 12 new networks from the last (= 10th) time step 100 | sim1 <- simulate(fit, nsim = 12) 101 | 102 | # simulate 1 network from the first time step 103 | sim2 <- simulate(fit, index = 1) 104 | 105 | # simulate network from t = 5 with larger covariate coefficient 106 | coefs <- coef(fit) 107 | coefs["edgecov.covariates[[i]]"] <- 0.5 108 | sim3 <- simulate(fit, index = 5, coef = coefs) 109 | } 110 | 111 | } 112 | \references{ 113 | Desmarais, Bruce A. and Skyler J. Cranmer (2012): Statistical Mechanics of 114 | Networks: Estimation and Uncertainty. \emph{Physica A} 391: 1865--1876. 115 | \doi{10.1016/j.physa.2011.10.018}. 116 | 117 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 118 | Exponential Random Graph Models with btergm: Estimation and Bootstrap 119 | Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1--36. 120 | \doi{10.18637/jss.v083.i06}. 121 | } 122 | -------------------------------------------------------------------------------- /man/btergm-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/btergm.R 3 | \docType{class} 4 | \name{btergm-class} 5 | \alias{btergm-class} 6 | \alias{show,btergm-method} 7 | \alias{coef,btergm-method} 8 | \alias{nobs,btergm-method} 9 | \alias{btergm.se} 10 | \alias{confint,btergm-method} 11 | \alias{timesteps.btergm} 12 | \alias{summary,btergm-method} 13 | \title{An S4 class to represent a fitted TERGM by bootstrapped MPLE} 14 | \usage{ 15 | \S4method{show}{btergm}(object) 16 | 17 | \S4method{coef}{btergm}(object, invlogit = FALSE, ...) 18 | 19 | \S4method{nobs}{btergm}(object) 20 | 21 | btergm.se(object, print = FALSE) 22 | 23 | \S4method{confint}{btergm}(object, parm, level = 0.95, type = "perc", invlogit = FALSE, ...) 24 | 25 | timesteps.btergm(object) 26 | 27 | \S4method{summary}{btergm}(object, level = 0.95, type = "perc", invlogit = FALSE, ...) 28 | } 29 | \arguments{ 30 | \item{object}{A \code{btergm} object.} 31 | 32 | \item{invlogit}{Apply inverse logit transformation to the estimates and/or 33 | confidence intervals? That is, \eqn{\frac{1}{1 + \exp(-x)}}, where \eqn{x} 34 | is the respective value.} 35 | 36 | \item{...}{Further arguments to be passed through to the \code{confint} 37 | function.} 38 | 39 | \item{print}{Should the formatted coefficient table be printed to the R 40 | console along with significance stars (\code{print = TRUE}), or should the 41 | plain coefficient matrix be returned (\code{print = FALSE})?} 42 | 43 | \item{parm}{Parameters (specified by integer position or character string).} 44 | 45 | \item{level}{The significance level for computation of the confidence 46 | intervals. The default is \code{0.95} (that is, an alpha value of 0.05). 47 | Other common values include \code{0.999}, \code{0.99}, and \code{0.9}.} 48 | 49 | \item{type}{Type of confidence interval, e.g., basic bootstrap interval 50 | (\code{type = "basic"}), percentile-based interval (\code{type = "perc"}, 51 | which is the default option), or bias-adjusted and accelerated confidence 52 | interval (\code{type = "bca"}). All options from the \code{type} argument 53 | of the \link[boot]{boot.ci} function in the boot package can be used to 54 | generate confidence intervals.} 55 | } 56 | \description{ 57 | An S4 class to represent a fitted TERGM by bootstrapped MPLE. 58 | 59 | Show the coefficients of a \code{btergm} object. 60 | } 61 | \details{ 62 | \code{btergm} objects result from the estimation of a bootstrapped TERGM via 63 | the \code{\link{btergm}} function. \code{btergm} objects contain the 64 | coefficients, the bootstrapping samples of the coefficients, the number of 65 | replications, the number of observations, the number of time steps, the 66 | original formula, and the response, effects and weights objects that were fed 67 | into the \code{glm} call for estimating the model. 68 | } 69 | \section{Functions}{ 70 | \itemize{ 71 | \item \code{coef(btergm)}: Return the coefficients of a \code{btergm} object. 72 | 73 | \item \code{nobs(btergm)}: Return the number of observations saved in a 74 | \code{btergm} object. 75 | 76 | \item \code{btergm.se()}: Create a coefficient table from a \code{btergm} 77 | object 78 | 79 | Create a coefficient matrix with standard errors and p-values. 80 | 81 | This function can create a coefficient matrix with coefficients, standard 82 | errors, z-scores, and p-values, based on a fitted \code{btergm} object. 83 | If the argument \code{print = TRUE} is used, the matrix is printed to the R 84 | console as a formatted coefficient matrix with significance stars instead. 85 | Note that confidence intervals are the preferred way of interpretation for 86 | bootstrapped TERGMs; standard errors are only accurate if the bootstrapped 87 | data are normally distributed, which is not always the case. Various methods 88 | for checking for normality for each model term are available, for example 89 | quantile-quantile plots (e.g., \code{qqnorm(x@boot$t[, 1])} for the first 90 | model term in the \code{btergm} object called \code{x}). 91 | 92 | \item \code{confint(btergm)}: Return the confidence intervals for estimates in a 93 | \code{btergm} object. 94 | 95 | \item \code{timesteps.btergm()}: Return the number of time steps saved in a 96 | \code{btergm} object. 97 | 98 | \item \code{summary(btergm)}: Summary of a fitted \code{btergm} object. 99 | 100 | }} 101 | \section{Slots}{ 102 | 103 | \describe{ 104 | \item{\code{coef}}{Object of class \code{"numeric"}. The coefficients.} 105 | 106 | \item{\code{boot}}{Object of class \code{"matrix"}. The bootstrapping sample.} 107 | 108 | \item{\code{R}}{Object of class \code{"numeric"}. Number of replications.} 109 | 110 | \item{\code{nobs}}{Object of class \code{"numeric"}. Number of observations.} 111 | 112 | \item{\code{time.steps}}{Object of class \code{"numeric"}. Number of time steps.} 113 | 114 | \item{\code{formula}}{Object of class \code{"formula"}. The original model formula 115 | (without indices for the time steps).} 116 | 117 | \item{\code{formula2}}{The revised formula with the object references after applying 118 | the \code{\link{tergmprepare}} function.} 119 | 120 | \item{\code{response}}{Object of class \code{"integer"}. The response variable.} 121 | 122 | \item{\code{effects}}{Object of class \code{"data.frame"}. The effects that went 123 | into the \code{glm} call.} 124 | 125 | \item{\code{weights}}{Object of class \code{"integer"}. The weights of the 126 | observations.} 127 | 128 | \item{\code{auto.adjust}}{Object of class \code{"logical"}. Indicates whether 129 | automatic adjustment of dimensions was done before estimation.} 130 | 131 | \item{\code{offset}}{Object of class \code{"logical"}. Indicates whether an offset 132 | matrix with structural zeros was used.} 133 | 134 | \item{\code{directed}}{Object of class \code{"logical"}. Are the dependent networks 135 | directed?} 136 | 137 | \item{\code{bipartite}}{Object of class \code{"logical"}. Are the dependent networks 138 | bipartite?} 139 | 140 | \item{\code{nvertices}}{Number of vertices.} 141 | 142 | \item{\code{data}}{The data after processing by the \code{\link{tergmprepare}} 143 | function.} 144 | }} 145 | 146 | \seealso{ 147 | Other tergm-classes: 148 | \code{\link{createBtergm}()}, 149 | \code{\link{createMtergm}()}, 150 | \code{\link{createTbergm}()}, 151 | \code{\link{mtergm-class}}, 152 | \code{\link{tbergm-class}} 153 | } 154 | \concept{tergm-classes} 155 | -------------------------------------------------------------------------------- /tests/testthat/test-interpret.R: -------------------------------------------------------------------------------- 1 | context("test micro-level interpretation functions") 2 | 3 | test_that("edgeprob works with one-mode ERGM with network object", { 4 | skip_on_cran() 5 | set.seed(12345) 6 | data("chemnet") 7 | com <- committee %*% t(committee) 8 | nw <- network(pol, directed = TRUE) 9 | model1 <- ergm::ergm(nw ~ edges + edgecov(com) + istar(2)) 10 | expect_error(ep <- edgeprob(model1), NA) 11 | expect_s3_class(ep, "data.frame") 12 | expect_equal(dim(ep), c(870, 10)) 13 | expect_equal(colnames(ep), c("tie", "edges", "edgecov.com[[i]]", "istar2", "i", "j", "t", "i.name", "j.name", "probability")) 14 | expect_equal(ep$i[1], 1) 15 | expect_equal(ep$j[1], 2) 16 | expect_equal(ep$i.name[1], "BMA") 17 | expect_equal(ep$j.name[1], "BML") 18 | expect_false(any(is.na(ep$i.name))) 19 | expect_false(any(is.na(ep$j.name))) 20 | expect_equal(class(ep$probability), "numeric") 21 | expect_equal(class(ep$i), "integer") 22 | expect_equal(class(ep$j), "integer") 23 | expect_equal(class(ep$t), "integer") 24 | expect_equal(class(ep$i.name), "character") 25 | expect_equal(class(ep$j.name), "character") 26 | expect_equal(class(ep$tie), "integer") 27 | expect_lte(max(ep$probability), 1) 28 | expect_gte(max(ep$probability), 0) 29 | }) 30 | 31 | test_that("edgeprob works with one-mode ERGM with matrix", { 32 | skip_on_cran() 33 | set.seed(12345) 34 | data("chemnet") 35 | com <- committee %*% t(committee) 36 | model1 <- ergm::ergm(pol ~ edges + edgecov(com) + istar(2)) 37 | expect_error(ep <- edgeprob(model1), NA) 38 | expect_s3_class(ep, "data.frame") 39 | expect_equal(dim(ep), c(870, 10)) 40 | expect_equal(colnames(ep), c("tie", "edges", "edgecov.com[[i]]", "istar2", "i", "j", "t", "i.name", "j.name", "probability")) 41 | expect_equal(ep$i[1], 1) 42 | expect_equal(ep$j[1], 2) 43 | expect_equal(ep$i.name[1], "BMA") 44 | expect_equal(ep$j.name[1], "BML") 45 | expect_false(any(is.na(ep$i.name))) 46 | expect_false(any(is.na(ep$j.name))) 47 | expect_equal(class(ep$probability), "numeric") 48 | expect_equal(class(ep$i), "integer") 49 | expect_equal(class(ep$j), "integer") 50 | expect_equal(class(ep$t), "integer") 51 | expect_equal(class(ep$i.name), "character") 52 | expect_equal(class(ep$j.name), "character") 53 | expect_equal(class(ep$tie), "integer") 54 | expect_lte(max(ep$probability), 1) 55 | expect_gte(max(ep$probability), 0) 56 | }) 57 | 58 | test_that("edgeprob works with bipartite ERGM", { 59 | skip_on_cran() 60 | set.seed(12345) 61 | data("chemnet") 62 | cm <- network(committee, bipartite = TRUE, directed = FALSE) 63 | set.vertex.attribute(cm, "type", types[, 1]) 64 | suppressMessages(model1 <- ergm::ergm(cm ~ edges + nodefactor("type", levels = 1) + b1star(2))) 65 | expect_error(ep <- edgeprob(model1), NA) 66 | expect_s3_class(ep, "data.frame") 67 | expect_equal(dim(ep), c(600, 10)) 68 | expect_equal(colnames(ep), c("tie", "edges", "nodefactor.type.gov", "b1star2", "i", "j", "t", "i.name", "j.name", "probability")) 69 | expect_equal(ep$i[1], 1) 70 | expect_equal(ep$j[1], 31) 71 | expect_equal(ep$i.name[1], "BMA") 72 | expect_equal(ep$j.name[1], "IPU") 73 | expect_false(any(is.na(ep$i.name))) 74 | expect_false(any(is.na(ep$j.name))) 75 | expect_equal(class(ep$probability), "numeric") 76 | expect_equal(class(ep$i), "integer") 77 | expect_equal(class(ep$j), "integer") 78 | expect_equal(class(ep$t), "integer") 79 | expect_equal(class(ep$i.name), "character") 80 | expect_equal(class(ep$j.name), "character") 81 | expect_equal(class(ep$tie), "integer") 82 | expect_lte(max(ep$probability), 1) 83 | expect_gte(max(ep$probability), 0) 84 | }) 85 | 86 | test_that("edgeprob works with ergm, btergm, and mtergm objects, with and without curved terms", { 87 | skip_on_cran() 88 | 89 | # simulate networks with fixed and changing covariate and gwidegree 90 | set.seed(12345) 91 | nnodes <- 30 92 | T <- 15 93 | sim <- list() 94 | fixed_covariate <- matrix(rnorm(nnodes^2), nrow = nnodes, ncol = nnodes) 95 | changing_covariate <- list() 96 | for (t in 1:T) { 97 | changing <- matrix(rnorm(nnodes^2), nrow = nnodes, ncol = nnodes) 98 | changing_covariate[[t]] <- changing 99 | sim[[t]] <- ergm::simulate_formula(network::network(nnodes) ~ edges + edgecov(fixed_covariate) + edgecov(changing) + gwidegree(0.5, fixed = TRUE), 100 | nsim = 1, 101 | coef = c(-2, 0.3, 0.6, 1.4)) 102 | } 103 | 104 | # btergm with fixed GW decay 105 | expect_silent({ 106 | fit1 <- suppressWarnings(btergm(sim ~ edges + edgecov(fixed_covariate) + edgecov(changing_covariate) + gwidegree(1.0, fixed = TRUE), 107 | R = 100, verbose = FALSE)) 108 | }) 109 | expect_length(coef(fit1), 4) 110 | expect_silent(ep1 <- edgeprob(fit1)) 111 | expect_s3_class(ep1, "data.frame") 112 | expect_equivalent(dim(ep1), c(13050, 11)) 113 | 114 | # btergm with variable GW decay: currently unsupported in edgeprob 115 | expect_warning(fit2 <- btergm(sim ~ edges + edgecov(fixed_covariate) + edgecov(changing_covariate) + gwidegree(fixed = FALSE), 116 | R = 20, verbose = FALSE), "NAs generated during bootstrap") 117 | 118 | # mtergm with fixed GW decay 119 | expect_silent({ 120 | fit3 <- mtergm(sim ~ edges + edgecov(fixed_covariate) + edgecov(changing_covariate) + gwidegree(1.0, fixed = TRUE), 121 | verbose = FALSE) 122 | }) 123 | expect_length(coef(fit3), 4) 124 | expect_silent(ep3 <- edgeprob(fit3)) 125 | expect_s3_class(ep3, "data.frame") 126 | expect_equivalent(dim(ep3), c(13050, 11)) 127 | 128 | # mtergm with variable GW decay: currently unsupported in edgeprob 129 | expect_silent({ 130 | fit4 <- mtergm(sim ~ edges + edgecov(fixed_covariate) + edgecov(changing_covariate) + gwidegree(fixed = FALSE), 131 | verbose = FALSE) 132 | }) 133 | expect_length(coef(fit4), 5) 134 | expect_error(ep4 <- edgeprob(fit4), "MPLE-based \\(T\\)ERGMs with variable GW\\* decay are currently not supported") 135 | 136 | # ergm with fixed GW decay 137 | nnodes <- 50 138 | set.seed(12345) 139 | cov1 <- matrix(rnorm((nnodes)^2), nrow = nnodes, ncol = nnodes) 140 | cov2 <- matrix(rnorm((nnodes)^2), nrow = nnodes, ncol = nnodes) 141 | sim <- ergm::simulate_formula(network::network(nnodes) ~ edges + edgecov(cov1) + edgecov(cov2) + gwidegree(1.0, fixed = TRUE), 142 | nsim = 1, 143 | coef = c(-3, 0.3, 0.6, 0.8)) 144 | expect_silent({ 145 | suppressMessages(fit5 <- ergm::ergm(sim ~ edges + edgecov(cov1) + edgecov(cov2) + gwidegree(1.0, fixed = TRUE), 146 | verbose = FALSE)) 147 | }) 148 | expect_length(coef(fit5), 4) 149 | expect_silent(ep5 <- edgeprob(fit5)) 150 | expect_s3_class(ep5, "data.frame") 151 | expect_equivalent(dim(ep5), c(2450, 11)) 152 | 153 | # test validity of coefficients 154 | expect_equivalent(coef(fit1) - coef(fit3), rep(0, 4), tolerance = 0.1) 155 | }) -------------------------------------------------------------------------------- /man/tergm-terms.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/timecov.R 3 | \name{tergm-terms} 4 | \alias{tergm-terms} 5 | \alias{memory} 6 | \alias{timecov} 7 | \alias{delrecip} 8 | \title{Temporal dependencies for TERGMs} 9 | \usage{ 10 | timecov( 11 | covariate, 12 | minimum = 1, 13 | maximum = length(covariate), 14 | transform = function(t) 1 + (0 * t) + (0 * t^2), 15 | onlytime = FALSE 16 | ) 17 | } 18 | \arguments{ 19 | \item{covariate}{The list of networks or matrices for which to create a time 20 | covariate. This can be the list of networks on the left-hand side of the 21 | formula, in which case a time trend is created as a covariate list of 22 | matrices, or it can be a list of networks or matrices that is used as a 23 | dyadic covariate on the right-hand side of the formula, in which case an 24 | interaction effect between the time trend and the covariate is created. If 25 | used as a model term inside a formula, \code{covariate = NULL} is 26 | permitted, in which case the networks on the left-hand side will be used to 27 | form a time trend.} 28 | 29 | \item{minimum, maximum}{For time steps below the \code{minimum} value and 30 | above the \code{maximum} value, the time covariate is set to 0. These 31 | arguments can be used to create step-wise, discrete effects, for example to 32 | use a value of 0 up to an external event and 1 from that event onwards in 33 | order to control for influences of external events.} 34 | 35 | \item{transform}{In the default case, edges are modeled as being linearly 36 | increasingly important over time (i.e., a linear time trend). By tweaking 37 | the \code{transform} function, arbitrary functional forms of time can be 38 | tested. For example, \code{transform = sqrt} (for a geometrically 39 | decreasing time effect), \code{transform = function(x) x^2} (for a 40 | geometrically increasing time effect), \code{transform = function(t) t} 41 | (for a linear time trend) or polynomial functional forms (e.g., 42 | \code{transform = function(t) 0 + (1 * t) + (1 * t^2)}) can be used.} 43 | 44 | \item{onlytime}{If \code{TRUE}, return a time trend only. If \code{FALSE}, 45 | return an interaction between the time trend and the covariate. Note that 46 | the model term may need to be called twice or more inside a formula: one 47 | time to create the time trend main effect and one time for each 48 | interaction term; you also need to include the main effects for the 49 | covariates separately using \code{edgecov} or similar terms.} 50 | } 51 | \description{ 52 | Network statistics that span multiple time points. 53 | 54 | Transform a covariate using a function of time. 55 | } 56 | \details{ 57 | In addition to the ERGM user terms that can be estimated within a single 58 | network (see \link[ergm]{ergm-terms}), the \pkg{btergm} package provides 59 | additional model terms that can be used within a formula. These additional 60 | statistics span multiple time periods and are therefore called "temporal 61 | dependencies." Examples include memory terms (i.e., positive autoregression, 62 | dyadic stability, edge innovation, or edge loss), delayed reciprocity or 63 | mutuality, and time covariates (i.e., functions of time or interactions with 64 | time): 65 | \describe{ 66 | \item{\code{delrecip(mutuality = FALSE, lag = 1)}}{The \code{delrecip} term 67 | checks for delayed reciprocity. For example, if node \code{j} is tied to 68 | node \code{i} at \code{t = 1}, does this lead to a reciprocation of that 69 | tie back from \code{i} to \code{j} at \code{t = 2}? If 70 | \code{mutuality = TRUE} is set, this extends not only to ties, but also 71 | non-ties. That is, if \code{i} is not tied to \code{j} at \code{t = 1}, 72 | will this lead to \code{j} not being tied to \code{i} at \code{t = 2}, in 73 | addition to positively reciprocal patterns over time? The \code{lag} 74 | argument controls the size of the temporal lag: with \code{lag = 1}, 75 | reciprocity over one consecutive time period is checked. Note that as 76 | \code{lag} increases, the number of time steps on the dependent variable 77 | decreases.} 78 | \item{\code{memory(type = "stability", lag = 1)}}{Memory terms control for 79 | the impact of a previous network on the current network. Four different 80 | types of memory terms are available: positive autoregression 81 | (\code{type = "autoregression"}) checks whether previous ties are carried 82 | over to the current network; dyadic stability (\code{type = "stability"}) 83 | checks whether both edges and non-edges are stable between the previous 84 | and the current network; edge loss (\code{type = "loss"}) checks whether 85 | ties in the previous network have been dissolved and no longer exist in 86 | the current network; and edge innovation (\code{type = "innovation"}) 87 | checks whether previously unconnected nodes have the tendency to become 88 | tied in the current network. The \code{lag} argument accepts integer 89 | values and controls whether the comparison is made with the previous 90 | network (\code{lag = 1}), the pre-previous network (\code{lag = 2}) etc. 91 | Note that as \code{lag} increases, the number of time steps on the 92 | dependent variable decreases.} 93 | \item{\code{timecov(x = NULL, minimum = 1, maximum = NULL, 94 | transform = function(t) t)}}{The \code{timecov} model term checks for 95 | linear or non-linear time trends with regard to edge formation. 96 | Optionally, this can be combined with a covariate to create an 97 | interaction effect between a dyadic covariate and time in order to test 98 | whether the importance of a covariate increases or decreases over time. 99 | In the default case, edges modeled as being linearly increasingly 100 | important over time. By tweaking the \code{transform} function, 101 | arbitrary functional forms of time can be tested. For example, 102 | \code{transform = sqrt} (for a geometrically decreasing time effect), 103 | \code{transform = function(x) x^2} (for a geometrically increasing time 104 | effect), \code{transform = function(t) t} (for a linear time trend) or 105 | polynomial functional forms (e.g., \code{0 + (1 * t) + (1 * t^2)}) can 106 | be used. For time steps below the \code{minimum} value and above the 107 | \code{maximum} value, the time covariate is set to 0. These arguments 108 | can be used to create step-wise, discrete effects, for example to use a 109 | value of 0 up to an external event and 1 from that event onwards in 110 | order to control for influences of external events.} 111 | } 112 | 113 | The \code{timecov} model term checks for linear or non-linear time trends 114 | with regard to edge formation. Optionally, this can be combined with a 115 | covariate to create an interaction effect between a dyadic covariate and time 116 | in order to test whether the importance of a covariate increases or decreases 117 | over time. The function can either be used in a formula with 118 | \code{\link{btergm}}, \code{\link{mtergm}}, or \code{\link{tbergm}}, or it 119 | can be executed directly for manual inclusion of the results as a covariate. 120 | } 121 | \section{Functions}{ 122 | \itemize{ 123 | \item \code{timecov()}: Time trends and temporal covariate interactions 124 | 125 | }} 126 | \references{ 127 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2017): 128 | Temporal Exponential Random Graph Models with btergm: Estimation and 129 | Bootstrap Confidence Intervals. \emph{Journal of Statistical Software} 130 | 83(6): 1-36. \doi{10.18637/jss.v083.i06}. 131 | } 132 | -------------------------------------------------------------------------------- /R/tbergm.R: -------------------------------------------------------------------------------- 1 | # redefine S3 as S4 classes for proper handling as part of the 'tbergm' class 2 | setOldClass(c("bergm", "bergm")) 3 | 4 | #' An S4 class to represent a fitted TERGM using Bayesian estimation 5 | #' 6 | #' An S4 class to represent a fitted TERGM using Bayesian estimation. 7 | #' 8 | #' \code{tbergm} objects result from Bayesian estimation of a TERGM using the 9 | #' \code{\link{tbergm}} function. They contain the original \code{bergm} object 10 | #' and some additional information. 11 | #' 12 | #' @slot time.steps Object of class \code{"numeric"}. Number of time steps. 13 | #' @slot formula Object of class \code{"formula"}. The original model formula 14 | #' (without indices for the time steps). 15 | #' @slot formula2 The revised formula with the object references after applying 16 | #' the \code{\link{tergmprepare}} function. 17 | #' @slot auto.adjust Object of class \code{"logical"}. Indicates whether 18 | #' automatic adjustment of dimensions was done before estimation. 19 | #' @slot offset Object of class \code{"logical"}. Indicates whether an offset 20 | #' matrix with structural zeros was used. 21 | #' @slot directed Object of class \code{"logical"}. Are the dependent networks 22 | #' directed? 23 | #' @slot bipartite Object of class \code{"logical"}. Are the dependent networks 24 | #' bipartite? 25 | #' @slot estimate Estimate: \code{"bergm"} for Bayesian estimation. 26 | #' @slot bergm The original \code{bergm} object as estimated by the 27 | #' \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package. 28 | #' @slot nvertices Number of vertices. 29 | #' @slot data The data after processing by the \code{\link{tergmprepare}} 30 | #' function. 31 | #' 32 | #' @author Philip Leifeld 33 | #' 34 | #' @family tergm-classes 35 | #' 36 | #' @export 37 | setClass(Class = "tbergm", 38 | slots = c( 39 | time.steps = "numeric", 40 | formula = "formula", 41 | formula2 = "character", 42 | auto.adjust = "logical", 43 | offset = "logical", 44 | directed = "logical", 45 | bipartite = "logical", 46 | estimate = "character", 47 | bergm = "bergm", 48 | nvertices = "matrix", 49 | data = "list" 50 | ), 51 | validity = function(object) { 52 | if (!is.numeric(object@time.steps)) { 53 | stop("'time.steps' must be a numeric value of length 1.") 54 | } 55 | if (!"formula" %in% class(object@formula)) { 56 | stop("'formula' is not a 'formula' object.") 57 | } 58 | return(TRUE) 59 | } 60 | ) 61 | 62 | #' Constructor for \linkS4class{tbergm} objects 63 | #' 64 | #' Constructor for \linkS4class{tbergm} objects. 65 | #' 66 | #' Create an S4 \linkS4class{tbergm} object using this constructor function. 67 | #' 68 | #' @param estimate Estimate: \code{"bergm"} for Bayesian estimation. 69 | #' @param bergm The original \code{bergm} object as estimated by the 70 | #' \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package. 71 | #' @inheritParams createBtergm 72 | #' 73 | #' @author Philip Leifeld 74 | #' 75 | #' @family tergm-classes 76 | createTbergm <- function(time.steps, formula, formula2, auto.adjust, offset, 77 | directed, bipartite, estimate, bergm, nvertices, 78 | data) { 79 | new("tbergm", time.steps = time.steps, formula = formula, formula2 = formula2, 80 | auto.adjust = auto.adjust, offset = offset, directed = directed, 81 | bipartite = bipartite, estimate = estimate, bergm = bergm, 82 | nvertices = nvertices, data = data) 83 | } 84 | 85 | #' @describeIn tbergm-class Show the coefficients of a \code{tbergm} object. 86 | #' 87 | #' @param object A \code{tbergm} object. 88 | #' 89 | #' @export 90 | setMethod(f = "show", signature = "tbergm", definition = function(object) { 91 | summary(object@bergm) 92 | } 93 | ) 94 | 95 | #' @describeIn tbergm-class Return the number of observations saved in a 96 | #' \code{tbergm} object. 97 | #' 98 | #' @param object A \code{tbergm} object. 99 | #' 100 | #' @export 101 | setMethod(f = "nobs", signature = "tbergm", definition = function(object) { 102 | if (object@bipartite == TRUE) { 103 | n <- sum(1 - object@data$offsmat) 104 | } else { 105 | n <- sum(1 - object@data$offsmat) - nrow(object@data$offsmat) 106 | } 107 | t <- object@time.steps 108 | return(c("Number of time steps" = t, "Number of observations" = n)) 109 | } 110 | ) 111 | 112 | #' @describeIn tbergm-class Return the number of time steps saved in a 113 | #' \code{tbergm} object. 114 | #' 115 | #' @param object A \code{tbergm} object. 116 | #' 117 | #' @export 118 | timesteps.tbergm <- function(object) { 119 | return(object@time.steps) 120 | } 121 | 122 | #' @describeIn tbergm-class Summary of a fitted \code{tbergm} object. 123 | #' 124 | #' @param object A \code{tbergm} object. 125 | #' @param ... Further arguments for the \code{summary} function in the 126 | #' \pkg{Bergm} package. 127 | #' 128 | #' @export 129 | setMethod(f = "summary", signature = "tbergm", definition = function(object, 130 | ...) { 131 | summary(object@bergm, ...) 132 | } 133 | ) 134 | 135 | #' Estimate a TERGM using Bayesian estimation 136 | #' 137 | #' Estimate a TERGM using Bayesian estimation. 138 | #' 139 | #' The \code{tbergm} function computes TERGMs by Bayesian estimation via 140 | #' blockdiagonal matrices and structural zeros. It acts as a wrapper for the 141 | #' \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package. 142 | #' 143 | #' @param ... Further arguments to be handed over to the 144 | #' \code{\link[Bergm]{bergm}} function in the \pkg{Bergm} package. 145 | #' @inheritParams btergm 146 | #' 147 | #' @author Philip Leifeld 148 | #' 149 | #' @seealso \code{\link{btergm}} \code{\link{mtergm}} 150 | #' 151 | #' @references Caimo, Alberto and Nial Friel (2012): Bergm: Bayesian Exponential 152 | #' Random Graphs in R. \emph{Journal of Statistical Software} 61(2): 1-25. 153 | #' \doi{10.18637/jss.v061.i02}. 154 | #' 155 | #' @export 156 | tbergm <- function(formula, returndata = FALSE, verbose = TRUE, ...) { 157 | 158 | if (!requireNamespace("Bergm", quietly = TRUE)) { 159 | stop("tbergm requires the 'Bergm' package to be installed.\n", 160 | "To do this, enter 'install.packages(\"Bergm\")'.") 161 | } 162 | 163 | # call tergmprepare and integrate results as a child environment in the chain 164 | l <- tergmprepare(formula = formula, offset = FALSE, blockdiag = TRUE, 165 | verbose = verbose) 166 | for (i in 1:length(l$covnames)) { 167 | assign(l$covnames[i], l[[l$covnames[i]]]) 168 | } 169 | assign("offsmat", l$offsmat) 170 | form <- as.formula(l$form, env = environment()) 171 | 172 | # compile data for creating a tbergm object later; return if necessary 173 | dta <- list() 174 | for (i in 1:length(l$covnames)) { 175 | dta[[l$covnames[i]]] <- l[[l$covnames[i]]] 176 | } 177 | dta$offsmat <- l$offsmat 178 | if (returndata == TRUE) { 179 | message("Returning a list with data.") 180 | return(dta) 181 | } 182 | 183 | if (verbose == TRUE) { 184 | message("Estimating...") 185 | } 186 | b <- Bergm::bergm(form, offset.coef = -1000, ...) 187 | 188 | # get coefficients and other details 189 | mat <- as.matrix(l$networks) 190 | if (l$bipartite == TRUE) { 191 | dyads <- sum(1 - l$offsmat) 192 | } else { 193 | dyads <- sum(1 - l$offsmat) - nrow(mat) 194 | } 195 | 196 | # create tbergm object 197 | object <- createTbergm( 198 | time.steps = l$time.steps, 199 | formula = formula, 200 | formula2 = l$form, 201 | auto.adjust = l$auto.adjust, 202 | offset = TRUE, 203 | directed = l$directed, 204 | bipartite = l$bipartite, 205 | estimate = "bergm", 206 | bergm = b, 207 | nvertices = l$nvertices, 208 | data = dta 209 | ) 210 | 211 | if (verbose == TRUE) { 212 | message("Done. The Bergm object is stored in the @bergm slot of the fitted object. The data are in @data.") 213 | } 214 | 215 | return(object) 216 | } -------------------------------------------------------------------------------- /R/checkdegeneracy.R: -------------------------------------------------------------------------------- 1 | #' Check for degeneracy in fitted TERGMs 2 | #' 3 | #' Check for degeneracy in fitted TERGMs. 4 | #' 5 | #' The methods for the generic \code{degeneracy} function implement a degeneracy 6 | #' check for \code{btergm} and \code{mtergm} objects. For \code{btergm}, this 7 | #' works by comparing the global statistics of simulated networks to those of 8 | #' the observed networks at each observed time step. If the global statistics 9 | #' differ significantly, this is indicated by small p-values. If there are many 10 | #' significant results, this indicates degeneracy. For \code{mtergm}, the 11 | #' \code{mcmc.diagnostics} function from the \pkg{ergm} package is used. 12 | #' 13 | #' @param object A \code{btergm} or \code{mtergm} object, as estimated using the 14 | #' \code{btergm} or \code{mtergm} function. 15 | #' @param nsim The number of networks to be simulated at each time step. This 16 | #' number should be sufficiently large for a meaningful comparison. If 17 | #' possible, much more than 1,000 simulations. 18 | #' @param MCMC.burnin Internally, this package uses the simulation facilities of 19 | #' the \pkg{ergm} package to create new networks against which to compare the 20 | #' original network(s) for goodness-of-fit assessment. This argument sets the 21 | #' MCMC burnin to be passed over to the simulation command. The default value 22 | #' is \code{10000}. There is no general rule of thumb on the selection of this 23 | #' parameter, but if the results look suspicious (e.g., when the model fit is 24 | #' perfect), increasing this value may be helpful. 25 | #' @param MCMC.interval Internally, this package uses the simulation facilities 26 | #' of the \pkg{ergm} package to create new networks against which to compare 27 | #' the original network(s) for goodness-of-fit assessment. This argument sets 28 | #' the MCMC interval to be passed over to the simulation command. The default 29 | #' value is \code{1000}, which means that every 1000th simulation outcome from 30 | #' the MCMC sequence is used. There is no general rule of thumb on the 31 | #' selection of this parameter, but if the results look suspicious (e.g., when 32 | #' the model fit is perfect), increasing this value may be helpful. 33 | #' @param verbose Print details? 34 | #' @param x A \code{degeneracy} object created by the \code{checkdegeneracy} 35 | #' function. 36 | #' @param center If \code{TRUE}, print/plot the simulated minus the target 37 | #' statistics, with an expected value of 0 in a non-degenerate model. If 38 | #' \code{FALSE}, print/plot the distribution of simulated statistics and show 39 | #' the target statistic separately. 40 | #' @param t Time indices to include, e.g., \code{t = 2:4} for time steps 2 to 4. 41 | #' @param terms Indices of the model terms to include, e.g., \code{terms = 1:3} 42 | #' includes the first three statistics. 43 | #' @param vbar Show vertical bar for target statistic in histogram. 44 | #' @param main Main title of the plot. 45 | #' @param xlab Label on the x-axis. Defaults to the name of the statistic. 46 | #' @param target.col Color of the vertical bar for the target statistic. 47 | #' Defaults to red. 48 | #' @param target.lwd Line width of the vertical bar for the target statistic. 49 | #' Defaults to 3. 50 | #' @param ... Arbitrary further arguments for subroutines. 51 | #' 52 | #' @return A list with target statistics and simulations. 53 | #' 54 | #' @references 55 | #' Hanneke, Steve, Wenjie Fu and Eric P. Xing (2010): Discrete Temporal Models 56 | #' of Social Networks. \emph{Electronic Journal of Statistics} 4: 585--605. 57 | #' \doi{10.1214/09-EJS548}. 58 | #' 59 | #' Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 60 | #' Exponential Random Graph Models with btergm: Estimation and Bootstrap 61 | #' Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1-36. 62 | #' \doi{10.18637/jss.v083.i06}. 63 | #' 64 | #' @docType methods 65 | #' @aliases checkdegeneracy-methods 66 | #' @importFrom ergm simulate_formula control.simulate.formula mcmc.diagnostics 67 | #' @export 68 | setGeneric("checkdegeneracy", function(object, ...) 69 | standardGeneric("checkdegeneracy"), package = "btergm") 70 | 71 | #' @noRd 72 | checkdegeneracy.mtergm <- function(object, ...) { 73 | ergm::mcmc.diagnostics(object@ergm, ...) 74 | } 75 | 76 | #' @rdname checkdegeneracy 77 | setMethod("checkdegeneracy", signature = className("mtergm", "btergm"), 78 | definition = checkdegeneracy.mtergm) 79 | 80 | #' @noRd 81 | checkdegeneracy.btergm <- function(object, nsim = 1000, MCMC.interval = 1000, 82 | MCMC.burnin = 10000, verbose = FALSE) { 83 | if (nsim < 2) { 84 | stop("The 'nsim' argument must be greater than 1.") 85 | } 86 | 87 | # call tergmprepare and integrate results in local environment 88 | l <- tergmprepare(formula = getformula(object), offset = object@offset, 89 | verbose = verbose) 90 | for (i in 1:length(l$covnames)) { 91 | assign(l$covnames[i], l[[l$covnames[i]]]) 92 | } 93 | assign("offsmat", l$offsmat) 94 | form <- as.formula(l$form) 95 | offset <- object@offset 96 | target <- l$networks 97 | 98 | # extract coefficients from object 99 | if ("btergm" %in% class(object) && offset == TRUE) { 100 | coefs <- c(coef(object), -Inf) # -Inf for offset matrix 101 | } else { 102 | coefs <- coef(object) 103 | } 104 | 105 | # adjust formula at each step, and simulate networks 106 | sim <- list() 107 | target.stats <- list() 108 | degen <- list() 109 | for (index in 1:l$time.steps) { 110 | i <- index 111 | if (verbose == TRUE) { 112 | f.i <- gsub("\\[\\[i\\]\\]", paste0("[[", index, "]]"), 113 | paste(deparse(form), collapse = "")) 114 | f.i <- gsub("\\s+", " ", f.i) 115 | f.i <- gsub("^networks", l$lhs.original, f.i) 116 | message(paste("Simulating", nsim, 117 | "networks from the following formula:\n", f.i, "\n")) 118 | } 119 | target.stats[[index]] <- summary(form) 120 | degen[[index]] <- ergm::simulate_formula(form, 121 | nsim = nsim, 122 | coef = coefs, 123 | output = "stats", 124 | control = control.simulate.formula(MCMC.interval = MCMC.interval, 125 | MCMC.burnin = MCMC.burnin)) 126 | if (offset == TRUE || "mtergm" %in% class(object)) { 127 | degen[[i]] <- degen[[i]][, -ncol(degen[[i]])] # remove offset statistic 128 | } 129 | } 130 | 131 | if (verbose == TRUE) { 132 | message("Checking degeneracy...") 133 | } 134 | object <- list() 135 | object$target.stats <- target.stats 136 | object$sim <- degen 137 | class(object) <- "degeneracy" 138 | if (verbose == TRUE) { 139 | message("Done.") 140 | } 141 | return(object) 142 | } 143 | 144 | #' @rdname checkdegeneracy 145 | setMethod("checkdegeneracy", signature = className("btergm", "btergm"), 146 | definition = checkdegeneracy.btergm) 147 | 148 | 149 | #' @rdname checkdegeneracy 150 | print.degeneracy <- function(x, center = FALSE, t = 1:length(x$sim), 151 | terms = 1:length(x$target.stats[[1]]), ...) { 152 | for (i in t) { 153 | message(paste0("\nDegeneracy check for network ", i, ":")) 154 | if (center == TRUE) { 155 | sm <- coda::as.mcmc.list(coda::as.mcmc(x$sim[[i]])) 156 | sm <- statnet.common::sweep.mcmc.list(sm, x$target.stats[[i]], "-")[[1]] # diff 157 | q <- t(apply(as.matrix(sm), 2, function(x) { 158 | quantile(x, probs = c(0.025, 0.25, 0.5, 0.75, 0.975)) 159 | })) 160 | } else { 161 | q <- t(apply(x$sim[[i]], 2, function(x) { 162 | quantile(x, probs = c(0.025, 0.25, 0.5, 0.75, 0.975)) 163 | })) 164 | q <- cbind("obs" = x$target.stats[[i]], q) 165 | } 166 | rn <- rownames(q)[terms] 167 | cn <- colnames(q) 168 | q <- q[terms, ] 169 | if (!is.matrix(q)) { 170 | q <- matrix(q, nrow = 1) 171 | rownames(q) <- rn 172 | colnames(q) <- cn 173 | } 174 | printCoefmat(q) 175 | } 176 | } 177 | 178 | #' @rdname checkdegeneracy 179 | plot.degeneracy <- function(x, center = TRUE, t = 1:length(x$sim), 180 | terms = 1:length(x$target.stats[[1]]), vbar = TRUE, main = NULL, 181 | xlab = NULL, target.col = "red", target.lwd = 3, ...) { 182 | for (i in t) { 183 | for (j in terms) { 184 | if (is.null(main)) { 185 | m <- paste0(colnames(x$sim[[i]])[j], " at t = ", i) 186 | } else { 187 | m <- main 188 | } 189 | if (is.null(xlab)) { 190 | xl <- colnames(x$sim[[i]])[j] 191 | } else { 192 | xl <- xlab 193 | } 194 | if (center == FALSE) { 195 | hist(x$sim[[i]][, j], main = m, xlab = xl, ...) 196 | if (vbar == TRUE) { 197 | abline(v = x$target.stats[[i]][j], col = target.col, lwd = target.lwd) 198 | } 199 | } else { 200 | centered <- x$sim[[i]][, j] - x$target.stats[[i]][j] 201 | hist(centered, main = m, xlab = xl, ...) 202 | if (vbar == TRUE) { 203 | abline(v = 0, col = target.col, lwd = target.lwd) 204 | } 205 | } 206 | } 207 | } 208 | } -------------------------------------------------------------------------------- /R/timecov.R: -------------------------------------------------------------------------------- 1 | #' Temporal dependencies for TERGMs 2 | #' 3 | #' Network statistics that span multiple time points. 4 | #' 5 | #' In addition to the ERGM user terms that can be estimated within a single 6 | #' network (see \link[ergm]{ergm-terms}), the \pkg{btergm} package provides 7 | #' additional model terms that can be used within a formula. These additional 8 | #' statistics span multiple time periods and are therefore called "temporal 9 | #' dependencies." Examples include memory terms (i.e., positive autoregression, 10 | #' dyadic stability, edge innovation, or edge loss), delayed reciprocity or 11 | #' mutuality, and time covariates (i.e., functions of time or interactions with 12 | #' time): 13 | #' \describe{ 14 | #' \item{\code{delrecip(mutuality = FALSE, lag = 1)}}{The \code{delrecip} term 15 | #' checks for delayed reciprocity. For example, if node \code{j} is tied to 16 | #' node \code{i} at \code{t = 1}, does this lead to a reciprocation of that 17 | #' tie back from \code{i} to \code{j} at \code{t = 2}? If 18 | #' \code{mutuality = TRUE} is set, this extends not only to ties, but also 19 | #' non-ties. That is, if \code{i} is not tied to \code{j} at \code{t = 1}, 20 | #' will this lead to \code{j} not being tied to \code{i} at \code{t = 2}, in 21 | #' addition to positively reciprocal patterns over time? The \code{lag} 22 | #' argument controls the size of the temporal lag: with \code{lag = 1}, 23 | #' reciprocity over one consecutive time period is checked. Note that as 24 | #' \code{lag} increases, the number of time steps on the dependent variable 25 | #' decreases.} 26 | #' \item{\code{memory(type = "stability", lag = 1)}}{Memory terms control for 27 | #' the impact of a previous network on the current network. Four different 28 | #' types of memory terms are available: positive autoregression 29 | #' (\code{type = "autoregression"}) checks whether previous ties are carried 30 | #' over to the current network; dyadic stability (\code{type = "stability"}) 31 | #' checks whether both edges and non-edges are stable between the previous 32 | #' and the current network; edge loss (\code{type = "loss"}) checks whether 33 | #' ties in the previous network have been dissolved and no longer exist in 34 | #' the current network; and edge innovation (\code{type = "innovation"}) 35 | #' checks whether previously unconnected nodes have the tendency to become 36 | #' tied in the current network. The \code{lag} argument accepts integer 37 | #' values and controls whether the comparison is made with the previous 38 | #' network (\code{lag = 1}), the pre-previous network (\code{lag = 2}) etc. 39 | #' Note that as \code{lag} increases, the number of time steps on the 40 | #' dependent variable decreases.} 41 | #' \item{\code{timecov(x = NULL, minimum = 1, maximum = NULL, 42 | #' transform = function(t) t)}}{The \code{timecov} model term checks for 43 | #' linear or non-linear time trends with regard to edge formation. 44 | #' Optionally, this can be combined with a covariate to create an 45 | #' interaction effect between a dyadic covariate and time in order to test 46 | #' whether the importance of a covariate increases or decreases over time. 47 | #' In the default case, edges modeled as being linearly increasingly 48 | #' important over time. By tweaking the \code{transform} function, 49 | #' arbitrary functional forms of time can be tested. For example, 50 | #' \code{transform = sqrt} (for a geometrically decreasing time effect), 51 | #' \code{transform = function(x) x^2} (for a geometrically increasing time 52 | #' effect), \code{transform = function(t) t} (for a linear time trend) or 53 | #' polynomial functional forms (e.g., \code{0 + (1 * t) + (1 * t^2)}) can 54 | #' be used. For time steps below the \code{minimum} value and above the 55 | #' \code{maximum} value, the time covariate is set to 0. These arguments 56 | #' can be used to create step-wise, discrete effects, for example to use a 57 | #' value of 0 up to an external event and 1 from that event onwards in 58 | #' order to control for influences of external events.} 59 | #' } 60 | #' 61 | #' @references Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2017): 62 | #' Temporal Exponential Random Graph Models with btergm: Estimation and 63 | #' Bootstrap Confidence Intervals. \emph{Journal of Statistical Software} 64 | #' 83(6): 1-36. \doi{10.18637/jss.v083.i06}. 65 | #' 66 | #' @name tergm-terms 67 | #' @aliases tergm-terms memory timecov delrecip 68 | NULL 69 | 70 | #' Transform a covariate using a function of time 71 | #' 72 | #' Transform a covariate using a function of time. 73 | #' 74 | #' The \code{timecov} model term checks for linear or non-linear time trends 75 | #' with regard to edge formation. Optionally, this can be combined with a 76 | #' covariate to create an interaction effect between a dyadic covariate and time 77 | #' in order to test whether the importance of a covariate increases or decreases 78 | #' over time. The function can either be used in a formula with 79 | #' \code{\link{btergm}}, \code{\link{mtergm}}, or \code{\link{tbergm}}, or it 80 | #' can be executed directly for manual inclusion of the results as a covariate. 81 | #' 82 | #' @param covariate The list of networks or matrices for which to create a time 83 | #' covariate. This can be the list of networks on the left-hand side of the 84 | #' formula, in which case a time trend is created as a covariate list of 85 | #' matrices, or it can be a list of networks or matrices that is used as a 86 | #' dyadic covariate on the right-hand side of the formula, in which case an 87 | #' interaction effect between the time trend and the covariate is created. If 88 | #' used as a model term inside a formula, \code{covariate = NULL} is 89 | #' permitted, in which case the networks on the left-hand side will be used to 90 | #' form a time trend. 91 | #' @param minimum,maximum For time steps below the \code{minimum} value and 92 | #' above the \code{maximum} value, the time covariate is set to 0. These 93 | #' arguments can be used to create step-wise, discrete effects, for example to 94 | #' use a value of 0 up to an external event and 1 from that event onwards in 95 | #' order to control for influences of external events. 96 | #' @param transform In the default case, edges are modeled as being linearly 97 | #' increasingly important over time (i.e., a linear time trend). By tweaking 98 | #' the \code{transform} function, arbitrary functional forms of time can be 99 | #' tested. For example, \code{transform = sqrt} (for a geometrically 100 | #' decreasing time effect), \code{transform = function(x) x^2} (for a 101 | #' geometrically increasing time effect), \code{transform = function(t) t} 102 | #' (for a linear time trend) or polynomial functional forms (e.g., 103 | #' \code{transform = function(t) 0 + (1 * t) + (1 * t^2)}) can be used. 104 | #' @param onlytime If \code{TRUE}, return a time trend only. If \code{FALSE}, 105 | #' return an interaction between the time trend and the covariate. Note that 106 | #' the model term may need to be called twice or more inside a formula: one 107 | #' time to create the time trend main effect and one time for each 108 | #' interaction term; you also need to include the main effects for the 109 | #' covariates separately using \code{edgecov} or similar terms. 110 | #' 111 | #' @describeIn tergm-terms Time trends and temporal covariate interactions 112 | #' @aliases timecov 113 | #' 114 | #' @importFrom network is.network 115 | #' @export 116 | timecov <- function(covariate, minimum = 1, maximum = length(covariate), 117 | transform = function(t) 1 + (0 * t) + (0 * t^2), onlytime = FALSE) { 118 | if (!"list" %in% class(covariate)) { 119 | stop("'covariate' must be a list of matrices or network objects.") 120 | } 121 | for (i in 1:length(covariate)) { 122 | if (is.network(covariate[[i]])) { 123 | covariate[[i]] <- as.matrix(covariate[[i]]) 124 | } else if (!is.matrix(covariate[[i]])) { 125 | stop("'covariate' must be a list of matrices or network objects.") 126 | } 127 | } 128 | if (is.null(minimum) || is.null(maximum) || !is.numeric(minimum) || 129 | !is.numeric(maximum) || length(minimum) > 1 || length(maximum) > 1) { 130 | stop("'minimum' and 'maximum' must be single numeric values.") 131 | } 132 | if (is.null(transform)) { 133 | transform <- function(t) 1 + (0 * t) + (0 * t^2) 134 | } else if (!is.function(transform)) { 135 | stop("'transform' must be a function.") 136 | } 137 | l <- 1:length(covariate) 138 | values <- transform(l) # apply transformation of time 139 | if (is.null(values) || any(is.null(values)) || any(!is.finite(values)) || 140 | any(is.na(values)) || any(!is.numeric(values))) { 141 | stop("The 'transform' function produces non-numeric values.") 142 | } 143 | values <- values * (l >= minimum) * (l <= maximum) # interval dummy 144 | timecov <- list() 145 | for (i in 1:length(l)) { # create matrix at each time step 146 | if (onlytime == FALSE) { 147 | timecov[[i]] <- covariate[[i]] * matrix(values[i], nrow = 148 | nrow(covariate[[i]]), ncol = ncol(covariate[[i]])) 149 | } else { 150 | timecov[[i]] <- matrix(values[i], nrow = nrow(covariate[[i]]), 151 | ncol = ncol(covariate[[i]])) 152 | } 153 | } 154 | for (i in 1:length(timecov)) { 155 | rownames(timecov[[i]]) <- rownames(covariate[[i]]) 156 | colnames(timecov[[i]]) <- colnames(covariate[[i]]) 157 | } 158 | return(timecov) 159 | } 160 | -------------------------------------------------------------------------------- /man/chemnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/chemnet.R 3 | \docType{data} 4 | \name{chemnet} 5 | \alias{chemnet} 6 | \alias{pol} 7 | \alias{scito} 8 | \alias{scifrom} 9 | \alias{infrep} 10 | \alias{committee} 11 | \alias{types} 12 | \alias{intpos} 13 | \title{German Toxic Chemicals Policy Network in the 1980s (Volker Schneider)} 14 | \format{ 15 | \describe{ 16 | \item{\code{pol}}{is a directed 30 x 30 adjancency matrix indicating which 17 | row actor sends political/strategic information to which column actor. 18 | \code{1} indicates an information exchange tie, and \code{0} indicates the 19 | absence of a network tie.} 20 | 21 | \item{\code{scito}}{is a directed 30 x 30 adjacency matrix indicating which 22 | row actor sends technical/scientific information to which column actor. 23 | \code{1} indicates an information exchange tie, and \code{0} indicates the 24 | absence of a network tie. In contrast to political/strategic information 25 | exchange, two separate survey questions were asked about 26 | technical/scientific information exchange: sending information, and 27 | receiving information. The two matrices contain the same relation but one 28 | time from the sender's perspective and one time from the receiver's 29 | perspective. By combining the two matrices, one can create a "confirmed" 30 | technical/scientific information exchange relation. The \code{scito} matrix 31 | contains ties from the sender's perspective.} 32 | 33 | \item{\code{scifrom}}{is a directed 30 x 30 adjacency matrix indicating 34 | which row actor receives technical/scientific information from which column 35 | actor. \code{1} indicates an information exchange tie, and \code{0} 36 | indicates the absence of a network tie. In contrast to political/strategic 37 | information exchange, two separate survey questions were asked about 38 | technical/scientific information exchange: sending information, and 39 | receiving information. The two matrices contain the same relation but one 40 | time from the sender's perspective and one time from the receiver's 41 | perspective. By combining the two matrices, one can create a "confirmed" 42 | technical/scientific information exchange relation. The \code{scifrom} 43 | matrix contains ties from the receiver's perspective.} 44 | 45 | \item{\code{infrep}}{is a directed 30 x 30 adjancency matrix indicating 46 | which row actor deems which column actor "particularly influential". 47 | \code{1} indicates such a tie, and \code{0} indicates the absence of an 48 | influence attribution tie.} 49 | 50 | \item{\code{committee}}{is a 30 x 20 two-mode (bipartite) network matrix 51 | indicating which row actor is a member of which policy committee/forum (as 52 | indicated by the column labels). \code{1} indicates a membership tie, and 53 | \code{0} indicates non-membership.} 54 | 55 | \item{\code{types}}{is a one-column data.frame where the \code{type} 56 | variable contains the actor type of each node. The following values are 57 | possible:} 58 | \itemize{ 59 | \item \code{gov} (government actor, e.g., a federal ministry) 60 | \item \code{ig} (interest group) 61 | \item \code{io} (international organization) 62 | \item \code{par} (political party) 63 | \item \code{sci} (scientific organization) 64 | } 65 | 66 | \item{\code{intpos}}{is a 30 x 6 matrix containing the interest positions of 67 | the 30 political actors on the six most salient political issues related to a 68 | pending new chemicals law. \code{-1} indicates a negative stance, i.e., the 69 | actor rejects the proposal; \code{1} indicates a positive stance, i.e., the 70 | actor supports the proposal; and \code{0} indicates a neutral or absent 71 | opinion.} 72 | } 73 | } 74 | \source{ 75 | The data were collected using paper-based questionnaires. The 76 | questionnaires were administered in personal interviews (PAPI). Further 77 | information, including the actual survey, data on additional actors, the 78 | full names of the policy committees/forums, and the full list of 79 | unabbreviated actor names can be found online at \doi{10.7910/DVN/ONDFVJ} in 80 | the replication archive of Leifeld and Schneider (2012). 81 | 82 | \itemize{ 83 | \item Replication archive: \doi{10.7910/DVN/ONDFVJ} 84 | \item AJPS publication: \doi{10.1111/j.1540-5907.2011.00580.x} 85 | } 86 | 87 | The dataset is publicly available. Questions about the data or the original 88 | study should be directed to Volker Schneider 89 | , the author of the original study and 90 | person who collected the data. 91 | } 92 | \description{ 93 | German Toxic Chemicals Policy Network in the 1980s (Volker Schneider). 94 | } 95 | \details{ 96 | The chemnet dataset contains network and attribute data and for the 30 most 97 | influential political actors with regard to toxic chemicals regulation in 98 | Germany in 1983/1984. While the original dataset contains up to 47 actors, 99 | this dataset contains the "complete influence core" of mutually relevant 100 | actors. The data are cross-sectional. There are no missing data; the 101 | response rate was 100 percent. Volker Schneider (University of Konstanz) 102 | collected this dataset for his dissertation (Schneider 1988). The dataset 103 | was later re-used for a journal publication on information exchange in 104 | policy networks (Leifeld and Schneider 2012). 105 | 106 | The chemnet dataset contains network relations on political/strategic and 107 | technical/scientific information exchange, influence attribution, and 108 | membership in policy committees/forums, as well as nodal attributes on the 109 | actor type and opinions about the six most salient issues related to the 110 | political process that was leading to a new chemicals law at the time being. 111 | } 112 | \examples{ 113 | \dontrun{ 114 | # Replication code for Leifeld and Schneider (2012), AJPS. 115 | # Note that the estimates can only be reproduced approximately 116 | # due to internal changes in the statnet package. 117 | 118 | # preparatory steps 119 | library("network") 120 | library("sna") 121 | library("ergm") 122 | library("btergm") 123 | library("texreg") 124 | seed <- 12345 125 | set.seed(seed) 126 | data("chemnet") 127 | 128 | # create confirmed network relation 129 | sci <- scito * t(scifrom) # equation 1 in the AJPS paper 130 | prefsim <- dist(intpos, method = "euclidean") # equation 2 131 | prefsim <- max(prefsim) - prefsim # equation 3 132 | prefsim <- as.matrix(prefsim) 133 | committee <- committee \%*\% t(committee) # equation 4 134 | diag(committee) <- 0 # the diagonal has no meaning 135 | types <- types[, 1] # convert to vector 136 | 137 | # create network objects and store attributes 138 | nw.pol <- network(pol) # political/stratgic information exchange 139 | set.vertex.attribute(nw.pol, "orgtype", types) 140 | set.vertex.attribute(nw.pol, "betweenness", 141 | betweenness(nw.pol)) # centrality 142 | 143 | nw.sci <- network(sci) # technical/scientific information exchange 144 | set.vertex.attribute(nw.sci, "orgtype", types) 145 | set.vertex.attribute(nw.sci, "betweenness", 146 | betweenness(nw.sci)) # centrality 147 | 148 | # ERGM: model 1 in the AJPS paper; only preference similarity 149 | model1 <- ergm(nw.pol ~ edges + edgecov(prefsim), 150 | control = control.ergm(seed = seed)) 151 | summary(model1) 152 | 153 | # ERGM: model 2 in the AJPS paper; complete model 154 | model2 <- ergm(nw.pol ~ 155 | edges + 156 | edgecov(prefsim) + 157 | mutual + 158 | nodemix("orgtype", base = -7) + 159 | nodeifactor("orgtype", base = -1) + 160 | nodeofactor("orgtype", base = -5) + 161 | edgecov(committee) + 162 | edgecov(nw.sci) + 163 | edgecov(infrep) + 164 | gwesp(0.1, fixed = TRUE) + 165 | gwdsp(0.1, fixed = TRUE), 166 | control = control.ergm(seed = seed) 167 | ) 168 | summary(model2) 169 | 170 | # ERGM: model 3 in the AJPS paper; only preference similarity 171 | model3 <- ergm(nw.sci ~ edges + edgecov(prefsim), 172 | control = control.ergm(seed = seed)) 173 | summary(model3) 174 | 175 | # ERGM: model 4 in the AJPS paper; complete model 176 | model4 <- ergm(nw.sci ~ 177 | edges + 178 | edgecov(prefsim) + 179 | mutual + 180 | nodemix("orgtype", base = -7) + 181 | nodeifactor("orgtype", base = -1) + 182 | nodeofactor("orgtype", base = -5) + 183 | edgecov(committee) + 184 | edgecov(nw.pol) + 185 | edgecov(infrep) + 186 | gwesp(0.1, fixed = TRUE) + 187 | gwdsp(0.1, fixed = TRUE), 188 | control = control.ergm(seed = seed) 189 | ) 190 | summary(model4) 191 | 192 | # regression table using the texreg package 193 | screenreg(list(model1, model2, model3, model4)) 194 | 195 | # goodness of fit using the btergm package 196 | gof2 <- gof(model2, roc = FALSE, pr = FALSE) 197 | gof2 # print gof output 198 | plot(gof2) # visual inspection of GOF 199 | 200 | gof4 <- gof(model4, roc = FALSE, pr = FALSE) 201 | gof4 202 | plot(gof4) 203 | 204 | # MCMC diagnostics 205 | pdf("diagnostics2.pdf") 206 | mcmc.diagnostics(model2) 207 | dev.off() 208 | 209 | pdf("diagnostics4.pdf") 210 | mcmc.diagnostics(model4) 211 | dev.off() 212 | } 213 | } 214 | \references{ 215 | Leifeld, Philip and Volker Schneider (2012): Information Exchange in Policy 216 | Networks. \emph{American Journal of Political Science} 53(3): 731--744. 217 | \doi{10.1111/j.1540-5907.2011.00580.x}. 218 | 219 | Schneider, Volker (1988): \emph{Politiknetzwerke der Chemikalienkontrolle. 220 | Eine Analyse einer transnationalen Politikentwicklung}. Walter de Gruyter: 221 | Berlin/New York. 222 | 223 | Schneider, Volker and Philip Leifeld (2009): Ueberzeugungssysteme, 224 | Diskursnetzwerke und politische Kommunikation: Ein zweiter Blick auf die 225 | deutsche Chemikalienkontrolle der 1980er Jahre. In: Volker Schneider, Frank 226 | Janning, Philip Leifeld and Thomas Malang (editors): \emph{Politiknetzwerke. 227 | Modelle, Anwendungen und Visualisierungen}. Pages 139--158. Wiesbaden: VS 228 | Verlag fuer Sozialwissenschaften. 229 | } 230 | \keyword{datasets} 231 | -------------------------------------------------------------------------------- /R/chemnet.R: -------------------------------------------------------------------------------- 1 | #' German Toxic Chemicals Policy Network in the 1980s (Volker Schneider) 2 | #' 3 | #' German Toxic Chemicals Policy Network in the 1980s (Volker Schneider). 4 | #' 5 | #' The chemnet dataset contains network and attribute data and for the 30 most 6 | #' influential political actors with regard to toxic chemicals regulation in 7 | #' Germany in 1983/1984. While the original dataset contains up to 47 actors, 8 | #' this dataset contains the "complete influence core" of mutually relevant 9 | #' actors. The data are cross-sectional. There are no missing data; the 10 | #' response rate was 100 percent. Volker Schneider (University of Konstanz) 11 | #' collected this dataset for his dissertation (Schneider 1988). The dataset 12 | #' was later re-used for a journal publication on information exchange in 13 | #' policy networks (Leifeld and Schneider 2012). 14 | #' 15 | #' The chemnet dataset contains network relations on political/strategic and 16 | #' technical/scientific information exchange, influence attribution, and 17 | #' membership in policy committees/forums, as well as nodal attributes on the 18 | #' actor type and opinions about the six most salient issues related to the 19 | #' political process that was leading to a new chemicals law at the time being. 20 | #' 21 | #' @name chemnet 22 | #' 23 | #' @aliases chemnet pol scito scifrom infrep committee types intpos 24 | #' 25 | #' @docType data 26 | #' 27 | #' @format 28 | #' \describe{ 29 | #' \item{\code{pol}}{is a directed 30 x 30 adjancency matrix indicating which 30 | #' row actor sends political/strategic information to which column actor. 31 | #' \code{1} indicates an information exchange tie, and \code{0} indicates the 32 | #' absence of a network tie.} 33 | #' 34 | #' \item{\code{scito}}{is a directed 30 x 30 adjacency matrix indicating which 35 | #' row actor sends technical/scientific information to which column actor. 36 | #' \code{1} indicates an information exchange tie, and \code{0} indicates the 37 | #' absence of a network tie. In contrast to political/strategic information 38 | #' exchange, two separate survey questions were asked about 39 | #' technical/scientific information exchange: sending information, and 40 | #' receiving information. The two matrices contain the same relation but one 41 | #' time from the sender's perspective and one time from the receiver's 42 | #' perspective. By combining the two matrices, one can create a "confirmed" 43 | #' technical/scientific information exchange relation. The \code{scito} matrix 44 | #' contains ties from the sender's perspective.} 45 | #' 46 | #' \item{\code{scifrom}}{is a directed 30 x 30 adjacency matrix indicating 47 | #' which row actor receives technical/scientific information from which column 48 | #' actor. \code{1} indicates an information exchange tie, and \code{0} 49 | #' indicates the absence of a network tie. In contrast to political/strategic 50 | #' information exchange, two separate survey questions were asked about 51 | #' technical/scientific information exchange: sending information, and 52 | #' receiving information. The two matrices contain the same relation but one 53 | #' time from the sender's perspective and one time from the receiver's 54 | #' perspective. By combining the two matrices, one can create a "confirmed" 55 | #' technical/scientific information exchange relation. The \code{scifrom} 56 | #' matrix contains ties from the receiver's perspective.} 57 | #' 58 | #' \item{\code{infrep}}{is a directed 30 x 30 adjancency matrix indicating 59 | #' which row actor deems which column actor "particularly influential". 60 | #' \code{1} indicates such a tie, and \code{0} indicates the absence of an 61 | #' influence attribution tie.} 62 | #' 63 | #' \item{\code{committee}}{is a 30 x 20 two-mode (bipartite) network matrix 64 | #' indicating which row actor is a member of which policy committee/forum (as 65 | #' indicated by the column labels). \code{1} indicates a membership tie, and 66 | #' \code{0} indicates non-membership.} 67 | #' 68 | #' \item{\code{types}}{is a one-column data.frame where the \code{type} 69 | #' variable contains the actor type of each node. The following values are 70 | #' possible:} 71 | #' \itemize{ 72 | #' \item \code{gov} (government actor, e.g., a federal ministry) 73 | #' \item \code{ig} (interest group) 74 | #' \item \code{io} (international organization) 75 | #' \item \code{par} (political party) 76 | #' \item \code{sci} (scientific organization) 77 | #' } 78 | #' 79 | #' \item{\code{intpos}}{is a 30 x 6 matrix containing the interest positions of 80 | #' the 30 political actors on the six most salient political issues related to a 81 | #' pending new chemicals law. \code{-1} indicates a negative stance, i.e., the 82 | #' actor rejects the proposal; \code{1} indicates a positive stance, i.e., the 83 | #' actor supports the proposal; and \code{0} indicates a neutral or absent 84 | #' opinion.} 85 | #' } 86 | #' 87 | #' @references 88 | #' Leifeld, Philip and Volker Schneider (2012): Information Exchange in Policy 89 | #' Networks. \emph{American Journal of Political Science} 53(3): 731--744. 90 | #' \doi{10.1111/j.1540-5907.2011.00580.x}. 91 | #' 92 | #' Schneider, Volker (1988): \emph{Politiknetzwerke der Chemikalienkontrolle. 93 | #' Eine Analyse einer transnationalen Politikentwicklung}. Walter de Gruyter: 94 | #' Berlin/New York. 95 | #' 96 | #' Schneider, Volker and Philip Leifeld (2009): Ueberzeugungssysteme, 97 | #' Diskursnetzwerke und politische Kommunikation: Ein zweiter Blick auf die 98 | #' deutsche Chemikalienkontrolle der 1980er Jahre. In: Volker Schneider, Frank 99 | #' Janning, Philip Leifeld and Thomas Malang (editors): \emph{Politiknetzwerke. 100 | #' Modelle, Anwendungen und Visualisierungen}. Pages 139--158. Wiesbaden: VS 101 | #' Verlag fuer Sozialwissenschaften. 102 | #' 103 | #' @source The data were collected using paper-based questionnaires. The 104 | #' questionnaires were administered in personal interviews (PAPI). Further 105 | #' information, including the actual survey, data on additional actors, the 106 | #' full names of the policy committees/forums, and the full list of 107 | #' unabbreviated actor names can be found online at \doi{10.7910/DVN/ONDFVJ} in 108 | #' the replication archive of Leifeld and Schneider (2012). 109 | #' 110 | #' \itemize{ 111 | #' \item Replication archive: \doi{10.7910/DVN/ONDFVJ} 112 | #' \item AJPS publication: \doi{10.1111/j.1540-5907.2011.00580.x} 113 | #' } 114 | #' 115 | #' The dataset is publicly available. Questions about the data or the original 116 | #' study should be directed to Volker Schneider 117 | #' , the author of the original study and 118 | #' person who collected the data. 119 | #' 120 | #' @keywords datasets 121 | #' 122 | #' @examples 123 | #' \dontrun{ 124 | #' # Replication code for Leifeld and Schneider (2012), AJPS. 125 | #' # Note that the estimates can only be reproduced approximately 126 | #' # due to internal changes in the statnet package. 127 | #' 128 | #' # preparatory steps 129 | #' library("network") 130 | #' library("sna") 131 | #' library("ergm") 132 | #' library("btergm") 133 | #' library("texreg") 134 | #' seed <- 12345 135 | #' set.seed(seed) 136 | #' data("chemnet") 137 | #' 138 | #' # create confirmed network relation 139 | #' sci <- scito * t(scifrom) # equation 1 in the AJPS paper 140 | #' prefsim <- dist(intpos, method = "euclidean") # equation 2 141 | #' prefsim <- max(prefsim) - prefsim # equation 3 142 | #' prefsim <- as.matrix(prefsim) 143 | #' committee <- committee %*% t(committee) # equation 4 144 | #' diag(committee) <- 0 # the diagonal has no meaning 145 | #' types <- types[, 1] # convert to vector 146 | #' 147 | #' # create network objects and store attributes 148 | #' nw.pol <- network(pol) # political/stratgic information exchange 149 | #' set.vertex.attribute(nw.pol, "orgtype", types) 150 | #' set.vertex.attribute(nw.pol, "betweenness", 151 | #' betweenness(nw.pol)) # centrality 152 | #' 153 | #' nw.sci <- network(sci) # technical/scientific information exchange 154 | #' set.vertex.attribute(nw.sci, "orgtype", types) 155 | #' set.vertex.attribute(nw.sci, "betweenness", 156 | #' betweenness(nw.sci)) # centrality 157 | #' 158 | #' # ERGM: model 1 in the AJPS paper; only preference similarity 159 | #' model1 <- ergm(nw.pol ~ edges + edgecov(prefsim), 160 | #' control = control.ergm(seed = seed)) 161 | #' summary(model1) 162 | #' 163 | #' # ERGM: model 2 in the AJPS paper; complete model 164 | #' model2 <- ergm(nw.pol ~ 165 | #' edges + 166 | #' edgecov(prefsim) + 167 | #' mutual + 168 | #' nodemix("orgtype", base = -7) + 169 | #' nodeifactor("orgtype", base = -1) + 170 | #' nodeofactor("orgtype", base = -5) + 171 | #' edgecov(committee) + 172 | #' edgecov(nw.sci) + 173 | #' edgecov(infrep) + 174 | #' gwesp(0.1, fixed = TRUE) + 175 | #' gwdsp(0.1, fixed = TRUE), 176 | #' control = control.ergm(seed = seed) 177 | #' ) 178 | #' summary(model2) 179 | #' 180 | #' # ERGM: model 3 in the AJPS paper; only preference similarity 181 | #' model3 <- ergm(nw.sci ~ edges + edgecov(prefsim), 182 | #' control = control.ergm(seed = seed)) 183 | #' summary(model3) 184 | #' 185 | #' # ERGM: model 4 in the AJPS paper; complete model 186 | #' model4 <- ergm(nw.sci ~ 187 | #' edges + 188 | #' edgecov(prefsim) + 189 | #' mutual + 190 | #' nodemix("orgtype", base = -7) + 191 | #' nodeifactor("orgtype", base = -1) + 192 | #' nodeofactor("orgtype", base = -5) + 193 | #' edgecov(committee) + 194 | #' edgecov(nw.pol) + 195 | #' edgecov(infrep) + 196 | #' gwesp(0.1, fixed = TRUE) + 197 | #' gwdsp(0.1, fixed = TRUE), 198 | #' control = control.ergm(seed = seed) 199 | #' ) 200 | #' summary(model4) 201 | #' 202 | #' # regression table using the texreg package 203 | #' screenreg(list(model1, model2, model3, model4)) 204 | #' 205 | #' # goodness of fit using the btergm package 206 | #' gof2 <- gof(model2, roc = FALSE, pr = FALSE) 207 | #' gof2 # print gof output 208 | #' plot(gof2) # visual inspection of GOF 209 | #' 210 | #' gof4 <- gof(model4, roc = FALSE, pr = FALSE) 211 | #' gof4 212 | #' plot(gof4) 213 | #' 214 | #' # MCMC diagnostics 215 | #' pdf("diagnostics2.pdf") 216 | #' mcmc.diagnostics(model2) 217 | #' dev.off() 218 | #' 219 | #' pdf("diagnostics4.pdf") 220 | #' mcmc.diagnostics(model4) 221 | #' dev.off() 222 | #' } 223 | NULL -------------------------------------------------------------------------------- /tests/testthat/test-btergm.R: -------------------------------------------------------------------------------- 1 | context("test btergm estimation") 2 | 3 | set.seed(12345) 4 | networks <- list() 5 | for(i in 1:10) { # create 10 random networks with 10 actors 6 | mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10) 7 | diag(mat) <- 0 # loops are excluded 8 | nw <- network::network(mat) # create network object 9 | networks[[i]] <- nw # add network to the list 10 | } 11 | 12 | covariates <- list() 13 | for (i in 1:10) { # create 10 matrices as covariate 14 | mat <- matrix(rnorm(100), nrow = 10, ncol = 10) 15 | covariates[[i]] <- mat # add matrix to the list 16 | } 17 | 18 | test_that("btergm estimation works", { 19 | set.seed(12345) 20 | fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE)) 21 | expect_equal(round(unname(coef(fit)), 4), c(-1.1707, 0.0543, 0.0045)) 22 | expect_equal(names(coef(fit)), c("edges", "istar2", "edgecov.covariates[[i]]")) 23 | expect_equal(class(fit@boot), "boot") 24 | expect_equal(fit@boot$R, 100) 25 | expect_equal(fit@R, 100) 26 | expect_equal(fit@nobs, 900) 27 | expect_equal(fit@time.steps, 10) 28 | expect_equal(class(fit@formula), "formula") 29 | expect_equal(class(fit@formula2), "character") 30 | expect_equal(fit@formula, as.formula("networks ~ edges + istar(2) + edgecov(covariates)")) 31 | expect_equal(fit@formula2, "networks[[i]] ~ edges + istar(2) + edgecov(covariates[[i]])") 32 | expect_equal(length(fit@response), 900) 33 | expect_equal(is.numeric(fit@response), TRUE) 34 | expect_equal(class(fit@effects), "data.frame") 35 | expect_equal(dim(fit@effects), c(900, 3)) 36 | expect_equal(unique(fit@effects$edges), 1) 37 | expect_equal(median(fit@effects$istar2), 2) 38 | expect_equal(round(mean(fit@effects$`edgecov.covariates[[i]]`), 4), -0.0144) 39 | expect_equal(unique(fit@weights), 1) 40 | expect_equal(fit@auto.adjust, FALSE) 41 | expect_equal(fit@offset, FALSE) 42 | expect_equal(fit@directed, TRUE) 43 | expect_equal(fit@bipartite, FALSE) 44 | expect_equal(unname(rowSums(fit@nvertices)), c(100, 100)) 45 | expect_equal(round(confint(fit)[1, 3], 1), -1.4) 46 | expect_equal(round(confint(fit)[1, 4], 1), -0.8) 47 | expect_equal(round(confint(fit)[2, 3], 0), 0) 48 | expect_equal(round(confint(fit)[2, 4], 1), 0.1) 49 | expect_equal(round(confint(fit)[3, 3], 1), -0.1) 50 | expect_equal(round(confint(fit)[3, 4], 1), 0.1) 51 | expect_true(all(round(confint(fit)[, 1] - confint(fit)[, 2], 1) == 0)) 52 | }) 53 | 54 | test_that("fastglm works like speedglm", { 55 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 56 | 57 | set.seed(12345) 58 | fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE)) 59 | set.seed(12345) 60 | fit2 <- btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, usefastglm = TRUE, verbose = FALSE) 61 | expect_equal(all(round(confint(fit), 4) == round(confint(fit2), 4)), TRUE) 62 | }) 63 | 64 | test_that("offset argument in btergm works without composition change", { 65 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 66 | set.seed(12345) 67 | fit1 <- btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, offset = FALSE, usefastglm = TRUE, verbose = FALSE) 68 | set.seed(12345) 69 | fit2 <- btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, offset = TRUE, usefastglm = TRUE, verbose = FALSE) 70 | expect_equal(confint(fit1), confint(fit2)) 71 | }) 72 | 73 | test_that("offset argument in btergm works with composition change", { 74 | skip_on_cran() 75 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 76 | 77 | # example taken from 2018 JSS article 78 | require("sna") 79 | data("knecht") 80 | 81 | # step 1: make sure the network matrices have node labels 82 | for (i in 1:length(friendship)) { 83 | rownames(friendship[[i]]) <- 1:nrow(friendship[[i]]) 84 | colnames(friendship[[i]]) <- 1:ncol(friendship[[i]]) 85 | } 86 | rownames(primary) <- rownames(friendship[[1]]) 87 | colnames(primary) <- colnames(friendship[[1]]) 88 | sex <- demographics$sex 89 | names(sex) <- 1:length(sex) 90 | 91 | # step 2: imputation of NAs and removal of absent nodes: 92 | suppressMessages(friendship <- handleMissings(friendship, na = 10, method = "remove")) 93 | suppressMessages(friendship <- handleMissings(friendship, na = NA, method = "fillmode")) 94 | 95 | # step 3: add nodal covariates to the networks 96 | for (i in 1:length(friendship)) { 97 | s <- adjust(sex, friendship[[i]]) 98 | friendship[[i]] <- network(friendship[[i]]) 99 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], "sex", s) 100 | idegsqrt <- sqrt(degree(friendship[[i]], cmode = "indegree")) 101 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], 102 | "idegsqrt", idegsqrt) 103 | odegsqrt <- sqrt(degree(friendship[[i]], cmode = "outdegree")) 104 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], 105 | "odegsqrt", odegsqrt) 106 | } 107 | expect_equal(unname(sapply(friendship, network.size)), c(26, 26, 25, 25)) 108 | 109 | # step 4: estimate models 110 | set.seed(12345) 111 | m1 <- btergm(friendship ~ edges + mutual + ttriple + 112 | transitiveties + ctriple + nodeicov("idegsqrt") + 113 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 114 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 115 | edgecov(primary) + delrecip + memory(type = "stability"), 116 | R = 100, usefastglm = TRUE, offset = TRUE, verbose = FALSE) 117 | set.seed(12345) 118 | m2 <- btergm(friendship ~ edges + mutual + ttriple + 119 | transitiveties + ctriple + nodeicov("idegsqrt") + 120 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 121 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 122 | edgecov(primary) + delrecip + memory(type = "stability"), 123 | R = 100, usefastglm = TRUE, offset = FALSE, verbose = FALSE) 124 | 125 | # test results 126 | expect_equal(dim(confint(m1)), c(14, 4)) 127 | expect_equal(dim(confint(m2)), c(14, 4)) 128 | expect_equal(all(confint(m1)[, 4] - confint(m1)[, 3] > 0), TRUE) 129 | expect_equal(all(confint(m2)[, 4] - confint(m2)[, 3] > 0), TRUE) 130 | expect_equal(m1@offset, TRUE) 131 | expect_equal(m2@offset, FALSE) 132 | expect_equal(sapply(m1@data$offsmat, sum), c(0, 51, 51)) 133 | expect_equal(sapply(m2@data$offsmat, sum), c(0, 0, 0)) 134 | expect_equal(unname(nobs(m1)), c(3, 1850, 100)) 135 | expect_equal(nobs(m1), nobs(m2)) 136 | }) 137 | 138 | test_that("mtergm estimation works", { 139 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 140 | 141 | set.seed(12345) 142 | fit1 <- btergm(networks ~ edges + istar(2) + edgecov(covariates), usefastglm = TRUE, verbose = FALSE) 143 | set.seed(12345) 144 | fit2 <- mtergm(networks ~ edges + istar(2) + edgecov(covariates), verbose = FALSE) 145 | expect_equivalent(coef(fit1) - coef(fit2), rep(0, 3), tolerance = 0.05) 146 | expect_equivalent(coef(fit2), c(-1.17, 0.06, 0.00), tolerance = 0.05) 147 | expect_equivalent(fit2@se, c(0.193, 0.079, 0.074), tolerance = 0.05) 148 | expect_equal(class(fit2)[1], "mtergm") 149 | expect_equal(class(fit2@ergm), "ergm") 150 | }) 151 | 152 | test_that("simulation of new networks works", { 153 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 154 | 155 | # for btergm 156 | fit1 <- btergm(networks ~ edges + istar(2) + edgecov(covariates), usefastglm = TRUE, verbose = FALSE) 157 | sim1 <- simulate(fit1, 5, verbose = FALSE) 158 | expect_equal(length(sim1), 5) 159 | expect_equal(class(sim1[[1]]), "network") 160 | 161 | # with an index 162 | sim1b <- simulate(fit1, index = 3, verbose = FALSE) 163 | expect_equal(class(sim1b), "network") 164 | 165 | # for mtergm 166 | fit2 <- mtergm(networks ~ edges + istar(2) + edgecov(covariates), verbose = FALSE) 167 | sim2 <- simulate(fit2, 5, verbose = FALSE) 168 | expect_equal(length(sim2), 5) 169 | expect_equal(class(sim2[[1]]), "network") 170 | }) 171 | 172 | test_that("tbergm estimation works", { 173 | skip_on_cran() 174 | skip_if_not_installed("Bergm", minimum_version = "5.0.2") 175 | set.seed(12345) 176 | fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE)) 177 | suppressMessages(suppressWarnings(fit_b <- tbergm(networks ~ edges + istar(2) + edgecov(covariates), verbose = FALSE))) 178 | expect_s3_class(fit_b@bergm, "bergm") 179 | expect_length(fit_b@bergm$ess, 4) 180 | expect_length(fit_b@data, 3) 181 | expect_equal(dim(as.matrix(fit_b@bergm$Theta)), c(8000, 4)) 182 | expect_silent(g <- btergm::gof(fit_b, verbose = FALSE)) 183 | expect_s3_class(g, "gof") 184 | expect_length(g, 7) 185 | expect_equivalent(nobs(fit_b), c(10, 900)) 186 | expect_equivalent(coef(fit) - apply(fit_b@bergm$Theta, 2, mean)[-4], rep(0, 3), tolerance = 0.2) 187 | }) 188 | 189 | test_that("bipartite network objects work, including composition change, with and without offset", { 190 | skip_on_cran() 191 | skip_if_not_installed("fastglm", minimum_version = "0.0.1") 192 | set.seed(12345) 193 | nw_matrix <- lapply(1:20, function(x) { 194 | mat <- matrix(rbinom(200, 1, 0.2), nrow = 20) 195 | rownames(mat) <- letters[1:20] 196 | colnames(mat) <- letters[1:10] 197 | mat <- mat[sample(1:20, 18), sample(1:10, 9)] 198 | }) 199 | nw_network <- lapply(nw_matrix, function(x) { 200 | network::network(x, bipartite = TRUE, directed = FALSE) 201 | }) 202 | expect_warning(model1 <- btergm(nw_matrix ~ edges + threetrail + kstar(2) + memory("autoregression"), R = 50, offset = FALSE, verbose = FALSE, usefastglm = TRUE), regexp = NA) 203 | expect_warning(model2 <- btergm(nw_network ~ edges + threetrail + kstar(2) + memory("autoregression"), R = 50, offset = FALSE, verbose = FALSE, usefastglm = TRUE), regexp = NA) 204 | expect_warning(model3 <- btergm(nw_matrix ~ edges + threetrail + kstar(2) + memory("autoregression"), R = 50, offset = TRUE, verbose = FALSE, usefastglm = TRUE), regexp = NA) 205 | expect_warning(model4 <- btergm(nw_network ~ edges + threetrail + kstar(2) + memory("autoregression"), R = 50, offset = TRUE, verbose = FALSE, usefastglm = TRUE), regexp = NA) 206 | expect_length(model1@coef, 4) 207 | expect_length(model2@coef, 4) 208 | expect_length(model3@coef, 4) 209 | expect_length(model4@coef, 4) 210 | expect_equal(dim(confint(model1)), c(4, 4)) 211 | expect_equal(dim(confint(model2)), c(4, 4)) 212 | expect_equal(dim(confint(model3)), c(4, 4)) 213 | expect_equal(dim(confint(model4)), c(4, 4)) 214 | }) -------------------------------------------------------------------------------- /man/knecht.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/knecht.R 3 | \docType{data} 4 | \name{knecht} 5 | \alias{knecht} 6 | \alias{friendship} 7 | \alias{demographics} 8 | \alias{primary} 9 | \alias{delinquency} 10 | \alias{alcohol} 11 | \alias{advice} 12 | \title{Longitudinal classroom friendship network and behavior (Andrea Knecht)} 13 | \format{ 14 | Note: the data have to be transformed before they can be used with 15 | \pkg{btergm} and related packages (see examples below). 16 | \describe{ 17 | \item{\code{friendship}}{is a list of adjacency matrices at four time 18 | points, containing friendship nominations of the column node by the row 19 | node. The following values are used: \code{0} = no, \code{1} = yes, 20 | \code{NA} = missing, \code{10} = not a member of the classroom (structural 21 | zero).} 22 | 23 | \item{\code{demographics}}{is a data frame with 26 rows (the pupils) and 24 | four demographic variables about the pupils:} \itemize{ \item\code{sex} 25 | (\code{1} = girl, \code{2} = boy) \item\code{age} (in years) 26 | \item\code{ethnicity} (\code{1} = Dutch, \code{2} = other, \code{0} = 27 | missing) \item\code{religion} (\code{1} = Christian, \code{2} = 28 | non-religious, \code{3} = non-Christian religion, \code{0} = missing) } 29 | 30 | \item{\code{primary}}{is a 26 x 26 matrix indicating whether two pupils 31 | attended the same primary school. \code{0} = no, \code{1} = yes.} 32 | 33 | \item{\code{delinquency}}{is a data frame with 26 rows (the pupils) and 34 | four columns (the four time steps). It contains the rounded average of four 35 | items (stealing, vandalizing, fighting, graffiti). Categories: frequency 36 | over last three months, \code{1} = never, \code{2} = once, \code{3} = 2--4 37 | times, \code{4} = 5--10 times, \code{5} = more than 10 times; \code{0} = 38 | missing.} 39 | 40 | \item{\code{alcohol}}{is a data frame with 26 rows (the pupils) and 3 41 | columns (waves 2, 3, and 4). It contains data on alcohol use (\dQuote{How 42 | often did you drink alcohol with friends in the last three months?}). 43 | Categories: \code{1} = never, \code{2} = once, \code{3} = 2--4 times, 44 | \code{4} = 5--10 times, \code{5} = more than 10 times; \code{0} = missing.} 45 | 46 | \item{\code{advice}}{is a data frame with one variable, \dQuote{school 47 | advice}, the assessment given at the end of primary school about the school 48 | capabilities of the pupil (\code{4} = low, \code{8} = high, \code{0} = 49 | missing)} 50 | } 51 | } 52 | \source{ 53 | The data were gathered by Andrea Knecht, as part of her PhD 54 | research, building on methods developed by Chris Baerveldt, initiator and 55 | supervisor of the project. The project is funded by the Netherlands 56 | Organisation for Scientific Research NWO, grant 401-01-554, and is part of 57 | the research program "Dynamics of Networks and Behavior" with principle 58 | investigator Tom A. B. Snijders. 59 | 60 | \itemize{ 61 | \item Complete original data: \doi{10.17026/dans-z9b-h2bp} 62 | \item This excerpt in Siena format: 63 | \url{http://www.stats.ox.ac.uk/~snijders/siena/klas12b.zip} 64 | \item Siena dataset description: 65 | \url{http://www.stats.ox.ac.uk/~snijders/siena/tutorial2010_data.htm} 66 | } 67 | 68 | Permission to redistribute this dataset along with this package was granted 69 | by Andrea Knecht on April 17, 2014. Questions about the data or the original 70 | study should be directed to her. 71 | } 72 | \description{ 73 | Longitudinal classroom friendship network and behavior (Andrea Knecht). 74 | } 75 | \details{ 76 | The Knecht dataset contains the friendship network of 26 pupils in a Dutch 77 | school class measured at four time points along with several demographic and 78 | behavioral covariates like age, sex, ethnicity, religion, delinquency, 79 | alcohol consumption, primary school co-attendance, and school advice. Some 80 | of these covariates are constant while others vary over time. 81 | 82 | The full dataset (see Knecht 2006 and 2008) contains a large number of 83 | classrooms while the dataset presented here is an excerpt based on one 84 | single classroom. This excerpt was first used in a tutorial for the software 85 | \pkg{Siena} and the corresponding R package \pkg{RSiena} (Snijders, Steglich 86 | and van de Bunt 2010). The following description was largely copied from the 87 | original data description provided on the homepage of the \pkg{Siena} 88 | project (see below for the URL). 89 | 90 | The data were collected between September 2003 and June 2004 by Andrea 91 | Knecht, supervised by Chris Baerveldt, at the Department of Sociology of the 92 | University of Utrecht (NL). The entire study is reported in Knecht (2008). 93 | The project was funded by the Netherlands Organisation for Scientific 94 | Research NWO, grant 401-01-554. The 26 students were followed over their 95 | first year at secondary school during which friendship networks as well as 96 | other data were assessed at four time points at intervals of three months. 97 | There were 17 girls and 9 boys in the class, aged 11--13 at the beginning of 98 | the school year. Network data were assessed by asking students to indicate 99 | up to twelve classmates which they considered good friends. Delinquency is 100 | defined as a rounded average over four types of minor delinquency (stealing, 101 | vandalism, graffiti, and fighting), measured in each of the four waves of 102 | data collection. The five-point scale ranged from `never' to `more than 10 103 | times', and the distribution is highly skewed. In a range of 1--5, the mode 104 | was 1 at all four waves, the average rose over time from 1.4 to 2.0, and the 105 | value 5 was never observed. 106 | } 107 | \examples{ 108 | \dontrun{ 109 | # ==================================================================== 110 | # The following example was taken from the JSS article about btergm 111 | # that is referenced above (Leifeld, Cranmer and Desmarais 2018). 112 | # ==================================================================== 113 | 114 | require("texreg") 115 | require("sna") 116 | require("btergm") 117 | require("RSiena") 118 | data("knecht") 119 | 120 | # step 1: make sure the network matrices have node labels 121 | for (i in 1:length(friendship)) { 122 | rownames(friendship[[i]]) <- 1:nrow(friendship[[i]]) 123 | colnames(friendship[[i]]) <- 1:ncol(friendship[[i]]) 124 | } 125 | rownames(primary) <- rownames(friendship[[1]]) 126 | colnames(primary) <- colnames(friendship[[1]]) 127 | sex <- demographics$sex 128 | names(sex) <- 1:length(sex) 129 | 130 | # step 2: imputation of NAs and removal of absent nodes: 131 | friendship <- handleMissings(friendship, na = 10, method = "remove") 132 | friendship <- handleMissings(friendship, na = NA, method = "fillmode") 133 | 134 | # step 3: add nodal covariates to the networks 135 | for (i in 1:length(friendship)) { 136 | s <- adjust(sex, friendship[[i]]) 137 | friendship[[i]] <- network(friendship[[i]]) 138 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], "sex", s) 139 | idegsqrt <- sqrt(degree(friendship[[i]], cmode = "indegree")) 140 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], 141 | "idegsqrt", idegsqrt) 142 | odegsqrt <- sqrt(degree(friendship[[i]], cmode = "outdegree")) 143 | friendship[[i]] <- set.vertex.attribute(friendship[[i]], 144 | "odegsqrt", odegsqrt) 145 | } 146 | sapply(friendship, network.size) 147 | 148 | # step 4: plot the networks 149 | pdf("knecht.pdf") 150 | par(mfrow = c(2, 2), mar = c(0, 0, 1, 0)) 151 | for (i in 1:length(friendship)) { 152 | plot(network(friendship[[i]]), main = paste("t =", i), 153 | usearrows = TRUE, edge.col = "grey50") 154 | } 155 | dev.off() 156 | 157 | # step 5: estimate TERGMS without and with temporal dependencies 158 | model.2a <- btergm(friendship ~ edges + mutual + ttriple + 159 | transitiveties + ctriple + nodeicov("idegsqrt") + 160 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 161 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 162 | edgecov(primary), R = 100) 163 | 164 | model.2b <- btergm(friendship ~ edges + mutual + ttriple + 165 | transitiveties + ctriple + nodeicov("idegsqrt") + 166 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 167 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 168 | edgecov(primary) + delrecip + memory(type = "stability"), 169 | R = 100) 170 | 171 | # step 6: alternatively, estimate via MCMC-MLE: 172 | model.2d <- mtergm(friendship ~ edges + mutual + ttriple + 173 | transitiveties + ctriple + nodeicov("idegsqrt") + 174 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 175 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 176 | edgecov(primary) + delrecip + memory(type = "stability"), 177 | control = control.ergm(MCMC.samplesize = 5000, MCMC.interval = 2000)) 178 | 179 | # step 7: GOF assessment with out-of-sample prediction 180 | # (note the commentaries and corrections at 181 | # https://doi.org/10.1017/nws.2022.7 and 182 | # https://doi.org/10.1017/nws.2022.6) 183 | model.2e <- btergm(friendship[1:3] ~ edges + mutual + ttriple + 184 | transitiveties + ctriple + nodeicov("idegsqrt") + 185 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 186 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 187 | edgecov(primary) + delrecip + memory(type = "stability"), 188 | R = 100) 189 | 190 | gof.2e <- gof(model.2e, nsim = 100, target = friendship[[4]], 191 | formula = friendship[3:4] ~ edges + mutual + ttriple + 192 | transitiveties + ctriple + nodeicov("idegsqrt") + 193 | nodeicov("odegsqrt") + nodeocov("odegsqrt") + 194 | nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 195 | edgecov(primary) + delrecip + memory(type = "stability"), 196 | coef = coef(model.2b), statistics = c(esp, dsp, geodesic, 197 | deg, triad.undirected, rocpr)) 198 | pdf("gof-2e.pdf", width = 8, height = 6) 199 | plot(gof.2e) 200 | dev.off() 201 | } 202 | } 203 | \references{ 204 | Knecht, Andrea (2006): \emph{Networks and Actor Attributes in Early 205 | Adolescence} [2003/04]. Utrecht, The Netherlands Research School ICS, 206 | Department of Sociology, Utrecht University. (ICS-Codebook no. 61). 207 | 208 | Knecht, Andrea (2008): \emph{Friendship Selection and Friends' Influence. 209 | Dynamics of Networks and Actor Attributes in Early Adolescence}. PhD 210 | Dissertation, University of Utrecht. \url{ 211 | https://dspace.library.uu.nl/handle/1874/25950}. 212 | 213 | Knecht, Andrea, Tom A. B. Snijders, Chris Baerveldt, Christian E. G. 214 | Steglich, and Werner Raub (2010): Friendship and Delinquency: Selection and 215 | Influence Processes in Early Adolescence. \emph{Social Development} 19(3): 216 | 494--514. \doi{10.1111/j.1467-9507.2009.00564.x}. 217 | 218 | Leifeld, Philip and Skyler J. Cranmer (2019): A Theoretical and Empirical 219 | Comparison of the Temporal Exponential Random Graph Model and the Stochastic 220 | Actor-Oriented Model. Network Science 7(1): 20--51. 221 | \doi{10.1017/nws.2018.26}. 222 | 223 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 224 | Exponential Random Graph Models with btergm: Estimation and Bootstrap 225 | Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1--36. 226 | \doi{10.18637/jss.v083.i06}. 227 | 228 | Snijders, Tom A. B., Christian E. G. Steglich, and Gerhard G. van de Bunt 229 | (2010): Introduction to Actor-Based Models for Network Dynamics. 230 | \emph{Social Networks} 32: 44--60. \doi{10.1016/j.socnet.2009.02.004}. 231 | 232 | Steglich, Christian E. G. and Andrea Knecht (2009): Die statistische Analyse 233 | dynamischer Netzwerkdaten. In: Stegbauer, Christian and Roger Haeussling 234 | (editors), \emph{Handbuch der Netzwerkforschung}, Wiesbaden: Verlag fuer 235 | Sozialwissenschaften. 236 | } 237 | \keyword{datasets} 238 | -------------------------------------------------------------------------------- /R/knecht.R: -------------------------------------------------------------------------------- 1 | #' Longitudinal classroom friendship network and behavior (Andrea Knecht) 2 | #' 3 | #' Longitudinal classroom friendship network and behavior (Andrea Knecht). 4 | #' 5 | #' The Knecht dataset contains the friendship network of 26 pupils in a Dutch 6 | #' school class measured at four time points along with several demographic and 7 | #' behavioral covariates like age, sex, ethnicity, religion, delinquency, 8 | #' alcohol consumption, primary school co-attendance, and school advice. Some 9 | #' of these covariates are constant while others vary over time. 10 | #' 11 | #' The full dataset (see Knecht 2006 and 2008) contains a large number of 12 | #' classrooms while the dataset presented here is an excerpt based on one 13 | #' single classroom. This excerpt was first used in a tutorial for the software 14 | #' \pkg{Siena} and the corresponding R package \pkg{RSiena} (Snijders, Steglich 15 | #' and van de Bunt 2010). The following description was largely copied from the 16 | #' original data description provided on the homepage of the \pkg{Siena} 17 | #' project (see below for the URL). 18 | #' 19 | #' The data were collected between September 2003 and June 2004 by Andrea 20 | #' Knecht, supervised by Chris Baerveldt, at the Department of Sociology of the 21 | #' University of Utrecht (NL). The entire study is reported in Knecht (2008). 22 | #' The project was funded by the Netherlands Organisation for Scientific 23 | #' Research NWO, grant 401-01-554. The 26 students were followed over their 24 | #' first year at secondary school during which friendship networks as well as 25 | #' other data were assessed at four time points at intervals of three months. 26 | #' There were 17 girls and 9 boys in the class, aged 11--13 at the beginning of 27 | #' the school year. Network data were assessed by asking students to indicate 28 | #' up to twelve classmates which they considered good friends. Delinquency is 29 | #' defined as a rounded average over four types of minor delinquency (stealing, 30 | #' vandalism, graffiti, and fighting), measured in each of the four waves of 31 | #' data collection. The five-point scale ranged from `never' to `more than 10 32 | #' times', and the distribution is highly skewed. In a range of 1--5, the mode 33 | #' was 1 at all four waves, the average rose over time from 1.4 to 2.0, and the 34 | #' value 5 was never observed. 35 | #' 36 | #' @name knecht 37 | #' 38 | #' @aliases knecht friendship demographics primary delinquency alcohol advice 39 | #' 40 | #' @docType data 41 | #' 42 | #' @format 43 | #' Note: the data have to be transformed before they can be used with 44 | #' \pkg{btergm} and related packages (see examples below). 45 | #' \describe{ 46 | #' \item{\code{friendship}}{is a list of adjacency matrices at four time 47 | #' points, containing friendship nominations of the column node by the row 48 | #' node. The following values are used: \code{0} = no, \code{1} = yes, 49 | #' \code{NA} = missing, \code{10} = not a member of the classroom (structural 50 | #' zero).} 51 | #' 52 | #' \item{\code{demographics}}{is a data frame with 26 rows (the pupils) and 53 | #' four demographic variables about the pupils:} \itemize{ \item\code{sex} 54 | #' (\code{1} = girl, \code{2} = boy) \item\code{age} (in years) 55 | #' \item\code{ethnicity} (\code{1} = Dutch, \code{2} = other, \code{0} = 56 | #' missing) \item\code{religion} (\code{1} = Christian, \code{2} = 57 | #' non-religious, \code{3} = non-Christian religion, \code{0} = missing) } 58 | #' 59 | #' \item{\code{primary}}{is a 26 x 26 matrix indicating whether two pupils 60 | #' attended the same primary school. \code{0} = no, \code{1} = yes.} 61 | #' 62 | #' \item{\code{delinquency}}{is a data frame with 26 rows (the pupils) and 63 | #' four columns (the four time steps). It contains the rounded average of four 64 | #' items (stealing, vandalizing, fighting, graffiti). Categories: frequency 65 | #' over last three months, \code{1} = never, \code{2} = once, \code{3} = 2--4 66 | #' times, \code{4} = 5--10 times, \code{5} = more than 10 times; \code{0} = 67 | #' missing.} 68 | #' 69 | #' \item{\code{alcohol}}{is a data frame with 26 rows (the pupils) and 3 70 | #' columns (waves 2, 3, and 4). It contains data on alcohol use (\dQuote{How 71 | #' often did you drink alcohol with friends in the last three months?}). 72 | #' Categories: \code{1} = never, \code{2} = once, \code{3} = 2--4 times, 73 | #' \code{4} = 5--10 times, \code{5} = more than 10 times; \code{0} = missing.} 74 | #' 75 | #' \item{\code{advice}}{is a data frame with one variable, \dQuote{school 76 | #' advice}, the assessment given at the end of primary school about the school 77 | #' capabilities of the pupil (\code{4} = low, \code{8} = high, \code{0} = 78 | #' missing)} 79 | #' } 80 | #' 81 | #' @references 82 | #' Knecht, Andrea (2006): \emph{Networks and Actor Attributes in Early 83 | #' Adolescence} [2003/04]. Utrecht, The Netherlands Research School ICS, 84 | #' Department of Sociology, Utrecht University. (ICS-Codebook no. 61). 85 | #' 86 | #' Knecht, Andrea (2008): \emph{Friendship Selection and Friends' Influence. 87 | #' Dynamics of Networks and Actor Attributes in Early Adolescence}. PhD 88 | #' Dissertation, University of Utrecht. \url{ 89 | #' https://dspace.library.uu.nl/handle/1874/25950}. 90 | #' 91 | #' Knecht, Andrea, Tom A. B. Snijders, Chris Baerveldt, Christian E. G. 92 | #' Steglich, and Werner Raub (2010): Friendship and Delinquency: Selection and 93 | #' Influence Processes in Early Adolescence. \emph{Social Development} 19(3): 94 | #' 494--514. \doi{10.1111/j.1467-9507.2009.00564.x}. 95 | #' 96 | #' Leifeld, Philip and Skyler J. Cranmer (2019): A Theoretical and Empirical 97 | #' Comparison of the Temporal Exponential Random Graph Model and the Stochastic 98 | #' Actor-Oriented Model. Network Science 7(1): 20--51. 99 | #' \doi{10.1017/nws.2018.26}. 100 | #' 101 | #' Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 102 | #' Exponential Random Graph Models with btergm: Estimation and Bootstrap 103 | #' Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1--36. 104 | #' \doi{10.18637/jss.v083.i06}. 105 | #' 106 | #' Snijders, Tom A. B., Christian E. G. Steglich, and Gerhard G. van de Bunt 107 | #' (2010): Introduction to Actor-Based Models for Network Dynamics. 108 | #' \emph{Social Networks} 32: 44--60. \doi{10.1016/j.socnet.2009.02.004}. 109 | #' 110 | #' Steglich, Christian E. G. and Andrea Knecht (2009): Die statistische Analyse 111 | #' dynamischer Netzwerkdaten. In: Stegbauer, Christian and Roger Haeussling 112 | #' (editors), \emph{Handbuch der Netzwerkforschung}, Wiesbaden: Verlag fuer 113 | #' Sozialwissenschaften. 114 | #' 115 | #' @source The data were gathered by Andrea Knecht, as part of her PhD 116 | #' research, building on methods developed by Chris Baerveldt, initiator and 117 | #' supervisor of the project. The project is funded by the Netherlands 118 | #' Organisation for Scientific Research NWO, grant 401-01-554, and is part of 119 | #' the research program "Dynamics of Networks and Behavior" with principle 120 | #' investigator Tom A. B. Snijders. 121 | #' 122 | #' \itemize{ 123 | #' \item Complete original data: \doi{10.17026/dans-z9b-h2bp} 124 | #' \item This excerpt in Siena format: 125 | #' \url{http://www.stats.ox.ac.uk/~snijders/siena/klas12b.zip} 126 | #' \item Siena dataset description: 127 | #' \url{http://www.stats.ox.ac.uk/~snijders/siena/tutorial2010_data.htm} 128 | #' } 129 | #' 130 | #' Permission to redistribute this dataset along with this package was granted 131 | #' by Andrea Knecht on April 17, 2014. Questions about the data or the original 132 | #' study should be directed to her. 133 | #' 134 | #' @keywords datasets 135 | #' 136 | #' @examples 137 | #' \dontrun{ 138 | #' # ==================================================================== 139 | #' # The following example was taken from the JSS article about btergm 140 | #' # that is referenced above (Leifeld, Cranmer and Desmarais 2018). 141 | #' # ==================================================================== 142 | #' 143 | #' require("texreg") 144 | #' require("sna") 145 | #' require("btergm") 146 | #' require("RSiena") 147 | #' data("knecht") 148 | #' 149 | #' # step 1: make sure the network matrices have node labels 150 | #' for (i in 1:length(friendship)) { 151 | #' rownames(friendship[[i]]) <- 1:nrow(friendship[[i]]) 152 | #' colnames(friendship[[i]]) <- 1:ncol(friendship[[i]]) 153 | #' } 154 | #' rownames(primary) <- rownames(friendship[[1]]) 155 | #' colnames(primary) <- colnames(friendship[[1]]) 156 | #' sex <- demographics$sex 157 | #' names(sex) <- 1:length(sex) 158 | #' 159 | #' # step 2: imputation of NAs and removal of absent nodes: 160 | #' friendship <- handleMissings(friendship, na = 10, method = "remove") 161 | #' friendship <- handleMissings(friendship, na = NA, method = "fillmode") 162 | #' 163 | #' # step 3: add nodal covariates to the networks 164 | #' for (i in 1:length(friendship)) { 165 | #' s <- adjust(sex, friendship[[i]]) 166 | #' friendship[[i]] <- network(friendship[[i]]) 167 | #' friendship[[i]] <- set.vertex.attribute(friendship[[i]], "sex", s) 168 | #' idegsqrt <- sqrt(degree(friendship[[i]], cmode = "indegree")) 169 | #' friendship[[i]] <- set.vertex.attribute(friendship[[i]], 170 | #' "idegsqrt", idegsqrt) 171 | #' odegsqrt <- sqrt(degree(friendship[[i]], cmode = "outdegree")) 172 | #' friendship[[i]] <- set.vertex.attribute(friendship[[i]], 173 | #' "odegsqrt", odegsqrt) 174 | #' } 175 | #' sapply(friendship, network.size) 176 | #' 177 | #' # step 4: plot the networks 178 | #' pdf("knecht.pdf") 179 | #' par(mfrow = c(2, 2), mar = c(0, 0, 1, 0)) 180 | #' for (i in 1:length(friendship)) { 181 | #' plot(network(friendship[[i]]), main = paste("t =", i), 182 | #' usearrows = TRUE, edge.col = "grey50") 183 | #' } 184 | #' dev.off() 185 | #' 186 | #' # step 5: estimate TERGMS without and with temporal dependencies 187 | #' model.2a <- btergm(friendship ~ edges + mutual + ttriple + 188 | #' transitiveties + ctriple + nodeicov("idegsqrt") + 189 | #' nodeicov("odegsqrt") + nodeocov("odegsqrt") + 190 | #' nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 191 | #' edgecov(primary), R = 100) 192 | #' 193 | #' model.2b <- btergm(friendship ~ edges + mutual + ttriple + 194 | #' transitiveties + ctriple + nodeicov("idegsqrt") + 195 | #' nodeicov("odegsqrt") + nodeocov("odegsqrt") + 196 | #' nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 197 | #' edgecov(primary) + delrecip + memory(type = "stability"), 198 | #' R = 100) 199 | #' 200 | #' # step 6: alternatively, estimate via MCMC-MLE: 201 | #' model.2d <- mtergm(friendship ~ edges + mutual + ttriple + 202 | #' transitiveties + ctriple + nodeicov("idegsqrt") + 203 | #' nodeicov("odegsqrt") + nodeocov("odegsqrt") + 204 | #' nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 205 | #' edgecov(primary) + delrecip + memory(type = "stability"), 206 | #' control = control.ergm(MCMC.samplesize = 5000, MCMC.interval = 2000)) 207 | #' 208 | #' # step 7: GOF assessment with out-of-sample prediction 209 | #' # (note the commentaries and corrections at 210 | #' # https://doi.org/10.1017/nws.2022.7 and 211 | #' # https://doi.org/10.1017/nws.2022.6) 212 | #' model.2e <- btergm(friendship[1:3] ~ edges + mutual + ttriple + 213 | #' transitiveties + ctriple + nodeicov("idegsqrt") + 214 | #' nodeicov("odegsqrt") + nodeocov("odegsqrt") + 215 | #' nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 216 | #' edgecov(primary) + delrecip + memory(type = "stability"), 217 | #' R = 100) 218 | #' 219 | #' gof.2e <- gof(model.2e, nsim = 100, target = friendship[[4]], 220 | #' formula = friendship[3:4] ~ edges + mutual + ttriple + 221 | #' transitiveties + ctriple + nodeicov("idegsqrt") + 222 | #' nodeicov("odegsqrt") + nodeocov("odegsqrt") + 223 | #' nodeofactor("sex") + nodeifactor("sex") + nodematch("sex") + 224 | #' edgecov(primary) + delrecip + memory(type = "stability"), 225 | #' coef = coef(model.2b), statistics = c(esp, dsp, geodesic, 226 | #' deg, triad.undirected, rocpr)) 227 | #' pdf("gof-2e.pdf", width = 8, height = 6) 228 | #' plot(gof.2e) 229 | #' dev.off() 230 | #' } 231 | NULL -------------------------------------------------------------------------------- /man/coauthor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ch_coauthor.R 3 | \docType{data} 4 | \name{coauthor} 5 | \alias{coauthor} 6 | \alias{ch_coauthor} 7 | \alias{ch_coaut} 8 | \alias{ch_dist100km} 9 | \alias{ch_en_article_sim} 10 | \alias{ch_nodeattr} 11 | \alias{ch_topicsim} 12 | \title{Swiss political science co-authorship network 2013} 13 | \format{ 14 | \describe{ 15 | \item{\code{ch_coaut}}{is an undirected, weighted 156 x 156 adjancency matrix 16 | indicating how many publications political scientist in Switzerland shared 17 | with each other as reported in late 2013, including only postdoctoral and 18 | professorial political scientists affiliated with research institutes or 19 | universities. The exact edge weight should be treated with caution because 20 | some publications were counted multiple times because they were reported by 21 | multiple co-authors. The diagonal contains the number of publications of 22 | the respective author. Leifeld and Ingold (2016) describe the data collection 23 | process in more detail.} 24 | 25 | \item{\code{ch_nodeattr}}{is a data frame with node attributes/variables for 26 | the 156 researchers, in the same alphabetical row order as the network 27 | matrix. The first twelve columns with column labels starting with "inst_" are 28 | affiliations with different institutions (1 = affiliated; 0 = no 29 | affiliation). The next seven columns with column labels starting with "city_" 30 | contain the locations of the researchers' institutional affiliations. The 31 | "phdyear" column contains the self-reported year of obtaining the PhD, and 32 | the "birthyear" column contains the self-reported or publicly available year 33 | of birth; these two variables contain many missing values. The "status" 34 | column indicates whether a researcher was listed as a professor or as having 35 | postdoctoral or other non-professorial status at the time. "chairtitle" is 36 | the name of the chair or research group the researcher reported to be a 37 | member of. "num_publications" is the total number of publications, 38 | "num_articles" the number of journal articles among them, "num_books" the 39 | number of books among them, "share_articles" the percentage of journal 40 | articles among the publications, and "share_books" the percentage of 41 | monographs and edited volumes among the publications. The four columns with 42 | column names starting with "lang_" contain the relative shares of English, 43 | French, German, Italian, and other languages among the publications of the 44 | researcher. The column "share_en_articles" contains the percentage of English 45 | journal articles among all publications of the researcher. "male" is a dummy 46 | variable indicating whether the author is male (1) or female (0). The 47 | variables contained here are described in Leifeld (2018).} 48 | 49 | \item{\code{ch_dist100km}}{is a 156 x 156 matrix containing the geographical 50 | distance between any two researchers measured in units of 100km (for a 51 | reasonable scaling of coefficients in a statistical model), computed over the 52 | latitude and longitude of their main institutional affiliations. The measure 53 | is included in Leifeld (2018).} 54 | 55 | \item{\code{ch_en_article_sim}}{is a 156 x 156 matrix containing the 56 | similarity between any two researchers in terms of the share of their work 57 | that is published in English and as journal articles. Values closer to 1.0 58 | indicate that two researchers were similar in their language and publication 59 | type portfolio while values closer to 0 indicate that they were relatively 60 | dissimilar. Only extra-dyadic publications were counted in establishing this 61 | similarity. I.e., if researcher A and B co-authored, their joint publications 62 | were not included in establishing their English article share similarity. 63 | This was done to reduce endogeneity/reverse causality when modeling 64 | co-authorship as a function of English article share similarity. The measure 65 | is described in Leifeld (2018).} 66 | 67 | \item{\code{ch_topicsim}}{is a 156 x 156 topic similarity matrix for the 68 | researchers. Topic similarities were computed by taking into account all 69 | words in the publication titles of any two researchers, excluding the 70 | publications they published as co-authors (i.e., only extra-dyadic 71 | publications, to reduce endogeneity/reverse causality in modeling 72 | co-authorship ties as a function of topic similarity). Topic similarity was 73 | established by computing the cosine similarity between the tf-idf scores for 74 | the title words of any two researchers (i.e., a vector space model). Leifeld 75 | (2018) contains more details on this procedure.} 76 | } 77 | } 78 | \source{ 79 | The data were collected from public information online. The full 80 | data collection details are described in Leifeld and Ingold (2016). 81 | } 82 | \description{ 83 | Swiss political science co-authorship network 2013 84 | } 85 | \details{ 86 | The Swiss political science co-authorship network 2013 dataset contains the 87 | co-authorship network of all political scientists at Swiss universities and 88 | research institutes in late 2013. The data are described in Leifeld and 89 | Ingold (2016) and Leifeld (2018). The data contained here include 90 | postdoctoral and professorial researchers but not PhD students, as in Leifeld 91 | (2018), without the PhD researchers included in Leifeld and Ingold (2016). 92 | For the full dataset, see the replication archive at DOI 93 | \doi{10.7910/DVN/85SK1M}. 94 | 95 | Leifeld and Ingold (2016) summarize the data collection strategy as follows: 96 | \emph{"Data gathering took place between July and December 2013. A single 97 | coder pursued a three-step coding procedure: he first created a list of all 98 | relevant university departments and research institutes that host political 99 | scientists in Switzerland, then he browsed the websites of these institutes 100 | and entered all researchers along with several details about them into a 101 | database, including their seniority status (predoctoral, postdoctoral, or 102 | professor) and the URL of their publication list (either the CV, the 103 | institutional website, a private homepage, or several of those items in order 104 | to get a complete publication profile of each person). After entering all 105 | researchers of an institute, the coder went through the researchers' 106 | publication lists and entered the following pieces of information for each 107 | publication into the database: the reporting author, the names of all 108 | co-authors, the title of the publication, the year, the name of the journal 109 | or book in which the publication appeared (if applicable), the names of all 110 | editors (if applicable), and a classification of the type of publication 111 | (academic journal, book chapter, monograph, edited volume, other). Most 112 | publications are relatively recent, but the earliest publications in the 113 | database date back to the 1960s. After completing these three steps, data 114 | entered at the beginning was double-checked in order to avoid bias due to new 115 | publications that may have shown up during the coding time period. This 116 | procedure is the best one can do in terms of completeness, but it should be 117 | clear that it crucially depends on the accuracy of the self-reported 118 | bibliographic information. For example, if a researcher did not update his or 119 | her CV or list of publications for the previous six months, those most recent 120 | publications only had a chance to enter the database if the co-authors listed 121 | the publication on their website. In some relatively rare cases, all authors 122 | failed to report recent updates, and this may cause minor inaccuracies in the 123 | network dataset, mostly affecting very recent publications in 2013 because 124 | there is, on average, a reporting lag."} 125 | 126 | Based on the collected publication data, a co-authorship network matrix with 127 | 156 nodes was created. In addition to this matrix, the dataset here contains 128 | node attribute data (institutional affiliations, location, demographics, 129 | language shares, publication type shares) and relational covariates 130 | (geographical distance, similarity in terms of the share of English articles, 131 | and topic similarity) as described in Leifeld (2018). The dataset can be used 132 | to replicate Leifeld (2018), but only approximately due to changes in the 133 | estimation routine in the \pkg{ergm} package since the article was published. 134 | } 135 | \examples{ 136 | \dontrun{ 137 | # Replication code for the full Swiss co-authorship ERGM in Leifeld (2018). 138 | # Note that the estimates can only be reproduced approximately due to 139 | # internal changes in the ergm package. 140 | 141 | library("network") 142 | library("ergm") 143 | 144 | data("ch_coauthor") 145 | 146 | # set up network object with node attributes 147 | ch_nw <- network(ch_coaut, directed = FALSE) 148 | set.vertex.attribute(ch_nw, "frequency", ch_nodeattr$num_publications) 149 | set.vertex.attribute(ch_nw, "status", as.character(ch_nodeattr$status)) 150 | set.vertex.attribute(ch_nw, "male", ch_nodeattr$male) 151 | set.vertex.attribute(ch_nw, "share_en_articles", 152 | ch_nodeattr$share_en_articles) 153 | 154 | # create same affiliation matrix 155 | ch_inst_indices <- which(grepl("^inst_.+", colnames(ch_nodeattr))) 156 | ch_same_affiliation <- as.matrix(ch_nodeattr[, ch_inst_indices]) \%*\% 157 | t(ch_nodeattr[, ch_inst_indices]) 158 | 159 | # create same chair matrix 160 | ch_nodeattr$chairtitle[ch_nodeattr$chairtitle == ""] <- NA 161 | ch_same_chair <- matrix(0, nrow = nrow(ch_same_affiliation), 162 | ncol = ncol(ch_same_affiliation)) 163 | for (i in 1:length(ch_nodeattr$chairtitle)) { 164 | for (j in 1:length(ch_nodeattr$chairtitle)) { 165 | if (i != j && 166 | !is.na(ch_nodeattr$chairtitle[i]) && 167 | !is.na(ch_nodeattr$chairtitle[j]) && 168 | ch_nodeattr$chairtitle[i] == ch_nodeattr$chairtitle[j] && 169 | ch_same_affiliation[i, j] == TRUE) { 170 | ch_same_chair[i, j] <- 1 171 | } 172 | } 173 | } 174 | rownames(ch_same_chair) <- rownames(ch_same_affiliation) 175 | colnames(ch_same_chair) <- colnames(ch_same_affiliation) 176 | 177 | # create supervision matrix (same chair + affiliation + mixed seniority) 178 | ch_supervision <- ch_same_affiliation * 179 | ch_same_chair * 180 | matrix(ch_nodeattr$status == "professor", 181 | nrow = nrow(ch_same_chair), 182 | ncol = ncol(ch_same_chair), 183 | byrow = FALSE) * 184 | matrix(ch_nodeattr$status != "professor", 185 | nrow = nrow(ch_same_chair), 186 | ncol = ncol(ch_same_chair), 187 | byrow = TRUE) 188 | 189 | # ERGM estimation 190 | ch_model <- ergm(ch_nw ~ 191 | edges + 192 | gwesp(0.3, fixed = TRUE) + 193 | gwdegree(0.4, fixed = TRUE) + 194 | nodecov("frequency") + 195 | nodefactor("status") + 196 | nodefactor("male") + 197 | nodematch("male") + 198 | edgecov(ch_dist100km) + 199 | edgecov(ch_same_affiliation) + 200 | edgecov(ch_same_chair) + 201 | edgecov(ch_supervision) + 202 | edgecov(ch_topicsim) + 203 | nodecov("share_en_articles") + 204 | edgecov(ch_en_article_sim), 205 | control = control.ergm(MCMLE.termination = "Hummel", 206 | MCMLE.effectiveSize = NULL)) 207 | summary(ch_model) # corresponds Column 1 in Table 3 in Leifeld (2018) 208 | } 209 | } 210 | \references{ 211 | Leifeld, Philip (2018): Polarization in the Social Sciences: Assortative 212 | Mixing in Social Science Collaboration Networks is Resilient to 213 | Interventions. \emph{Physica A: Statistical Mechanics and its Applications} 214 | 507: 510--523. \doi{10.1016/j.physa.2018.05.109}. Full replication data: 215 | \doi{10.7910/DVN/85SK1M}. 216 | 217 | Leifeld, Philip and Karin Ingold (2016): Co-authorship Networks in Swiss 218 | Political Research. \emph{Swiss Political Science Review} 22(2): 264--287. 219 | \doi{10.1111/spsr.12193}. 220 | } 221 | \keyword{datasets} 222 | -------------------------------------------------------------------------------- /man/btergm.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/btergm.R 3 | \name{btergm} 4 | \alias{btergm} 5 | \title{Estimate a TERGM by MPLE with temporal bootstrapping} 6 | \usage{ 7 | btergm( 8 | formula, 9 | R = 500, 10 | offset = FALSE, 11 | returndata = FALSE, 12 | parallel = c("no", "multicore", "snow"), 13 | ncpus = 1, 14 | cl = NULL, 15 | control.ergm = NULL, 16 | usefastglm = FALSE, 17 | verbose = TRUE, 18 | ... 19 | ) 20 | } 21 | \arguments{ 22 | \item{formula}{Formula for the TERGM. Model construction works like in the 23 | \pkg{ergm} package with the same model terms etc. (for a list of terms, see 24 | \code{help("\link[ergm]{ergm-terms}")}). The networks to be modeled on the 25 | left-hand side of the equation must be given either as a list of network 26 | objects with more recent networks last (i.e., chronological order) or as a 27 | list of matrices with more recent matrices at the end. \code{dyadcov} and 28 | \code{edgecov} terms accept time-independent covariates (as \code{network} 29 | or \code{matrix} objects) or time-varying covariates (as a list of networks 30 | or matrices with the same length as the list of networks to be modeled).} 31 | 32 | \item{R}{Number of bootstrap replications. The higher the number of 33 | replications, the more accurate but also the slower is the estimation.} 34 | 35 | \item{offset}{If \code{offset = TRUE} is set, a list of offset matrices 36 | (one for each time step) with structural zeros is handed over to the 37 | pseudolikelihood preparation routine. The offset matrices contain 38 | structural zeros where either the dependent networks or any of the 39 | covariates have missing nodes (if \code{auto.adjust = TRUE} is used). All 40 | matrices and network objects are inflated to the dimensions of the largest 41 | object, and the offset matrices inform the estimation preparation routine 42 | which dyads are constrained to be absent. After MPLE data preparation, the 43 | dyads with these structural zeros are removed before the GLM is estimated. 44 | If \code{offset = FALSE} is set, all nodes that are not present across all 45 | covariates and networks within a time step are removed completely from the 46 | respective object(s) before the change statistics are computed for all 47 | remaining dyads.} 48 | 49 | \item{returndata}{Return the processed input data instead of estimating and 50 | returning the model? In the \code{btergm} case, this will return a data 51 | frame with the dyads of the dependent variable/network and the change 52 | statistics for all covariates. In the \code{mtergm} case, this will return 53 | a list object with the blockdiagonal network object for the dependent 54 | variable and blockdiagonal matrices for all dyadic covariates and the 55 | offset matrix for the structural zeros.} 56 | 57 | \item{parallel}{Use multiple cores in a computer or nodes in a cluster to 58 | speed up bootstrapping computations. The default value \code{"no"} means 59 | parallel computing is switched off. If \code{"multicore"} is used, the 60 | \code{mclapply} function from the \pkg{parallel} package (formerly in the 61 | \pkg{multicore} package) is used for parallelization. This should run on 62 | any kind of system except MS Windows because it is based on forking. It is 63 | usually the fastest type of parallelization. If \code{"snow"} is used, the 64 | \code{parLapply} function from the \pkg{parallel} package (formerly in the 65 | \pkg{snow} package) is used for parallelization. This should run on any 66 | kind of system including cluster systems and including MS Windows. It is 67 | slightly slower than the former alternative if the same number of cores is 68 | used. However, \code{"snow"} provides support for MPI clusters with a large 69 | amount of cores, which \pkg{multicore} does not offer (see also the 70 | \code{cl} argument). The backend for the bootstrapping procedure is the 71 | \pkg{boot} package.} 72 | 73 | \item{ncpus}{The number of CPU cores used for parallel computing (only if 74 | \code{parallel} is activated). If the number of cores should be detected 75 | automatically on the machine where the code is executed, one can set 76 | \code{ncpus = detectCores()} after loading the \pkg{parallel} package. 77 | On some HPC clusters, the number of available cores is saved as an 78 | environment variable; for example, if MOAB is used, the number of 79 | available cores can sometimes be accessed using 80 | \code{Sys.getenv("MOAB_PROCCOUNT")}, depending on the implementation.} 81 | 82 | \item{cl}{An optional \pkg{parallel} or \pkg{snow} cluster for use if 83 | \code{parallel = "snow"}. If not supplied, a PSOCK cluster is created 84 | temporarily on the local machine.} 85 | 86 | \item{control.ergm}{ergm controls for \code{\link[ergm]{ergmMPLE}} calls. See 87 | \code{\link[ergm]{control.ergm}} for details.} 88 | 89 | \item{usefastglm}{Controls whether to use the \code{\link[fastglm]{fastglm}} 90 | estimation routine from the \pkg{fastglm} package with \code{method = 3}. 91 | Defaults to \code{FALSE} (and then uses 92 | \code{\link[speedglm:speedglm]{speedglm.wfit}} instead if available).} 93 | 94 | \item{verbose}{Print details about data preprocessing and estimation 95 | settings.} 96 | 97 | \item{...}{Further arguments to be handed over to the 98 | \code{\link[boot]{boot}} function.} 99 | } 100 | \description{ 101 | Estimate a TERGM by MPLE with temporal bootstrapping. 102 | } 103 | \details{ 104 | The \code{btergm} function computes temporal exponential random graph models 105 | (TERGM) by bootstrapped pseudolikelihood, as described in Desmarais and 106 | Cranmer (2012). It is faster than MCMC-MLE but only asymptotically unbiased 107 | the longer the time series of networks because it uses temporal bootstrapping 108 | to correct the standard errors. 109 | } 110 | \examples{ 111 | set.seed(5) 112 | 113 | networks <- list() 114 | for (i in 1:10) { # create 10 random networks with 10 actors 115 | mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10) 116 | diag(mat) <- 0 # loops are excluded 117 | nw <- network::network(mat) # create network object 118 | networks[[i]] <- nw # add network to the list 119 | } 120 | 121 | covariates <- list() 122 | for (i in 1:10) { # create 10 matrices as covariate 123 | mat <- matrix(rnorm(100), nrow = 10, ncol = 10) 124 | covariates[[i]] <- mat # add matrix to the list 125 | } 126 | 127 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100) 128 | summary(fit) # show estimation results 129 | 130 | # For examples with real data, see help("knecht") or help("alliances"). 131 | 132 | 133 | # Examples for parallel processing: 134 | 135 | # Some preliminaries: 136 | # - "Forking" means running the code on multiple cores in the same 137 | # computer. It's fast but consumes a lot of memory because all 138 | # objects are copied for each node. It's also restricted to 139 | # cores within a physical computer, i.e. no distribution over a 140 | # network or cluster. Forking does not work on Windows systems. 141 | # - "MPI" is a protocol for distributing computations over many 142 | # cores, often across multiple physical computers/nodes. MPI 143 | # is fast and can distribute the work across hundreds of nodes 144 | # (but remember that R can handle a maximum of 128 connections, 145 | # which includes file access and parallel connections). However, 146 | # it requires that the Rmpi package is installed and that an MPI 147 | # server is running (e.g., OpenMPI). 148 | # - "PSOCK" is a TCP-based protocol. It can also distribute the 149 | # work to many cores across nodes (like MPI). The advantage of 150 | # PSOCK is that it can as well make use of multiple nodes within 151 | # the same node or desktop computer (as with forking) but without 152 | # consuming too much additional memory. However, the drawback is 153 | # that it is not as fast as MPI or forking. 154 | # The following code provides examples for these three scenarios. 155 | 156 | # btergm works with clusters via the parallel package. That is, the 157 | # user can create a cluster object (of type "PSOCK", "MPI", or 158 | # "FORK") and supply it to the 'cl' argument of the 'btergm' 159 | # function. If no cluster object is provided, btergm will try to 160 | # create a temporary PSOCK cluster (if parallel = "snow") or it 161 | # will use forking (if parallel = "multicore"). 162 | 163 | \dontrun{ 164 | # To use a PSOCK cluster without providing an explicit cluster 165 | # object: 166 | require("parallel") 167 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 168 | R = 100, parallel = "snow", ncpus = 25) 169 | 170 | # Equivalently, a PSOCK cluster can be provided as follows: 171 | require("parallel") 172 | cores <- 25 173 | cl <- makeCluster(cores, type = "PSOCK") 174 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 175 | R = 100, parallel = "snow", ncpus = cores, cl = cl) 176 | stopCluster(cl) 177 | 178 | # Forking (without supplying a cluster object) can be used as 179 | # follows. 180 | require("parallel") 181 | cores <- 25 182 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 183 | R = 100, parallel = "multicore", ncpus = cores) 184 | stopCluster(cl) 185 | 186 | # Forking (by providing a cluster object) works as follows: 187 | require("parallel") 188 | cores <- 25 189 | cl <- makeCluster(cores, type = "FORK") 190 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 191 | R = 100, parallel = "snow", ncpus = cores, cl = cl) 192 | stopCluster(cl) 193 | 194 | # To use MPI, a cluster object MUST be created beforehand. In 195 | # this example, a MOAB HPC server is used. It stores the number of 196 | # available cores as a system option: 197 | require("parallel") 198 | cores <- as.numeric(Sys.getenv("MOAB_PROCCOUNT")) 199 | cl <- makeCluster(cores, type = "MPI") 200 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 201 | R = 100, parallel = "snow", ncpus = cores, cl = cl) 202 | stopCluster(cl) 203 | 204 | # In the following example, the Rmpi package is used to create a 205 | # cluster. This may not work on all systems; consult your local 206 | # support staff or the help files on your HPC server to find out how 207 | # to create a cluster object on your system. 208 | 209 | # snow/Rmpi start-up 210 | if (!is.loaded("mpi_initialize")) { 211 | library("Rmpi") 212 | } 213 | library(snow); 214 | 215 | mpirank <- mpi.comm.rank (0) 216 | if (mpirank == 0) { 217 | invisible(makeMPIcluster()) 218 | } else { 219 | sink (file="/dev/null") 220 | invisible(slaveLoop (makeMPImaster())) 221 | mpi.finalize() 222 | q() 223 | } 224 | # End snow/Rmpi start-up 225 | 226 | cl <- getMPIcluster() 227 | 228 | fit <- btergm(networks ~ edges + istar(2) + edgecov(covariates), 229 | R = 100, parallel = "snow", ncpus = 25, cl = cl) 230 | } 231 | 232 | } 233 | \references{ 234 | Cranmer, Skyler J., Tobias Heinrich and Bruce A. Desmarais 235 | (2014): Reciprocity and the Structural Determinants of the International 236 | Sanctions Network. \emph{Social Networks} 36(1): 5-22. 237 | \doi{10.1016/j.socnet.2013.01.001}. 238 | 239 | Desmarais, Bruce A. and Skyler J. Cranmer (2012): Statistical Mechanics of 240 | Networks: Estimation and Uncertainty. \emph{Physica A} 391: 1865--1876. 241 | \doi{10.1016/j.physa.2011.10.018}. 242 | 243 | Desmarais, Bruce A. and Skyler J. Cranmer (2010): Consistent Confidence 244 | Intervals for Maximum Pseudolikelihood Estimators. \emph{Neural Information 245 | Processing Systems 2010 Workshop on Computational Social Science and the 246 | Wisdom of Crowds}. 247 | 248 | Leifeld, Philip and Skyler J. Cranmer (2019): A Theoretical and Empirical 249 | Comparison of the Temporal Exponential Random Graph Model and the 250 | Stochastic Actor-Oriented Model. \emph{Network Science} 7(1): 20-51. 251 | \doi{10.1017/nws.2018.26}. 252 | 253 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2017): 254 | Temporal Exponential Random Graph Models with btergm: Estimation and 255 | Bootstrap Confidence Intervals. \emph{Journal of Statistical Software} 256 | 83(6): 1-36. \doi{10.18637/jss.v083.i06}. 257 | } 258 | \seealso{ 259 | \code{\link{mtergm}} \code{\link{tbergm}} 260 | } 261 | \author{ 262 | Philip Leifeld, Skyler J. Cranmer, Bruce A. Desmarais 263 | } 264 | -------------------------------------------------------------------------------- /man/gof-plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gofplot.R 3 | \docType{methods} 4 | \name{gof-plot} 5 | \alias{gof-plot} 6 | \alias{gofplot} 7 | \alias{plotgof} 8 | \alias{plot-gof} 9 | \alias{print,gof-method} 10 | \alias{print,univariate-method} 11 | \alias{print,boxplot-method} 12 | \alias{print,roc-method} 13 | \alias{print,pr-method} 14 | \alias{print,rocpr-method} 15 | \alias{plot,gof-method} 16 | \alias{plot,univariate-method} 17 | \alias{plot,boxplot-method} 18 | \alias{plot,roc-method} 19 | \alias{plot,pr-method} 20 | \alias{plot,rocpr-method} 21 | \alias{print.boxplot} 22 | \alias{print.roc} 23 | \alias{print.pr} 24 | \alias{print.rocpr} 25 | \alias{print.univariate} 26 | \alias{print.gof} 27 | \alias{plot.gof} 28 | \alias{plot.boxplot} 29 | \alias{plot.roc} 30 | \alias{plot.pr} 31 | \alias{plot.rocpr} 32 | \alias{plot.univariate} 33 | \title{Plot and print methods for GOF output} 34 | \usage{ 35 | \method{print}{boxplot}(x, ...) 36 | 37 | \method{print}{roc}(x, ...) 38 | 39 | \method{print}{pr}(x, ...) 40 | 41 | \method{print}{rocpr}(x, ...) 42 | 43 | \method{print}{univariate}(x, ...) 44 | 45 | \method{print}{gof}(x, ...) 46 | 47 | \method{plot}{gof}(x, mfrow = TRUE, ...) 48 | 49 | \method{plot}{boxplot}( 50 | x, 51 | relative = TRUE, 52 | transform = function(x) x, 53 | xlim = NULL, 54 | main = x$label, 55 | xlab = x$label, 56 | ylab = "Frequency", 57 | border = "darkgray", 58 | boxplot.lwd = 0.8, 59 | outline = FALSE, 60 | median = TRUE, 61 | median.col = "black", 62 | median.lty = "solid", 63 | median.lwd = 2, 64 | mean = TRUE, 65 | mean.col = "black", 66 | mean.lty = "dashed", 67 | mean.lwd = 1, 68 | ... 69 | ) 70 | 71 | \method{plot}{roc}( 72 | x, 73 | add = FALSE, 74 | main = x$label, 75 | avg = c("none", "horizontal", "vertical", "threshold"), 76 | spread.estimate = c("boxplot", "stderror", "stddev"), 77 | lwd = 3, 78 | rgraph = FALSE, 79 | col = "#bd0017", 80 | random.col = "#bd001744", 81 | ... 82 | ) 83 | 84 | \method{plot}{pr}( 85 | x, 86 | add = FALSE, 87 | main = x$label, 88 | avg = c("none", "horizontal", "vertical", "threshold"), 89 | spread.estimate = c("boxplot", "stderror", "stddev"), 90 | lwd = 3, 91 | rgraph = FALSE, 92 | col = "#5886be", 93 | random.col = "#5886be44", 94 | pr.poly = 0, 95 | ... 96 | ) 97 | 98 | \method{plot}{rocpr}( 99 | x, 100 | main = x$label, 101 | roc.avg = c("none", "horizontal", "vertical", "threshold"), 102 | roc.spread.estimate = c("boxplot", "stderror", "stddev"), 103 | roc.lwd = 3, 104 | roc.rgraph = FALSE, 105 | roc.col = "#bd0017", 106 | roc.random.col = "#bd001744", 107 | pr.avg = c("none", "horizontal", "vertical", "threshold"), 108 | pr.spread.estimate = c("boxplot", "stderror", "stddev"), 109 | pr.lwd = 3, 110 | pr.rgraph = FALSE, 111 | pr.col = "#5886be", 112 | pr.random.col = "#5886be44", 113 | pr.poly = 0, 114 | ... 115 | ) 116 | 117 | \method{plot}{univariate}( 118 | x, 119 | main = x$label, 120 | sim.hist = TRUE, 121 | sim.bar = TRUE, 122 | sim.density = TRUE, 123 | obs.hist = FALSE, 124 | obs.bar = TRUE, 125 | obs.density = TRUE, 126 | sim.adjust = 1, 127 | obs.adjust = 1, 128 | sim.lwd = 2, 129 | obs.lwd = 2, 130 | sim.col = "black", 131 | obs.col = "red", 132 | ... 133 | ) 134 | } 135 | \arguments{ 136 | \item{x}{An object created by one of the \code{gof} methods.} 137 | 138 | \item{...}{Arbitrary further arguments.} 139 | 140 | \item{mfrow}{Should the GOF plots come out separately (\code{mfrow = FALSE}), 141 | or should all statistics be aligned in a single diagram 142 | (\code{mfrow = TRUE})? Returning the plots separately can be helpful if the 143 | output is redirected to a multipage PDF or TIFF file.} 144 | 145 | \item{relative}{Print relative frequencies (as opposed to absolute 146 | frequencies) of a statistic on the y axis?} 147 | 148 | \item{transform}{A function which transforms the y values used for the 149 | boxplots. For example, if some of the values become very large and make the 150 | output illegible, \code{transform = function(x) x^0.1} or a similar 151 | transformation of the values can be used. Note that logarithmic 152 | transformations often produce infinite values because \code{log(0) = -Inf}, 153 | so one should rather use something like 154 | \code{transform = function(x) log1p} to avoid infinite values.} 155 | 156 | \item{xlim}{Horizontal limit of the boxplots. Only the maximum value must be 157 | provided, e.g., \code{xlim = 8}.} 158 | 159 | \item{main}{Main title of a GOF plot.} 160 | 161 | \item{xlab}{Label of the x-axis of a GOF plot.} 162 | 163 | \item{ylab}{Label of the y-axis of a GOF plot.} 164 | 165 | \item{border}{Color of the borders of the boxplots.} 166 | 167 | \item{boxplot.lwd}{Line width of boxplot.} 168 | 169 | \item{outline}{Print outliers in the boxplots?} 170 | 171 | \item{median}{Plot the median curve for the observed network?} 172 | 173 | \item{median.col}{Color of the median of the observed network statistic.} 174 | 175 | \item{median.lty}{Line type of median line. For example "dashed" or "solid".} 176 | 177 | \item{median.lwd}{Line width of median line.} 178 | 179 | \item{mean}{Plot the mean curve for the observed network?} 180 | 181 | \item{mean.col}{Color of the mean of the observed network statistic.} 182 | 183 | \item{mean.lty}{Line type of mean line. For example "dashed" or "solid".} 184 | 185 | \item{mean.lwd}{Line width of mean line.} 186 | 187 | \item{add}{Add the ROC and/or PR curve to an existing plot?} 188 | 189 | \item{avg}{Averaging pattern for the ROC and PR curve(s) if multiple target 190 | time steps were used. Allowed values are \code{"none"} (plot all curves 191 | separately), \code{"horizontal"} (horizontal averaging), \code{"vertical"} 192 | (vertical averaging), and \code{"threshold"} (threshold (= cutoff) 193 | averaging). Note that while threshold averaging is always feasible, 194 | vertical and horizontal averaging are not well-defined if the graph cannot 195 | be represented as a function x->y and y->x, respectively. More information 196 | can be obtained from the help pages of the \pkg{ROCR} package, the 197 | functions of which are employed here.} 198 | 199 | \item{spread.estimate}{When multiple target time steps are used and curve 200 | averaging is enabled, the variation around the average curve can be 201 | visualized as standard error bars (\code{"stderror"}), standard deviation 202 | bars (\code{"stddev"}), or by using box plots (\code{"boxplot"}). Note that 203 | the function plotCI, which is used internally by the \pkg{ROCR} package to 204 | draw error bars, might raise a warning if the spread of the curves at 205 | certain positions is 0. More details can be found in the documentation of 206 | the \pkg{ROCR} package, the functions of which are employed here.} 207 | 208 | \item{lwd}{Line width.} 209 | 210 | \item{rgraph}{Should an ROC or PR curve also be drawn for a random graph? 211 | This serves as a baseline against which to compare the actual ROC or PR 212 | curve.} 213 | 214 | \item{col}{Color of the ROC or PR curve.} 215 | 216 | \item{random.col}{Color of the ROC or PR curve of the random graph 217 | prediction.} 218 | 219 | \item{pr.poly}{If a value of \code{0} is set, nothing special happens. If a 220 | value of \code{1} is set, a straight line is fitted through the PR curve 221 | and displayed. Values between \code{2} and \code{9} fit higher-order 222 | polynomial curves through the PR curve and display the resulting curve. 223 | This argument allows to check whether the imputation of the first precision 224 | value in the PR curve yielded a reasonable result (in case the value had to 225 | be imputed).} 226 | 227 | \item{roc.avg}{Averaging pattern for the ROC curve(s) if multiple target time 228 | steps were used. Allowed values are \code{"none"} (plot all curves 229 | separately), \code{"horizontal"} (horizontal averaging), \code{"vertical"} 230 | (vertical averaging), and \code{"threshold"} (threshold (= cutoff) 231 | averaging). Note that while threshold averaging is always feasible, 232 | vertical and horizontal averaging are not well-defined if the graph cannot 233 | be represented as a function x->y and y->x, respectively. More information 234 | can be obtained from the help pages of the \pkg{ROCR} package, the 235 | functions of which are employed here.} 236 | 237 | \item{roc.spread.estimate}{When multiple target time steps are used and curve 238 | averaging is enabled, the variation around the average curve can be 239 | visualized as standard error bars (\code{"stderror"}), standard deviation 240 | bars (\code{"stddev"}), or by using box plots (\code{"boxplot"}). Note that 241 | the function plotCI, which is used internally by the \pkg{ROCR} package to 242 | draw error bars, might raise a warning if the spread of the curves at 243 | certain positions is 0. More details can be found in the documentation of 244 | the \pkg{ROCR} package, the functions of which are employed here.} 245 | 246 | \item{roc.lwd}{Line width.} 247 | 248 | \item{roc.rgraph}{Should an ROC curve also be drawn for a random graph? This 249 | serves as a baseline against which to compare the actual ROC curve.} 250 | 251 | \item{roc.col}{Color of the ROC curve.} 252 | 253 | \item{roc.random.col}{Color of the ROC curve of the random graph prediction.} 254 | 255 | \item{pr.avg}{Averaging pattern for the PR curve(s) if multiple target time 256 | steps were used. Allowed values are \code{"none"} (plot all curves 257 | separately), \code{"horizontal"} (horizontal averaging), \code{"vertical"} 258 | (vertical averaging), and \code{"threshold"} (threshold (= cutoff) 259 | averaging). Note that while threshold averaging is always feasible, 260 | vertical and horizontal averaging are not well-defined if the graph cannot 261 | be represented as a function x->y and y->x, respectively. More information 262 | can be obtained from the help pages of the \pkg{ROCR} package, the 263 | functions of which are employed here.} 264 | 265 | \item{pr.spread.estimate}{When multiple target time steps are used and curve 266 | averaging is enabled, the variation around the average curve can be 267 | visualized as standard error bars (\code{"stderror"}), standard deviation 268 | bars (\code{"stddev"}), or by using box plots (\code{"boxplot"}). Note that 269 | the function plotCI, which is used internally by the \pkg{ROCR} package to 270 | draw error bars, might raise a warning if the spread of the curves at 271 | certain positions is 0. More details can be found in the documentation of 272 | the \pkg{ROCR} package, the functions of which are employed here.} 273 | 274 | \item{pr.lwd}{Line width.} 275 | 276 | \item{pr.rgraph}{Should an PR curve also be drawn for a random graph? This 277 | serves as a baseline against which to compare the actual PR curve.} 278 | 279 | \item{pr.col}{Color of the PR curve.} 280 | 281 | \item{pr.random.col}{Color of the PR curve of the random graph prediction.} 282 | 283 | \item{sim.hist}{Draw a histogram for the simulated networks?} 284 | 285 | \item{sim.bar}{Draw a bar for the median of the statistic for the simulated 286 | networks?} 287 | 288 | \item{sim.density}{Draw a density curve fot the statistic for the simulated 289 | networks?} 290 | 291 | \item{obs.hist}{Draw a histogram for the observed networks?} 292 | 293 | \item{obs.bar}{Draw a bar for the median of the statistic for the observed 294 | networks?} 295 | 296 | \item{obs.density}{Draw a density curve fot the statistic for the observed 297 | networks?} 298 | 299 | \item{sim.adjust}{Bandwidth adjustment parameter for the density curve.} 300 | 301 | \item{obs.adjust}{Bandwidth adjustment parameter for the density curve.} 302 | 303 | \item{sim.lwd}{Line width for the simulated networks.} 304 | 305 | \item{obs.lwd}{Line width for the observed network(s).} 306 | 307 | \item{sim.col}{Color for the simulated networks.} 308 | 309 | \item{obs.col}{Color for the observed network(s).} 310 | } 311 | \description{ 312 | Plot and print methods for goodness-of-fit output for network models. 313 | } 314 | \details{ 315 | These plot and print methods serve to display the output generated by the 316 | \code{gof} function and its methods. See the help page of 317 | \code{\link{gof-methods}} for details on how to compute goodness-of-fit 318 | statistics. 319 | } 320 | \references{ 321 | Leifeld, Philip, Skyler J. Cranmer and Bruce A. Desmarais (2018): Temporal 322 | Exponential Random Graph Models with btergm: Estimation and Bootstrap 323 | Confidence Intervals. \emph{Journal of Statistical Software} 83(6): 1--36. 324 | \doi{10.18637/jss.v083.i06}. 325 | } 326 | -------------------------------------------------------------------------------- /R/ch_coauthor.R: -------------------------------------------------------------------------------- 1 | #' Swiss political science co-authorship network 2013 2 | #' 3 | #' Swiss political science co-authorship network 2013 4 | #' 5 | #' The Swiss political science co-authorship network 2013 dataset contains the 6 | #' co-authorship network of all political scientists at Swiss universities and 7 | #' research institutes in late 2013. The data are described in Leifeld and 8 | #' Ingold (2016) and Leifeld (2018). The data contained here include 9 | #' postdoctoral and professorial researchers but not PhD students, as in Leifeld 10 | #' (2018), without the PhD researchers included in Leifeld and Ingold (2016). 11 | #' For the full dataset, see the replication archive at DOI 12 | #' \doi{10.7910/DVN/85SK1M}. 13 | #' 14 | #' Leifeld and Ingold (2016) summarize the data collection strategy as follows: 15 | #' \emph{"Data gathering took place between July and December 2013. A single 16 | #' coder pursued a three-step coding procedure: he first created a list of all 17 | #' relevant university departments and research institutes that host political 18 | #' scientists in Switzerland, then he browsed the websites of these institutes 19 | #' and entered all researchers along with several details about them into a 20 | #' database, including their seniority status (predoctoral, postdoctoral, or 21 | #' professor) and the URL of their publication list (either the CV, the 22 | #' institutional website, a private homepage, or several of those items in order 23 | #' to get a complete publication profile of each person). After entering all 24 | #' researchers of an institute, the coder went through the researchers' 25 | #' publication lists and entered the following pieces of information for each 26 | #' publication into the database: the reporting author, the names of all 27 | #' co-authors, the title of the publication, the year, the name of the journal 28 | #' or book in which the publication appeared (if applicable), the names of all 29 | #' editors (if applicable), and a classification of the type of publication 30 | #' (academic journal, book chapter, monograph, edited volume, other). Most 31 | #' publications are relatively recent, but the earliest publications in the 32 | #' database date back to the 1960s. After completing these three steps, data 33 | #' entered at the beginning was double-checked in order to avoid bias due to new 34 | #' publications that may have shown up during the coding time period. This 35 | #' procedure is the best one can do in terms of completeness, but it should be 36 | #' clear that it crucially depends on the accuracy of the self-reported 37 | #' bibliographic information. For example, if a researcher did not update his or 38 | #' her CV or list of publications for the previous six months, those most recent 39 | #' publications only had a chance to enter the database if the co-authors listed 40 | #' the publication on their website. In some relatively rare cases, all authors 41 | #' failed to report recent updates, and this may cause minor inaccuracies in the 42 | #' network dataset, mostly affecting very recent publications in 2013 because 43 | #' there is, on average, a reporting lag."} 44 | #' 45 | #' Based on the collected publication data, a co-authorship network matrix with 46 | #' 156 nodes was created. In addition to this matrix, the dataset here contains 47 | #' node attribute data (institutional affiliations, location, demographics, 48 | #' language shares, publication type shares) and relational covariates 49 | #' (geographical distance, similarity in terms of the share of English articles, 50 | #' and topic similarity) as described in Leifeld (2018). The dataset can be used 51 | #' to replicate Leifeld (2018), but only approximately due to changes in the 52 | #' estimation routine in the \pkg{ergm} package since the article was published. 53 | #' 54 | #' @name coauthor 55 | #' 56 | #' @aliases ch_coauthor ch_coaut ch_dist100km ch_en_article_sim ch_nodeattr 57 | #' ch_topicsim 58 | #' 59 | #' @docType data 60 | #' 61 | #' @format 62 | #' \describe{ 63 | #' \item{\code{ch_coaut}}{is an undirected, weighted 156 x 156 adjancency matrix 64 | #' indicating how many publications political scientist in Switzerland shared 65 | #' with each other as reported in late 2013, including only postdoctoral and 66 | #' professorial political scientists affiliated with research institutes or 67 | #' universities. The exact edge weight should be treated with caution because 68 | #' some publications were counted multiple times because they were reported by 69 | #' multiple co-authors. The diagonal contains the number of publications of 70 | #' the respective author. Leifeld and Ingold (2016) describe the data collection 71 | #' process in more detail.} 72 | #' 73 | #' \item{\code{ch_nodeattr}}{is a data frame with node attributes/variables for 74 | #' the 156 researchers, in the same alphabetical row order as the network 75 | #' matrix. The first twelve columns with column labels starting with "inst_" are 76 | #' affiliations with different institutions (1 = affiliated; 0 = no 77 | #' affiliation). The next seven columns with column labels starting with "city_" 78 | #' contain the locations of the researchers' institutional affiliations. The 79 | #' "phdyear" column contains the self-reported year of obtaining the PhD, and 80 | #' the "birthyear" column contains the self-reported or publicly available year 81 | #' of birth; these two variables contain many missing values. The "status" 82 | #' column indicates whether a researcher was listed as a professor or as having 83 | #' postdoctoral or other non-professorial status at the time. "chairtitle" is 84 | #' the name of the chair or research group the researcher reported to be a 85 | #' member of. "num_publications" is the total number of publications, 86 | #' "num_articles" the number of journal articles among them, "num_books" the 87 | #' number of books among them, "share_articles" the percentage of journal 88 | #' articles among the publications, and "share_books" the percentage of 89 | #' monographs and edited volumes among the publications. The four columns with 90 | #' column names starting with "lang_" contain the relative shares of English, 91 | #' French, German, Italian, and other languages among the publications of the 92 | #' researcher. The column "share_en_articles" contains the percentage of English 93 | #' journal articles among all publications of the researcher. "male" is a dummy 94 | #' variable indicating whether the author is male (1) or female (0). The 95 | #' variables contained here are described in Leifeld (2018).} 96 | #' 97 | #' \item{\code{ch_dist100km}}{is a 156 x 156 matrix containing the geographical 98 | #' distance between any two researchers measured in units of 100km (for a 99 | #' reasonable scaling of coefficients in a statistical model), computed over the 100 | #' latitude and longitude of their main institutional affiliations. The measure 101 | #' is included in Leifeld (2018).} 102 | #' 103 | #' \item{\code{ch_en_article_sim}}{is a 156 x 156 matrix containing the 104 | #' similarity between any two researchers in terms of the share of their work 105 | #' that is published in English and as journal articles. Values closer to 1.0 106 | #' indicate that two researchers were similar in their language and publication 107 | #' type portfolio while values closer to 0 indicate that they were relatively 108 | #' dissimilar. Only extra-dyadic publications were counted in establishing this 109 | #' similarity. I.e., if researcher A and B co-authored, their joint publications 110 | #' were not included in establishing their English article share similarity. 111 | #' This was done to reduce endogeneity/reverse causality when modeling 112 | #' co-authorship as a function of English article share similarity. The measure 113 | #' is described in Leifeld (2018).} 114 | #' 115 | #' \item{\code{ch_topicsim}}{is a 156 x 156 topic similarity matrix for the 116 | #' researchers. Topic similarities were computed by taking into account all 117 | #' words in the publication titles of any two researchers, excluding the 118 | #' publications they published as co-authors (i.e., only extra-dyadic 119 | #' publications, to reduce endogeneity/reverse causality in modeling 120 | #' co-authorship ties as a function of topic similarity). Topic similarity was 121 | #' established by computing the cosine similarity between the tf-idf scores for 122 | #' the title words of any two researchers (i.e., a vector space model). Leifeld 123 | #' (2018) contains more details on this procedure.} 124 | #' } 125 | #' 126 | #' @references 127 | #' Leifeld, Philip (2018): Polarization in the Social Sciences: Assortative 128 | #' Mixing in Social Science Collaboration Networks is Resilient to 129 | #' Interventions. \emph{Physica A: Statistical Mechanics and its Applications} 130 | #' 507: 510--523. \doi{10.1016/j.physa.2018.05.109}. Full replication data: 131 | #' \doi{10.7910/DVN/85SK1M}. 132 | #' 133 | #' Leifeld, Philip and Karin Ingold (2016): Co-authorship Networks in Swiss 134 | #' Political Research. \emph{Swiss Political Science Review} 22(2): 264--287. 135 | #' \doi{10.1111/spsr.12193}. 136 | #' 137 | #' @source The data were collected from public information online. The full 138 | #' data collection details are described in Leifeld and Ingold (2016). 139 | #' 140 | #' @keywords datasets 141 | #' 142 | #' @examples 143 | #' \dontrun{ 144 | #' # Replication code for the full Swiss co-authorship ERGM in Leifeld (2018). 145 | #' # Note that the estimates can only be reproduced approximately due to 146 | #' # internal changes in the ergm package. 147 | #' 148 | #' library("network") 149 | #' library("ergm") 150 | #' 151 | #' data("ch_coauthor") 152 | #' 153 | #' # set up network object with node attributes 154 | #' ch_nw <- network(ch_coaut, directed = FALSE) 155 | #' set.vertex.attribute(ch_nw, "frequency", ch_nodeattr$num_publications) 156 | #' set.vertex.attribute(ch_nw, "status", as.character(ch_nodeattr$status)) 157 | #' set.vertex.attribute(ch_nw, "male", ch_nodeattr$male) 158 | #' set.vertex.attribute(ch_nw, "share_en_articles", 159 | #' ch_nodeattr$share_en_articles) 160 | #' 161 | #' # create same affiliation matrix 162 | #' ch_inst_indices <- which(grepl("^inst_.+", colnames(ch_nodeattr))) 163 | #' ch_same_affiliation <- as.matrix(ch_nodeattr[, ch_inst_indices]) %*% 164 | #' t(ch_nodeattr[, ch_inst_indices]) 165 | #' 166 | #' # create same chair matrix 167 | #' ch_nodeattr$chairtitle[ch_nodeattr$chairtitle == ""] <- NA 168 | #' ch_same_chair <- matrix(0, nrow = nrow(ch_same_affiliation), 169 | #' ncol = ncol(ch_same_affiliation)) 170 | #' for (i in 1:length(ch_nodeattr$chairtitle)) { 171 | #' for (j in 1:length(ch_nodeattr$chairtitle)) { 172 | #' if (i != j && 173 | #' !is.na(ch_nodeattr$chairtitle[i]) && 174 | #' !is.na(ch_nodeattr$chairtitle[j]) && 175 | #' ch_nodeattr$chairtitle[i] == ch_nodeattr$chairtitle[j] && 176 | #' ch_same_affiliation[i, j] == TRUE) { 177 | #' ch_same_chair[i, j] <- 1 178 | #' } 179 | #' } 180 | #' } 181 | #' rownames(ch_same_chair) <- rownames(ch_same_affiliation) 182 | #' colnames(ch_same_chair) <- colnames(ch_same_affiliation) 183 | #' 184 | #' # create supervision matrix (same chair + affiliation + mixed seniority) 185 | #' ch_supervision <- ch_same_affiliation * 186 | #' ch_same_chair * 187 | #' matrix(ch_nodeattr$status == "professor", 188 | #' nrow = nrow(ch_same_chair), 189 | #' ncol = ncol(ch_same_chair), 190 | #' byrow = FALSE) * 191 | #' matrix(ch_nodeattr$status != "professor", 192 | #' nrow = nrow(ch_same_chair), 193 | #' ncol = ncol(ch_same_chair), 194 | #' byrow = TRUE) 195 | #' 196 | #' # ERGM estimation 197 | #' ch_model <- ergm(ch_nw ~ 198 | #' edges + 199 | #' gwesp(0.3, fixed = TRUE) + 200 | #' gwdegree(0.4, fixed = TRUE) + 201 | #' nodecov("frequency") + 202 | #' nodefactor("status") + 203 | #' nodefactor("male") + 204 | #' nodematch("male") + 205 | #' edgecov(ch_dist100km) + 206 | #' edgecov(ch_same_affiliation) + 207 | #' edgecov(ch_same_chair) + 208 | #' edgecov(ch_supervision) + 209 | #' edgecov(ch_topicsim) + 210 | #' nodecov("share_en_articles") + 211 | #' edgecov(ch_en_article_sim), 212 | #' control = control.ergm(MCMLE.termination = "Hummel", 213 | #' MCMLE.effectiveSize = NULL)) 214 | #' summary(ch_model) # corresponds Column 1 in Table 3 in Leifeld (2018) 215 | #' } 216 | NULL --------------------------------------------------------------------------------