# How to test if social structure is non-random and resulting from genetic relatedness – and how to deal with demography effect

I am trying to construct (undirected) social network based on co-occurence of individuals. Clustering algorithm will be later applied on this network to find some distinct subgroups. Issue is that studied animal species has very short longevity (or rather very high mortality due to predators). It causes that not all of the relationships in my network may have existed at the same time. If you look on the diagram below, the "red" individuals are almost extincted after 3-4 years*, but they have the "longest" time to "meet" other individuals, whereas "blue" individuals have only two years to "meet" others.

Theoretically I can assume that each individual has expected longevity less than 10 years. Therefore not catching of "red" individual 5 or 6 years after tagging does not necessarily means that is dead.

How to include this time effect into social network?

Specific questions I want to answer: First question: Are observed social connections distinct from a connections explained solely by shared space use? i.e., How to test if associations are random or preferred?

If answer to first question will be that associations between individuals are NOT random, then I have a second qeustion...

Does social structure correlates with genetic relatedness? i.e., are closely related individuals more often together? (DNA profiles of all inividuals are bolow)

Here I created some data structurally similar to my database:

data <- data.frame(obs_date = c("C1","C2","C3","C4","C5","C6","C1","C2",
"C3","C4","C1","C2","C3","C1","C2","C3",
"C4","C5","C6","C7","C1","C3","C4","C5",
"C6","C7","C8","C3","C4","C5","C6","C7",
"C3","C4","C5","C6","C3","C4","C5","C3",
"C4","C5","C6","C5","C6","C7","C8","C5",
"C5","C6","C7","C8","C5","C6","C7","C7",
"C7","C8","C7","C8","C7","C8","C7","C8"),
ind_id = rep(LETTERS[1:20], times = c(6,4,3,7,1,6,5,4,
3,2,2,4,1,4,3,1,2,2,2,2)),
obs = rep(c("seen","not_seen","seen","not_seen","seen",
"not_seen","seen","not_seen","seen"),
times = c(3,1,4,1,9,1,9,3,33)))


Here I added genetic structure. Data are completely fabricated, but they should reflect close genetic relatedness between same collor individuals. Aditionally "violet" individuals are offsprings of "blue", "blue" are offsprings of "green", "green" are offsprings of "red".

gen.raw <- matrix(c("a","g","g","g","c","g","a","a","g","g","g","g","t","c","t","c","t","t","a","a","t","t","a","a",
"a","g","g","g","c","g","a","a","g","g","g","g","c","c","t","c","t","t","a","a","t","c","a","a",
"a","g","g","g","c","g","g","a","g","g","g","g","c","c","t","t","c","t","a","a","t","c","a","a",
"a","g","t","t","t","g","g","a","g","g","g","g","c","c","t","t","c","t","a","a","a","c","a","a",
"a","g","t","t","t","g","g","a","g","g","g","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","g","a","g","g","g","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","g","g","g","g","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","a","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","t","t","g","a","c","g","t","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","c","t","g","a","c","g","g","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","c","t","g","a","c","g","g","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","c","t","g","a","c","g","g","c","g","c","c","t","t","c","t","t","a","t","c","a","a",
"a","g","t","c","t","g","a","c","g","c","c","g","t","c","t","t","c","t","t","a","t","c","a","a"),
byrow = TRUE, ncol = 24)
rownames(gen.raw) <- LETTERS[1:20]


Ok, source data are given above. Now I will create two distance matrices. First is association matrix derived from co-occurence data represented by OR-SP index. Observed Roost-Sharing Proportion is calculated for each pair of individuals by dividing the number of days two individuals were found together by the number of all possible days they could be together (overlap bewteen first and last recordngs of both individuals).

# matrix of days roosting together
EG <- expand.grid(unique(data$ind_id), unique(data$ind_id))

data_seen <- subset(data, obs == "seen")

my.length.dt <- numeric(nrow(EG))
for (i in 1:nrow(EG)) {
my.length.dt[i] <- length(intersect(as.vector(data_seen$obs_date[data_seen$ind_id == EG[i, 1]]),
as.vector(data_seen$obs_date[data_seen$ind_id == EG[i, 2]])))
days.together <- matrix(my.length.dt, byrow = TRUE, ncol = length(unique(data$ind_id))) colnames(days.together) <- rownames(days.together) <- unique(data$ind_id)
}
days.together

# matrix of all possible potentional roosting days
EG <- expand.grid(unique(data$ind_id), unique(data$ind_id))
my.length.rdp <- numeric(nrow(EG))
for (i in 1:nrow(EG)) {
my.length.rdp[i] <- length(intersect(as.vector(data$obs_date[data$ind_id == EG[i, 1]]),
as.vector(data$obs_date[data$ind_id == EG[i, 2]])))
roosting_days_possible <- matrix(my.length.rdp, byrow = TRUE, ncol = length(unique(data$ind_id))) colnames(roosting_days_possible) <- rownames(roosting_days_possible) <- unique(data$ind_id)
}
roosting_days_possible

# OBSERVED ROOST-SHARING PROPORTION
OSP <- days.together/roosting_days_possible
OSP[ is.nan(OSP) ] <- 0
diag(OSP) <- 0

# So here is association matrix derived from co-occurence data
round(OSP,2)
# social distance matrix
soc_dist <- as.dist(OSP)


Next step is to take DNA sequences and make genetic relatedness matrix

# creating matrix of relatedness
library(ape)
gen.str <- as.DNAbin(gen.raw)
my.gen.dist <- dist.dna(gen.str)
fit <- hclust(my.gen.dist, method="ward")
plot(fit) # display dendogram


Finally, here I compare social distance with genetic distance by Mantel test.

library(ade4)
mantel.rtest(soc_dist, my.gen.dist, nrepet = 9999)


Does its result (p > 0.05) mean that there is no correlation between social and genetic structure?

Is this appropriate solution to answer my question? Any ideas?

I also found that for social structure might be better this type of graph instead of dendrogram. Good for finding distinct social group.

# Show social structure
library(igraph)
g <- graph.adjacency(OSP, weighted=TRUE, mode ="undirected")
g <- simplify(g)
# set labels and degrees of vertices
V(g)$label <- V(g)$name
V(g)\$degree <- degree(g)
wc <- walktrap.community(g)
plot(wc, g)


Now I will create two distance matrices. First is association matrix derived from co-occurence data represented by OR-SP index. Observed Roost-Sharing Proportion is calculated for each pair of individuals by dividing the number of days two individuals were found together by the number of all possible days they could be together (overlap bewteen first and last recordngs of both individuals).

Regarding this step, I think you would be much more successful if you used Agent-based Modeling (ABM). The approach you describe appears convoluted to me, and ABM code will likely be much simpler -- meaning easier to implement, test, refine, and validate -- and also much more justifiable from a theoretical and empirical point of view. The model you are trying to create is somewhat common within the field of Computational Social Science, which includes ABM as a primary modeling/simulation method.

I suggest that you use NetLogo (https://ccl.northwestern.edu/netlogo/). Each of your individuals would be agents in NetLogo. Each time step, each agent executes a program that determines its interaction with the environment, other agents, and changes in its internal state. (If you like, you can also add predator agents, but it sounds like you would like to treat mortality rate as a constant probability each time step, or maybe as a function of age and time.) You can program agent movements in a 2D physical space ("patches"), and therefore you can simulate the process of agents meeting other agents and "roosting" together. They can mate and have babies, too, and passing on their genetic material as internal state for the new agents. Along the way, agents form social network ties, by what ever rules you implement. NetLogo has built-in capabilities for dynamic graphs (i.e. social networks) between agents, and also visualization.

After you program your simulation, just run it N times with random initial conditions, where N is chosen to be large enough to give you adequate statistical confidence in your final analysis. You can record data (i.e. your "observations") every T steps to simulate yearly data. You only need to use R for statistical analysis at the end the runs. There is a NetLogo extension to import and export data to R here: https://github.com/NetLogo/NetLogo/wiki/Extensions.

You might have reservations about this approach since it involves learning a new system and a new language (NetLogo has it's own scripting language). However, it's quite easy to learn (many beginners and non-programmers learn it and use it successfully). But the primary benefit is that you are modeling the phenomena of interest in a very direct and natural way which greatly simplifies the task and greatly reduces the chances of error along the way.

I compare social distance with genetic distance by Mantel test.

Regarding this step, it think it is vital that you first establish that the space of possible social distances is a metric space and that it has characteristics that make it comparable to genetic distance within the space of possible genomes. Just because you have both in matrix form is not sufficient, in my view (though I don't have experience with this particular test). For example, genetic distance = zero means identical genomes, right? But what does "social distance = zero" mean? If zero is undefined for social distance, then it fails the definition of a metric (see: https://en.wikipedia.org/wiki/Metric_space#Definition).

Second, I think you should be measuring change in distances. You have some initial conditions that involve both initial social distances and initial genetic distances. After some number of years, though mating, mortality, and geographic/social mixing, your population has a final set of social distances and genetic distances. The ABM approach makes this more visible.

Regarding the Mantel test in particular, you might also evaluate Bayesian alternatives. From the Wikipedia entry: "...the Mantel and partial Mantel tests can be flawed in the presence of spatial auto-correlation and return erroneously low p-values See e.g. Guillot and Rousset, 2013 [3])" A Bayesian approach might be able to avoid this problem and also others associated with Null Hypothesis Significance Testing (NHST). However, I don't have a specific suggestion on a Bayesian approach for this test.

I think your question might be less of a method question and more of a theoretical question about what you are trying to achieve with your data.

If you are interested in co-occurences only, does it really matters that some individuals have more time than other to form ties in order to find subgroups?

Now, you could think of some ways to correct for the fact that some individuals have more time to "meet" others because they are caught more often. You could use the strength of ties to reflect this. You could for example divide the strength of a tie by the number of observation windows an individual is present in. The disadvantage of this is that the strength of the tie will be different for individuals in the same dyad, essentially making your network into a weighted directed network where all ties are reciprocated. The disadvantage of this is that the algorithm in the walktrap.community() function ignores edge direction. This means you would need to look for another community detection algorithm.

Another way of reducing the problem with individuals having more or less time to "meet" would be to create several snapshots of your network in which you only keep ties that appear at the n previous periods (you will need your specific knowledge of the species studied and the data to determine which value for n makes sense). This means that ties will decay after a while and that will reduce the tendency of the individuals that are present more often to be the most central. The disadvantage is that you end up with several networks that reflect the state of relationships at different time and therefore you will not be able to obtain one stable community membership for each individual. Instead, running your community detection algorithm on each snapshot of the network you will get a more dynamic picture of change in community membership over time.

As I said at the beginning, I believe it is a theoretical question more than anything else. You need to ask yourself questions such as: why do I want to classify individuals into groups? What do I think it means to be members of the same group? Answering those questions will inform how you approach the analysis of your network.

• Dear @AntoineVernet. Thank you for your response. I have done substantial edits to question. I hope for better clarity what I want to do. – Ladislav Naďo May 24 '15 at 21:15