diff --git a/Network Science/funciones_network_science.py b/Network Science/funciones_network_science.py new file mode 100644 index 0000000000000000000000000000000000000000..ae9c0ed0bc738cab5b69168621dbe951d1728dec --- /dev/null +++ b/Network Science/funciones_network_science.py @@ -0,0 +1,362 @@ +#! /usr/bin/env python + +""" +# --------------------------------------------------------------------------- +# +# funciones_network_science.py +# +# Archivo con todas las funciones que he utilizado para aplicar métricas de +# ciencia de redes a redes bipartitas y proyectadas. +# +# TFG: Ciencia de redes y reposicionamiento de fármacos: potencial a través +# de la medicina de redes +# +# María Marín Tercero +# ---------------------------------------------------------------------------- +""" + +import pandas as pd +import numpy as np +import seaborn as sns +import matplotlib.pyplot as plt +import networkx as nx +from networkx.algorithms import bipartite +import nxpd + +# ================================================================================= + +def asociaciones(archivo, var1, var2, nombre_var1, nombre_var2, ax): + """ + Esta función toma un archivo que representa asociaciones entre dos grupos disjuntos de nodos. Finalmente, representa en un histograma la distribución del número de asociaciones de un grupo de nodos con nodos de la otra clase. + Datos de entrada: + 1. archivo: Archivo con asociaciones entre nodos organizado en dos columnas, una para cada grupo de nodos + 2. var1: Nombre de una columna + 3. var2: Nombre de la otra columna + 4. nombre_var1: Nombre de los nodos representados en la columna 1 + 5 nombre_var2: Nombre de los nodos representados en la columna 2 + 6. ax: Variable empleada para la representación organizada en un subplot + + """ + conteo = archivo.groupby(var1)[var2].count().reset_index() #cuento el número de asociaciones del grupo var1 con el grupo var2 + + #Represento los resultados + ax.hist(conteo[var2], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Nº de ' + nombre_var2, fontsize=12) + ax.set_ylabel(nombre_var1, fontsize=12) + ax.set_title('Distribución de '+ r'$\mathbf{' + nombre_var2 + '}$' + " por " + r'$\mathbf{' + nombre_var1 + '}$' , fontsize=12) + +# ================================================================================= + +def matriz_spl_enfermedades_bip(archivo, graf): + """ + Esta función genera una matriz bidimensional que contiene los Shortest Path + Lenghts de todos los nodos enfermedades de una red bipartita compuesta por dos + grupos disjuntos de nodos. + Datos de entrada: + 1. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 2. graf: Red bipartita + """ + + enfermedades = list(set(archivo['dis'])) #lista de enfermedades (columna "dis" del archivo) + + matriz_spl = np.zeros((len(enfermedades), len(enfermedades))) #Inicializo una matriz de ceros para almacenar los SPL + + #Calculo los SPL entre todas las parejas de enfermedades + for i in range(len(enfermedades)): #enfermedad_i + for j in range(i + 1, len(enfermedades)): #enfermedad_i+1 + enfermedad_i = enfermedades[i] #guardo el nodo enfermedad_i en la fila i de la matriz + enfermedad_j = enfermedades[j] #guardo el nodo enfermedad_i+1 en la columna j de la matriz + try: #me aseguro de que exista un camino entre ambas enfermedades + spl = nx.shortest_path_length(graf, source=enfermedad_i, target=enfermedad_j) #calculo su spl + matriz_spl[i, j] = spl #añado el valor a la matriz en su posición correspondiente + matriz_spl[j, i] = spl #añado el valor a la matriz en su posición correspondiente + except nx.NetworkXNoPath: #si no hay camino, continúo + pass + #Creo un DataFrame con la matriz de SPLs + df_matriz_spl = pd.DataFrame(matriz_spl, index=enfermedades, columns=enfermedades) + + return df_matriz_spl + +# ================================================================================= + +def matriz_spl_var_bip(archivo, graf, nombre_var): + """ + Esta función genera una matriz bidimensional que contiene los Shortest Path + Lenghts para el grupo de nodos distinto a enfermedades en una red bipartita + que contiene dos grupos diferenets de nodos, siendo uno enfermedades. El otro + grupo varía en función de la red. + Datos de entrada: + 1. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 2. graf: Red bipartita + 3. nombre_var: Identificador del grupo de nodos distinto a enfermedades en el archivo + """ + + var = list(set(archivo[str(nombre_var)])) #lista de nodos del grupo disjunto a enfermedades (columna con el identificador añadido en los datos de entrada) + + matriz_spl = np.zeros((len(var), len(var))) #Inicializo una matriz de ceros para almacenar los SPL + + #Calculo los SPL entre todas las parejas de los nodos seleccionados + for i in range(len(var)): #nodos_i + for j in range(i + 1, len(var)): #nodo_i+1 + var_i = var[i] #guardo el nodo_i en la fila i de la matriz + var_j = var[j] #guardo el nodo_i+1 en la columna j de la matriz + try: #me aseguro de que exista un camino entre ambos nodos + spl = nx.shortest_path_length(graf, source=var_i, target=var_j) #calculo su spl + matriz_spl[i, j] = spl #añado el valor a la matriz en su posición correspondiente + matriz_spl[j, i] = spl #añado el valor a la matriz en su posición correspondiente + except nx.NetworkXNoPath: #si no hay camino, continúo + pass + + #Creo un DataFrame con la matriz de SPLs + df_matriz_spl = pd.DataFrame(matriz_spl, index=var, columns=var) + + return df_matriz_spl + +# ================================================================================= + +def matriz_spl_enfermedades_proj(archivo, graf): + """ + Esta función genera una matriz bidimensional que contiene los Shortest Path + Lenghts para las enfermedades en una red proyectada. + Datos de entrada: + 1. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 2. graf: Red bipartita + """ + all_shortest_paths = dict(nx.all_pairs_shortest_path_length(graf)) #creo un diccionario con los spls entre todas las parejas de nodos de la red + + enfermedades = list(graf.nodes) # Obtengo la lista de nodos enfermedades + + matriz_spl = np.zeros((len(enfermedades), len(enfermedades))) #Inicializo una matriz de ceros para almacenar los SPL + + for i, enfermedad_i in enumerate(enfermedades): #enfermedad_i + for j, enfermedad_j in enumerate(enfermedades): #enfermedad_j + matriz_spl[i, j] = all_shortest_paths[enfermedad_i].get(enfermedad_j, 0) #añado a la matriz el spl entre la enfermedad_i y la enfermedad_j según el diccionario con el total de spls + + #Creo un DataFrame con la matriz de SPLs + df_matriz_spl = pd.DataFrame(matriz_spl, index=enfermedades, columns=enfermedades) + return df_matriz_spl + +# ================================================================================= +def degree_bip(graf, archivo, nombre_graf, nombre_var, ax, ax1): + """ + Esta función calcula y representa la distribución de grados de dos + grupos disjuntos de nodos en una red bipartita, siendo uno de esos grupos + el correspondiente a enfermedades + Datos de entrada: + 1. graf: Red bipartita + 2. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 3. nombre_graf: Nombre de la red + 4. nombre_var: Nombre del grupo disjunto de nodos distinto a enfermedades + 5. ax: Variable empleada para la representación organizada de la primera distribución en un subplot + 6. ax1: Variable empleada para la representación organizada de la segunda distribución en un subplot + """ + degrees_dict = dict(graf.degree()) # diccionario con los grados de los nodos del grafo + + nodos_enf = set(archivo.iloc[:, 0]) # selecciono el set de nodos enfermedades en el archivo (que se encuentran en la primera columna) + nodos_var = set(archivo.iloc[:, 1]) # selecciono el set de nodos del grupo variable en el archivo (que se encuentran en la segunda columna) + + # Filtro los degrees para nodos de enfermedades y variables + degree_enf = [degrees_dict[clave] for clave in nodos_enf if clave in degrees_dict] + degree_var = [degrees_dict[clave] for clave in nodos_var if clave in degrees_dict] + + # Represento la distribución de grados de las enfermedades + G_lista_enf = pd.DataFrame(list(zip(nodos_enf, degree_enf)), columns=['node', 'degree']) + G_plot_enf = G_lista_enf.groupby('degree').count() + ax.scatter(G_plot_enf.index, G_plot_enf['node'], marker='o', color='#79C4FF', s=10) + ax.set_yscale('log') + ax.set_xscale('log') + ax.set_xlabel('Grado', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de grados de ' + r'$\mathbf{' + "enfermedades" + '}$' +' en\n' + r'$\mathbf{' + "red" + '}$' + " " + r'$\mathbf{' + "bipartita" + '}$'+ " "+ r'$\mathbf{' + nombre_graf + '}$' , fontsize=12) + + # Represento la distribución de grados del grupo disjunto de nodos distinto a enfermedades + G_lista_var = pd.DataFrame(list(zip(nodos_var, degree_var)), columns=['node', 'degree']) + G_plot_var = G_lista_var.groupby('degree').count() + ax1.scatter(G_plot_var.index, G_plot_var['node'], marker='o', color='#79C4FF', s=10) + ax1.set_yscale('log') + ax1.set_xscale('log') + ax1.set_xlabel('Grado', fontsize=10) + ax1.set_ylabel('Nº nodos', fontsize=10) + ax1.set_title('Distribución de grados de ' + r'$\mathbf{' + nombre_var + '}$' + ' en\n' + r'$\mathbf{' + " red" + '}$' + " "+ r'$\mathbf{' + "bipartita" + '}$'+ " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=12) + + +# ================================================================================= + +def degree_proj(graf, nombre_graf, ax): + """ + Esta función calcula y representa la distribución de grados de las enfermedades + en una red proyectada. + Datos de entrada: + 1. graf: Red bipartita + 2. nombre_graf: Nombre de la red + 3. ax: Variable empleada para la representación organizada de la distribución en un subplot + """ + degrees_dict = dict(graf.degree()) # diccionario con los grados de los nodos del grafo + + nodos = list(degrees_dict.keys()) # lista con los nodos de la red (claves del diccionario anterior) + degrees = list(degrees_dict.values()) # lista con los grados de los nodos de la red (valores del diccionario anterior) + + # Represento la distribución de grados + G_lista = pd.DataFrame(list(zip(nodos, degrees)), columns=['node', 'degree']) + G_plot = G_lista.groupby('degree').count() + ax.scatter(G_plot.index, G_plot['node'], marker='o', color='#79C4FF', s=10) + ax.set_yscale('log') + ax.set_xscale('log') + ax.set_xlabel('Grado', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de grados de ' + r'$\mathbf{' + "enfermedades" + '}$'+ ' en\n' + r'$\mathbf{' + " red "+ '}$'+ " "+ r'$\mathbf{' + "proyectada" + '}$' + " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=12) + +# ================================================================================= + +def average_spl(matriz_spl, nombre_graf, nombre_var, ax, bipartita = True): + """ + Esta función realiza una distribución de los Average SPL d los nodos de un grafo. + Datos de entrada: + 1. matriz_spl: Matriz con la información sobre los SPLs entre todas las parejas de nodos + 2. nombre_graf: Nombre de la red + 3. nombre_var: Nombre del tipo de nodos representado + 4. ax: Variable empleada para la representación organizada de la distribución en un subplot + 5. Bipartita: dependiendo de si es igualue o False, añade el título correspondiente a una red bipartita o una proyectada, respectivamente. + """ + average_spl_por_nodo = matriz_spl.mean(axis=0) #Calculo el Average_SPL de cada nodo (media de cada columna de la matriz de SPLs) + + df_average_spl = pd.DataFrame({'Nodo': matriz_spl.index, 'AverageSPL': average_spl_por_nodo.values}) #creo un DataFrame con los resultados + + # Represento la distribución de los Average Shortest Path Lengths + ax.hist(df_average_spl['AverageSPL'], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Average Shortest Path Lengths', style='italic', fontsize=12) + ax.set_ylabel('Nº nodos', fontsize=12) + if bipartita == True: + ax.set_title('Distribución de Average SPLs de ' + r'$\mathbf{' + nombre_var + '}$'+ ' en\n' + r'$\mathbf{' + " red "+ '}$'+ " "+ r'$\mathbf{' + "bipartita" + '}$' + " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=12) + else: + ax.set_title('Distribución de Average SPLs de ' + r'$\mathbf{' + "enfermedades" + '}$'+ ' en\n' + r'$\mathbf{' + " red "+ '}$'+ " "+ r'$\mathbf{' + "proyectada" + '}$' + " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=12) + +# ================================================================================= + +def centrality_b_bip(graf, archivo, nombre_graf, nombre_var, ax, ax1): + """ + Esta función calcula y representa la distribución de centralidad de intermediación + de dos grupos disjuntos de nodos en una red bipartita, siendo uno de esos grupos + el correspondiente a enfermedades + Datos de entrada: + 1. graf: Red bipartita + 2. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 3. nombre_graf: Nombre de la red + 4. nombre_var: Nombre del grupo disjunto de nodos distinto a enfermedades + 5. ax: Variable empleada para la representación organizada de la primera distribución en un subplot + 6. ax1: Variable empleada para la representación organizada de la segunda distribución en un subplot + + """ + #Calculo la centralidad de intermediación de todos los nodos del grafo + bc = nx.betweenness_centrality(graf, k=None, normalized=True, weight=None, endpoints=False, seed=None) + + nodos_enf = set(archivo.iloc[:, 0]) #selecciono el set de nodos enfermedades en el archivo (que se encuentran en la primera columna) + nodos_var = set(archivo.iloc[:, 1]) #selecciono el set de nodos del grupo variable en el archivo (que se encuentran en la segunda columna) + + #Filtro la centralidad de intermediación para ambos grupos de nodos + bc_enf = {clave: bc[clave] for clave in nodos_enf if clave in bc} + bc_var = {clave: bc[clave] for clave in nodos_var if clave in bc} + + #Creo DataFrames para la centralidad de intermediación de ambos grupos de nodos + df_bc_enf = pd.DataFrame({'Nodo': list(bc_enf.keys()), 'Centralidad de intermediación': list(bc_enf.values())}) + df_bc_var = pd.DataFrame({'Nodo': list(bc_var.keys()), 'Centralidad de intermediación': list(bc_var.values())}) + + #Represento la centralidad de intermediación de las enfermedades + ax.hist(df_bc_enf['Centralidad de intermediación'], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Centralidad de intermediación', style='italic', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de centralidad de intermediación de ' + r'$\mathbf{' + "enfermedades" + '}$' +' en\n' + r'$\mathbf{' + "red" + '}$' + " " + r'$\mathbf{' + "bipartita" + '}$'+ " "+ r'$\mathbf{' + nombre_graf + '}$' , fontsize=10) + + #Represento la centralidad de intermediación del grupo disjunto de nodos distinto a enfermedades + ax1.hist(df_bc_var['Centralidad de intermediación'], bins=20, color='#79C4FF', edgecolor='black') + ax1.set_xlabel('Centralidad de intermediación', style='italic', fontsize=10) + ax1.set_ylabel('Nº nodos', fontsize=10) + ax1.set_title('Distribución de centralidad de intermediación de ' + r'$\mathbf{' + nombre_var + '}$' + ' en\n' + r'$\mathbf{' + " red" + '}$' + " "+ r'$\mathbf{' + "bipartita" + '}$'+ " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=10) + +# ================================================================================= + +def centrality_b_proj(graf, nombre_graf, ax): + """ + Esta función calcula y representa la distribución de centralidad de intermediación + de enfermedades en una red proyectada. + Datos de entrada: + 1. graf: Red bipartita + 2. nombre_graf: Nombre de la red + 3. ax: Variable empleada para la representación organizada de la distribución en un subplot + """ + #Calculo la centralidad de intermediación de todos los nodos del grafo + bc= nx.betweenness_centrality(graf, k=None, normalized=True, weight=None, endpoints=False, seed=None) + + #Creo un DataFrame para la centralidad de intermediación de las enfermedades + df_bc = pd.DataFrame({'Nodo': list(bc.keys()), 'Centralidad de intermediación': list(bc.values())}) + + #Represento la centralidad de intermediación de las enfermedades + ax.hist(df_bc['Centralidad de intermediación'], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Centralidad de intermediación',style='italic', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de centralidad de intermediación de ' + r'$\mathbf{' + "enfermedades" + '}$'+ ' en\n' + r'$\mathbf{' + " red "+ '}$'+ " "+ r'$\mathbf{' + "proyectada" + '}$' + " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=10) + +# ================================================================================= + +def centrality_c_bip(graf, archivo, nombre_graf, nombre_var, ax, ax1): + """ + Esta función calcula y representa la distribución de centralidad de cercanía + de dos grupos disjuntos de nodos en una red bipartita, siendo uno de esos grupos + el correspondiente a enfermedades + Datos de entrada: + 1. graf: Red bipartita + 2. archivo: Archivo con los datos sobre los enlaces entre los nodos de la red + 3. nombre_graf: Nombre de la red + 4. nombre_var: Nombre del grupo disjunto de nodos distinto a enfermedades + 5. ax: Variable empleada para la representación organizada de la primera distribución en un subplot + 6. ax1: Variable empleada para la representación organizada de la segunda distribución en un subplot + + """ + #Calculo la centralidad de intermediación de todos los nodos del grafo + cc = nx.closeness_centrality(graf, u=None, distance=None, wf_improved=True) + + nodos_enf = set(archivo.iloc[:, 0]) #selecciono el set de nodos enfermedades en el archivo (que se encuentran en la primera columna) + nodos_var = set(archivo.iloc[:, 1]) #selecciono el set de nodos del grupo variable en el archivo (que se encuentran en la segunda columna) + + #Filtro la centralidad de cercanía para ambos grupos de nodos + cc_enf = {clave: cc[clave] for clave in nodos_enf if clave in cc} + cc_var = {clave: cc[clave] for clave in nodos_var if clave in cc} + + #Creo DataFrames para la centralidad de cercanía de ambos grupos de nodos + df_cc_enf = pd.DataFrame({'Nodo': list(cc_enf.keys()), 'Centralidad de cercanía': list(cc_enf.values())}) + df_cc_var = pd.DataFrame({'Nodo': list(cc_var.keys()), 'Centralidad de cercanía': list(cc_var.values())}) + + #Represento la centralidad de intermediación de las enfermedades + ax.hist(df_cc_enf['Centralidad de cercanía'], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Centralidad de cercanía', style='italic', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de centralidad de cercanía de ' + r'$\mathbf{' + "enfermedades" + '}$' +' en\n' + r'$\mathbf{' + "red" + '}$' + " " + r'$\mathbf{' + "bipartita" + '}$'+ " "+ r'$\mathbf{' + nombre_graf + '}$' , fontsize=10) + + #Represento la centralidad de cercanía del grupo disjunto de nodos distinto a enfermedades + ax1.hist(df_cc_var['Centralidad de cercanía'], bins=20, color='#79C4FF', edgecolor='black') + ax1.set_xlabel('Centralidad de cercanía', style='italic', fontsize=10) + ax1.set_ylabel('Nº nodos', fontsize=10) + ax1.set_title('Distribución de centralidad de cercanía de ' + r'$\mathbf{' + nombre_var + '}$' + ' en\n' + r'$\mathbf{' + " red" + '}$' + " "+ r'$\mathbf{' + "bipartita" + '}$'+ " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=10) + +# ================================================================================= + +def centrality_c_proj(graf, nombre_graf,ax): + """ + Esta función calcula y representa la distribución de centralidad de cercanía + de enfermedades en una red proyectada. + Datos de entrada: + 1. graf: Red bipartita + 2. nombre_graf: Nombre de la red + 3. ax: Variable empleada para la representación organizada de la distribución en un subplot + """ + #Calculo la centralidad de cercanía de todos los nodos del grafo + cc= nx.closeness_centrality(graf, u=None, distance=None, wf_improved=True) + #Creo un DataFrame para la centralidad de cercanía de las enfermedades + df_cc = pd.DataFrame({'Nodo': list(cc.keys()), 'Centralidad de cercanía': list(cc.values())}) + #Represento la centralidad de cercanía de alas enfermedades + ax.hist(df_cc['Centralidad de cercanía'], bins=20, color='#79C4FF', edgecolor='black') + ax.set_xlabel('Centralidad de cercanía',style='italic', fontsize=10) + ax.set_ylabel('Nº nodos', fontsize=10) + ax.set_title('Distribución de centralidad de cercanía de ' + r'$\mathbf{' + "enfermedades" + '}$'+ ' en\n' + r'$\mathbf{' + " red "+ '}$'+ " "+ r'$\mathbf{' + "proyectada" + '}$' + " " + r'$\mathbf{' + nombre_graf + '}$', fontsize=10) + +# =================================================================================