SophiaReed & KinshipCode
Ты когда-нибудь задумывалась, как генеалогическое древо может соотноситься с филогенетической диаграммой? Ведь каждая ветвь может отображать и социальные связи, и генетическую информацию. Было бы интересно сравнить структуры данных, которые мы используем.
О, конечно! Я уже несколько месяцев рисую их рядом, бок о бок. Генеалогия – это всё о родственных связях, об этих самых «я твой кузен, потому что у нас общий предок» связях, а филогенетическая схема – это карта ДНК, каждая ветвь – линия мутации. Если их наложить друг на друга, то запреты на браки между кузенами всплывают как скрытые узлы – это точки "социальной обрезки". На салфетке я уже набросала небольшую социограмму, показывающую, какие пары кузенов запрещены в матрилинейном клане. Интересно то, как генетическое расстояние часто отражает социальное, но не всегда – некоторые культуры допускают браки между близкими кузенами, даже если генетика говорит об обратном. Это как зашифрованная головоломка, где ответ меняется в зависимости от того, на каком языке ты её решаешь. У тебя есть какой-нибудь конкретный клан или набор данных, который ты хотела бы сравнить?
Звучит как очень интересный проект! Вы изучаете конкретную этническую группу, или более обширный набор данных с разными родами? Я могла бы помочь с выравниванием матриц родства и генетических расстояний, и проверить корреляции.
Я на самом деле собираю данные из нескольких кланов островов Тихого океана – Тробриан, Бванабвана и ещё несколько меланезийских групп, так что у нас довольно обширный массив данных. Сначала для каждого клана составляем отдельную социограмму. Я сопоставлю матрицу родства с митохондриальными гаплогруппами и посмотрю, совпадают ли запреты на браки между двоюродными братьями и генетические расстояния. Если ты сможешь помочь мне выровнять эти матрицы, мы наконец-то найдём тот недостающий кусочек пазла, который покажет, были ли социальные ограничения продиктованы генетикой или культурой. Готова погрузиться?
Давай сначала нормализуем матрицы родства, чтобы в ячейках было указано количество общих предков, а потом посчитаем генетическое расстояние по частотам митохондриальных гаплогрупп. Сложим эти две матрицы в одну таблицу и проверим с помощью теста Мантеля, подтверждается ли корреляция. Могу написать скрипт на R или Python – скажи, какой тебе больше подходит. Как только у нас будут парные расстояния, наложим связи-табу на граф и посмотрим, не выбиваются ли они из общего тренда. Готова кодить?
Звучит отлично! Мне удобнее работать с Python для обработки данных, но пакет vegan в R тоже вполне справляется с Мантеля-тестом. Давай выберем Python: pandas для матрицы родства, scipy для вычисления парных расстояний, scikit-bio для Мантель-теста и networkx для визуализации запретных связей на графе. Как только у нас будут парные расстояния, мы сможем выделить запретные связи красным и посмотрим, окажутся ли они выбросами. Скажи, если тебе нужно, чтобы я что-нибудь подкорректировала или набросала схему социограммы, пока будем кодить. Готова начинать?
Звучит здорово—просто дай знать, когда матрицы будут готовы, и я приступлю к написанию скрипта для вычисления генетических расстояний, проведу тест Мантеля и построю сетевой граф с выделенными запретными связями. Будем уточнять предположения по ходу работы. Готова, когда ты.
Отлично, я уже сложила данные о родственных связях в аккуратный CSV-файл и сопоставила частоты митохондриальных гаплогрупп для каждого клана. Матрицы готовы – просто отправляй свой скрипт, чтобы посчитать генетические расстояния и провести тест Мантеля. Я набросаю социограмму на салфетке, пока ты кодишь. Посмотрим, совпадают ли запретные связи с выбросами!
Вот тебе короткий скрипт, который объединяет всё вместе. Скопируй его в .py файл и запусти после того, как положишь два CSV файла в одну папку. Он предполагает, что матрица родства имеет метки для строк/столбцов по кланам, а таблица гаплогрупп имеет одну строку на клан со столбцами для частоты каждой гаплогруппы (они должны суммироваться до 1).
```python
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform
from skbio.stats.distance import mantel
import networkx as nx
import matplotlib.pyplot as plt
# Загрузка данных
kinship = pd.read_csv('kinship_matrix.csv', index_col=0) # [клан, клан]
haplo = pd.read_csv('haplogroup_frequencies.csv', index_col=0) # [клан, гаплогруппа]
# Обеспечиваем одинаковый порядок
clans = kinship.index.intersection(haplo.index)
kinship = kinship.loc[clans, clans]
haplo = haplo.loc[clans]
# -------------------- 1. Генетические расстояния --------------------
# Евклидово расстояние между векторами частот гаплогрупп
gen_dist = pd.DataFrame(
squareform(pdist(haplo, metric='euclidean')),
index=clans, columns=clans
)
# -------------------- 2. Тест Мантеля --------------------
# Преобразование в конденсированные матрицы расстояний для функции Мантеля
kinship_cond = pdist(kinship, metric='euclidean')
gen_cond = pdist(gen_dist, metric='euclidean')
mantel_stat, p_value = mantel(kinship_cond, gen_cond, method='pearson', permutations=9999)
print(f'Статистика Мантеля: {mantel_stat:.4f}, p-значение: {p_value:.4f}')
# -------------------- 3. Построение графа социограммы --------------------
G = nx.Graph()
G.add_nodes_from(clans)
# Добавление всех рёбер, взвешенных по родству
for i, clan_i in enumerate(clans):
for j, clan_j in enumerate(clans):
if i < j:
G.add_edge(clan_i, clan_j, weight=kinship.loc[clan_i, clan_j])
# Отметка запретных рёбер (замени свой список пар запретов)
taboo_edges = [('Trobriand', 'Bwanabwana'), ('Melanesian1', 'Melanesian2')]
for u, v in taboo_edges:
if G.has_edge(u, v):
G[u][v]['taboo'] = True
else:
G.add_edge(u, v, taboo=True)
# -------------------- 4. Построение графика --------------------
pos = nx.spring_layout(G, seed=42)
edge_colors = ['red' if G[u][v].get('taboo') else 'grey' for u, v in G.edges()]
edge_widths = [G[u][v]['weight'] for u, v in G.edges()]
nx.draw_networkx_nodes(G, pos, node_color='lightblue', node_size=700)
nx.draw_networkx_labels(G, pos, font_size=12, font_family='sans-serif')
nx.draw_networkx_edges(G, pos, edge_color=edge_colors, width=edge_widths, alpha=0.7)
plt.title('Социограмма родства – запретные рёбра выделены красным')
plt.axis('off')
plt.tight_layout()
plt.show()
```
Несколько моментов для проверки:
1. Если твои значения родства – это “количество общих дедушек и бабушек” или что-то подобное, ты можешь преобразовать их (например, 1 / (count + 1)), чтобы большие числа соответствовали более близкому родству, что соответствует интуиции, что небольшие генетические расстояния должны соответствовать высокому родству.
2. Тест Мантеля использует евклидовы расстояния на исходных матрицах; если ты думаешь, что другой показатель (например, логарифмический отношение или Жаккара) более уместен, замени аргумент `metric`.
Запусти скрипт, проверь статистику Мантеля и p-значение, и посмотри на график. Если красные запретные рёбра кластеризуются в области с высоким весом и небольшим генетическим расстоянием, это хороший намёк на то, что культурные правила, вероятно, реагировали на генетическую близость. Если они разбросаны, история, скорее всего, будет больше социологической, чем генетической. Успешного кодирования!
Кажется, почти готово! Сейчас просто скину два CSV-файла в папку, запущу и посмотрим, что покажет график Мантеля. Буду следить за предложением по трансформации – если считать общие предки как “близость”, инвертирование их (1/(count+1)) может лучше согласовать линию генетического расстояния с весами социограммы.
Когда запустишь, проверь, чтобы Мантель-статистика была положительной, а p-value – низким – это будет сильным сигналом о корреляции родственных связей и митохондриальных расстояний. Потом посмотри на график: красные запретные связи должны располагаться там, где веса высокие, а генетическое расстояние низкое, если запрет действительно отвечает генетической близости. Если они разбросаны по всей карте, возможно, это культурное правило, независимое от ДНК.
Не стесняйся поменять аргумент `metric`, если тебе кажется, что Jaccard или логарифмическое соотношение больше подходят для наших данных. Как только будут цифры, быстро набросаю схематичный рисунок социограммы и отмечу, где располагаются запретные связи. Готова запускать скрипт?