Epistemic Network Analysis (ENA) is a method for identifying and quantifying connections among elements in coded data and representing them in dynamic network models. A key feature of the ENA tool is that it enables researchers to compare different networks, both visually and through summary statistics that reflect the weighted structure of connections.
Researchers have used ENA to analyze phenomena including: cognitive connections students make while solving complex problems; interactions among brain regions in fMRI data; social gaze coordination; integration of operative skills during surgical procedures; and many others.
Python users: see
python/README.mdfor theqe-enapackage (import ena).
install.packages("rENA")
install.packages("rENA", repos = c("https://cran.qe-libs.org", "https://cran.rstudio.org"))
library(rENA)
codes <- c("Data", "Technical.Constraints", "Performance.Parameters",
"Client.and.Consultant.Requests", "Design.Reasoning", "Collaboration")
units <- c("UserName", "Condition", "GroupName")
conversation <- c("Condition", "GroupName")
# Simple form
model <- RS.data |>
accumulate(units, codes, conversation, default_window = 4) |>
model()
# Granular form (equivalent)
model <- RS.data |>
accumulate(units, codes, conversation, default_window = 4) |>
sphere_norm() |>
center() |>
rotate() |>
project() |>
optimize()
model$line.weights # normalised adjacency vectors (units × connections)
model$points.rotated # unit positions in ENA space
model$rotation.set$node.positions # code node positions
The accumulation step can be run independently of modeling — useful for inspecting raw networks, exporting them, or reusing the same accumulation with multiple rotation methods.
accum <- RS.data |> accumulate(units, codes, conversation, default_window = 4)
# Then pipe into model(), sphere_norm(), etc. separately
model <- accum |> sphere_norm() |> center() |> rotate() |> project() |> optimize()
| Rotation | Function |
|---|---|
| SVD (default) | rotate() |
| Means | rotate(ena.rotate.by.mean, g1, g2) |
| Generalised (GMR) | rotate(ena.rotate.by.generalized, x) |
| Regression (V ~ x) | rotate(ena.rotate.by.hena.regression, x) |
| Regression (x ~ V) | rotate(ena.rotate.by.hena.regression_2, x) |
| Custom matrix | rotate(mat) |
# Means rotation
model_mr <- RS.data |>
accumulate(units, codes, conversation, default_window = 4) |>
sphere_norm() |> center() |>
rotate(ena.rotate.by.mean,
RS.data$Condition == "FirstGame",
RS.data$Condition == "SecondGame") |>
project() |> optimize()
# Generalised rotation
model_gmr <- RS.data |>
accumulate(units, codes, conversation, default_window = 4) |>
sphere_norm() |> center() |>
rotate(ena.rotate.by.generalized, RS.data$Condition) |>
project() |> optimize()
| Option | Argument |
|---|---|
| Window back | default_window = 4 |
| Window forward | window_forward = 2 |
| Infinite window | default_window = Inf |
| Binary co-occurrence | weight.by = "binary" |
| Weighted co-occurrence | weight.by = "sum" |
ena.plot() and the ena.plot.*() layer functions draw with plotly by default,
or with qeviz (the library behind webENA's
network plots) when you add backend = "qeviz", or for the whole session:
options(rENA.plot.backend = "qeviz")

Compare the plot backends →: three views drawn by rENA with each backend and by qeviz's own functions, with the code for each.