2012-10-22 24 views
8

Sto usando pydot per disegnare grafici in python. Mi piacerebbe rappresentare un albero decisionale, dire qualcosa di simile (A1, A2, A3 sono attributi e due classi sono 0 e 1:pydot: è possibile tracciare due nodi diversi con la stessa stringa?

 a1>3 
    / \ 
    a2>10 a3>-7 
/\ /\ 
    1 0 1 0 

Tuttavia, utilizzando pydot, solo due foglie vengono creati e l'albero sembra che questo (png allegata):

 a1>3 
    / \ 
    a2>10 a3>-7 
     | X | 
     1  0 

Ora, in questo caso semplice la logica è soddisfacente, ma negli alberi più grandi è nodi interni disordinato appartenenti a diversi rami sono unificati

il semplice codice I'. m utilizzando è:

import pydot 
graph = pydot.Dot(graph_type='graph') 
edge = pydot.Edge("a_1>3", "a_2>10") 
graph.add_edge(edge) 
edge = pydot.Edge("a_1>3", "a_3>-7") 
graph.add_edge(edge) 
edge = pydot.Edge("a_2>10", "1") 
graph.add_edge(edge) 
edge = pydot.Edge("a_2>10", "0") 
graph.add_edge(edge) 
edge = pydot.Edge("a_3>-7", "1") 
graph.add_edge(edge) 
edge = pydot.Edge("a_3>-7", "0") 
graph.add_edge(edge) 
graph.write_png('simpleTree.png') 

Ho anche provato a creare oggetti nodo diversi da creare i bordi e poi aggiungerlo al grafico ma sembra che pydot controlli il pool di nodi per i nodi con lo stesso nome invece di crearne uno nuovo.

Qualche idea? Grazie!

the image created by the code above

risposta

13

I suoi nodi hanno sempre bisogno di un nomi univoci, altrimenti non è possibile assegnare loro un nome unico per collegare tra di loro bordi. Tuttavia, è possibile assegnare a ciascun nodo un'etichetta, che è ciò che viene visualizzato al momento del rendering.

Quindi è necessario aggiungere nodi con ID univoci:

graph = pydot.Dot(graph_type='graph') 
graph.add_node(pydot.Node('literal_0_0', label='0')) 
graph.add_node(pydot.Node('literal_0_1', label='0')) 
graph.add_node(pydot.Node('literal_1_0', label='1')) 
graph.add_node(pydot.Node('literal_1_1', label='1')) 

quindi aggiungere bordi del grafico che collegano questi nodi:

edge = pydot.Edge("a_2>10", "literal_0_0") 
graph.add_edge(edge) 
edge = pydot.Edge("a_2>10", "literal_1_0") 
graph.add_edge(edge) 
edge = pydot.Edge("a_3>-7", "literal_0_1") 
graph.add_edge(edge) 
edge = pydot.Edge("a_3>-7", "literal_1_1") 
graph.add_edge(edge) 

Insieme al resto dei bordi definiti questo rende:

graph with correct edges

1

La risposta "canonica" è quella di utilizzare il modulo uuid dalla libreria standard, come networkxdoes here.

Questo è meglio che usare id per creare i nomi dei nodi per pydot che corrispondono ai nodi nel grafico originale, perché se (in teoria) un oggetto nodo viene eliminato mentre si sta costruendo il grafico pydot, allora che id vinto' essere necessariamente unico Al contrario, gli oggetti UUID creati sono unici, persistenti e indipendenti dalla durata dei nodi originali.

Tuttavia, perché ciò accada, qualcosa di molto strano deve essere in corso mentre si crea il grafico pydot, che è piuttosto improbabile. Il vantaggio dell'utilizzo di id consiste nel fatto che non è necessario creare e trasferire una mappatura dai nodi originali agli oggetti UUID (in modo da creare coerentemente i bordi dopo aver aggiunto i nodi).

Un caso interessante sono grafici annidati: due grafici differenti possono contenere lo stesso oggetto hashable in networkx (diciamo a), quindi id non può più essere utilizzato direttamente sul nodo. In tal caso, è comunque possibile utilizzare id combinando la coppia (nodo, grafico) come: str(id(node)) + str(id(graph)).