Wie man testet, ob die soziale Struktur nicht zufällig ist und aus genetischer Verwandtschaft resultiert - und wie man mit demografischen Effekten umgeht

7

Ich versuche, ein (ungerichtetes) soziales Netzwerk aufzubauen, das auf dem gleichzeitigen Auftreten von Individuen basiert . Der Clustering-Algorithmus wird später in diesem Netzwerk angewendet, um bestimmte Untergruppen zu finden. Das Problem ist, dass untersuchte Tierarten eine sehr kurze Lebensdauer haben (oder eher eine sehr hohe Sterblichkeit aufgrund von Raubtieren). Dies führt dazu, dass möglicherweise nicht alle Beziehungen in meinem Netzwerk gleichzeitig bestanden haben. Wenn Sie sich das folgende Diagramm ansehen, sind die "roten" Personen nach 3-4 Jahren * fast ausgestorben, aber sie haben die "längste" Zeit, um andere Personen zu "treffen" , während "blaue" Personen nur zwei Jahre Zeit haben, sich zu "treffen" " andere.

Geben Sie hier die Bildbeschreibung ein

Theoretisch kann ich davon ausgehen, dass jeder Einzelne eine Lebensdauer von weniger als 10 Jahren erwartet hat. Daher bedeutet das Nichtfangen von "roten" Personen 5 oder 6 Jahre nach dem Markieren nicht unbedingt, dass sie tot sind.

Wie kann dieser Zeiteffekt in ein soziales Netzwerk aufgenommen werden?

Spezifische Fragen, die ich beantworten möchte: Erste Frage: Unterscheiden sich beobachtete soziale Verbindungen von Verbindungen, die ausschließlich durch die Nutzung des gemeinsamen Raums erklärt werden? dh wie zu testen, ob Assoziationen zufällig oder bevorzugt sind?

Wenn die Antwort auf die erste Frage lautet , dass Assoziationen zwischen Individuen NICHT zufällig sind, dann habe ich eine zweite Frage ...

Korreliert die soziale Struktur mit der genetischen Verwandtschaft? dh sind eng verwandte Personen häufiger zusammen? (DNA-Profile aller Personen sind fett)

Hier habe ich einige Daten erstellt, die meiner Datenbank strukturell ähnlich sind:

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)))

Hier habe ich genetische Struktur hinzugefügt. Die Daten sind vollständig fabriziert, sollten jedoch eine enge genetische Verwandtschaft zwischen denselben Collor-Individuen widerspiegeln. Zusätzlich sind "violette" Individuen Nachkommen von "blau" , "blau" sind Nachkommen von "grün" , "grün" sind Nachkommen von "rot" .

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, die Quelldaten sind oben angegeben. Jetzt werde ich zwei Distanzmatrizen erstellen . Erstens ist die Assoziationsmatrix aus Koexistenzdaten abgeleitet, die durch den OR-SP-Index dargestellt werden . Der beobachtete Roost-Sharing-Anteil wird für jedes Personenpaar berechnet, indem die Anzahl der Tage, an denen zwei Personen zusammen gefunden wurden, durch die Anzahl aller möglichen Tage geteilt wird, an denen sie zusammen sein könnten (Überlappung zwischen der ersten und der letzten Aufzeichnung beider Personen).

# 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)

Der nächste Schritt besteht darin, DNA-Sequenzen zu nehmen und eine Matrix für genetische Verwandtschaft zu erstellen

# 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 

Schließlich vergleiche ich hier die soziale Distanz mit der genetischen Distanz nach dem Mantel-Test .

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

Bedeutet das Ergebnis (p> 0,05), dass kein Zusammenhang zwischen sozialer und genetischer Struktur besteht?

Ist diese Lösung geeignet, um meine Frage zu beantworten? Irgendwelche Ideen?

Ich fand auch, dass für die soziale Struktur diese Art von Grafik besser sein könnte als Dendrogramm. Gut, um eine bestimmte soziale Gruppe zu finden.

# 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)
Ladislav Naďo
quelle

Antworten:

2

Jetzt werde ich zwei Distanzmatrizen erstellen. Erstens ist die Assoziationsmatrix aus Koexistenzdaten abgeleitet, die durch den OR-SP-Index dargestellt werden. Der beobachtete Roost-Sharing-Anteil wird für jedes Personenpaar berechnet, indem die Anzahl der Tage, an denen zwei Personen zusammen gefunden wurden, durch die Anzahl aller möglichen Tage geteilt wird, an denen sie zusammen sein könnten (Überlappung zwischen der ersten und der letzten Aufzeichnung beider Personen).

In Bezug auf diesen Schritt denke ich, dass Sie viel erfolgreicher wären, wenn Sie Agent-based Modeling (ABM) verwenden würden. Der von Ihnen beschriebene Ansatz erscheint mir kompliziert, und ABM-Code wird wahrscheinlich viel einfacher sein - was bedeutet, dass er einfacher zu implementieren, zu testen, zu verfeinern und zu validieren ist - und auch aus theoretischer und empirischer Sicht viel vertretbarer. Das Modell, das Sie erstellen möchten, ist im Bereich der Computational Social Science, zu dem ABM als primäre Modellierungs- / Simulationsmethode gehört, weit verbreitet.

Ich schlage vor, dass Sie NetLogo verwenden ( https://ccl.northwestern.edu/netlogo/ ). Jede Ihrer Personen wäre ein Agent in NetLogo. In jedem Zeitschritt führt jeder Agent ein Programm aus, das seine Interaktion mit der Umgebung, anderen Agenten und Änderungen seines internen Status bestimmt. (Wenn Sie möchten, können Sie auch Raubtier-Agenten hinzufügen, aber es klingt so, als würden Sie die Sterblichkeitsrate bei jedem Zeitschritt als konstante Wahrscheinlichkeit oder möglicherweise als Funktion von Alter und Zeit behandeln.) Sie können Agentenbewegungen in 2D programmieren physischer Speicherplatz ("Patches"), und daher können Sie den Prozess simulieren, bei dem Agenten andere Agenten treffen und gemeinsam "roosting". Sie können sich paaren und auch Babys bekommen und ihr genetisches Material als inneren Zustand für die neuen Wirkstoffe weitergeben. Auf dem Weg bilden Agenten soziale Netzwerkverbindungen nach den von Ihnen implementierten Regeln. NetLogo verfügt über integrierte Funktionen für dynamische Diagramme (dh soziale Netzwerke) zwischen Agenten.

Nachdem Sie Ihre Simulation programmiert haben, führen Sie sie einfach N- mal mit zufälligen Anfangsbedingungen aus, wobei N so groß gewählt wird, dass Sie ein ausreichendes statistisches Vertrauen in Ihre endgültige Analyse erhalten. Sie können alle T- Schritte Daten (dh Ihre "Beobachtungen") aufzeichnen , um jährliche Daten zu simulieren. Sie müssen R nur für statistische Analysen am Ende der Läufe verwenden. Es gibt eine NetLogo- Erweiterung zum Importieren und Exportieren von Daten nach R hier: https://github.com/NetLogo/NetLogo/wiki/Extensions .


Möglicherweise haben Sie Vorbehalte gegen diesen Ansatz, da Sie ein neues System und eine neue Sprache lernen müssen ( NetLogo verfügt über eine eigene Skriptsprache). Es ist jedoch recht einfach zu lernen (viele Anfänger und Nicht-Programmierer lernen es und setzen es erfolgreich ein). Der Hauptvorteil besteht jedoch darin, dass Sie die interessierenden Phänomene auf sehr direkte und natürliche Weise modellieren , was die Aufgabe erheblich vereinfacht und die Fehlerwahrscheinlichkeit auf diesem Weg erheblich verringert.


Ich vergleiche soziale Distanz mit genetischer Distanz durch Mantel-Test.

In Bezug auf diesen Schritt ist es wichtig, dass Sie zunächst feststellen, dass der Raum möglicher sozialer Entfernungen ein metrischer Raum ist und dass er Merkmale aufweist, die ihn mit der genetischen Distanz innerhalb des Raums möglicher Genome vergleichbar machen. Nur weil Sie beide in Matrixform haben, reicht es meiner Ansicht nach nicht aus (obwohl ich keine Erfahrung mit diesem speziellen Test habe). Zum Beispiel bedeutet genetischer Abstand = Null identische Genome, oder? Aber was bedeutet "soziale Distanz = Null"? Wenn Null für die soziale Distanz nicht definiert ist, schlägt die Definition einer Metrik fehl (siehe: https://en.wikipedia.org/wiki/Metric_space#Definition ).

Zweitens denke ich, dass Sie die Änderung der Entfernungen messen sollten . Sie haben einige Anfangsbedingungen, die sowohl anfängliche soziale Entfernungen als auch anfängliche genetische Entfernungen beinhalten. Nach einigen Jahren, obwohl Paarung, Sterblichkeit und geografische / soziale Vermischung, hat Ihre Bevölkerung eine endgültige Reihe von sozialen und genetischen Entfernungen. Der ABM-Ansatz macht dies sichtbarer.


Insbesondere im Hinblick auf den Mantel-Test können Sie auch Bayes'sche Alternativen bewerten. Aus dem Wikipedia-Eintrag: "... der Mantel- und der partielle Mantel-Test können bei räumlicher Autokorrelation fehlerhaft sein und falsch niedrige p-Werte zurückgeben. Siehe z. B. Guillot und Rousset, 2013 [3])" Ein Bayes'scher Ansatz könnte sein in der Lage, dieses Problem und auch andere im Zusammenhang mit Null Hypothesis Significance Testing (NHST) zu vermeiden. Ich habe jedoch keinen konkreten Vorschlag für einen Bayes'schen Ansatz für diesen Test.

MrMeritology
quelle
3

Ich denke, Ihre Frage ist möglicherweise weniger eine Methodenfrage als vielmehr eine theoretische Frage darüber, was Sie mit Ihren Daten erreichen möchten.

Wenn Sie nur an Koexistenz interessiert sind, ist es dann wirklich wichtig, dass einige Personen mehr Zeit als andere haben, um Bindungen zu knüpfen, um Untergruppen zu finden?

Nun könnten Sie sich einige Möglichkeiten vorstellen, um die Tatsache zu korrigieren, dass einige Personen mehr Zeit haben, andere zu "treffen", weil sie häufiger gefangen werden. Sie könnten die Stärke der Bindungen nutzen, um dies zu reflektieren. Sie können beispielsweise die Stärke einer Krawatte durch die Anzahl der Beobachtungsfenster teilen, in denen sich eine Person befindet. Der Nachteil besteht darin, dass die Stärke der Krawatte für Personen in derselben Dyade unterschiedlich ist, wodurch Ihr Netzwerk im Wesentlichen gewichtet wird gerichtetes Netzwerk, in dem alle Bindungen erwidert werden. Dies hat den Nachteil, dass der Algorithmus in der walktrap.community()Funktion die Kantenrichtung ignoriert. Dies bedeutet, dass Sie nach einem anderen Community-Erkennungsalgorithmus suchen müssen.

Eine andere Möglichkeit, das Problem mit Personen zu verringern, die mehr oder weniger Zeit haben, sich zu "treffen", besteht darin, mehrere Schnappschüsse Ihres Netzwerks zu erstellen, in denen Sie nur Verbindungen beibehalten, die in den n vorherigen Perioden aufgetreten sind (Sie benötigen Ihr spezifisches Wissen über die untersuchten Arten und die Daten, um zu bestimmen, welcher Wert für nmacht Sinn). Dies bedeutet, dass die Bindungen nach einer Weile abnehmen und die Tendenz der Personen, die häufiger anwesend sind, verringert wird, am zentralsten zu sein. Der Nachteil ist, dass Sie am Ende mehrere Netzwerke haben, die den Status der Beziehungen zu unterschiedlichen Zeiten widerspiegeln, und daher nicht für jede Person eine stabile Community-Mitgliedschaft erhalten können. Wenn Sie stattdessen Ihren Community-Erkennungsalgorithmus für jeden Snapshot des Netzwerks ausführen, erhalten Sie ein dynamischeres Bild der Änderung der Community-Mitgliedschaft im Laufe der Zeit.

Wie ich am Anfang sagte, glaube ich, dass es mehr als alles andere eine theoretische Frage ist. Sie müssen sich Fragen stellen wie: Warum möchte ich Einzelpersonen in Gruppen einteilen? Was bedeutet es meiner Meinung nach, Mitglieder derselben Gruppe zu sein? Wenn Sie diese Fragen beantworten, erfahren Sie, wie Sie sich der Analyse Ihres Netzwerks nähern.

Antoine Vernet
quelle
Sehr geehrtes @AntoineVernet. Danke für Ihre Antwort. Ich habe wesentliche Änderungen vorgenommen, um Fragen zu stellen. Ich hoffe auf bessere Klarheit, was ich tun möchte.
Ladislav Naďo