7
votes

NetworkX - Définition des attributs de nœud à partir du dataframe

J'ai du mal à comprendre comment ajouter des attributs aux nœuds de mon réseau à partir des colonnes de mon dataframe.

J'ai fourni un exemple de mon dataframe ci-dessous, il y a environ 10 colonnes au total, mais je n'utilise que les 5 colonnes ci-dessous lors de la création de mon réseau.

Malheureusement, pour le moment, je ne peux faire fonctionner que les attributs de périphérie avec mon réseau, je le fais comme indiqué ci-dessous:

id:   attribute1:   attribute2:     attribute3:
jim      tall          red             fat
john     small         blue            fat
...

Le réseau sera un réseau dirigé. Les attributs affichés dans le dataframe ci-dessous sont les attributs des nœuds 'node_from'. Les nœuds 'node_to' apparaissent parfois comme des nœuds 'node_from'. Tous les nœuds qui peuvent éventuellement être affichés dans le réseau et leurs attributs respectifs sont affichés dans la table df_attributes_only.

df_relationship:

node_from:  node_to: ........ attribute1:   attribute2:   attribute3:
    jim      john    ........    tall          red             fat
    ...

Toutes les colonnes ont des mots comme valeurs, pas des chiffres.

J'ai aussi un autre dataframe qui a chaque nœud possible et leurs attributs:

df_attributes_only:

g = nx.from_pandas_dataframe(df, 'node_from', 'node_to', edge_attr=['attribute1','attribute2','attribute3'])

Je dois essentiellement attribuer les trois attributs ci-dessus à leur identifiant respectif, de sorte que chaque nœud a ses 3 attributs attachés.

Toute aide sur la façon dont je pourrais faire fonctionner les attributs de nœud avec mon réseau est grandement appréciée.


1 commentaires

Question rapide sur les attributs. Décrivent-ils les nœuds qu'ils connectent ou décrivent-ils d'une manière ou d'une autre la relation? Par exemple, Jim est-il grand et gros? Est-ce que cela décrit d'une manière ou d'une autre la relation entre Jim et autre chose? Y a-t-il des cas où il pourrait y avoir plusieurs attributs, par exemple y a-t-il une autre entrée pour jim qui montre la relation, mais le répertorie comme court et gras? Jim aura-t-il une relation multiple?


3 Réponses :


2
votes

nx.from_pandas_dataframe (et from_pandas_edgelist dans la dernière version stable 2.2), convertit conceptuellement une edgelist en graphique. C'est-à-dire que chaque ligne de la trame de données représente une arête, qui est une paire de 2 nœuds différents .

En utilisant cette API, il n'est pas possible de lire les attributs des nœuds. Cela a du sens, car chaque ligne a deux nœuds différents et conserver des colonnes spécifiques pour les différents nœuds serait fastidieux et peut entraîner des écarts. Par exemple, considérez le dataframe suivant:

#List of desired source attributes:
src_attributes = ['src_attr_1', 'src_attr_2', 'src_attr_3']

# Iterate over df rows and set source node attributes:
for index, row in df.iterrows():
    src_attr_dict = {k: row.to_dict()[k] for k in src_attributes}    
    G.nodes[row['node_from']].update(src_attr_dict)

Quelle devrait être la valeur 'src_attr_1' pour le nœud a? Est-ce 0 ou 2? De plus, nous devons garder deux colonnes pour chaque attribut (puisqu'il s'agit d'un attribut de nœud, les deux nœuds de chaque arête devraient l'avoir). À mon avis, ce serait une mauvaise conception de le supporter, et je suppose que c'est pourquoi l'API NetworkX ne le fait pas.

Vous pouvez toujours lire les attributs des nœuds, après avoir converti le df en graphique, comme suit:

import networkx as nx
import pandas as pd

# Build a sample dataframe (with 2 edges: 0 -> 1, 0 -> 2, node 0 has attr_1 value of 'a', node 1 has 'b', node 2 has 'c')
d = {'node_from': [0, 0], 'node_to': [1, 2], 'src_attr_1': ['a','a'], 'tgt_attr_1': ['b', 'c']}
df = pd.DataFrame(data=d)
G = nx.from_pandas_edgelist(df, 'node_from', 'node_to')

# Iterate over df rows and set the source and target nodes' attributes for each row:
for index, row in df.iterrows():
    G.nodes[row['node_from']]['attr_1'] = row['src_attr_1']
    G.nodes[row['node_to']]['attr_1'] = row['tgt_attr_1']

print(G.edges())
print(G.nodes(data=True))

Edit:

Si vous souhaitez avoir une grande liste d'attributs pour le nœud source, vous pouvez extraire automatiquement le dictionnaire de ces colonnes comme suit :

node_from node_to src_attr_1 tgt_attr_1
  a         b         0         3
  a         c         2         4


1 commentaires

Les commentaires ne sont pas destinés à une discussion approfondie; cette conversation a été déplacée vers chat .



0
votes

Ceci s'appuie sur la réponse de @ zohar.kom. Il existe un moyen de résoudre ce problème sans itération. Cette réponse peut être optimisée. Je suppose que les attributs décrivent le node_from .

Commencez par un graphique d'une edgelist (comme dans l'anser de @ zohar.kom):

 # Add the attributes one at a time.
 attr_dict = nodes.set_index('node_from')['attribute1'].to_dict()
 nx.set_node_attributes(G,attr_dict,'attr1')

 attr_dict = nodes.set_index('node_from')['attribute2'].to_dict()
 nx.set_node_attributes(G,attr_dict,'attr2')

 attr_dict = nodes.set_index('node_from')['attribute3'].to_dict()
 nx.set_node_attributes(G,attr_dict,'attr3')

Vous pouvez d'abord ajouter les nœuds et les attributs.

 # Create a mask with only the first records
 mask = ~df['node_from'].duplicated()
 # Get a list of nodes with attributes
 nodes = df[mask][['node_from','attribute1','attribute2','attribute3']]

Cette méthode d'ajout de nœuds à partir d'un dataframe provient de cette réponse .

 G = nx.from_pandas_edgelist(df, 'node_from', 'node_to')

Résultat similaire à @ zohar.kom, mais avec moins d'itérations.


5 commentaires

Quel est le nom de la colonne pour node from ? C'est ce qui doit figurer dans cette colonne.


TiAddendum, je me rends compte en postant cela, j'ai oublié les guillemets. J'ai édité cela dans la réponse.


Oui, mais votre exemple ne montre nulle part où le détail du nœud apparaîtra. Quels champs contiennent des données pour les nœuds?


Dans l'exemple, vous répertoriez à la fois jim et john sur une seule ligne des données source avec les attributs tall, red et fat dans les champs d'attribut. Cela signifie-t-il que Jim et John ont tous deux ces attributs? Sinon, quel nom de colonne stocke les attributs du node_to?


Oui, vous devrez ajouter des données pour nodes_to . Aucune quantité de code ne vous permettra d'inventer des données qui ne sont pas là. Une fois ajouté, vous utiliseriez simplement le même code exact en remplaçant node_to par node_from et les nouveaux champs qui décrivent le node_to pour les old_fileds décrivant node_from. Si vous interrogez les données et avez la possibilité d'extraire ces colonnes, j'ai l'impression qu'il existe probablement un meilleur moyen de résoudre tout ce processus, mais nous n'aurons pas suffisamment d'informations pour passer cet appel.



10
votes

À partir de Networkx 2.0 , vous pouvez saisir un dictionnaire de dictionnaires dans nx.set_node_attributes pour définir les attributs de plusieurs nœuds. Il s'agit d'une approche beaucoup plus simplifiée par rapport à l'itération manuelle sur chaque nœud. Les clés de dictionnaire externes représentent chaque nœud et les clés de dictionnaires internes correspondent aux attributs que vous souhaitez définir pour chaque nœud. Quelque chose comme ceci:

df_attributes_only = pd.DataFrame(
    [['jim', 'tall', 'red', 'fat'], ['john', 'small', 'blue', 'fat']],
    columns=['id', 'attribute1', 'attribute2', 'attribute3']
)
node_attr = df_attributes_only.set_index('id').to_dict('index')
nx.set_node_attributes(g, node_attr)

g.nodes['jim']


>>> {'attribute1': 'tall', 'attribute2': 'red', 'attribute3': 'fat'}

Vous pouvez trouver plus de détails dans la documentation .


En utilisant votre exemple, en supposant que votre index est id , vous pouvez convertir votre dataframe df_attributes_only des attributs de nœud à ce format et ajoutez à votre graphique:

attrs = {
    node0: {attr0: val00, attr1: val01},
    node1: {attr0: val10, attr1: val11},
    node2: {attr0: val20, attr1: val21},
}
nx.set_node_attributes(G, attrs)


0 commentaires