web-dev-qa-db-fra.com

Charger des nœuds avec des attributs et des arêtes de DataFrame à NetworkX

Je suis nouveau en utilisant Python pour travailler avec des graphiques: NetworkX. Jusqu'à présent, j'ai utilisé Gephi. Les étapes standard (mais pas les seules possibles) sont les suivantes:

  1. Chargez les informations sur les nœuds à partir d'une table/feuille de calcul. une des colonnes doit être ID et les autres sont des métadonnées sur les nœuds (les nœuds sont des personnes, donc le genre, les groupes ... normalement utilisés pour la coloration). Comme:

    id;NormalizedName;Gender
    per1;Jesús;male
    per2;Abraham;male
    per3;Isaac;male
    per4;Jacob;male
    per5;Judá;male
    per6;Tamar;female
    ...
    
  2. Chargez ensuite les bords également à partir d'une table/feuille de calcul, en utilisant les mêmes noms pour les nœuds que dans l'ID de colonne de la feuille de calcul avec 4 colonnes (Cible, Source, Poids et Type):

    Target;Source;Weight;Type
    per1;per2;3;Undirected
    per3;per4;2;Undirected
    ...
    

Ce sont les deux images que j'ai et que je veux charger en Python. À propos de NetworkX, il semble qu'il ne soit pas tout à fait possible de charger deux tables (une pour les nœuds, une pour les arêtes) dans le même graphique et je ne suis pas sûr de ce qui serait le meilleur moyen:

  1. Devrais-je créer un graphique uniquement avec les informations sur les nœuds du DataFrame, puis ajouter (ajouter) les arêtes de l'autre DataFrame? Si tel est le cas et que nx.from_pandas_dataframe () attend des informations sur les contours, je suppose que je ne devrais pas les utiliser pour créer les nœuds ... Devrais-je simplement transmettre les informations sous forme de listes?

  2. Devrais-je créer un graphique uniquement avec les informations sur les bords du DataFrame, puis ajouter à chaque nœud les informations des autres DataFrame en tant qu'attributs? Y a-t-il un meilleur moyen de le faire que d’itérer sur le DataFrame et les nœuds?

11
José

Créez le graphe pondéré à partir du tableau Edge à l’aide de nx.from_pandas_dataframe :

import networkx as nx
import pandas as pd

edges = pd.DataFrame({'source' : [0, 1],
                      'target' : [1, 2],
                      'weight' : [100, 50]})

nodes = pd.DataFrame({'node' : [0, 1, 2],
                      'name' : ['Foo', 'Bar', 'Baz'],
                      'gender' : ['M', 'F', 'M']})

G = nx.from_pandas_dataframe(edges, 'source', 'target', 'weight')

Ajoutez ensuite les attributs de noeud des dictionnaires en utilisant set_node_attributes :

nx.set_node_attributes(G, 'name', pd.Series(nodes.name, index=nodes.node).to_dict())
nx.set_node_attributes(G, 'gender', pd.Series(nodes.gender, index=nodes.node).to_dict())

Ou parcourez le graphique pour ajouter les attributs de nœud:

for i in sorted(G.nodes()):
    G.node[i]['name'] = nodes.name[i]
    G.node[i]['gender'] = nodes.gender[i]

Mettre à jour:

À partir de nx 2.0, l'ordre des arguments de nx.set_node_attributes a changé : (G, values, name=None)

En utilisant l'exemple ci-dessus:

nx.set_node_attributes(G, pd.Series(nodes.gender, index=nodes.node).to_dict(), 'gender')
14
harryscholes

Voici essentiellement la même réponse, mais mise à jour avec quelques détails renseignés. Nous allons commencer par la même configuration, mais ici, il n'y aura pas d'index pour les nœuds, mais des noms pour adresser @LancelotHolmes commenter et le rendre plus général:

import networkx as nx
import pandas as pd

linkData = pd.DataFrame({'source' : ['Amy', 'Bob'],
                  'target' : ['Bob', 'Cindy'],
                  'weight' : [100, 50]})

nodeData = pd.DataFrame({'name' : ['Amy', 'Bob', 'Cindy'],
                  'type' : ['Foo', 'Bar', 'Baz'],
                  'gender' : ['M', 'F', 'M']})

G = nx.from_pandas_edgelist(linkData, 'source', 'target', True, nx.DiGraph())

Ici, le paramètre True indique à NetworkX de conserver toutes les propriétés dans linkData en tant que propriétés de lien. Dans ce cas, j'ai défini un type DiGraph, mais si vous n'en avez pas besoin, vous pouvez en faire un autre type de manière évidente.

Maintenant, étant donné que vous devez faire correspondre le nom de noeud nodeData au nom des noeuds générés à partir de linkData, vous devez définir l'index du cadre de données nodeData sur la propriété name avant de le transformer en dictionnaire afin que NetworkX 2.x puisse le charger. comme les attributs de noeud.

nx.set_node_attributes(G, nodeData.set_index('name').to_dict('index'))

Cela charge l'ensemble de la structure de données nodeData dans un dictionnaire dans lequel la clé est le nom et les autres propriétés sont des paires clé: valeur dans cette clé (c'est-à-dire des propriétés de nœud normales dans lesquelles l'indice du nœud est son nom).

1
Aaron Bramson

Une petite remarque:

from_pandas_dataframe ne fonctionne pas dans nx 2, en référence à celui-ci

G = nx.from_pandas_dataframe(edges, 'source', 'target', 'weight')

Je pense que dans nx 2.0 ça va comme ça:

G = nx.from_pandas_edgelist(edges, source = "Source", target = "Target")
0
Ioanna