| Title: | Projected Actor Locations for Spatial Interaction Modeling |
|---|---|
| Description: | Implements the Projected Actor Locations (PALS) method for spatial modeling of dyadic interactions between geographically mobile actors, as described in Kim, Liu and Desmarais (2023) <doi:10.1017/psrm.2022.6>. PALS applies exponential-smoothing weights to the spatiotemporal histories of a focal actor and its interaction partners ("alters") to project the location of future interactions. The package provides projection, maximum-similarity parameter estimation by minimizing great-circle (Haversine) prediction error, nonparametric bootstrap with multiple-imputation (Rubin's Rules) pooling, dyadic distance covariate construction, visualization, and a simulated example dataset of subnational conflict. |
| Authors: | Bruce A. Desmarais [aut, cre], Sangyeon Kim [aut], Howard Liu [aut] |
| Maintainer: | Bruce A. Desmarais <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-07-02 21:40:15 UTC |
| Source: | https://github.com/cran/palsr |
Quantifies uncertainty in PALS parameter estimates and in projected actor
locations by resampling events with replacement and re-estimating the model on
each bootstrap replicate, following Kim, Liu and Desmarais (2023). Each replicate
yields a parameter vector and (optionally) a set of Projected Actor Locations; the
collection of replicate PAL sets can be treated as multiple imputations and pooled
with Rubin's Rules (see pool_rubin()).
bootstrap_pals( events, R = 50, model = c("four", "one"), predict_time = NULL, actors = NULL, seed = NULL, ... )bootstrap_pals( events, R = 50, model = c("four", "one"), predict_time = NULL, actors = NULL, seed = NULL, ... )
events |
A pal_events object. |
R |
Number of bootstrap replicates (default |
model |
|
predict_time |
Optional |
actors |
For projection, which actors to project (default: all in |
seed |
Optional integer seed; replicate |
... |
Further arguments passed to estimate_pals (e.g. |
Resampling is over rows of events (the nonparametric event bootstrap).
Duplicated events are kept as ordinary repeated events. Replicates whose
optimizer fails to converge are retained but flagged via the convergence
column of estimates.
An object of class pals_boot with components:
estimatesAn R-row data.frame of replicate parameter estimates.
estimateThe point estimate on the full sample (an estimate_pals fit).
projectionsIf predict_time was given, a data.frame of projected
lon/lat for every actor-time-replicate combination; otherwise NULL.
R, model, call
Bookkeeping.
Methods: print(), summary() (bootstrap SEs / percentile intervals), and
coef() (the full-sample point estimate).
estimate_pals(), pool_rubin().
ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) bt <- bootstrap_pals(ev, R = 10, model = "one", seed = 1) summary(bt)ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) bt <- bootstrap_pals(ev, R = 10, model = "one", seed = 1) summary(bt)
Estimates the PALS smoothing parameters by minimizing the mean great-circle (Haversine) distance between observed event locations and the locations predicted from each event's preceding history ("marching forward": every prediction uses only events strictly earlier than the event being predicted).
estimate_pals( events, fit_events = NULL, model = c("four", "one"), start = NULL, method = NULL, aggregate = c("mean", "sum"), alter_weight = c("normalized", "legacy"), eps = 0.01, radius = 6371.0088, cutoff = c("day", "month", "year"), control = list() )estimate_pals( events, fit_events = NULL, model = c("four", "one"), start = NULL, method = NULL, aggregate = c("mean", "sum"), alter_weight = c("normalized", "legacy"), eps = 0.01, radius = 6371.0088, cutoff = c("day", "month", "year"), control = list() )
events |
A pal_events object providing the actor histories. |
fit_events |
Optional |
model |
|
start |
Optional numeric starting vector on the optimizer's scale
( |
method |
Optimizer method passed to stats::optim ( |
aggregate |
|
alter_weight, eps, cutoff
|
See project_pal. |
radius |
Sphere radius for the Haversine objective (km). |
control |
A list of control parameters for stats::optim. |
An object of class pals_fit with components params (estimated
pals_params), model, objective (minimized mean/sum distance), n_used
(events contributing), convergence, optim (raw optimizer output), events,
settings, and call. Methods: print(), summary(), coef(), predict(),
plot().
project_pals(), predict_event_locations(), bootstrap_pals().
ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) fit <- estimate_pals(ev, model = "one") fit coef(fit)ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) fit <- estimate_pals(ev, model = "one") fit coef(fit)
Vectorized great-circle distance between longitude/latitude points. Arguments are recycled to a common length, so any may be length 1.
haversine(lon1, lat1, lon2, lat2, radius = 6371.0088)haversine(lon1, lat1, lon2, lat2, radius = 6371.0088)
lon1, lat1, lon2, lat2
|
Numeric vectors of coordinates in decimal degrees. |
radius |
Sphere radius in the desired output units. Defaults to the mean Earth
radius, |
A numeric vector of distances. NA in any coordinate gives NA.
haversine(0, 0, 0, 1) # ~111 km per degree of latitude haversine(7.4, 9.1, 8.5, 12.0) # Abuja-ish to Kano-ishhaversine(0, 0, 0, 1) # ~111 km per degree of latitude haversine(7.4, 9.1, 8.5, 12.0) # Abuja-ish to Kano-ish
Real dyadic conflict events in Nigeria, bundled from the replication archive for the authors' study, Kim, Liu and Desmarais (2023). Each row is a recorded interaction between two actors at a known date and location; this is the data on which the PALS method was developed and validated.
nigeria_aclednigeria_acled
A pal_events object (a data.frame subclass) with 1,549 rows and 5 columns:
Character name of the first actor in the dyad.
Character name of the second actor in the dyad.
Date of the event.
Event longitude (decimal degrees).
Event latitude (decimal degrees).
These data are part of the publicly available replication materials for that study and can be downloaded directly from the Harvard Dataverse at doi:10.7910/DVN/NLWWPE.
Public replication archive for Kim, Liu and Desmarais (2023), Harvard Dataverse, doi:10.7910/DVN/NLWWPE.
Kim, S., Liu, H., and Desmarais, B. A. (2023). Spatial modeling of dyadic geopolitical interactions between moving actors. Political Science Research and Methods, 11(3), 633-644. doi:10.1017/psrm.2022.6
data(nigeria_acled) nigeria_acled fit <- estimate_pals(nigeria_acled, model = "one") coef(fit)data(nigeria_acled) nigeria_acled fit <- estimate_pals(nigeria_acled, model = "one") coef(fit)
A deterministic, seeded simulation of dyadic interaction events among 25 mobile
actors over 2000-2016, with the qualitative spatiotemporal structure that the PALS
method targets: actors drift slowly through space and interact preferentially with
nearby actors. It is produced by simulate_conflict_events() and is used in the
package examples, tests, and vignette so that they run without any external
dependency. It complements nigeria_acled, the bundled real-data example.
nigeria_simnigeria_sim
A pal_events object (a data.frame subclass) with 1500 rows and 5
columns:
Character id of the first actor in the dyad.
Character id of the second actor in the dyad.
Date of the event.
Event longitude (decimal degrees).
Event latitude (decimal degrees).
Generated by data-raw/nigeria_sim.R via
simulate_conflict_events(n_actors = 25, n_events = 1500, years = 2000:2016, seed = 20230101).
data(nigeria_sim) nigeria_sim fit <- estimate_pals(nigeria_sim, model = "one") coef(fit)data(nigeria_sim) nigeria_sim fit <- estimate_pals(nigeria_sim, model = "one") coef(fit)
Builds the dyadic distance covariate used to model interaction likelihood: the Haversine distance between the two actors' Projected Actor Locations.
pal_distance( events, dyads, params, transform = c("none", "log"), offset = 0.01, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )pal_distance( events, dyads, params, transform = c("none", "log"), offset = 0.01, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )
events |
A pal_events object. |
dyads |
A |
params |
A pals_params or fitted estimate_pals object. |
transform |
|
offset |
Offset added before logging (default |
alter_weight, eps, cutoff
|
See project_pal. |
dyads augmented with pal_distance (and, for transform = "log",
pal_log_distance).
ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) fit <- estimate_pals(ev, model = "one") dy <- data.frame(actor1 = "G01", actor2 = "G02", time = as.Date("2012-12-01")) pal_distance(ev, dy, fit)ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) fit <- estimate_pals(ev, model = "one") dy <- data.frame(actor1 = "G01", actor2 = "G02", time = as.Date("2012-12-01")) pal_distance(ev, dy, fit)
pal_events() builds the core data object used throughout palsr: a table of
dyadic interaction events, each involving two actors at a known time and location.
Projected Actor Locations are computed from these histories.
pal_events( data, actor1 = "actor1", actor2 = "actor2", time = "time", lon = "lon", lat = "lat", drop_self = TRUE )pal_events( data, actor1 = "actor1", actor2 = "actor2", time = "time", lon = "lon", lat = "lat", drop_self = TRUE )
data |
A |
actor1, actor2
|
Column names (length-1 character) identifying the two actors involved in each event. The pair is treated as unordered. |
time |
Column name of the event time. Must be a |
lon, lat
|
Column names of the event longitude and latitude, in decimal degrees. |
drop_self |
Logical; drop events whose two actors are identical (default |
Longitudes must lie in [-180, 180] and latitudes in [-90, 90]. Rows with
missing actor, time, or coordinate values are dropped with a message.
An object of class pal_events (a data.frame subclass) with canonical
columns actor1, actor2, time, lon, lat, sorted by time.
df <- data.frame( from = c("A", "A", "B"), to = c("B", "C", "C"), when = as.Date(c("2001-01-01", "2001-06-01", "2002-01-01")), x = c(7.1, 8.0, 7.5), y = c(9.0, 9.4, 10.1) ) ev <- pal_events(df, actor1 = "from", actor2 = "to", time = "when", lon = "x", lat = "y") evdf <- data.frame( from = c("A", "A", "B"), to = c("B", "C", "C"), when = as.Date(c("2001-01-01", "2001-06-01", "2002-01-01")), x = c(7.1, 8.0, 7.5), y = c(9.0, 9.4, 10.1) ) ev <- pal_events(df, actor1 = "from", actor2 = "to", time = "when", lon = "x", lat = "y") ev
A lightweight container for the four PALS smoothing parameters. Use it to project actor locations with known parameters (e.g. values reported in a paper), without estimating them from data.
pals_params(alpha, beta = 0, gamma = 0, eta = 0, model = c("four", "one"))pals_params(alpha, beta = 0, gamma = 0, eta = 0, model = c("four", "one"))
alpha |
Time-decay of the focal actor's own event history ( |
beta |
Time-decay of the alters' event histories ( |
gamma |
Intercept of the logistic mixing weight |
eta |
Slope of the logistic mixing weight on the event-count ratio. Ignored in the one-parameter model. |
model |
Either |
The mixing weight is , where
compares the number of focal events
() with the number of alter events (). The projected location is
times the recency-weighted mean of the focal actor's own past event
locations plus times the recency-weighted mean of its alters' locations.
See project_pals() and the package vignette.
An object of class pals_params.
p <- pals_params(alpha = 0.9, beta = 0.2, gamma = -10, eta = -10) p pals_params(alpha = 0.9, model = "one")p <- pals_params(alpha = 0.9, beta = 0.2, gamma = -10, eta = -10) p pals_params(alpha = 0.9, model = "one")
Combines per-imputation point estimates and variances of a scalar quantity into a
single pooled estimate with a variance that accounts for both within- and
between-imputation uncertainty (Rubin, 1987). Use it to pool estimands computed on
each bootstrap/imputation replicate of bootstrap_pals() — for example a
regression coefficient from a dyadic model fit on each replicate's PAL distances.
pool_rubin(estimates, variances, df = FALSE, dfcom = Inf)pool_rubin(estimates, variances, df = FALSE, dfcom = Inf)
estimates |
Numeric vector of per-imputation point estimates |
variances |
Numeric vector of per-imputation variances |
df |
Logical; if |
dfcom |
Complete-data degrees of freedom, used only when |
With imputations,
and total variance . The fraction of missing
information is . When df = TRUE, the Barnard-Rubin (1999)
small-sample degrees of freedom are used.
A one-row data.frame with the pooled estimate qbar, within-imputation
variance ubar, between-imputation variance b, total variance t, standard
error se, fraction of missing information fmi, and (if df = TRUE) df and
p.value.
Rubin, D. B. (1987). Multiple Imputation for Nonresponse in Surveys. Wiley.
Barnard, J. and Rubin, D. B. (1999). Small-sample degrees of freedom with multiple imputation. Biometrika, 86(4), 948-955.
# Five imputations of a coefficient and its variance. q <- c(1.10, 0.95, 1.20, 1.05, 0.98) u <- c(0.04, 0.05, 0.045, 0.038, 0.052) pool_rubin(q, u) pool_rubin(q, u, df = TRUE, dfcom = 100)# Five imputations of a coefficient and its variance. q <- c(1.10, 0.95, 1.20, 1.05, 0.98) u <- c(0.04, 0.05, 0.045, 0.038, 0.052) pool_rubin(q, u) pool_rubin(q, u, df = TRUE, dfcom = 100)
For each dyadic target (a pair of actors at a time), predicts the interaction location as the mean of the two actors' Projected Actor Locations. Optionally scores the prediction against an observed location.
predict_event_locations( events, targets, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )predict_event_locations( events, targets, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )
events |
A pal_events object supplying the histories. |
targets |
A |
params |
A pals_params or fitted estimate_pals object. |
alter_weight, eps, cutoff
|
See project_pal. |
targets augmented with pred_lon, pred_lat, and (if observed lon/lat
were supplied) error_km, the Haversine distance between predicted and observed
locations. Predictions are NA when both actors lack usable history.
ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) fit <- estimate_pals(ev, model = "one") tg <- ev[ev$time > as.Date("2012-01-01"), ] head(predict_event_locations(ev, tg, fit))ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) fit <- estimate_pals(ev, model = "one") tg <- ev[ev$time > as.Date("2012-01-01"), ] head(predict_event_locations(ev, tg, fit))
Project locations from a fitted PALS model
## S3 method for class 'pals_fit' predict( object, newdata = NULL, predict_time = NULL, type = c("pal", "event"), actors = NULL, ... )## S3 method for class 'pals_fit' predict( object, newdata = NULL, predict_time = NULL, type = c("pal", "event"), actors = NULL, ... )
object |
A |
newdata |
Optional pal_events (for |
predict_time |
For |
type |
|
actors |
For |
... |
Unused. |
A data.frame of projections (see project_pals / predict_event_locations).
ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) fit <- estimate_pals(ev, model = "one") predict(fit, predict_time = as.Date("2013-12-01"), type = "pal")[1:5, ]ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) fit <- estimate_pals(ev, model = "one") predict(fit, predict_time = as.Date("2013-12-01"), type = "pal")[1:5, ]
Computes the Projected Actor Location (PAL) for one actor at one or more prediction times, given a parameter set.
project_pal( events, actor, predict_time, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )project_pal( events, actor, predict_time, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )
events |
A pal_events object. |
actor |
The focal actor id (length-1 character). |
predict_time |
A |
params |
A pals_params object or a fitted estimate_pals object. |
alter_weight |
|
eps |
Numerical offset inside each age weight (default |
cutoff |
History granularity: |
A data.frame with one row per prediction time and columns actor, time,
lon, lat, n_focal, n_alter, has_history. lon/lat are NA when the
actor has no prior events.
ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) p <- pals_params(alpha = 0.9, model = "one") project_pal(ev, actor = "G01", predict_time = as.Date("2010-12-01"), params = p)ev <- simulate_conflict_events(n_actors = 8, n_events = 200, seed = 1) p <- pals_params(alpha = 0.9, model = "one") project_pal(ev, actor = "G01", predict_time = as.Date("2010-12-01"), params = p)
Computes Projected Actor Locations for a set of actors at one or more prediction times (an actor-by-time grid).
project_pals( events, actors = NULL, predict_time, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )project_pals( events, actors = NULL, predict_time, params, alter_weight = c("normalized", "legacy"), eps = 0.01, cutoff = c("day", "month", "year") )
events |
A pal_events object. |
actors |
Character vector of actor ids. Defaults to all actors in |
predict_time |
A |
params |
A pals_params object or a fitted estimate_pals object. |
alter_weight |
|
eps |
Numerical offset inside each age weight (default |
cutoff |
History granularity: |
A data.frame with columns actor, time, lon, lat, n_focal,
n_alter, has_history (one row per actor-time combination).
ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) p <- pals_params(alpha = 0.9, beta = 0.2, gamma = -10, eta = -10) pal <- project_pals(ev, predict_time = as.Date("2010-12-01"), params = p) head(pal)ev <- simulate_conflict_events(n_actors = 10, n_events = 300, seed = 1) p <- pals_params(alpha = 0.9, beta = 0.2, gamma = -10, eta = -10) pal <- project_pals(ev, predict_time = as.Date("2010-12-01"), params = p) head(pal)
Generates a synthetic dataset of dyadic interaction events with the qualitative structure PALS is designed for: a set of armed-group-like actors, each following a slowly drifting spatial trajectory, that interact preferentially with nearby actors. Useful for examples, tests, and the package vignette. The geographic frame approximates Nigeria, echoing the application in Kim, Liu and Desmarais (2023).
simulate_conflict_events( n_actors = 30, n_events = 2000, years = 2000:2016, drift = 0.18, jitter = 0.25, decay = 2, bbox = c(2.7, 14.7, 4, 13.9), seed = NULL )simulate_conflict_events( n_actors = 30, n_events = 2000, years = 2000:2016, drift = 0.18, jitter = 0.25, decay = 2, bbox = c(2.7, 14.7, 4, 13.9), seed = NULL )
n_actors |
Number of actors (default 30). |
n_events |
Number of dyadic events (default 2000). |
years |
Integer vector of years to span (default |
drift |
Standard deviation (decimal degrees per year) of each actor's directional
drift; larger values make actors more mobile (default |
jitter |
Standard deviation (decimal degrees) of event-location noise around the
midpoint of the two actors' current locations (default |
decay |
Spatial interaction scale (degrees): partners are chosen with probability
proportional to |
bbox |
Bounding box |
seed |
Optional integer seed for reproducibility. |
A pal_events object with columns actor1, actor2, time, lon, lat.
ev <- simulate_conflict_events(n_actors = 12, n_events = 400, seed = 42) ev summary(ev)ev <- simulate_conflict_events(n_actors = 12, n_events = 400, seed = 42) ev summary(ev)