1 Why Marketers Should Care About Friend Graphs

Every like, friend request, tagged photo, and shared post leaves behind a trace of who is connected to whom. That trace is a network, and networks are one of the richest sources of customer insight available to marketing teams.

Two customers who share many mutual friends may be more likely to respond to the same campaign than two unrelated customers. A customer who sits at the center of many friendship triangles may be someone other people listen to. Two customers who are not directly connected but share several mutual contacts may represent a warm introduction opportunity.

This tutorial uses the igraph package in R to explore the same core ideas found in large-scale graph tools such as Spark GraphFrames. The analyses focus on practical marketing decisions:

  • identifying shared customer characteristics;
  • finding tightly connected groups;
  • recommending new connections;
  • identifying potential influencers; and
  • understanding website authority through PageRank.

2 Network Fundamentals

A network, also called a graph, contains two basic elements:

  • Vertices or nodes: the people, customers, pages, or other objects being studied.
  • Edges: the relationships or connections between those objects.

2.1 My Personal Circle: Family and CSUCI

Before analyzing a large social-media network, it helps to begin with a network that is familiar. The following example represents my own circle. It includes my immediate family, extended family on both sides, several cousins, and the professional and academic relationships I have developed at CSUCI.

Because individual relatives and CSUCI contacts are not named here, descriptive labels are used. These labels can be replaced with actual first names later without changing the network structure.

2.2 My Family Network

This family graph places me at the center of the network. My closest connections are my mom, my dad, and my brother Justin. My dad connects our immediate family to his brother, Uncle Todd. Uncle Todd is married to Aunt Laura, and their sons Josh and Danny are my cousins.

family_edges <- tribble(
  ~from, ~to,

  # My direct family connections
  "Britnay", "Mom",
  "Britnay", "Dad",
  "Britnay", "Justin",
  "Britnay", "Aunt Laura",
  "Britnay", "Uncle Todd",
  "Britnay", "Josh",
  "Britnay", "Danny",

  # Family relationships
  "Mom", "Dad",
  "Mom", "Justin",
  "Dad", "Justin",
  "Dad", "Uncle Todd",
  "Uncle Todd", "Aunt Laura",
  "Uncle Todd", "Josh",
  "Aunt Laura", "Josh",
  "Uncle Todd", "Danny",
  "Aunt Laura", "Danny",
  "Josh", "Danny"
)

family_nodes <- tibble(
  name = c(
    "Britnay", "Mom", "Dad", "Justin",
    "Aunt Laura", "Uncle Todd", "Josh", "Danny"
  ),
  group = c(
    "Me", "Immediate Family", "Immediate Family", "Immediate Family",
    "Dad's Side", "Dad's Side", "Cousins", "Cousins"
  )
)

family_graph <- graph_from_data_frame(
  d = family_edges,
  vertices = family_nodes,
  directed = FALSE
)

# Manual coordinates keep Britnay in the center of the graph.
family_layout <- matrix(
  c(
     0.0,  0.0,  # Britnay
    -1.6,  1.2,  # Mom
     1.6,  1.2,  # Dad
    -1.7, -1.1,  # Justin
     2.8, -0.2,  # Aunt Laura
     2.8,  1.2,  # Uncle Todd
     1.7, -1.5,  # Josh
     3.2, -1.5   # Danny
  ),
  ncol = 2,
  byrow = TRUE,
  dimnames = list(family_nodes$name, c("x", "y"))
)

plot(
  family_graph,
  layout = family_layout[V(family_graph)$name, ],
  vertex.size = ifelse(V(family_graph)$name == "Britnay", 38, 27),
  vertex.label.cex = 0.82,
  vertex.label.dist = 0.3,
  vertex.frame.color = "white",
  edge.width = 1.6,
  edge.curved = 0.08,
  main = "My Family Connection Network"
)

In this graph:

  • I am positioned at the center of the network.
  • Mom, Dad, and Justin form my immediate-family circle.
  • Dad connects the immediate family to Uncle Todd, his brother.
  • Aunt Laura and Uncle Todd connect to their sons, Josh and Danny.
  • My direct connections to each person show that they are all part of my personal social circle.
family_degree <- tibble(
  person = V(family_graph)$name,
  degree = as.numeric(degree(family_graph))
) %>%
  arrange(desc(degree), person)

family_degree %>%
  kable(caption = "Connections in My Family Network") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  )
Connections in My Family Network
person degree
Britnay 7
Uncle Todd 5
Aunt Laura 4
Dad 4
Danny 4
Josh 4
Justin 3
Mom 3

Knowledge check: Why does Britnay have the highest degree in this graph?
Answer: Britnay is directly connected to every family member shown, so she has the greatest number of direct connections.

2.3 My CSUCI Network

My CSUCI network includes relationships developed through work, graduate school, class projects, and campus collaboration. The generic labels below can be replaced with actual names or specific departments later.

csuci_edges <- tribble(
  ~from, ~to,
  "Britnay", "Manager",
  "Britnay", "Marketing Team",
  "Britnay", "Recruitment Team",
  "Britnay", "Admissions Team",
  "Britnay", "Student Assistants",
  "Britnay", "Program Directors",
  "Britnay", "Faculty Partners",
  "Britnay", "Graduate Professors",
  "Britnay", "Class Teammates",
  "Britnay", "Graduate Cohort",
  "Britnay", "Campus Partners",

  # Workplace collaboration
  "Manager", "Marketing Team",
  "Manager", "Recruitment Team",
  "Marketing Team", "Student Assistants",
  "Marketing Team", "Program Directors",
  "Recruitment Team", "Admissions Team",
  "Recruitment Team", "Program Directors",
  "Admissions Team", "Student Assistants",
  "Program Directors", "Faculty Partners",
  "Faculty Partners", "Campus Partners",

  # Graduate-program collaboration
  "Graduate Professors", "Class Teammates",
  "Graduate Professors", "Graduate Cohort",
  "Class Teammates", "Graduate Cohort",
  "Class Teammates", "Marketing Team",
  "Graduate Cohort", "Campus Partners"
)

csuci_nodes <- tibble(name = unique(c(csuci_edges$from, csuci_edges$to))) %>%
  mutate(
    group = case_when(
      name == "Britnay" ~ "Me",
      name %in% c(
        "Manager", "Marketing Team", "Recruitment Team", "Admissions Team",
        "Student Assistants", "Program Directors", "Faculty Partners"
      ) ~ "CSUCI Workplace",
      name %in% c("Graduate Professors", "Class Teammates", "Graduate Cohort") ~
        "Graduate Program",
      TRUE ~ "Campus Network"
    )
  )

csuci_graph <- graph_from_data_frame(
  d = csuci_edges,
  vertices = csuci_nodes,
  directed = FALSE
)

set.seed(500)
plot(
  csuci_graph,
  layout = layout_with_fr(csuci_graph),
  vertex.size = ifelse(V(csuci_graph)$name == "Britnay", 36, 28),
  vertex.label.cex = 0.78,
  vertex.label.dist = 0.4,
  vertex.frame.color = "white",
  edge.width = 1.7,
  main = "My CSUCI Academic and Professional Network"
)

The CSUCI graph demonstrates how one person may belong to multiple overlapping communities. My role connects workplace teams, academic relationships, and broader campus partners. This is important in social network analysis because people who connect otherwise separate groups often have high betweenness centrality and can help information travel across the organization.

csuci_centrality <- tibble(
  connection = V(csuci_graph)$name,
  degree = as.numeric(degree(csuci_graph)),
  betweenness = as.numeric(
    betweenness(csuci_graph, directed = FALSE, normalized = TRUE)
  )
) %>%
  arrange(desc(betweenness), desc(degree))

csuci_centrality %>%
  kable(
    caption = "Centrality in My CSUCI Network",
    digits = 3
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  )
Centrality in My CSUCI Network
connection degree betweenness
Britnay 11 0.567
Marketing Team 5 0.052
Recruitment Team 4 0.024
Program Directors 4 0.024
Class Teammates 4 0.018
Graduate Cohort 4 0.018
Admissions Team 3 0.009
Faculty Partners 3 0.009
Student Assistants 3 0.009
Campus Partners 3 0.009
Manager 3 0.006
Graduate Professors 3 0.000

Knowledge check: Why might a person with high betweenness centrality be important at CSUCI?
Answer: That person connects different teams or communities and can help information, resources, and collaboration move between them.

2.4 My Combined Circle

The final graph combines my family and CSUCI relationships. It illustrates that a person’s social network often contains multiple communities connected through one central individual.

combined_edges <- bind_rows(
  family_edges %>% mutate(network = "Family"),
  csuci_edges %>% mutate(network = "CSUCI")
) %>%
  distinct(from, to, .keep_all = TRUE)

combined_nodes <- tibble(
  name = unique(c(combined_edges$from, combined_edges$to))
) %>%
  mutate(
    community = case_when(
      name == "Britnay" ~ "Me",
      name %in% family_nodes$name ~ "Family",
      TRUE ~ "CSUCI"
    )
  )

combined_graph <- graph_from_data_frame(
  d = combined_edges,
  vertices = combined_nodes,
  directed = FALSE
)

set.seed(580)
plot(
  combined_graph,
  layout = layout_with_fr(combined_graph),
  vertex.size = ifelse(V(combined_graph)$name == "Britnay", 38, 20),
  vertex.label.cex = 0.62,
  vertex.label.dist = 0.3,
  vertex.frame.color = "white",
  edge.width = 1.2,
  main = "My Combined Family and CSUCI Circle"
)

In this combined graph, I function as the main bridge between two major communities. The family network represents personal support and long-term relationships, while the CSUCI network represents academic, professional, and collaborative relationships. Together, they demonstrate how network analysis can reveal the different roles a person occupies across social settings.

3 The Facebook Circles-Style Dataset

The Stanford Network Analysis Project’s Facebook Circles dataset is an anonymized snapshot of Facebook friendships. It contains users as vertices, friendship links as edges, and user attributes such as birthday, hometown, employer, and school.

For this tutorial, we simulate a smaller network that has the same general structure:

  • Vertices: id, birthday, hometown, employer_id, and school_id
  • Edges: src and dst, representing friendship connections

4 Simulating the Data

4.1 Step 1: Create the Vertex Table

set.seed(580)

n_users <- 150

vertices <- tibble(
  id = 1:n_users,
  birthday = sample(
    seq(as.Date("1985-01-01"), as.Date("2005-12-31"), by = "day"),
    n_users,
    replace = TRUE
  ),
  hometown = sample(paste("City", LETTERS[1:8]), n_users, replace = TRUE),
  employer_id = sample(1:12, n_users, replace = TRUE),
  school_id = sample(1:10, n_users, replace = TRUE)
)

vertices %>%
  slice_head(n = 5) %>%
  kable(caption = "First Five Rows of the Simulated User Table") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
First Five Rows of the Simulated User Table
id birthday hometown employer_id school_id
1 1989-08-29 City B 4 5
2 1991-01-04 City B 12 5
3 1998-04-19 City A 1 4
4 1986-02-27 City H 10 9
5 1988-04-09 City H 5 2

Each row represents one user. The attributes allow marketers to examine questions such as whether connected users share a school, employer, hometown, or birthday.

4.2 Step 2: Create the Edge Table

A Barabasi-Albert preferential-attachment graph creates a realistic hub pattern in which a small number of users are highly connected while most users have fewer connections.

set.seed(580)

g_sim <- sample_pa(
  n = n_users,
  power = 1.1,
  m = 3,
  directed = FALSE
)

edges <- as_data_frame(g_sim, what = "edges") %>%
  transmute(
    src = as.integer(from),
    dst = as.integer(to)
  )

edges %>%
  slice_head(n = 5) %>%
  kable(caption = "First Five Rows of the Simulated Friendship Table") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
First Five Rows of the Simulated Friendship Table
src dst
1 2
1 3
2 3
1 4
2 4

Knowledge check: Which table stores friendship relationships?
Answer: The edge table.

5 Building the Graph

The graph_from_data_frame() function combines the edge table and vertex table into one graph object.

fb_graph <- graph_from_data_frame(
  d = edges,
  vertices = vertices,
  directed = FALSE
)

fb_graph
## IGRAPH 9a45c36 UN-- 150 444 -- 
## + attr: name (v/c), birthday (v/n), hometown (v/c), employer_id (v/n),
## | school_id (v/n)
## + edges from 9a45c36 (vertex names):
##  [1] 1 --2  1 --3  2 --3  1 --4  2 --4  3 --4  3 --5  1 --5  4 --5  2 --6 
## [11] 3 --6  1 --6  1 --7  2 --7  3 --7  4 --8  2 --8  6 --8  4 --9  1 --9 
## [21] 3 --9  3 --10 2 --10 1 --10 6 --11 1 --11 3 --11 11--12 1 --12 6 --12
## [31] 6 --13 12--13 10--13 2 --14 11--14 3 --14 4 --15 8 --15 3 --15 3 --16
## [41] 6 --16 11--16 13--17 10--17 2 --17 4 --18 6 --18 5 --18 9 --19 1 --19
## [51] 2 --19 18--20 11--20 1 --20 6 --21 1 --21 10--21 10--22 3 --22 6 --22
## [61] 17--23 11--23 19--23 13--24 10--24 2 --24 6 --25 1 --25 23--25 1 --26
## + ... omitted several edges

5.1 Viewing Edge Triplets

A triplet displays an edge together with the attributes of both users connected by that edge.

triplets <- as_data_frame(fb_graph, what = "edges") %>%
  transmute(
    from = as.integer(from),
    to = as.integer(to)
  ) %>%
  left_join(vertices, by = c("from" = "id")) %>%
  rename(
    a_birthday = birthday,
    a_hometown = hometown,
    a_employer_id = employer_id,
    a_school_id = school_id
  ) %>%
  left_join(vertices, by = c("to" = "id")) %>%
  rename(
    b_birthday = birthday,
    b_hometown = hometown,
    b_employer_id = employer_id,
    b_school_id = school_id
  )

triplets %>%
  slice_head(n = 3) %>%
  kable(caption = "Sample Triplets: An Edge and Both Users' Attributes") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = TRUE,
    font_size = 12
  )
Sample Triplets: An Edge and Both Users’ Attributes
from to a_birthday a_hometown a_employer_id a_school_id b_birthday b_hometown b_employer_id b_school_id
1 2 1989-08-29 City B 4 5 1991-01-04 City B 12 5
1 3 1989-08-29 City B 4 5 1998-04-19 City A 1 4
2 3 1991-01-04 City B 12 5 1998-04-19 City A 1 4

6 Graph Analysis and Marketing Applications

6.1 1. Finding Friends With the Same Birthday

A shared birthday is a simple personalization opportunity for a loyalty campaign or a celebrate-together promotion.

same_birthday <- triplets %>%
  filter(a_birthday == b_birthday) %>%
  transmute(
    user_a = from,
    user_b = to,
    shared_birthday = a_birthday
  )

if (nrow(same_birthday) == 0) {
  tibble(result = "No directly connected users share the exact same birthday in this simulation.") %>%
    kable(caption = "Friend Pairs Who Share a Birthday") %>%
    kable_styling(full_width = FALSE)
} else {
  same_birthday %>%
    slice_head(n = 5) %>%
    kable(caption = "Friend Pairs Who Share a Birthday") %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
}
Friend Pairs Who Share a Birthday
result
No directly connected users share the exact same birthday in this simulation.

Marketing takeaway: A loyalty program could offer a shared discount to connected customers who celebrate a birthday on the same date or in the same month.

6.2 2. Counting Friendship Triangles

A triangle consists of three users who are all mutually connected. Users in many triangles may belong to cohesive communities where recommendations spread through trusted relationships.

triangle_counts <- tibble(
  id = as.integer(V(fb_graph)$name),
  triangle_count = count_triangles(fb_graph)
) %>%
  left_join(vertices, by = "id") %>%
  arrange(desc(triangle_count))

triangle_counts %>%
  slice_head(n = 10) %>%
  select(id, hometown, employer_id, school_id, triangle_count) %>%
  kable(caption = "Users Embedded in the Most Friendship Triangles") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Users Embedded in the Most Friendship Triangles
id hometown employer_id school_id triangle_count
1 City B 4 5 50
3 City A 1 4 36
2 City B 12 5 35
6 City E 6 1 29
4 City H 10 9 15
11 City F 2 4 14
10 City C 5 2 13
21 City C 5 1 9
16 City A 6 2 7
29 City G 4 2 7
triangle_counts %>%
  slice_head(n = 10) %>%
  mutate(id = reorder(as.factor(id), triangle_count)) %>%
  ggplot(aes(x = id, y = triangle_count)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Top 10 Users by Friendship Triangle Count",
    x = "User ID",
    y = "Number of Triangles"
  ) +
  theme_minimal()

Marketing takeaway: Triangle-dense groups are strong candidates for referral campaigns, group discounts, ambassador programs, and community-building initiatives.

Knowledge check: What does a high triangle count usually indicate?
Answer: The user is embedded in many tightly connected groups.

6.3 3. Friends of Friends

A friend-of-friend recommendation identifies two users who are not directly connected but share at least one mutual friend. We strengthen the recommendation by also requiring the two users to share the same school.

# Calculate shortest-path distances between all users.
distance_matrix <- distances(fb_graph)

# Pairs at distance 2 are friends-of-friends but are not direct friends.
fof_pairs <- which(distance_matrix == 2, arr.ind = TRUE) %>%
  as.data.frame() %>%
  as_tibble() %>%
  transmute(
    user_a = as.integer(rownames(distance_matrix)[row]),
    user_b = as.integer(colnames(distance_matrix)[col])
  ) %>%
  filter(user_a < user_b) %>%
  distinct()

friends_of_friends <- fof_pairs %>%
  left_join(
    vertices %>% select(id, school_id, hometown),
    by = c("user_a" = "id")
  ) %>%
  rename(
    school_id_a = school_id,
    hometown_a = hometown
  ) %>%
  left_join(
    vertices %>% select(id, school_id, hometown),
    by = c("user_b" = "id")
  ) %>%
  rename(
    school_id_b = school_id,
    hometown_b = hometown
  ) %>%
  filter(school_id_a == school_id_b)

friends_of_friends %>%
  slice_head(n = 10) %>%
  kable(caption = "Friend-of-Friend Pairs Who Attended the Same School") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Friend-of-Friend Pairs Who Attended the Same School
user_a user_b school_id_a hometown_a school_id_b hometown_b
5 9 2 City H 2 City B
5 10 2 City H 2 City C
9 10 2 City B 2 City C
3 12 4 City A 4 City H
1 13 5 City B 5 City D
2 13 5 City B 5 City D
5 14 2 City H 2 City B
9 14 2 City B 2 City B
10 14 2 City C 2 City B
5 16 2 City H 2 City A

Marketing takeaway: Mutual connections combined with a shared school, employer, or hometown can power warmer and more relevant recommendations for alumni networks, event platforms, and professional communities.

6.4 4. Identifying Influencers With PageRank

PageRank measures not only how many connections a person has, but also how well connected those connections are.

pr <- page_rank(fb_graph, damping = 0.85)$vector

influencers <- tibble(
  id = as.integer(names(pr)),
  pagerank = as.numeric(pr),
  degree = as.numeric(degree(fb_graph, v = names(pr)))
) %>%
  left_join(vertices, by = "id") %>%
  arrange(desc(pagerank))

influencers %>%
  slice_head(n = 10) %>%
  select(id, hometown, employer_id, school_id, degree, pagerank) %>%
  kable(
    caption = "Top 10 Potential Influencers by PageRank",
    digits = 4
  ) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Top 10 Potential Influencers by PageRank
id hometown employer_id school_id degree pagerank
1 City B 4 5 39 0.0385
2 City B 12 5 35 0.0350
3 City A 1 4 28 0.0275
6 City E 6 1 25 0.0244
11 City F 2 4 23 0.0235
21 City C 5 1 17 0.0174
10 City C 5 2 17 0.0172
18 City B 11 1 16 0.0166
4 City H 10 9 16 0.0161
19 City D 11 3 14 0.0152
influencers %>%
  ggplot(aes(x = degree, y = pagerank)) +
  geom_point(alpha = 0.7) +
  geom_smooth(method = "lm", se = FALSE) +
  labs(
    title = "Degree and PageRank Are Related but Not Identical",
    x = "Number of Direct Connections",
    y = "PageRank Score"
  ) +
  theme_minimal()

Marketing takeaway: PageRank can help marketers shortlist brand ambassadors whose influence extends through other well-connected users rather than relying only on follower count.

Knowledge check: Why does PageRank rank a user highly?
Answer: The user is connected to other highly connected users.

7 PageRank for CSUCI Website Popularity

PageRank was originally designed for websites. Each hyperlink acts like a vote from one page to another. Because hyperlinks are directional, a website link network must be modeled as a directed graph.

The following example models an illustrative internal-link network for California State University Channel Islands (CSUCI). It shows how major university pages can pass authority to admissions, academic programs, Extended University, and individual program pages.

csuci_links <- tribble(
  ~from, ~to, ~url_to,
  "CSUCI Home", "About CSUCI", "csuci.edu/about/",
  "CSUCI Home", "Academics", "csuci.edu/academics/",
  "CSUCI Home", "Admissions", "csuci.edu/admissions/",
  "CSUCI Home", "Financial Aid", "csuci.edu/financialaid/",
  "CSUCI Home", "Library", "library.csuci.edu/",
  "CSUCI Home", "News", "news.csuci.edu/",
  "CSUCI Home", "Extended University", "ext.csuci.edu/",

  "Admissions", "CSUCI Home", "csuci.edu/",
  "Admissions", "Academics", "csuci.edu/academics/",
  "Admissions", "Financial Aid", "csuci.edu/financialaid/",
  "Admissions", "Extended University", "ext.csuci.edu/",
  "Admissions", "Online Programs", "ext.csuci.edu/programs/online-programs.htm",

  "Extended University", "CSUCI Home", "csuci.edu/",
  "Extended University", "Admissions", "csuci.edu/admissions/",
  "Extended University", "Online Programs", "ext.csuci.edu/programs/online-programs.htm",
  "Extended University", "Graduate Programs", "ext.csuci.edu/programs/graduate-programs.htm",
  "Extended University", "Degree Completion", "ext.csuci.edu/programs/degree-completion.htm",
  "Extended University", "MBA Program", "ext.csuci.edu/programs/mba/",

  "Online Programs", "MBA Program", "ext.csuci.edu/programs/mba/",
  "Graduate Programs", "MBA Program", "ext.csuci.edu/programs/mba/",
  "Degree Completion", "Online Programs", "ext.csuci.edu/programs/online-programs.htm"
)

csuci_graph <- graph_from_data_frame(
  csuci_links %>% select(from, to),
  directed = TRUE
)

csuci_pr <- page_rank(csuci_graph, damping = 0.85)$vector

csuci_rank <- tibble(
  page = names(csuci_pr),
  pagerank = as.numeric(csuci_pr)
) %>%
  arrange(desc(pagerank))

csuci_rank %>%
  kable(
    caption = "Illustrative CSUCI Website Pages Ranked by PageRank",
    digits = 4
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  )
Illustrative CSUCI Website Pages Ranked by PageRank
page pagerank
MBA Program 0.2184
Online Programs 0.1245
CSUCI Home 0.0727
Extended University 0.0714
Academics 0.0714
Financial Aid 0.0714
Admissions 0.0697
Graduate Programs 0.0609
Degree Completion 0.0609
About CSUCI 0.0596
Library 0.0596
News 0.0596

7.1 Static CSUCI Website Network

V(csuci_graph)$pagerank <- csuci_pr[V(csuci_graph)$name]

set.seed(580)
plot(
  csuci_graph,
  layout = layout_with_fr(csuci_graph),
  vertex.size = 15 + rescale(V(csuci_graph)$pagerank, to = c(0, 35)),
  vertex.color = col_numeric("Blues", domain = NULL)(V(csuci_graph)$pagerank),
  vertex.label.color = "black",
  vertex.label.cex = 0.75,
  vertex.frame.color = "white",
  edge.arrow.size = 0.4,
  edge.color = "grey60",
  main = "Illustrative CSUCI Internal-Link Network"
)

Pages linked by several important source pages receive more authority. In this example, pages such as Admissions and Extended University act as hubs because they connect users to several academic and enrollment-related pages.

Marketing takeaway: CSUCI can strengthen the visibility of priority program pages by linking to them from high-authority pages such as the university homepage, Admissions, Extended University, relevant academic departments, and university news stories.

8 Interactive CSUCI Network With D3.js

The networkD3 package converts the CSUCI website graph into an interactive force-directed network. Users can drag, zoom, and explore the graph in the knitted HTML file.

nodes_d3 <- tibble(
  name = V(csuci_graph)$name,
  pagerank = V(csuci_graph)$pagerank,
  group = 1
)

links_d3 <- as_data_frame(csuci_graph, what = "edges") %>%
  transmute(
    source = match(from, nodes_d3$name) - 1,
    target = match(to, nodes_d3$name) - 1,
    value = 1
  )

forceNetwork(
  Links = as.data.frame(links_d3),
  Nodes = as.data.frame(nodes_d3),
  Source = "source",
  Target = "target",
  Value = "value",
  NodeID = "name",
  Group = "group",
  Nodesize = "pagerank",
  radiusCalculation = htmlwidgets::JS("Math.sqrt(d.nodesize) * 40 + 6"),
  linkDistance = 130,
  opacity = 0.9,
  zoom = TRUE,
  arrows = TRUE,
  fontSize = 14,
  bounded = TRUE
)

Knowledge check: What does networkD3 add?
Answer: It lets the user drag, zoom, and explore the CSUCI network interactively in a browser.

9 Real-World Marketing Applications

9.1 Targeted Marketing and Advertising

Shared attributes can support personalized promotions, while tightly connected friend groups can be targeted with group-oriented campaigns.

9.2 Event Planning and Networking

Mutual connections, shared schools, and shared employers can support attendee matchmaking, alumni engagement, and professional introductions.

9.3 Influencer Marketing

PageRank can identify users whose influence is reinforced by the importance of their connections, rather than by follower count alone.

9.4 Community Detection

Dense clusters reveal communities that already exist. Marketers can support these communities through relevant content, events, and referral programs.

10 Monetizing Network Insights

Potential business applications include:

  • social-network analytics consulting;
  • custom recommendation engines;
  • premium event matchmaking;
  • community-management platforms; and
  • network-based advertising and influencer targeting.

11 Conclusion

Social network analysis turns a collection of relationships into actionable marketing information. In this tutorial, R and igraph were used to examine shared customer attributes, friendship triangles, friends of friends, influential users, and website authority.

These techniques support four practical marketing decisions:

  1. who should receive personalized offers;
  2. which groups are likely to respond together;
  3. which people should be introduced to each other; and
  4. which users or web pages have the greatest network influence.

Final knowledge check: Which method is most similar to LinkedIn’s “People You May Know”?
Answer: Friend-of-friend analysis.

12 References

Bostock, M., Ogievetsky, V., & Heer, J. (2011). D3: Data-driven documents. IEEE Transactions on Visualization and Computer Graphics, 17(12), 2301-2309.

California State University, Bakersfield. (2026). Home, admissions, and College of Business and Public Administration webpages. Accessed July 23, 2026.

Csardi, G., & Nepusz, T. (2006). The igraph software package for complex network research. InterJournal, Complex Systems, 1695.

Gandrud, C., Allaire, J. J., & Russell, K. (n.d.). networkD3: D3 JavaScript network graphs from R [R package].

Leskovec, J., & Krevl, A. (2014). SNAP datasets: Stanford Large Network Dataset Collection. Stanford Network Analysis Project.

Page, L., Brin, S., Motwani, R., & Winograd, T. (1999). The PageRank citation ranking: Bringing order to the web. Stanford InfoLab.

Scaibu. (2024, November 18). Facebook Circles: A deeper dive into social network analysis using GraphFrames. Medium.

Xu, Z. “Jimmy.” (2018). Social network analysis using R. RPubs.