diff --git a/docs/articles/get_started.html b/docs/articles/get_started.html index ed4fce3..c564ba6 100644 --- a/docs/articles/get_started.html +++ b/docs/articles/get_started.html @@ -37,7 +37,7 @@ graphTweets - 0.5.1 + 0.5.2 @@ -104,7 +104,7 @@

Get Started

John Coene

-

2019-02-21

+

2019-02-28

@@ -114,10 +114,10 @@

2019-02-21

graphTweets 4.0 has been redisigned to work hand-in-hand with rtweet. Let’s start by getting some tweets. If you’re unsure how to get started, head over to the rtweet website, everything is very well explained. We’ll get 1,000 tweets on #rstats, exluding re-tweets.

-
library(rtweet)
-
-# 1'000 tweets on #rstats, excluding retweets
-tweets <- search_tweets("#rstats", n = 500, include_rts = FALSE)
+
library(rtweet)
+
+# 1'000 tweets on #rstats, excluding retweets
+tweets <- search_tweets("#rstats", n = 500, include_rts = FALSE)

Now we can start using graphTweets.

  1. Get the edges using gt_edges.
  2. @@ -126,105 +126,105 @@

    2019-02-21

    igraph

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_graph() -> graph
    -
    -class(graph)
    -#> [1] "igraph"
    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_graph() -> graph
    +
    +class(graph)
    +#> [1] "igraph"

    List

    If you do not want to return an igraph object, use gt_collect, it will return a list of two data.frames; edges and nodes.

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_collect() -> edges
    -
    -names(edges)
    -#> [1] "edges" "nodes"
    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_collect() -> edges
    +
    +names(edges)
    +#> [1] "edges" "nodes"

    (It also returns nodes but it’s empty since we only ran gt_edges).

    So far we only used gt_edges to extract the edges, we can also extract the nodes.

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_nodes() %>% 
    -  gt_collect() -> graph
    -
    -lapply(graph, nrow) # number of edges and nodes
    -#> $edges
    -#> [1] 299
    -#> 
    -#> $nodes
    -#> [1] 354
    -lapply(graph, names) # names of data.frames returned
    -#> $edges
    -#> [1] "source" "target" "n"     
    -#> 
    -#> $nodes
    -#> [1] "nodes" "type"  "n"
    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_nodes() %>% 
    +  gt_collect() -> graph
    +
    +lapply(graph, nrow) # number of edges and nodes
    +#> $edges
    +#> [1] 271
    +#> 
    +#> $nodes
    +#> [1] 350
    +lapply(graph, names) # names of data.frames returned
    +#> $edges
    +#> [1] "source" "target" "n"     
    +#> 
    +#> $nodes
    +#> [1] "nodes" "type"  "n"

    On graphTweets version 0.4.1 gt_nodes returns the number of edges the node is present in: n_edges. Here I used gt_collect, you can, again, use gt_graph if you want to return an igraph object.

    Adding nodes has not bring much to table however, gt_nodes takes another argument, meta, which if set to TRUE will return meta data on each node, where availbale*. More information on passing meta data to nodes further down the document.

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_nodes(meta = TRUE) %>% 
    -  gt_collect() -> graph
    -
    -# lapply(graph, names) # names of data.frames returned
    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_nodes(meta = TRUE) %>% 
    +  gt_collect() -> graph
    +
    +# lapply(graph, names) # names of data.frames returned

    Note that you can also pass meta-data to edges if needed.

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name, created_at) %>% 
    -  gt_nodes(meta = TRUE) %>% 
    -  gt_collect() -> graph
    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name, created_at) %>% 
    +  gt_nodes(meta = TRUE) %>% 
    +  gt_collect() -> graph

    Before we plot out graph, we’re going to modify some of the meta-data, a lot of NA are returned (where the meta-data was not available *).

    Here I use sigmajs to plot the graph.

    -
    library(dplyr)
    -library(sigmajs) # for plots
    -#> Welcome to sigmajs
    -#> 
    -#> Docs: sigmajs.john-coene.com
    -
    -tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_nodes() %>% 
    -  gt_collect() -> gt
    -
    -nodes <- gt$nodes %>% 
    -  mutate(
    -    id = nodes,
    -    label = nodes,
    -    size = n,
    -    color = "#1967be"
    -  ) 
    -
    -edges <- gt$edges %>% 
    -  mutate(
    -    id = 1:n()
    -  )
    -
    -sigmajs() %>% 
    -  sg_force_start() %>% 
    -  sg_nodes(nodes, id, label, size, color) %>% 
    -  sg_edges(edges, id, source, target) %>% 
    -  sg_force_stop(10000)
    -
    -

    Let’s look at communities, we’ll return an igraph object with gt_graph so we can easily run a community finding algorithm from the igraph package.

    -
    tweets %>% 
    -  gt_edges(screen_name, mentions_screen_name) %>% 
    -  gt_graph() -> g
    -
    -class(g)
    -#> [1] "igraph"
    +
    library(dplyr)
    +library(sigmajs) # for plots
    +#> Welcome to sigmajs
    +#> 
    +#> Docs: sigmajs.john-coene.com
    +
    +tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_nodes() %>% 
    +  gt_collect() -> gt
    +
    +nodes <- gt$nodes %>% 
    +  mutate(
    +    id = nodes,
    +    label = nodes,
    +    size = n,
    +    color = "#1967be"
    +  ) 
    +
    +edges <- gt$edges %>% 
    +  mutate(
    +    id = 1:n()
    +  )
    +
    +sigmajs() %>% 
    +  sg_force_start() %>% 
    +  sg_nodes(nodes, id, label, size, color) %>% 
    +  sg_edges(edges, id, source, target) %>% 
    +  sg_force_stop(10000)
    +
    +

    Let’s look at communities, we’ll return an igraph object with gt_graph so we can easily run a community finding algorithm from the igraph package.

    +
    tweets %>% 
    +  gt_edges(screen_name, mentions_screen_name) %>% 
    +  gt_graph() -> g
    +
    +class(g)
    +#> [1] "igraph"

    Users to Hashtags

    -
    library(rtweet)
    -tweets <- search_tweets("#rstats OR #python", n = 1000, include_rts = FALSE, token = token, lang = "en")
    +
    library(rtweet)
    +tweets <- search_tweets("#rstats OR #python", n = 1000, include_rts = FALSE, token = token, lang = "en")

    The same principles follow, we simply use gt_edges_hash and pass the hashtags column as returned by rtweet. This creates a tibble of edges from screen_name to hashtags used in each tweet.

    -
    net <- tweets %>% 
    -  gt_edges(screen_name, hashtags) %>% 
    -  gt_nodes() %>% 
    -  gt_collect()
    +
    net <- tweets %>% 
    +  gt_edges(screen_name, hashtags) %>% 
    +  gt_nodes() %>% 
    +  gt_collect()

    We’ll visualise the graph with sigmajs. Let’s prepare the data to meet the library’s requirements.

    Apologies for not getting into details here but sigmajs is very well documented, check the website if you want to understand it all.

    - +
    edges <- net$edges
    +nodes <- net$nodes
    +
    +edges$id <- seq(1, nrow(edges))
    +nodes$id <- nodes$nodes
    +nodes$label <- nodes$nodes
    +nodes$size <- nodes$n
    +nodes$color <- ifelse(nodes$type == "user", "#0084b4", "#1dcaff")

    Let’s visualise it.

    -
    -

    We use sg_layout to layout the graph and sg_neightbours to highlight nodes on click.

    +
    +

    We use sg_layout to layout the graph and sg_neightbours to highlight nodes on click.

    Retweets

    You can also build networks of retweets.

    -
    tweets <- search_tweets("#rstats filter:retweets", n = 500, include_rts = TRUE, token = token, lang = "en")
    -#> Searching for tweets...
    -#> Finished collecting tweets!
    -
    net <- tweets %>% 
    -  gt_edges(screen_name, retweet_screen_name) %>% 
    -  gt_nodes() %>% 
    -  gt_collect()
    -
    -c(edges, nodes) %<-% net
    -
    -edges$id <- 1:nrow(edges)
    -edges$size <- edges$n
    -
    -nodes$id <- nodes$nodes
    -nodes$label <- nodes$nodes
    -nodes$size <- nodes$n
    -
    -sigmajs() %>% 
    -  sg_nodes(nodes, id, size, label) %>% 
    -  sg_edges(edges, id, source, target) %>% 
    -  sg_layout() %>% 
    -  sg_cluster(colors = c("#0C46A0FF", "#41A5F4FF")) %>% 
    -  sg_settings(
    -    edgeColor = "default",
    -    defaultEdgeColor = "#d3d3d3"
    -  ) %>% 
    -  sg_neighbours()
    -
    - +
    tweets <- search_tweets("#rstats filter:retweets", n = 500, include_rts = TRUE, token = token, lang = "en")
    +#> Searching for tweets...
    +#> Finished collecting tweets!
    +
    net <- tweets %>% 
    +  gt_edges(screen_name, retweet_screen_name) %>% 
    +  gt_nodes() %>% 
    +  gt_collect()
    +
    +c(edges, nodes) %<-% net
    +
    +edges$id <- 1:nrow(edges)
    +edges$size <- edges$n
    +
    +nodes$id <- nodes$nodes
    +nodes$label <- nodes$nodes
    +nodes$size <- nodes$n
    +
    +sigmajs() %>% 
    +  sg_nodes(nodes, id, size, label) %>% 
    +  sg_edges(edges, id, source, target) %>% 
    +  sg_layout() %>% 
    +  sg_cluster(colors = c("#0C46A0FF", "#41A5F4FF")) %>% 
    +  sg_settings(
    +    edgeColor = "default",
    +    defaultEdgeColor = "#d3d3d3"
    +  ) %>% 
    +  sg_neighbours()
    +
    +

    Retweets & Quotes

    We can bind quoted tweets (surely they should be considered as retweets) using gt_bind_edges.

    -
    net <- tweets %>% 
    -  gt_edges(screen_name, retweet_screen_name) %>% 
    -    gt_edges_bind(screen_name, quoted_screen_name) %>% 
    -  gt_nodes() %>% 
    -  gt_collect()
    -
    -c(edges, nodes) %<-% net
    -
    -edges$id <- 1:nrow(edges)
    -edges$size <- edges$n
    -
    -nodes$id <- nodes$nodes
    -nodes$label <- nodes$nodes
    -nodes$size <- nodes$n
    -
    -sigmajs() %>% 
    -  sg_nodes(nodes, id, size, label) %>% 
    -  sg_edges(edges, id, source, target) %>% 
    -  sg_layout() %>% 
    -  sg_cluster(colors = c("#0C46A0FF", "#41A5F4FF")) %>% 
    -  sg_settings(
    -    edgeColor = "default",
    -    defaultEdgeColor = "#d3d3d3"
    -  ) %>% 
    -  sg_neighbours()
    -
    -

    ## Meta data

    +
    net <- tweets %>% 
    +  gt_edges(screen_name, retweet_screen_name) %>% 
    +    gt_edges_bind(screen_name, quoted_screen_name) %>% 
    +  gt_nodes() %>% 
    +  gt_collect()
    +
    +c(edges, nodes) %<-% net
    +
    +edges$id <- 1:nrow(edges)
    +edges$size <- edges$n
    +
    +nodes$id <- nodes$nodes
    +nodes$label <- nodes$nodes
    +nodes$size <- nodes$n
    +
    +sigmajs() %>% 
    +  sg_nodes(nodes, id, size, label) %>% 
    +  sg_edges(edges, id, source, target) %>% 
    +  sg_layout() %>% 
    +  sg_cluster(colors = c("#0C46A0FF", "#41A5F4FF")) %>% 
    +  sg_settings(
    +    edgeColor = "default",
    +    defaultEdgeColor = "#d3d3d3"
    +  ) %>% 
    +  sg_neighbours()
    +
    + +
    +
    +

    + Meta data

    You can pass meta data to the edges and subsequently to the nodes using gt_add_meta.

    -
    gt <- tweets %>% 
    -    gt_edges(screen_name, retweet_screen_name, followers_count, retweet_followers_count) %>% 
    -    gt_nodes() %>% 
    -    gt_add_meta(name = size, source = followers_count, target = retweet_followers_count)
    -
    -# size is now number of followers
    -head(gt$nodes)
    -#> # A tibble: 6 x 4
    -#>   nodes           type      n  size
    -#>   <chr>           <chr> <int> <int>
    -#> 1 _reactdev       user      3  2482
    -#> 2 _serverlessbot_ user      1   195
    -#> 3 0cool1          user      1   308
    -#> 4 2bftawfik       user      2    70
    -#> 5 aad34210        user      1  1013
    -#> 6 aambrus1        user      1   241
    -
    -gt$edges$id <- 1:nrow(gt$edges)
    -gt$nodes$id <- gt$nodes$nodes
    -gt$nodes$label <- gt$nodes$nodes
    -gt$nodes$color <- scales::col_numeric(c("#41A5F4FF", "#0C46A0FF"), NULL)(gt$nodes$size)
    -
    -sigmajs() %>% 
    -  sg_nodes(gt$nodes, id, size, label, color) %>% 
    -  sg_edges(gt$edges, id, source, target) %>% 
    -  sg_layout() %>% 
    -  sg_settings(
    -    edgeColor = "default",
    -    defaultEdgeColor = "#d3d3d3"
    -  ) %>% 
    -  sg_neighbours()
    -
    -

    * Some nodes are mentioned in tweets only and therefore have no meta-data associated.

    +
    gt <- tweets %>% 
    +    gt_edges(screen_name, retweet_screen_name, followers_count, retweet_followers_count) %>% 
    +    gt_nodes() %>% 
    +    gt_add_meta(name = size, source = followers_count, target = retweet_followers_count)
    +
    +# size is now number of followers
    +head(gt$nodes)
    +#> # A tibble: 6 x 4
    +#>   nodes        type      n  size
    +#>   <chr>        <chr> <int> <int>
    +#> 1 _ddjlab      user      1   125
    +#> 2 _hyperseven_ user      1  6194
    +#> 3 _lazappi_    user      1   919
    +#> 4 _lionelhenry user      1   834
    +#> 5 _reactdev    user      5  2537
    +#> 6 _sevillar    user      1   373
    +
    +gt$edges$id <- 1:nrow(gt$edges)
    +gt$nodes$id <- gt$nodes$nodes
    +gt$nodes$label <- gt$nodes$nodes
    +gt$nodes$color <- scales::col_numeric(c("#41A5F4FF", "#0C46A0FF"), NULL)(gt$nodes$size)
    +
    +sigmajs() %>% 
    +  sg_nodes(gt$nodes, id, size, label, color) %>% 
    +  sg_edges(gt$edges, id, source, target) %>% 
    +  sg_layout() %>% 
    +  sg_settings(
    +    edgeColor = "default",
    +    defaultEdgeColor = "#d3d3d3"
    +  ) %>% 
    +  sg_neighbours()
    +
    +

    * Some nodes are mentioned in tweets only and therefore have no meta-data associated.

    @@ -354,6 +358,7 @@

  3. Users to Hashtags
  4. Retweets
  5. Retweets & Quotes
  6. +
  7.  Meta data
  8. @@ -366,7 +371,7 @@

    -

    Site built with pkgdown 1.3.0.9000.

    +

    Site built with pkgdown 1.3.0.

    diff --git a/docs/articles/get_started_files/sigmajs-binding-0.1.3/sigmajs.js b/docs/articles/get_started_files/sigmajs-binding-0.1.3/sigmajs.js index cc59211..53e9f76 100644 --- a/docs/articles/get_started_files/sigmajs-binding-0.1.3/sigmajs.js +++ b/docs/articles/get_started_files/sigmajs-binding-0.1.3/sigmajs.js @@ -61,6 +61,10 @@ HTMLWidgets.widget({ settings: x.settings }); } + + if(s.clear === true){ + s.clear(); + } if(x.kill === true){ @@ -517,17 +521,17 @@ HTMLWidgets.widget({ // click stage s.bind('clickStage', function (e) { - Shiny.setInputValue(el.id + '_click_stage' + ":sigmajsParseJS", true); + Shiny.setInputValue(el.id + '_click_stage' + ":sigmajsParseJS", e.data, {priority: "event"}); }); // double click stage s.bind('doubleClickStage', function (e) { - Shiny.setInputValue(el.id + '_double_click_stage' + ":sigmajsParseJS", true); + Shiny.setInputValue(el.id + '_double_click_stage' + ":sigmajsParseJS", e.data, {priority: "event"}); }); // right click stage s.bind('rightClickStage', function (e) { - Shiny.setInputValue(el.id + '_right_click_stage' + ":sigmajsParseJS", true); + Shiny.setInputValue(el.id + '_right_click_stage' + ":sigmajsParseJS", e.data, {priority: "event"}); }); // double click node @@ -610,6 +614,8 @@ HTMLWidgets.widget({ Shiny.setInputValue(el.id + '_out_edges' + ":sigmajsParseJS", e.data.edge); }); } + + var initialized = true; }, @@ -617,11 +623,17 @@ HTMLWidgets.widget({ for(var name in s.renderers) s.renderers[name].resize(width, height); }, + + s: s, getCamera: function() { return cam; }, + getEl: function() { + return el.id; + }, + getChart: function () { return s; }, @@ -649,6 +661,20 @@ function get_sigma_graph(id) { return (s); } +// get element id +function get_sigma_element(id) { + + var htmlWidgetsObj = HTMLWidgets.find("#" + id); // find object + + var s; // define + + if (typeof htmlWidgetsObj != 'undefined') { // get chart if defined + s = htmlWidgetsObj.getEl(); + } + + return (s); +} + // get camera function get_sigma_camera(id) { @@ -904,6 +930,42 @@ if (HTMLWidgets.shinyMode) { } } ); + + Shiny.addCustomMessageHandler('sg_drop_nodes_p', + function (message) { + var s = get_sigma_graph(message.id); + if (typeof s != 'undefined') { + message.data.forEach((element) => { + s.graph.dropNode(element); + if (message.refresh === true && message.rate === "iteration") { + s.refresh(); + } + }); + + if (message.refresh === true && message.rate === "once") { + s.refresh(); + } + } + } + ); + + Shiny.addCustomMessageHandler('sg_drop_edges_p', + function (message) { + var s = get_sigma_graph(message.id); + if (typeof s != 'undefined') { + message.data.forEach((element) => { + s.graph.dropEdge(element); + if (message.refresh === true && message.rate === "iteration") { + s.refresh(); + } + }); + + if (message.refresh === true && message.rate === "once") { + s.refresh(); + } + } + } +); // add edges delay Shiny.addCustomMessageHandler('sg_drop_edges_delay_p', @@ -1024,6 +1086,67 @@ if (HTMLWidgets.shinyMode) { } } ); + + Shiny.addCustomMessageHandler('sg_get_nodes_p', + function (message) { + var s = get_sigma_graph(message.id); + var id = get_sigma_element(message.id); + if (typeof s != 'undefined') { + Shiny.setInputValue(id + '_nodes' + ":sigmajsParseJS", s.graph.nodes(), {priority: "event"}); + } + } + ); + + Shiny.addCustomMessageHandler('sg_get_edges_p', + function (message) { + var s = get_sigma_graph(message.id); + var id = get_sigma_element(message.id); + if (typeof s != 'undefined') { + Shiny.setInputValue(id + '_edges' + ":sigmajsParseJS", s.graph.edges(), {priority: "event"}); + } + } + ); + + // change node attributes + Shiny.addCustomMessageHandler('sg_change_nodes_p', + function (message) { + var s = get_sigma_graph(message.id); + var i = 0; + if (typeof s != 'undefined') { + s.graph.nodes().forEach((n) => { + n[message.message.attribute] = message.message.value[i]; + if (message.message.refresh === true && message.message.rate === "iteration") { + s.refresh(); + } + i = i + 1 + }); + + if (message.message.refresh === true && message.message.rate === "once") { + s.refresh(); + } + } + } + ); + + Shiny.addCustomMessageHandler('sg_change_edges_p', + function (message) { + var s = get_sigma_graph(message.id); + var i = 0; + if (typeof s != 'undefined') { + s.graph.edges().forEach((n) => { + n[message.message.attribute] = message.message.value[i]; + if (message.message.refresh === true && message.message.rate === "iteration") { + s.refresh(); + } + i = i + 1 + }); + + if (message.message.refresh === true && message.message.rate === "once") { + s.refresh(); + } + } + } + ); // filter greater than Shiny.addCustomMessageHandler('sg_filter_gt_p', @@ -1036,27 +1159,24 @@ if (HTMLWidgets.shinyMode) { if(message.target === "both"){ filter - .undo() .nodesBy(function(n) { return n[message.var] > message.input; - }) + }, message.name[0]) .edgesBy(function(e) { return e[message.var] > message.input; - }) + }, message.name[1]) .apply(); } else if(message.target === "nodes"){ filter - .undo() .nodesBy(function(n) { return n[message.var] > message.input; - }) + }, message.name) .apply(); } else { filter - .undo() .edgesBy(function(e) { return e[message.var] > message.input; - }) + }, message.name) .apply(); } @@ -1075,27 +1195,96 @@ if (HTMLWidgets.shinyMode) { if(message.target === "both"){ filter - .undo() .nodesBy(function(n) { return n[message.var] < message.input; - }) + }, message.name[0]) .edgesBy(function(e) { return e[message.var] < message.input; - }) + }, message.name[1]) .apply(); } else if(message.target === "nodes"){ filter - .undo() .nodesBy(function(n) { return n[message.var] < message.input; - }) + }, message.name) .apply(); } else { filter - .undo() .edgesBy(function(e) { return e[message.var] < message.input; - }) + }, message.name) + .apply(); + } + + } + } + ); + + // filter equal + Shiny.addCustomMessageHandler('sg_filter_eq_p', + function (message) { + var s = get_sigma_graph(message.id); + + if (typeof s != 'undefined') { + + var filter = new sigma.plugins.filter(s); + + if(message.target === "both"){ + filter + .nodesBy(function(n) { + return n[message.var] == message.input; + }, message.name[0]) + .edgesBy(function(e) { + return e[message.var] == message.input; + }, message.name[1]) + .apply(); + } else if(message.target === "nodes"){ + filter + .nodesBy(function(n) { + return n[message.var] == message.input; + }, message.name) + .apply(); + } else { + filter + .edgesBy(function(e) { + return e[message.var] == message.input; + }, message.name) + .apply(); + } + + } + } + ); + + // filter not equal + Shiny.addCustomMessageHandler('sg_filter_not_eq_p', + function (message) { + var s = get_sigma_graph(message.id); + + if (typeof s != 'undefined') { + + var filter = new sigma.plugins.filter(s); + + if(message.target === "both"){ + filter + .nodesBy(function(n) { + return n[message.var] != message.input; + }, message.name[0]) + .edgesBy(function(e) { + return e[message.var] != message.input; + }, message.name[1]) + .apply(); + } else if(message.target === "nodes"){ + filter + .nodesBy(function(n) { + return n[message.var] != message.input; + }, message.name) + .apply(); + } else { + filter + .edgesBy(function(e) { + return e[message.var] != message.input; + }, message.name) .apply(); } @@ -1103,4 +1292,21 @@ if (HTMLWidgets.shinyMode) { } ); + // filter undo + Shiny.addCustomMessageHandler('sg_filter_undo_p', + function (message) { + var s = get_sigma_graph(message.id); + + if (typeof s != 'undefined') { + + var filter = new sigma.plugins.filter(s); + + filter + .undo(message.name) + .apply() + + } + } + ); + }