Is it possible to generate a small GraphViz chart? - graphviz

I need to generate a small graphviz graph that represents a "price map" of the main graphviz. The main graph is:
digraph g {
graph [rankdir = "LR"];
a [label = "<f0>a | <f1> $139 | <f2> Chair | Brown" shape = "record"];
b [label = "<f0>b | <f1> $280 | <f2> Speakers | Grey" shape = "record"];
c [label = "<f0>c | <f1> $89 | <f2> Jacket | Beige" shape = "record"];
d [label = "<f0>d | <f1> $19 | <f2> Mug | Green" shape = "record"];
e [label = "<f0>e | <f1> $180 | <f2> Printer | Grey" shape = "record"];
a -> b; a -> c; c -> d; c -> e;
}
and it generates:
When I try to generate a "small" price map like:
digraph g {
graph [rankdir = "LR"];
node [shape=box];
a[color=black, fillcolor=yellow, style=filled];
b[color=black, fillcolor=red, style=filled];
c;
d;
e[color=black, fillcolor=yellow, style=filled];
a -> b; a -> c; c -> d; c -> e;
}
I get:
But I wanted a really small price map, like:
Is this possible?

You should really read the dot guide which is a pretty impressive view across using dot (although HTMl nodes don't feature so much): http://graphviz.org/pdf/dotguide.pdf
Try changing the shape to plain:
node [shape=plain];
Result:
Another possibility is to use HTML nodes - my answer in this example shows compact nodes https://stackoverflow.com/a/68320427/2318649
Another way to control spacing is useing nodesep and ranksep on the graph, for example:
digraph g {
graph [rankdir="LR" nodesep=0.1 ranksep=0.2];
node [shape=plain];
a[color=black, fillcolor=yellow, style=filled];
b[color=black, fillcolor=red, style=filled];
c;
d;
e[color=black, fillcolor=yellow, style=filled];
a -> b; a -> c; c -> d; c -> e;
}
Result:
Another way to minimize the space inside a box node is setting margin, width and height all to 0:
digraph g {
graph [rankdir="LR" nodesep=0.1 ranksep=0.2];
node [shape=box margin=0 width=0 height=0];
a[color=black, fillcolor=yellow, style=filled];
b[color=black, fillcolor=red, style=filled];
c;
d;
e[color=black, fillcolor=yellow, style=filled];
a -> b; a -> c; c -> d; c -> e;
}
Result:
You can control the arrow size on each edge, e.g.:
a -> b [arrowsize=0.5]; a -> c [arrowsize=0.5]; c -> d [arrowsize=0.5]; c -> e;
result:
Or you can control arrowsize across all edges:
digraph g {
graph [rankdir="LR" nodesep=0.1 ranksep=0.2];
node [shape=box margin=0 width=0 height=0];
edge [arrowsize=0.5]
a[color=black, fillcolor=yellow, style=filled];
b[color=black, fillcolor=red, style=filled];
c;
d;
e[color=black, fillcolor=yellow, style=filled];
a -> b; a -> c; c -> d; c -> e;
}
Result:
Read about attributes: http://graphviz.org/doc/info/attrs.html
Read about nodes: http://graphviz.org/doc/info/shapes.html
Read about using dot: http://graphviz.org/pdf/dotguide.pdf

Related

Graphviz ordering of nodes on the same rank

I made a graph and it looks like I want it to with one exception: the cluster containing nodes D and E should be placed between nodes B and C.
Here is the .dot:
digraph {
node[shape=rectangle]
graph [
newrank=true
];
Z
A
B
C
{
rank=same;
A -> B -> D;
D -> C[style=invis];
}
subgraph clusterSS {
style=dashed;
D[group=ss]
E[group=ss]
D -> E[style="dashed"; dir = none];
}
Z -> A
Z -> B
Z -> D
Z -> C
A -> P[dir = back]
B -> S
S -> B[constraint = false];
C -> S
S -> E[constraint = false]
E -> S[label = "Label"]
}
I expected that having edges between nodes
{
rank=same;
A -> B -> D;
D -> C[style=invis];
}
would ensure correct ordering, but it doesn't.
The ordering becomes correct once I delete the edge label between S and E, but I do need to keep the labels.
[non-trivial, not guaranteed to work as desired in any other circumstance]
Added clusters, invisible nodes,and more. ugh
digraph {
node[shape=rectangle]
graph [
newrank=true
];
{
rank=same;
A -> B -> D;
D -> C[style=invis];
}
subgraph clusterSS {
style=dashed;
D [group=ss]
E [group=ss]
D -> E[style="dashed"; dir = none];
}
subgraph clusterAP {
peripheries=0
A -> P[dir = back]
}
subgraph clusterB {
peripheries=0
B
bogus [style=invis shape=plain]
B->bogus [style=invis]
}
Z -> A
Z -> B
Z -> D
Z -> C
S [group=ss]
B -> S:nw
B -> S:w [dir=back]
C -> S
S -> E[constraint = false]
E -> S[label = "Label"]
}
Giving:

How can I create named edge "types" in Graphviz/dot/neato?

I need to draw a diagram with graphviz/dot where there are common edge types between nodes and am trying to find a way to define a label for each type of edge and then use that label multiple times in the diagram.
For example imagine the traditional ceiling fan FSM example where it's initially in state OFF and every time someone pulls the cord it changes to a new state based on the speed of the fan:
Pull Pull Pull
OFF ------> HIGH ------> MED ------> LOW
^ |
| Pull |
+------------------------------------+
Every edge is named "Pull" and I can define that in dot by using:
digraph fan {
OFF -> HIGH [label="Pull"];
HIGH -> MED [label="Pull"];
MED -> LOW [label="Pull"];
LOW -> OFF [label="Pull"];
}
BUT I don't want to keep specifying the same textual label every time because
My labels can get quite long so that's error-prone, and
My edges have other attributes like color in addition to label, and
I have a selection of multiple different types of edge so I want to make SURE that edge type "A" used in different contexts in the diagram always has all the same attributes.
I expected dot to have a syntax that would let me define names for my edge types, something like:
digraph fan {
edge_a [label="Pull"];
OFF -> HIGH edge_a;
HIGH -> MED edge_a;
MED -> LOW edge_a;
LOW -> OFF edge_a;
}
but of course what the really does is create a node named "Pull" and unlabeled edges.
I've been searching online for a few hours with no success. Anyone know how to define edge types up front to be used in multiple locations?
Update: #vaettchen had suggested defining an edge type then listing all of the transitions for that edge type, then defining the next edge type followed by it's transitions. While that would technically solve my problem, it would introduce a couple of others because my graphs today can look like:
digraph {
subgraph cluster_1 {
a -> b [label="type x", color=red, style=solid];
b -> a [label="type y", color=green, style=dashed];
b -> c [label="type x", color=red, style=solid];
c -> b [label="type y", color=green, style=dashed];
c -> d [label="type z", color=blue, style=dotted];
}
subgraph cluster_2 {
d -> e [label="type x", color=red, style=solid];
e -> d [label="type y", color=green, style=dashed];
e -> f [label="type x", color=red, style=solid];
f -> e [label="type y", color=green, style=dashed];
f -> c [label="type z", color=blue, style=dotted];
}
}
and to rearrange that by edge type I'd lose the immediate visual clarity in the code of having the bidirectional edges next to each other (a->b and b->a) and I'd have to explicitly list the nodes within each subgraph and I'd have to pull the subgraph-internal edge definitions up into the main graph:
digraph {
edge [label="type x", color=red, style=solid];
a -> b;
b -> c;
d -> e;
e -> f;
edge [label="type y", color=green, style=dashed];
b -> a;
c -> b;
e -> d;
f -> e;
edge [label="type z", color=blue, style=dotted];
c -> d;
f -> c;
subgraph cluster_1 {
a; b; c;
}
subgraph cluster_2 {
d; e; f;
}
}
So while it would solve the problem I asked about and I appreciate the suggestion, I'm not sure it's worth the tradeoff as you end up with the equivalent of a C program where you had to define all of your variables outside of the functions and organize them by their type rather than logical associations.
To be clear, given the above example what I was really hoping for would look like the following if such an "edge_type" definition keyword existed:
digraph {
edge_type edge_x [label="type x", color=red, style=solid];
edge_type edge_y [label="type y", color=green, style=dashed];
edge_type edge_z [label="type z", color=blue, style=dotted];
subgraph cluster_1 {
a -> b edge_x;
b -> a edge_y;
b -> c edge_x;
c -> b edge_y;
c -> d edge_z;
}
subgraph cluster_2 {
d -> e edge_x;
e -> d edge_y;
e -> f edge_x;
f -> e edge_y;
f -> c edge_z;
}
}
I think I got your solution, using m4 (thanks to Simon). Using and adapting your sample, I created a file called gv.m4:
digraph {
define(`edge_x',`[label="type x", color=red, style=solid]')
define(`edge_y',`[label="type y", color=green, style=dashed]')
define(`edge_z',`[label="type z", color=blue, style=dotted]')
subgraph cluster_1 {
a -> b edge_x;
b -> a edge_y;
b -> c edge_x;
c -> b edge_y;
c -> d edge_z;
}
subgraph cluster_2 {
d -> e edge_x;
e -> d edge_y;
e -> f edge_x;
f -> e edge_y;
f -> c edge_z;
}
}
and converted it with the simple command
m4 gv.m4 > gv.dot
which now contains your defined edges
digraph {
subgraph cluster_1 {
a -> b [label="type x", color=red, style=solid];
b -> a [label="type y", color=green, style=dashed];
b -> c [label="type x", color=red, style=solid];
c -> b [label="type y", color=green, style=dashed];
c -> d [label="type z", color=blue, style=dotted];
}
subgraph cluster_2 {
d -> e [label="type x", color=red, style=solid];
e -> d [label="type y", color=green, style=dashed];
e -> f [label="type x", color=red, style=solid];
f -> e [label="type y", color=green, style=dashed];
f -> c [label="type z", color=blue, style=dotted];
}
}
and yields the expected graph:
You can do much more with m4 - stuff that is missing in graphViz, like maintaining and (even conditionally) including subfiles. For example, if you put your two subgraphs into two separate files gv1.txt and gv2.txt, this would work nicely:
digraph incl
{
define(`edge_x',`[label="type x", color=red, style=solid]')
define(`edge_y',`[label="type y", color=green, style=dashed]')
define(`edge_z',`[label="type z", color=blue, style=dotted]')
include(gv1.txt)
include(gv2.txt)
e -> d[ color = yellow, label = "this is new!"];
}
Not really an answer but "food for thought" as I don't think named labels exist in graphviz: You could define a default label for the following edges. That works well if your workflow allows to define edges all in one place. Example:
digraph rs
{
node[ shape = box, style = rounded]
edge[ label = "pull" ];
{ A B } -> C;
G -> H;
C -> D[ label = "stop" ];
edge[ label = "push"];
D -> { E F };
edge[ color = red, fontcolor = red ];
{ E F } -> G;
}
which yields
I have also tried to implement your diagramm with
digraph fan
{
splines = ortho;
node [ shape=box ]
edge [ xlabel = "Pull", minlen = 4 ];
{ rank = same; OFF -> HIGH -> LOW; }
LOW:s -> OFF:s;
}
which produces
so it looks good but with all the tweaking is difficult to expand.
I struggled to download m4 on my machine and so opted for using graphviz through the python API where you can define a style as a dictionary and apply to nodes / edges as desired.
import graphviz
dot = graphviz.Digraph(comment='Test File')
nodeAttr_statement = dot.node_attr = {"shape": 'box', "style": 'filled', "fillcolor":"red"}
nodeAttr_question = dot.node_attr = {"shape": 'diamond', "style": 'filled', "fillcolor":"blue"}
dot.edge_attr
edge_Attr_sample = dot.edge_attr = {"arrowhead":'vee',"color":"yellow"}
edge_Attr_sample2 = dot.edge_attr = {"arrowhead": 'diamond', "color": "green"}
dot.node("A", "A", nodeAttr_statement)
dot.node("B", "B", nodeAttr_question )
dot.edge("A", "B", _attributes=edge_Attr_sample)
dot.edge("B", "A", _attributes=edge_Attr_sample2)
dot.format = 'pdf'
dot.render('test', view=True)
Output
// Test File
digraph {
node [fillcolor=blue shape=diamond style=filled]
edge [arrowhead=diamond color=green]
A [label=A fillcolor=red shape=box style=filled]
B [label=B fillcolor=blue shape=diamond style=filled]
A -> B [arrowhead=vee color=yellow]
B -> A [arrowhead=diamond color=green]
}
Output image from python script

subgraph changed alignment when put into a cluster

The graph I would like contains a top row; the rightmost node (T3) then points to A. A through E are in a vertical column, C and F are vertically aligned and H, I and J are vertically aligned. Additionally, C, F and H are horizontally aligned and E, G and J are horizontally aligned.
When I add subgraph cluster_0 in front of the already existing subgraphs, i.e. subgraph cluster_0 { rank=same; A -> B -> C-> D-> E; }, the subgraph becomes horizontally aligned...
How can I introduce the cluster without this occurring? Also, the edge from T3 to A is nearly straight. I would be nice if it went straight down, right angled to the left then right angled down to A.
Here is what works:
digraph G { rankdir = LR ranksep = 1.2 nodesep = 0.5
T1 -> T2 -> T3;
{ rank=same; A -> B -> C -> D -> E; }
C -> F
{ rank=same F -> G[style=invis] }
E->G
{ rankdir=LR rank=same H -> I -> J}
F -> H [style=dotted]
G -> J [style=invis]
edge [constraint=false]
T3->A
}
And here is what doesn't work
digraph G { rankdir = LR ranksep = 1.2 nodesep = 0.5
T1 -> T2 -> T3;
subgraph cluster_0 { rank=same; A -> B -> C -> D -> E; }
C -> F
subgraph cluster_1 { rank=same F -> G[style=invis] }
E->G
subgraph cluster_2 { rankdir=LR rank=same H -> I -> J}
F -> H [style=dotted]
G -> J [style=invis]
edge [constraint=false]
T3->A
}
This is about as close as I can get, but the clusters definitely introduce some differences. Also the "almost straight" lines I corrected with splines=ortho. I moved the ABCDE subgraph over with an invisible edge to T1.
digraph G { rankdir=TB ranksep = 0.5 nodesep = 0.5 splines=ortho
{rank=same T1 -> T2 -> T3;}
T1->A [style=invis]
subgraph cluster_0 {rank=min A -> B -> C -> D -> E; }
C -> F
subgraph cluster_1 { rank=same F -> G[style=invis] }
E->G
subgraph cluster_2 { rankdir=LR rank=same H -> I -> J}
F -> H [style=dotted]
G -> J [style=invis]
edge [constraint=false]
T3->A
}

trying to create ranked subgraphs in graphviz

I've tried to make something like this using Graphviz:
x y z
| | |
# | |
a#__\| |
# /#b |
# #__\|
# # /#c
# d#/__#
# #\ x
# # |
e#/__# |
#\ # |
But ranking doesn't seem to be working as I expect. I want e to be below all of the other nodes.
digraph x
{
rankdir = tb;
size = "7.5, 7.5";
rank = source;
a -> b -> c -> d -> e;
subgraph "cluster x"
{
style=filled;
color=lightgrey;
label="x";
a -> e [style=invis];
}
subgraph "cluster y"
{
label="y";
b -> d [style=invis];
}
subgraph "cluster z"
{
label="z";
c;
}
}
I've tried to use clusterrank = global which sort of works, but then the subgraphs are not separated into a more obvious column and there's overlap over the columns. It also is not going to the right like I want. The following image highlights one of the overlaps in red, but as you can see there are 4.
digraph x
{
rankdir = tb;
rankstep=equally;
clusterrank = global;
size = "7.5, 7.5";
a -> b -> c -> d -> e;
subgraph "cluster x"
{
style=filled;
color=lightgrey;
label="x";
a -> e [style=invis];
}
subgraph "cluster y"
{
label="y";
b -> d [style=invis];
}
subgraph "cluster z"
{
label="z";
c;
}
}
I've tried to make a separate cluster that is going to have a guaranteed top to bottom ranking and then rank the appropriate clusters together, but it does the same as the previous attempt, removing the boxes seen the the first attempt and causing unwanted overlapping.
digraph x
{
rankdir = tb;
1 -> 2 -> 3 -> 4 -> 5;
a -> b -> c -> d -> e;
{ rank=same; 1; a; }
{ rank=same; 2; b; }
{ rank=same; 3; c; }
{ rank=same; 4; d; }
{ rank=same; 5; e; }
subgraph "cluster x"
{
style=filled;
color=lightgrey;
label="x";
a -> e [style=invis];
}
subgraph "cluster y"
{
label="y";
b -> d [style=invis];
}
subgraph "cluster z"
{
label="z";
c;
}
}
Anyone have any ideas as to try and get the layout I want?
As a side note, I tried to login to the Graphviz forum regarding this matter, but found that logging in from this page doesn't seem to work. I keep getting a long timeout problem. I check my email account and nothing is there. I try creating a new account with the same email and it says that the account is already in use. I then try and get them to reset my password and I get another timeout problem.
Does anyone know who I can contact to try and fix that annoying login problem? Maybe someone who is already logged in can post that for me?
Run dot with -Gnewrank. That will get you what you want based on your sketch. If more tweaks are needed, please specify what you are after.
Your last solution will work as soon as you do a minor tuning
Use newrank=true to avoid of "unboxing" clusters
Play with splines=... to adjust arrows
Define label as separate nodes.
digraph x
{
rankdir = tb;
newrank=true;
splines=ortho;
0 -> 1 -> 2 -> 3 -> 4 -> 5;
X; Y; Z;
a -> b -> c -> d -> e;
{ rank=same; 0 X Y Z}
{ rank=same; 1; a; }
{ rank=same; 2; b; }
{ rank=same; 3; c; }
{ rank=same; 4; d; }
{ rank=same; 5; e; }
subgraph "cluster x"
{
style=filled;
color=lightgrey;
a -> e [style=invis];
}
subgraph "cluster y"
{
b -> d [style=invis];
}
subgraph "cluster z"
{
c;
}
}

Straight edge between clusters in Graphviz

I'm trying to have an edge between clusters in Graphviz where the edge does not affect the ranking.
This looks fine:
digraph {
subgraph clusterX {
A
B
}
subgraph clusterY {
C
D
}
A -> B
B -> C [constraint=false]
C -> D
}
However when I add a label to the C -> D edge the B -> C edge tries to circumvent said label (which looks ugly).
digraph {
subgraph clusterX {
A
B
}
subgraph clusterY {
C
D
}
A -> B
B -> C [constraint=false]
C -> D [label=yadda]
}
Any idea how I can keep the edge from B to C straight?
The easiest way to achieve this is to add splines=false to the dot file - this forces the rendering of the edges to be straight lines:
digraph {
splines=false;
subgraph clusterX {
A;
B;
}
subgraph clusterY {
C;
D;
}
A -> B;
B -> C [constraint=false];
C -> D [label=yadda];
}
Output:
You can use this version :
digraph G {
subgraph cluster_X {
A [ pos = "0,1!" ];
B [ pos = "0,0!" ];
}
subgraph cluster_Y {
C [ pos = "1,1!" ];
D [ pos = "1,0!" ];
}
A -> B
B -> C[label="yadda"]
C -> D;
}
Then you use neato (not dot)
neato -Tpng -oyadda.png yadda.dot
And the result is :
Instead of the label attribute, you can use the attributes xlabel or headlbel or taillabel.
Result with xlabel:
Script:
digraph {
subgraph clusterX { A B }
subgraph clusterY { C D }
A -> B
B -> C [constraint=false]
C -> D [xlabel=yadda]
}
Result with headlabel:
Script:
digraph {
subgraph clusterX { A B }
subgraph clusterY { C D }
A -> B
B -> C [constraint=false]
C -> D [headlabel=yadda]
}
Result with taillabel:
Script:
digraph {
subgraph clusterX { A B }
subgraph clusterY { C D }
A -> B
B -> C [constraint=false]
C -> D [taillabel=yadda]
}

Resources