<jupyter_start><jupyter_text>Royal Marriage Network The world marriage network takes all members of all dynasties into consideration. With this royal marriage network only the holders of Kingdom or Empire level titles and their children are used. This lets us see how alliances are formed by and between the most powerful dynasties.<jupyter_code>from pymongo import MongoClient
import pandas as pd
import datetime
client = MongoClient()
titles = client.ck2.titles<jupyter_output><empty_output><jupyter_text>Get all Holders of Kingdom and Empire Level Titles<jupyter_code>pipeline = [
    {
        "$match" : {"$or" : [{"title" : {"$regex" : '^k_.*'}}, {"title" : {"$regex" : '^e_.*'}}]}
    },
    {
        "$unwind" : "$holders"        
    },
    {
        "$lookup": 
        {
            "from" : "characters",
            "localField" : "holders.holder",
            "foreignField" : "_id",
            "as" : "title_holder"
        }
    },
    {
        "$unwind" : "$title_holder"
    },
    
    {
        "$project" : {"_id" : 0, "holder" : "$title_holder._id", "title": 1, "start_date" :"$holders.start_date", "end_date" : "$holders.end_date"}
    }
]
title_holders_set = set()
for title in titles.aggregate(pipeline):
    title_holders_set.add(title['holder'])<jupyter_output><empty_output><jupyter_text>Get all Children of these Holders and all Spouses<jupyter_code>characters = client.ck2.characters
pipeline = [
    {
        "$unwind" : "$parents"
    },
    {
        "$match" : {"$or" : [{"parents" : {"$in" : list(title_holders_set)}}, {"_id" : {"$in" : list(title_holders_set)}} ]}
    },
    {
        "$unwind" : "$spouse"
    },
    {
        "$lookup" :
        {
            "from" : "characters",
            "localField" : "spouse",
            "foreignField" : "_id",
            "as" : "spouse_data"
        }
    },
    {
        "$unwind" : "$spouse_data"        
    },
    {
        "$project" : {'source' : "$_id", "target" : "$spouse", "dynasty" : "$dnt", "spouse_dynasty" : "$spouse_data.dnt" }
    }
]
marriage_edge_list = pd.DataFrame(list(characters.aggregate(pipeline)))

marriage_edge_list = marriage_edge_list.dropna(axis=0, how="any")
marriage_edge_list = marriage_edge_list.reset_index(drop = True)

#Will contain all title holders, all their children and the spouses of both
total_chars = set(marriage_edge_list['source'].unique())
total_chars = total_chars.union(set(marriage_edge_list['target'].unique()))

total_dyns = set(marriage_edge_list['dynasty'].unique())
total_dyns = total_dyns.union(set(marriage_edge_list['spouse_dynasty'].unique()))<jupyter_output><empty_output><jupyter_text>Get all Dynasties involved<jupyter_code>dynasties = client.ck2.dynasties

dyns_as_ints = [int(i) for i in list(total_dyns)]
pipeline = [    
    {
        "$match" : {"_id" : {"$in" : dyns_as_ints}}
    },
    {
        "$project" : {"name" : 1, "culture" : 1, "religion" : 1}
    },
    {
        "$sort" : {"name" : 1}
    }
]
dyns = dynasties.aggregate(pipeline)<jupyter_output><empty_output><jupyter_text>Build Network Graph<jupyter_code>import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()

for dyn in dyns:
    if "name" in dyn.keys() and "culture" in dyn.keys() and "religion" in dyn.keys():
        G.add_node(dyn["_id"], name = dyn['name'], culture = dyn['culture'], religion = dyn["religion"], titled = False)
        
nodes = list(G.nodes())
        
for title in titles.aggregate(pipeline):
    if title["_id"] in nodes:
        G.node[title["_id"]]["titled"] = True
complete_set = set()

for i in range(len(marriage_edge_list)):
    if((marriage_edge_list.loc[i, "source"], marriage_edge_list.loc[i, "target"]) not in complete_set): #if it hasn't be set already
        if G.has_edge(marriage_edge_list.loc[i, "dynasty"], marriage_edge_list.loc[i, "spouse_dynasty"]):
            G.edge[marriage_edge_list.loc[i, "dynasty"]][marriage_edge_list.loc[i, "spouse_dynasty"]]["weight"] +=1
        else:               
            G.add_edge(marriage_edge_list.loc[i, "dynasty"], marriage_edge_list.loc[i, "spouse_dynasty"], weight = 1)
        complete_set.add( (marriage_edge_list.loc[i, "target"], marriage_edge_list.loc[i, "source"]) )
            
G.remove_nodes_from(nx.isolates(G)) #drop unconnected nodes       
nx.write_graphml(max(nx.connected_component_subgraphs(G), key=len), "ck2-Royal-Marrige-Network.graphml")<jupyter_output><empty_output><jupyter_text>The graphml file in the code above was opened in Gephi and the picture below was generated. Orange nodes are dynasties that have held Kingdom or Empire level titles while blue have not. Europe is on the left of the image, India is made up of the two clusters on the right wing. The bottom two clusters are Spain and North Africa, the middle is Greece and the Middle East and Eastern Europe/Mongol Empire make up the sparse patches across the top.Nodes are sized by PageRank and it is interesting to see that there are no large blue nodes. These would be non-titled dynasties that have married often to the immediate power brokers of a powerful title. Most likely a dynasty with many marriages into a title will eventually try to take control of the title or may inherit it.<jupyter_code>from IPython.display import Image
from IPython.core.display import HTML 
Image(url= "http://www.anquantarbuile.com/static/images/ck2/RoyalMarriageNetworkTitled.png")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13543976348854397, 'fraction_numerical': 0.0025868440502586844, 'mean_word_length': 3.662360034453058, 'pattern_counts': {'":': 2, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '648337', 'license': 'MIT', 'n_tokens_mistral': 1812, 'n_tokens_neox': 1789, 'n_words': 450, 'path': 'notebooks/8. Royal Marriage Network.ipynb', 'repo_name': 'maniacalbrain/ck2-social-networks', 'size': '5412'}