flowchart using digraph in Diagrammer - r

I'm trying out DiagrammeR to build some flowcharts and I'm interested in knowing whether I can include values from a secondary data frame (data pulled from a SQL database).
I'm using an example from the documentation and I want to add a random value from the Iris dataset.
I wish to add iris[2,4] as a subtext to the ELEPHANT node in my code below, is this possible?
library(DiagrammeR)
grViz("
digraph {
# graph attributes
graph [overlap = true]
# node attributes
node [shape = box,
fontname = Helvetica,
color = blue]
# edge attributes
edge [color = gray]
# node statements
A; B; C; D; ELEPHANT
F [color = black]
# node attributes
node [shape = circle,
fixedsize = true,
width = 0.9]
# node statements
1; 2; 3; 4; 5; 6; 7; 8
# edge statements
A->1; B->2 // gray
B->3 [color = red] // red
B->4 // gray
C->A [color = green] // green
1->D; ELEPHANT->A; 2->4; 1->5; 1->F // gray
ELEPHANT->6; 4->6; 5->7; 6->7 // gray
3->8 [color = blue] // blue
}
")

I believe what I'm looking for is a parameterized diagram which is detailed here https://mikeyharper.uk/flowcharts-in-r-using-diagrammer/

You can achieve this by adding 2 simple steps, please see the example code and comments below:
library(DiagrammeR)
grViz("
digraph {
# graph attributes
graph [overlap = true]
# node attributes
node [shape = box,
fontname = Helvetica,
color = blue]
# edge attributes
edge [color = gray]
# node statements
A; B; C; D; ELEPHANT[label = '##1'] # Here calls label from 1 below
F [color = black]
# node attributes
node [shape = circle,
fixedsize = true,
width = 0.9]
# node statements
1; 2; 3; 4; 5; 6; 7; 8
# edge statements
A->1; B->2 // gray
B->3 [color = red] // red
B->4 // gray
C->A [color = green] // green
1->D; ELEPHANT->A; 2->4; 1->5; 1->F // gray
ELEPHANT->6; 4->6; 5->7; 6->7 // gray
3->8 [color = blue] // blue
}
[1]: paste0('iris[2, 4] is ', iris[2, 4]) # here is the label and value
")
You should be able to see the same result like this

Related

DiagrammeR general flow direction

I would like to produce a diagram with DiagramR.
This is my code
require(DiagrammeR)
grViz("
digraph boxes_and_circles {
node [shape = box,
fontname = Helvetica]
A; B; C; D
B->A C->A D->B
}
")
But I get this result
And I need like this one
How can I set flow direction to up?
You could to set the parameter rankdir and add the color to edge and node:
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
rankdir = BT
node [shape = box,
fontname = Helvetica,
color = gray
]
A; B; C; D
edge [color = gray]
B->A C->A D->B
}
")

graphviz dot ignores 'rank' constraints

I constructed a simple graph:
digraph{
rankdir = LR;
{
rank="source";
Sa;
Sb;
Sc;
St;
}
St -> {t_1[label="t",shape=plaintext];}
Na;
{t_a[label="t",shape=plaintext];}->Na
Sa->Na;
Sb->Na;
Sc->Na;
subgraph cluster_b {
fillcolor = "#ddDDdd";
style=filled;
label="";
Nb1;
Nb;
Nb1->Nb;
}
{t_2[label="t",shape=plaintext];}->Nb1
Sa->Nb;
Nc;
{t_c[label="t",shape=plaintext];}->Nc
Nd;
{t_d[label="t",shape=plaintext];}->Nd
Na->Nd;
Nb->Nc;
Nd->O1;
Nc->Nd;
{
rank="sink";
O1;
}
}
view online here
It seems that dot is ignoring the rank="source".
According to the documentation
If rank="min", all nodes are placed on the minimum rank. If
rank="source", all nodes are placed on the minimum rank, and the only
nodes on the minimum rank belong to some subgraph whose rank attribute
is "source" or "min".
The Sx nodes should be the only ones on the lowest ranks.
(as if there would be an additional St->t_2[style=invis]; edge).
Is this a bug? do i misunderstand the documentation?
I vote "bug" (probably in software, maybe in documentation).
Here is a work-around, using the minlen attribute to "force" the Nb node (and the cluster) "down" the ranks.
digraph{
rankdir = LR;
{
rank="source";
Sa;
Sb;
Sc;
St;
}
St -> {t_1[label="t",shape=plaintext];}
Na;
{t_a[label="t",shape=plaintext];}->Na
Sa->Na;
Sb->Na;
Sc->Na;
subgraph cluster_b {
fillcolor = "#ddDDdd";
style=filled;
label="";
Nb1;
Nb;
Nb1->Nb;
}
{t_2[label="t",shape=plaintext];}->Nb1
Sa->Nb [minlen=3] // will this move the cluster??
Nc;
{t_c[label="t",shape=plaintext];}->Nc
Nd;
{t_d[label="t",shape=plaintext];}->Nd
Na->Nd;
Nb->Nc;
Nd->O1;
Nc->Nd;
{
rank="sink";
O1;
}
}
Giving:

add text to DiagrammeR flowchart flowline in R

The following code creates a flowchart in R with the DiagrammeR package. How can I add "Yes" or "No" (or any other text) to the flowlines connecting the shapes? When creating charts, my nodes will often be decisions, and I'd like to label the flowline answer as Yes/No.
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
Try to define the label for each flowline, like this:
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 [label='YES']
B->2 [label='NO']
B->3 [label='...']
B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
Hope it helps! :)

Coloring clusters by markers inside

I would like to know, how to color the clusters depending on icon in it.
My Data :
remorque time.stamp lat long geolocalisation maintenance temperature appairage
1 21/11/2017 10:36 48.86272 2.2875920 OnMouv noir
2 21/11/2017 10:36 43.60776 1.4421606 StartMouv rouge
3 21/11/2017 10:36 46.58619 0.3388710 OnMouv rouge
4 21/11/2017 10:36 45.76695 3.0556216 Life orange
5 21/11/2017 10:36 45.14555 1.4751652 EndMouv rouge
6 21/11/2017 10:36 46.81157 1.6936336 Life orange
7 21/11/2017 10:36 47.36223 0.6751146 alerte rouge
8 21/11/2017 10:36 47.36032 1.7441244 StartMouv
9 21/11/2017 10:36 48.85333 1.8215332 StartMouv
10 21/11/2017 10:36 48.84429 1.7913208 alerte
11 21/11/2017 10:36 48.81356 1.6759643 EndMouv
Example :
If there is an icon in my cluster, with appairage = rouge, the color of the cluster should be red.
If there is no red icon, If there is an icon in my cluster, with temperature = orange, the color of the cluster should be orange.
... for each variable (temperature, appairage, maintenance). And if all icons in the culster have their variables ok, the cluster should be green.
My map looks like :
I found a way to change the range for coloring the clusters on the Internet. But I don't want to color per number of markers in the cluster.
It is possible to color the clustered icons based on the properties of the icons clustered together. The easiest method might be to use htmlwidgets and call a javascript function on map rendering.
However, before getting to the htmlwidget, you need to set a clusterId for your clustered layer:
addAwesomeMarkers(clusterId = "cluster" ...
Now we can find this layer when in the htmlwidget:
function(el, x) {
map = this; // the map object
var cluster = map.layerManager.getLayer('cluster','cluster'); // the cluster layer
Within the cluster layer, we want to create a function for the icon property iconCreateFunction:
cluster.options.iconCreateFunction = function(d) {
// generate icon
}
This function should:
go through all the child markers represented by the clustered marker,
identify the highest rank of those child markers
return an appropriate icon
1. Iterating through child markers
For number one, and building on the above, we can iterate through each child marker with:
cluster.options.iconCreateFunction = function(c) {
var markers = c.getAllChildMarkers();
markers.forEach(function(m) {
// do something for each marker
})
}
I'm using c to represent a cluster, m to represent each individual child marker
2. Getting the Highest Ranked Marker
The primary challenge in the list is identifying the highest rank of the child icons - as the data is not bound to the icons we are limited in options. Assuming that the color of the icons corresponds to the color code of the item in the dataframe, I will use the color of the icon to determine its priority/rank. After determining the highest ranking child, I will color the cluster based on that child's rank.
I will color the cluster as follows (as I believe this is your intended result):
red if any child icons are red,
orange if none are red but there are some orange children, and
green if there is no orange or red children.
To get the color, I need to access the proper property. The color (fill) of an (awesome) marker resides at:
marker.options.icon.options.markerColor
To compare colors, I'll use an object to represent each color's rank, this will allow for a simple comparison of color:
var priority = {
'green':0,
'orange':1,
'red':2
}
This allows:
cluster.options.iconCreateFunction = function(c) {
var markers = c.getAllChildMarkers();
var priority = {
'green': 0,
'orange': 1,
'red': 2
};
var highestRank = 0; // defaults to the lowest level to start
markers.forEach(function(m) {
var color = m.options.icon.options.markerColor;
// check each marker to see if it is the highest value
if(priority[color] > highestRank) {
highestRank = priority[color];
}
})
}
3. Returning an Icon
Now that we have a value representing a color, we can return an icon. Leaflet clustered icons have limited styling options. They use L.divIcon(), which limits options somewhat. When combined with css styles for clustered labels, they create the familiar circle with green, yellow, and orange colors.
These default styles have the following css classes:
.marker-cluster-small // green
.marker-cluster-medium // yellow
.marker-cluster-large // orange
If we are happy with just using these classes, we can style the clustered polygons with minimal effort:
var styles = [
'marker-cluster-small', // green
'marker-cluster-medium', // yellow
'marker-cluster-large' // orange
]
var style = styles[highestRank];
var count = markers.length;
return L.divIcon({ html: '<div><span>'+count+'</span></div>', className: 'marker-cluster ' + style, iconSize: new L.Point(40, 40) });
The whole widget therefore looks like:
function(el,x) {
map = this;
var cluster = map.layerManager.getLayer('cluster','cluster');
cluster.options.iconCreateFunction = function(c) {
var markers = c.getAllChildMarkers();
var priority = {
'green': 0,
'orange': 1,
'red': 2
};
var highestRank = 0; // defaults to the lowest level to start
markers.forEach(function(m) {
var color = m.options.icon.options.markerColor;
// check each marker to see if it is the highest value
if(priority[color] > highestRank) {
highestRank = priority[color];
}
})
var styles = [
'marker-cluster-small', // green
'marker-cluster-medium', // yellow
'marker-cluster-large' // orange
]
var style = styles[highestRank];
var count = markers.length;
return L.divIcon({ html: '<div><span>'+count+'</span></div>', className: 'marker-cluster ' + style, iconSize: new L.Point(40, 40) });
}
}
Refining the Icons
Changing Colors
You probably want to have the high priority icons show up red. This can be done, but you need to add a css style to your map.
One way to do this at the same time as changing the icon function above is to append a style to the page with javascript in your widget. You need to make two styles, one for the div holding the icon, and one for the icon, you can do both at once:
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.red, .red div { background-color: rgba(255,0,0,0.6); }'; // set both at the same time
document.getElementsByTagName('head')[0].appendChild(style);
(from https://stackoverflow.com/a/1720483/7106086)
Don't forget to update what classes you are using in the styles array:
var styles = [
'marker-cluster-small', // green
'marker-cluster-medium', // yellow
'red' // red
]
Showing More Information in the Icon
You aren't limited to a number in the icon, you could show 1-3-5 , representing one high priority, three medium etc. You just need to keep track of how many child icons of each priority are in each cluster:
var children = [0,0,0];
markers.forEach(function(m) {
var color = m.options.icon.options.markerColor;
children[priority[color]]++; // increment the appropriate value in the children array.
...
Then show it with:
return L.divIcon({ html: '<div><span>'+children.reverse()+'</span>...
Givings something like:
Test Example
This should be copy and pastable to show everything except the additional text in the icon (using the code in these documentation examples as a base):
library(leaflet)
# first 20 quakes
df.20 <- quakes[1:50,]
getColor <- function(quakes) {
sapply(quakes$mag, function(mag) {
if(mag <= 4) {
"green"
} else if(mag <= 5) {
"orange"
} else {
"red"
} })
}
icons <- awesomeIcons(
icon = 'ios-close',
iconColor = 'black',
library = 'ion',
markerColor = getColor(df.20)
)
leaflet(df.20) %>% addTiles() %>%
addAwesomeMarkers(~long, ~lat, icon=icons, label=~as.character(mag), clusterOptions = markerClusterOptions(), group = "clustered", clusterId = "cluster") %>%
htmlwidgets::onRender("function(el,x) {
map = this;
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.red, .red div { background-color: rgba(255,0,0,0.6); }'; // set both at the same time
document.getElementsByTagName('head')[0].appendChild(style);
var cluster = map.layerManager.getLayer('cluster','cluster');
cluster.options.iconCreateFunction = function(c) {
var markers = c.getAllChildMarkers();
var priority = {
'green': 0,
'orange': 1,
'red': 2
};
var highestRank = 0; // defaults to the lowest level to start
markers.forEach(function(m) {
var color = m.options.icon.options.markerColor;
// check each marker to see if it is the highest value
if(priority[color] > highestRank) {
highestRank = priority[color];
}
})
var styles = [
'marker-cluster-small', // green
'marker-cluster-large', // orange
'red' // red
]
var style = styles[highestRank];
var count = markers.length;
return L.divIcon({ html: '<div><span>'+count+'</span></div>', className: 'marker-cluster ' + style, iconSize: new L.Point(40, 40) });
}
}")
You can now also use the iconCreateFunction in markerClusterOptions. Make sure you pass the variable that you want to use for your coloring in the markerOptions .
see also: leaflet R, how to make appearance of clustered icon related to statistics of the children?
An example is:
legend_pal <- hcl.colors(10, palette='Spectral', rev = T)
leaflet(quakes) %>% addTiles() %>% addMarkers(
options = markerOptions(mag = ~mag),
clusterOptions = markerClusterOptions(
iconCreateFunction=~JS(paste0("function (cluster) {
var markers = cluster.getAllChildMarkers();
var sum = 0;
for (i = 0; i < markers.length; i++) {
sum += Number(markers[i].options.mag);
}
var palette = ['", paste0(legend_pal, collapse="','"),"'];
var domain = [", paste0(sort(unique(na.omit(mag))), collapse=','),"];
var count = markers.length;
var avg = sum/count;
c = palette[Math.round(palette.length*(avg-Math.min(...domain))/(Math.max(...domain) - Math.min(...domain)))];
return L.divIcon({
html: '<div style=\"background-color:'+c+'\"><span>'+avg+'</span></div>',
className: 'marker-cluster',
iconSize: new L.Point(40, 40) });
}")))) %>%
addLegend(pal=colorNumeric(
palette = legend_pal,
domain = quakes$mag,
na.color = 'transparent'), values = ~mag)

How to use GraphViz graphs in DiagrammeR for R

I am trying to use a GraphViz graph in DiagrammeR. How can I do this?
myGraph = grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
and then I want to use it in DiagrammeR, but it will not allow it.
render_graph(myGraph)
Gives:
Error: class(graph) == "dgr_graph" are not all TRUE
Is there a way I need to convert or input the GraphViz graph into the DiagrammeR environment?
grViz takes a string describing the graph (vis.js style) : it is the interpreted by vis.js. Its return value is then an htmlwidget object.
render_graph takes a dgr_graph object, created using the create_graph function.
you can see in the DiagrammeR doc
library(DiagrammeR)
# Create a simple NDF
nodes <-
create_nodes(
nodes = 1:4,
type = "number")
# Create a simple EDF
edges <-
create_edges(
from = c(1, 1, 3, 1),
to = c(2, 3, 4, 4),
rel = "related")
# Create the graph object,
# incorporating the NDF and
# the EDF, and, providing
# some global attributes
graph <-
create_graph(
nodes_df = nodes,
edges_df = edges,
graph_attrs = "layout = neato",
node_attrs = "fontname = Helvetica",
edge_attrs = "color = gray20")
# View the graph
render_graph(graph)
DiagrammeR can produce Graphviz code : From the doc mentioned below : "If you'd like to return the Graphviz DOT code (to, perhaps, share it or use it directly with the Graphviz command-line utility), just use output = "DOT" in the render_graph()"
So
you can use create_graph to produce graphviz dot code
you can use graphviz dot code directly with grViz in DiagrammeR
Here the problem is with render_graph(myGraph), using just myGraph works like charm.
library(DiagrammeR)
myGraph = grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [overlap = true, fontsize = 10]
# several 'node' statements
node [shape = box,
fontname = Helvetica]
A; B; C; D; E; F
node [shape = circle,
fixedsize = true,
width = 0.9] // sets as circles
1; 2; 3; 4; 5; 6; 7; 8
# several 'edge' statements
A->1 B->2 B->3 B->4 C->A
1->D E->A 2->4 1->5 1->F
E->6 4->6 5->7 6->7 3->8
}
")
myGraph
render_graph(myGraph) Does not work in R.
Just myGraph works fine.

Resources