Skip to contents

Introduction

The imaginarycss package provides tools for analyzing Cognitive Social Structures (CSS), focusing on the discrepancies between actual social networks and individuals’ perceptions of those networks. The package implements the methods described in Tanaka and Vega Yon (2023), “Imaginary Structures as Evidence of Social Cognition” (doi: 10.1016/j.socnet.2023.11.005).

What Are Cognitive Social Structures?

Cognitive Social Structures (CSS) represent how individuals perceive the entire social network around them. While traditional network analysis asks “Who is connected to whom?”, CSS analysis asks “Who thinks who is connected to whom?”

When we compare these perceptions to the actual network structure, we can identify various types of perceptual errors, including:

  • False positives: perceiving connections that don’t exist.
  • False negatives: missing connections that do exist.
  • Reciprocity errors: misperceiving whether relationships are mutual.

These errors are not random—they reveal systematic cognitive biases in how people understand social structure.

Key Features

  • Network construction: create binary-array graph objects from matrices or lists of adjacency matrices.
  • Error analysis: compute reciprocity errors and the full imaginary census (10 motif categories).
  • Tie-level accuracy: estimate individual-level accuracy rates for perceiving ties and non-ties.
  • Null models: generate null distributions for statistical testing of imaginary structures.

Creating Barry Graph Objects

The fundamental data structure in imaginarycss is the barry_graph object. It stores a collection of networks: the first is the “true” (baseline) network and subsequent layers represent each perceiver’s view of the network.

From a List of Matrices

The most straightforward way to create a barry_graph is from a list of adjacency matrices. The first matrix is the true network; the rest are individual perceptions.

# True network (4 nodes)
true_net <- matrix(c(
  0, 1, 0, 1,
  1, 0, 1, 0,
  0, 1, 0, 1,
  1, 0, 1, 0
), nrow = 4, byrow = TRUE)

# Person 1 over-perceives ties (false positives)
perceived_net1 <- matrix(c(
  0, 1, 1, 1,
  1, 0, 1, 0,
  1, 1, 0, 1,
  1, 0, 1, 0
), nrow = 4, byrow = TRUE)

# Person 2 under-perceives ties (false negatives)
perceived_net2 <- matrix(c(
  0, 1, 0, 0,
  1, 0, 0, 0,
  0, 0, 0, 1,
  0, 0, 1, 0
), nrow = 4, byrow = TRUE)

graph <- new_barry_graph(list(true_net, perceived_net1, perceived_net2))
print(graph)
A barry_graph with 3 networks of size 4
.    .  1.00     .  1.00
 1.00     .  1.00     .
    .  1.00     .  1.00
 1.00     .  1.00     .
Skipping 8 rows. Skipping 8 columns. 

From a Block-Diagonal Matrix

Alternatively, you can pass a single block-diagonal matrix where each n x n block corresponds to a network layer.

# Build an 8x8 block-diagonal matrix (two 4x4 networks)
source_ <- c(1, 2, 3, 1)
target_ <- c(2, 1, 4, 4)
source_ <- c(source_, source_[-1] + 4)
target_ <- c(target_, target_[-1] + 4)

adjmat <- matrix(0L, nrow = 8, ncol = 8)
adjmat[cbind(source_, target_)] <- 1L

graph2 <- new_barry_graph(adjmat, n = 4)
print(graph2)
A barry_graph with 2 networks of size 4
.    .  1.00     .  1.00
 1.00     .     .     .
    .     .     .  1.00
    .     .     .     .
Skipping 4 rows. Skipping 4 columns. 

Inspecting Graph Attributes

Once a barry_graph is created, you can inspect its attributes and extract an edge list. The netsize attribute gives the number of nodes per layer, and endpoints marks the boundary indices of each layer in the underlying block-diagonal structure.

# Network size and layer boundaries
data.frame(
  Attribute = c("Network size", "Endpoints"),
  Value     = c(attr(graph, "netsize"),
                paste(attr(graph, "endpoints"), collapse = ", "))
)
##      Attribute Value
## 1 Network size     4
## 2    Endpoints 8, 12

# First rows of the edge list
head(barray_to_edgelist(graph))
##      source target
## [1,]      1      2
## [2,]      1      4
## [3,]      2      1
## [4,]      2      3
## [5,]      3      2
## [6,]      3      4