# --- COMPUATIONAL MODEL CODE --- #
team_knowledge_model <- function(cons_mean, ext_mean, cog_mean) {

  ####################
  # MODEL PARAMETERS #
  ####################

  # Sets the size of the team; stored in the variable "n_members"
  n_members <- 5

  # Sets the size of the possible knowledge pool; stored in the variable "n_info"
  # Value represents the total number of relevant unique pieces of knowledge
  # that could be known
  n_info <- 30

  # Maximum number of allowed model iterations
  # NOTE!! This is included present code for purposes of this example code to ensure the simulation does
  # not run for too long or produce errors, but it may not be desirable to end a model run prior to achieving
  # the desired stopping condition. In the present code, this value is used to initialize the size of both
  # the "agent_know" and "spoke" data objects as well as to end the while loop after the maximum number of
  # allowed model iterations is reached.
  max_iterations = 10000

  ###################
  # MODEL FUNCTIONS #
  ###################
  # Function to determine whether an agent attends to a piece of information shared by another agent
  # Function is named "attend_info" and requires two user-provided inputs to run:
  # (1) "rcvr": single integer between 1:n_members identifying which agent is attempting to attend to the shared information during this attempt
  # (2) "spkr": single integer between 1:n_members identifying which agent is attempting to share the information during this attempt
  # (3) "lrnr_cons": value between 0:1 representing the conscientiousness level of the learner agent
  # Output of the function is a single Boolean value (TRUE/FALSE) indicating whether agent attended to the information
  attend_info <- function(rcvr, spkr, lrnr_cons) {

    # Checks to see if the agent is NOT the speaker since the speaker cannot learn
    # If the speaker is NOT the agent specified as "rcvr" then the following code is executed
    if(rcvr != spkr) {

      # Sets the two possible outcomes for whether an agent attends to a speaker (T) or does not attend (F)
      att_outcomes <- c(TRUE, FALSE)

      # Sets the probability ("att_probs") of whether an agent attends to a speaker (lrnr_cons) or not to attend (1 - lrnr_cons)
      att_probs <- c(lrnr_cons, (1 - lrnr_cons))

      # Uses the sample function to randomly determine whether the agent attends to the speaker using the designated probabilities
      att <- sample(att_outcomes, size = 1, prob = att_probs)

      # If the speaker is the same as the agent specified as "rcvr" then the following code is executed
    } else {

      # Because speakers do not attempt to learn the information they share, set their attend value to FALSE
      att <- FALSE
    }

    # Identifies what is to be output from the "attend" function.
    # In this case, a single TRUE/FALSE value is returned indicating whether the agent chose to attend to the shared information during this attempt
    return(att)

    #	Ends the user-defined function, "attend" to execute the attending to information action/event
  }

  # Function to determine how much knowledge an agent learns about a shared piece of information during a learning attempt
  # Function is named "learn" and requires three user-provided inputs to run:
  # (1) "info": integer value between 1:n_info identifying which piece of knowledge is the focus of learning during the learning attempt
  # (2) "lrnr_know": vector of length n_info that indicates what the agents attempting to learn already knows (i.e., know_pool[lrnr,])
  # (3) "lrnr_cog": value between 0:1 representing the cognitive ability level of the learner agent
  # Output of the function is a vector of length n_info containing what the agent knows after this learning attempt
  learn <- function(info, lrnr_know, lrnr_cog) {

    # Update agent's knowledge pool based on learning
    # Add how much the agent is able to learn about the information ("lrn_cog") to how much is already know about the information (contained in "lrnr_know")
    lrnr_know[info] <- lrnr_know[info] + lrnr_cog

    # Sets any values in the agent's knowledge pool greater than 1 to the maximum allowable value (1)
    # This can happen if the agent's current learning attempt pushes its knowledge of the information pieced being learned over the maximum allowable value.
    lrnr_know[lrnr_know > 1] <- 1

    # Identifies what is to be output from the "learn" function
    # In this case, the agent's entire knowledge pool ("lrnr_know") is returned.
    # If the agent learned, this object includes updates to the agent's knowledge pool based on what it learned during this attempt
    # If the agent did not learn, this object simply returns what the agent's knowledge pool looked like prior to the attempt
    return(lrnr_know)

    #	Ends the user-defined function "learn" to execute the learning action/event
  }

  ##################
  # INITIALIZATION #
  ##################
  # Creates a vector of agent names called "agents"
  # Will name agents 1,2,3, ... n_members
  agents <- 1:n_members

  ### PSEUDOCODE STEP 1 ###
  ### Assign the extraversion, conscientiousness, and cognitive ability of agents ###

  # Extraversion / speaking rates of agents (scaled within the team):
  ## Assign each agent (n_members) the same value (ext_mean) and add a small amount of "noise"
  ## to this value using a normal distribution (rnorm)
  ## Result will be a vector ("ext"), containing all agents' raw extraversion scores
  ext <- rep(ext_mean, n_members) + rnorm(n_members, 0, sd = .05)
  ext[ext <= 0] <- .05
  ext[ext >= 1] <- .99

  ## Convert extraversion to a relative value scaled within-team
  ## sum(ext) calculates the total amount of extraversion in the team
  ## Result will divide each agents' raw extraversion level (stored in "ext") by the team total
  ext <- ext / sum(ext)

  # Conscientiousness / attention probability of agents:
  ## Assign each agent (n_members) the same value (cons_mean) and add a small amount of "noise"
  ## to this value using a normal distribution (rnorm)
  ## Result will be a vector ("cons"), containing all agents' raw conscientiousness scores
  cons <- rep(cons_mean, n_members) + rnorm(n_members, 0, sd = .05)
  cons[cons <= 0] <- .05
  cons[cons >= 1] <- .99

  # Cognitive ability of agents (proportion of knowledge learned per time hearing it):
  ## Assign each agent (n_members) the same value (cog_mean) and add a small amount of "noise"
  ## to this value using a normal distribution (rnorm)
  ## Result will be a vector ("cog"), containing all agents' raw cognitive ability scores
  cog <- rep(cog_mean, n_members) + rnorm(n_members, 0, sd = .05)
  cog[cog <= 0] <- .05
  cog[cog >= 1] <- .99

  ### PSEUDOCODE STEP 2 ###
  ### Assign agents an initial knowledge pool indicating what information they already know ###

  # Proportion of knowledge pool initially known per agent; stored in the variable "prop_info"
  # Currently sets all agents to having approximately equal amounts of knowledge
  # at the start of the simulation by dividing 100% (1) by the number of agents (n_members)
  prop_info <- 1 / n_members

  # For this example, which pieces of information each agent knows initially will be probabilistic.
  ## First, identify the values indicating whether a given piece of information is
  ## initially known: know it initially ("1") or not ("0")
  initial_info_state <- c(1, 0)

  ## Next, set the probability of knowing any given piece of information initially ("prop_info") or not (1-"prop_info")
  initial_info_probs <- c(prop_info, (1 - prop_info))

  ## Next, initialize a matrix object to hold agents' knowledge pool ("know_pool") with number of rows equal to number of
  ## agents ("n_members") and number of columns equal to number of information pieces ("n_info").
  ## Fill the matrix with all 0's initially
  know_pool <- matrix(0, nrow = n_members, ncol = n_info)

  ## Finally, populate the agent knowledge pool with initial knowledge of information by looping through each row
  ## of the knowledge pool object ("know_pool") and randomly sampling "n_info" values with replacement from the
  ## "initial_info_state" vector using the probabilities provided in "inital_info_probs".
  ## This procedure effectively populates each row of the "know_pool" object with a new vector indicating
  ## whether a piece of information is not initially known (0) or is known (1)
  ## Because the procedure used to determine whether an agent knows a given piece of information is random,
  ## there is a possibility that an agent could be initialized who knows nothing. For present purposes, we do not
  ## want to allow this possibility to occur. Thus, after populating the agent's knowledge pool, we check whether
  ## its knowledge pool contains no initially known information (sum(know_pool[i, ]) == 0). If this this is true,
  ## we randomly pick one piece of information in its knowledge pool and give that agent knowledge of it
  ## (know_pool[i, sample(1:n_info, size = 1) <- 1]).
  for(i in agents) {

    know_pool[i, ] <- sample(initial_info_state, size = n_info, prob = initial_info_probs, replace = TRUE)

    if(sum(know_pool[i, ]) == 0) {
      know_pool[i, sample(1:n_info, size = 1)] <- 1
    }

  }

  # IMPORTANT NOTE! The above procedure for creating the initial knowledge pool means that
  # some pieces of information may not be initially known by any agents on the team. In the present
  # model, this means that those pieces of information could NEVER be learned by agents. Thus
  # we need to use the initial knowledge pool object to establish the criterion for determining when
  # the team has learned all possible knowledge and the model should stop running using the following
  # steps:

  ## Calculate the total number of information pieces the team initially knows
  total_know <- sum(know_pool)

  ## Identify which pieces of information are initially known by at least one agent and therefore could
  ## be shared with/learned by other agents on the team.
  ## Initialize a vector of length n_info that will store the number of agents that know each piece of information.
  ## Initially populate vector with all 0's
  n_info_known <- rep(0, n_info)

  ## Next, determine the total number of agents that know each piece of information by computing the sum of each
  ## column in the knowledge pool matrix. This can be accomplished by using 1:n_info as the iterator in a for-loop,
  ## using the iterator to grab the data stored in each column of the know_pool matrix, and then summing
  for(i in 1:n_info) {

    n_info_known[i] <- sum(know_pool[, i])

  }

  ## Check whether each piece of knowledge is known by at least one person.
  ## This vector indicates the total number of information pieces that could be known/learned by
  ## any single agent.
  n_agents_know <- sum(n_info_known >= 1)

  ## Multiply the total number of information pieces that could be known/learned ("n_agents_know")
  ## by the number of agents ("n_members") to set the criteria for stopping the simulation when
  ## all agents know every information piece that is possible to learn
  possible_know <- n_members * n_agents_know

  ## Initialize the stopping criterion variable by comparing how much the team currently knows to
  ## how much they could possibly learn
  still_learning <- total_know < possible_know

  # Create a object to hold speaking data (who spoke and which piece of knowledge).
  ## The data will be structured as a matrix such that each row records the data generated
  ## during a given time point and columns will hold the identity of the speaker
  ## and which piece of knowledge was spoken.
  ## Because the number of time points required for the team to fully learn all pieces of
  ## information is unknown, the matrix object is intentionally created to contain an arbitrarily
  ## large number of rows given by "max_iterations".
  ## The entire matrix will initially be populated with "NA" values.
  spoke <- matrix(NA, nrow = max_iterations, ncol = 2)

  # Create a matrix to hold how many total pieces of information each agent knows over time.
  ## The data will be structured as a matrix such that each row records the data generated
  ## during a given time point and each column corresponds to how many pieces of information
  ## each agent fully knows at that time point.
  ## Because the number of time points required for the team to fully learn all pieces of
  ## information is unknown, the matrix object is intentionally created to contain an arbitrarily
  ## large number of rows given by "max_iterations".
  ## The entire matrix will initially be populated with "NA" values.
  agent_know <- matrix(NA, nrow = max_iterations, ncol = n_members)

  ### PSEUDOCODE STEP 3 ###
  # Initialize time index ("t") to record the number of time points
  # (i.e., initially set it to zero)
  t <- 0

  ###################
  # MODEL ALGORITHM #
  ###################

  ### PSEUDOCODE STEP 4 ###
  ### WHILE total knowledge in the team < total possible knowledge: ###

  # Beginning of while-loop that determines whether the team still has information to learn (still_learning)
  # When still_learning = TRUE (i.e., total know < possible_know), the code in the while-loop will run.
  # When still_learning = FALSE (i.e., total know == possible_know), the while-loop will stop running.
  while(still_learning) {

    ### PSEUDOCODE STEP 5 ###
    ### Iterate counter to t = t + 1 ###
    t <- t + 1

    ### PSEUDOCODE STEP 6 ###
    ### Select an agent to share knowledge based on extraversion ###

    # Randomly sample an agent to share by sampling one value ("size = 1") from the "agents" object
    # using the relative extraversion levels in the team ("ext")
    current_speaker <- sample(agents, size = 1, prob = ext)

    ### PSEUDOCODE STEP 7 ###
    ### Select an agent to share a piece of information based on extraversion ###

    # Identify the knowledge pool of the selected speaker by extracting the row ("current_speaker") containing
    # the speaker's knowledge pool from the overall knowledge pool object ("know_pool")
    speaker_know <- know_pool[current_speaker, ]

    # Identify which pieces of information the speaker is able to share (i.e., which pieces of information
    # they have fully learned).
    # This is accomplished by identifying which pieces of information in the speaker's knowledge pool
    # ("speakers_know") have a value equal to "1"
    speaker_possible_info <- which(speaker_know == 1)

    # Select one piece of information from the speaker's knowledge pool that is fully known to share
    info_shared <- sample(speaker_possible_info, size = 1)

    ### PSEUDOCODE STEP 8 ###
    ### FOR each agent on the team, attempt to learn shared information: ###

    # Beginning of for-loop that determines whether agents on the team learn the information that was shared.
    # The for-loop will perform the code below for each agent ("agents") one at a time
    for (i in agents) {

      ### PSEUDOCODE STEP 9 ###
      ### Determine whether agent attends to the shared information based on conscientiousness ###

      # Use attend() function to determine whether the current agent ("i") attends to the information shared
      # by the speaker ("current_speaker") using the agent's conscientiousness level ("cons")
      attend <- attend_info(rcvr = i, spkr = current_speaker, lrnr_cons = cons[i])

      ### PSEUDOCODE STEP 10 ###
      ### IF agent attends to speaker and has not yet fully learned the shared information, ###
      ### learn shared piece of information at a rate proportional to cognitive ability ###

      # Beginning of if-statement that determines whether agents learns the shared information.
      # The conditional expression evaluates TRUE if the agent attended to the shared information
      # ("attend == TRUE") and they do not yet fully know the shared information ("know_pool[i, info_shared] < 1")
      if(attend == TRUE && know_pool[i, info_shared] < 1) {

        # If the conditional expression evaluates TRUE, use the learn() function to update how much the
        # learner knows about the share piece of information ("info_shared) in the agent's knowledge pool
        # ("know_pool[i, ]") based on the agent's cognitive ability ("cog[i]")
        know_pool[i, ] <- learn(info = info_shared, lrnr_know = know_pool[i, ], lrnr_cog = cog[i])

        ## End of if-statement determining if agent attend to information
      }
      ## End of the for-loop performing learning for agents
    }

    # Record data generated during current model iteration.
    ## Speaking data
    ### Record which agent shared information into the row corresponding to the current
    ### time point ("t") and the first column ("1") of the the "spoke" object
    spoke[t, 1] <- current_speaker

    ### Record which piece of information was shared into the row corresponding to the current
    ### time point ("t") and the second column ("2") of the the "spoke" object
    spoke[t, 2] <- info_shared

    ## Learning data
    ### Record the total number of pieces of information fully learned by each agent at the current time point
    for (i in agents) {

      agent_know[t, i] <- sum(know_pool[i, ] == 1)

    }


    ### PSEUDOCODE STEP 11 ###
    ### Determine if team has fully learned all possible information. If so, go to Step 10; if not return to Step 5 ###

    # Count the total amount of knowledge known across the entire team
    total_know <- sum(know_pool)

    # Check if total knowledge learned is less than the possible knowledge that could be learned.
    # The result of this check is saved into the "still_learning" object and then reevaluted
    # to determine if the while-loop should continue
    still_learning <- total_know < possible_know

    # Stop running model if the maximum number of allowable iterations are reached.
    if(t == max_iterations) {break}

    # End of the while-loop performing model iterations
  }

  ###############
  # DATA OUTPUT #
  ###############

  # Cleaning data
  ## Remove all unnecessary rows from the "spoke" and "agent_know" objects by only saving the time points
  ## in which the team was learning and sharing ("1:t")
  spoke <- spoke[1:t, ]
  agent_know <- agent_know[1:t, ]

  # Create and return list object containing data generated by model
  return(list("agent_attributes" = data.frame("agent_id" = 1:n_members,
                                              "extraversion" = ext,
                                              "conscientiousness" = cons,
                                              "cognitive_ability" = cog),
              "agent_knowledge" = agent_know,
              "information_sharing" = spoke))

}
# End of computational model mode




# --- CODE FOR SIMULATING MODEL --- #
# R Library for running simulations using parallel processing
if(!require("snowfall")) {install.packages("snowfall")}
library(snowfall)

# (1) Initialize number of of parallel processors to use on local computer
## Warning: This will use the maximum number of cores available on the computer if possible!
library(parallel)
sfInit(parallel = TRUE, cpus = detectCores(), type = "SOCK")

# (2) Create simulation design table that specifies parameter values to use for each model run.
## Because of the stochastic nature of the model initialization and algorithm, the simulation should run the
## model times PER parameter combination. To create the simulation design table, we thus indicate the number of
## teams (i.e., observations) we wish to run under each parameter combination and then replicate each parameter
## combination that many times

### Number of teams (i.e., observations) to run for each parameter combination
n_teams = 250

### Create simulation design table
#### The final object ("conds") will be a data frame with number of rows equal to the total number of
#### simulated teams (i.e., observations) that will be run and columns indicating the parameter settings for the
#### extraversion, conscientiousness, and cognitive ability levels used for the model run.
#### The procedure below uses the expand.grid() function to create a table containing all the unique combinations of
#### the provided vectors. An apply() statement is then used to replicate each column of this table "n_teams" times
#### to create the parameter values that will be used for each model run
sim_conds = data.frame(
  apply(
    expand.grid(cons_mean = seq(from = .1, to = .9, by = .1),
                ext_mean = 1,
                cog_mean = seq(from = .1, to = .9, by = .1)),
    2,
    function(x) {
      rep(x, each = n_teams)
    }
  )
)

#### Transforms the simulation design table ("conds") from a data frame into a list ("conds_list") so that it can be
#### more easily used for parallel computing. The resultant structure is a list in which each element contains
#### the corresponding values from a single row of the simulation design table. For example conds_list[[1]] contains
#### the data from conds[1,], conds_list[[2]] contains the data from conds[2,], etc.
conds_list <- split(sim_conds, seq(nrow(conds)))

# (3) Export all data and packages necessary to each initialized core in order to run the model
sfExportAll()

# (4) Run simulation
## This procedure uses the sfClusterApplyLB() command from the snowfall package in R to iterate a specified
## function ("fun") across multiple computer cores for every set of given inputs ("x") and compile/return the
## output.

## In this example, the specified function is our computational model ("team_knowlege_model") and the inputs
## are the parameter values stored in each element of the simulation design list ("conds_list"). Note how the arguments
## defined in the model function shown here correspond to the named arguments created for our computational model function
## and the values assigned to those arguments correspond to the locations where the values for those arguments are store in
## each element of the simulation design list (i.e., the value for the "ext_mean" argument is always the first value in the
## list element, the value for "cons_mean" is always the second value, and the value for "cog_mean" is always the third value).
## Effectively then, this procedure instructs R to "loop" through each element of the simulation design list, assign the
## values contained in that element to the appropriate model parameter in the computational model function, and then run
## the model function under that parameter combination.
##
## The data generated by the simulation is saved to an object called "conds_dat". This data object will also be a
## list containing as many elements as there are elements in the simulation design list ("conds_list"). Each list element
## contains the data generated by a single run of computational model under the parameter settings given by the corresponding
## element from the simulation design list. That is, conds_dat[[1]] will contain the data generated by the computational
## model using the parameter values found in conds_list[[1]], conds_dat[[2]] the data generated using the parameter values
## found in conds_list[[2]], and so on.

sim_dat <- sfClusterApplyLB(x = conds_list,
                              fun = function(x) {
                                team_knowledge_model(cons_mean = as.numeric(x[1]),
                                                     cog_mean = as.numeric(x[2])
                                )
                              })

# (5) Stop parallel processing and return R to running on single-core. This command should always be executed
# to avoid potential errors or crashes once all parallel computations have been performed.
sfStop()

# (6) Save raw data
save(sim_conds, file = "team_know_sim_conds.RData")
save(sim_dat, file = "team_know_sim_dat.RData")





# --- CODE FOR PLOTTING DATA --- #
# Load and/or install libraries for data wrangling and plotting
library(tidyverse)
library(reshape2)
library(cowplot)

# Load simulation data and simulation design
# load("team_know_sim_dat.RData")
# load("team_know_sim_conds.RData")

# Plot heat map of average time for simulated teams to fully learn all information by condition
## Create aggregate data file for plotting
### Compute within-team mean conscientiousness, extraversion, and cognitive ability for all simulated teams
attribute_means <- as.data.frame(t(sapply(sim_dat, function(x) colMeans(x$agent_attributes))))
### Compute within-team SD for conscientiousness, extraversion, and cognitive ability for all simulated teams
attribute_sd <- as.data.frame(t(sapply(sim_dat, function(x) apply(x$agent_attributes, 2, sd))))
### Extract number of time points required for simulated teams to fully learn all information
#### Note: Maximum possible value will be equal to the value set for max_iterations in parameter settings of computational model code
time_to_complete <- sapply(sim_dat, function(x) nrow(x$agent_knowledge))

### Compile team-level data into single data frame
team_know_dat <- data.frame("team_id" = 1:nrow(sim_conds),
                            "cons_sim" = sim_conds$cons_mean,
                            "cog_sim" = sim_conds$cog_mean,
                            attribute_means[,3:4],
                            "time_to_complete" = time_to_complete)

### Create simulation-level data frame that computes average team cognitive ability, team conscientiousness, 
### and time required to fully learn all information across all teams in each simulation condition
team_know_plot_dat <- team_know_dat %>% 
  group_by(cog_sim, cons_sim) %>% 
  summarize("time" = mean(time_to_complete),
            "cog" = mean(cognitive_ability),
            "cons" = mean(conscientiousness))
#### Add variable indicating large values (to help with plotting)
team_know_plot_dat$large_value <- team_know_plot_dat$time > 1900

## Construct heat map plot
fig_team_know <- ggplot(team_know_plot_dat, aes(x = cog_sim,
                                                y = cons_sim,
                                                fill = time)) +
  
  geom_tile() +
  geom_text(aes(label = round(time,0), color = large_value)) +
  
  scale_color_manual(values = c("TRUE" = "gray30", "FALSE" = "white"), guide = "none") +
  
  scale_fill_viridis_c(limits = c(390,2500), 
                       labels = ~ifelse(.x < 2500, .x, ">2500"),
                       oob = scales::squish) +
  scale_x_continuous(breaks = seq(.1, .9, .1), expand = c(.02,0)) + 
  scale_y_continuous(breaks = seq(.1, .9, .1), expand = c(.02,0)) + 
  
  coord_fixed() +
  
  labs(x = "Mean team cognitive ability",
       y = "Mean team conscientiousness",
       fill = "Mean time to\nfully shared\nknowledge") +
  
  theme_minimal() +
  theme(legend.title = element_text(face = "bold", size = 10),
        legend.text = element_text(size = 11),
        legend.spacing.y = unit(1, "lines"),
        axis.text = element_text(size = 10),
        axis.title.x = element_text(face = "bold", size = 11, margin = margin(t = 10)),
        axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 10)),
        panel.grid.minor = element_blank()
  )

## Display plot (reproduce Figure 7 from manuscript)
fig_team_know

## Save plot to current working directory
# ggsave("fig_team_know.jpg",
#        path = getwd(),
#        fig_team_know,
#        width = 6.5,
#        height = 8,
#        units = "in",
#        dpi = 1080)


# Plot time series and recurrent plots for information sharing and agent speaking for single simulated team
## Select a team to plot
### Value should be between 1 and length of sim_dat
### sim_conds[team_id,] gives the parameter settings for the selected simulated team
team_id = 7500

### Extract data to plot for selected team 
info_sharing_plot <- as.data.frame(sim_dat[[team_id]]$information_sharing)
colnames(info_sharing_plot) <- c("agent_id", "info_id")

## Information sharing plots
### Create recurrence data
#### Identify all time periods in which the piece of information observed at each time period was shared
#### Result is list of length = nrows(info_sharing_plot) such that each list element corresponds to each time 
#### period/model iteration in which the simulated team performed. Each list element is a vector of values 
#### indicating ALL time periods in which the information piece shared at the current time period was observed again
info_recurrence <- lapply(info_sharing_plot$info_id, function(x) {
  which(info_sharing_plot$info_id == x)
})

### Prepare recurrence data for plotting
#### Create empty matrix
info_sharing_mat <- matrix(data = NA, nrow = nrow(info_sharing_plot), ncol = nrow(info_sharing_plot))
#### Loop through each row of matrix and insert a 1 in all cells where the same piece of information was observed
#### as being shared
for(i in 1:nrow(info_sharing_mat)) {
  info_sharing_mat[i,info_recurrence[[i]]] <- 1
}
#### Transform matrix data to long data format to facilitate plotting
info_sharing_mat_plot <- melt(info_sharing_mat, varnames = c("time_y", "time_x"))

### Create time series plot (line graph)
info_ts_plot <- ggplot(data = info_sharing_plot, 
                       aes(x = 1:nrow(info_sharing_plot),
                           y = info_id)) +
  
  geom_line(linewidth = .1) +
  
  labs(y = "Information piece") +
  
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        axis.text.y = element_text(size = 10),
        axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 15))
  )

### Create recrurrence plot
#### Note this approach creates a scatter plot that places a point in each (x,y) coordinate where the same piece
#### of information was observed as being shared. The way ggplot and geom_point() creates scatter plots means that
#### plotted points can "overlap" in adjacent (x,y) positions. You may need to tweak the size and stroke arguments
#### for geom_point to produce the desired visualization
info_rec_plot <- ggplot(data = info_sharing_mat_plot,
                        aes(x = time_x,
                            y = time_y,
                            color = factor(value))) +
  
  geom_point(shape = "square", 
             size = .2, 
             stroke = .1) + 

  scale_color_manual(values = c("black"), na.translate = FALSE) +
  
  labs(x = "Time period",
       y = "Time period",
       color = "Information piece") +
  
  guides(color = guide_legend(override.aes = list(size = 3))) +
  
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text = element_text(size = 10),
    axis.title.x = element_text(face = "bold", size = 11, margin = margin(t = 10)),
    axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 10)),
    panel.grid.minor = element_blank()
  )

### Create combined plot that places time series data above recurrence plot
fig_info_sharing <- plot_grid(info_ts_plot, info_rec_plot,
                              align = "v",
                              axis = "l",
                              rel_heights = c(1,2),
                              ncol = 1)

## Display plots
fig_info_sharing

## Save plot to current working directory
# ggsave("fig_info_sharing.jpg",
#        path = getwd(),
#        fig_info_sharing,
#        width = 6.5,
#        height = 8,
#        units = "in",
#        dpi = 1080)


## Agent speaking plots
### Follows same procedure as information sharing plots
### Create recurrence data
agent_recurrence <- lapply(info_sharing_plot$agent_id, function(x) {
  which(info_sharing_plot$agent_id == x)
})
agent_comm_mat <- matrix(data = NA, nrow = nrow(info_sharing_plot), ncol = nrow(info_sharing_plot))
for(i in 1:nrow(agent_comm_mat)) {
    agent_comm_mat[i,agent_recurrence[[i]]] <- 1
}
agent_comm_mat_plot <- melt(agent_comm_mat, varnames = c("time_y", "time_x"))

### Create time series plot (line graph)
agent_ts_plot <- ggplot(data = info_sharing_plot, 
                        aes(x = 1:nrow(info_sharing_plot),
                            y = agent_id)) +
  
  geom_line(linewidth = .1) +
  
  labs(y = "Agent") +
  
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        axis.text.y = element_text(size = 10),
        axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 20))
  )

### Create recrurrence plot
agent_rec_plot <- ggplot(data = agent_comm_mat_plot,
                         aes(x = time_x,
                             y = time_y,
                             color = factor(value))) +
  
  geom_point(shape = "square", 
             size = .2, 
             stroke = .1) + 
  
  scale_color_manual(values = c("black"), na.translate = FALSE) +
  
  labs(x = "Time period",
       y = "Time period",
       color = "Agent") +
  
  guides(color = guide_legend(override.aes = list(size = 3))) +
  
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text = element_text(size = 10),
    axis.title.x = element_text(face = "bold", size = 11, margin = margin(t = 10)),
    axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 10)),
    panel.grid.minor = element_blank()
  )

### Create combined plot that places time series data above recurrence plot
fig_agent_comm <- plot_grid(agent_ts_plot, agent_rec_plot,
                            align = "v",
                            axis = "l",
                            rel_heights = c(1,2),
                            ncol = 1)

## Display plots
fig_agent_comm

## Save plot to current working directory
# ggsave("fig_agent_comm.jpg",
#        path = getwd(),
#        fig_agent_comm,
#        width = 6.5,
#        height = 8,
#        units = "in",
#        dpi = 1080)

## Create plot that combines information sharing and agent speaking plots into a single visual (reproduce Figure 8)
fig_combined <- plot_grid(fig_info_sharing, fig_agent_comm, labels = c("A","B"))

## Display plot (reproduce Figure 8 from manuscript)
fig_combined

## Save plot to current working directory
# ggsave("fig_combined.jpg",
#        path = getwd(),
#        fig_combined,
#        width = 8,
#        height = 6,
#        units = "in",
#        dpi = 1080)
