1 2# IGraph R package 3# Copyright (C) 2005-2012 Gabor Csardi <csardi.gabor@gmail.com> 4# 334 Harvard street, Cambridge, MA 02139 USA 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19# 02110-1301 USA 20# 21################################################################### 22 23################################################################### 24# Constructors 25################################################################### 26 27update_es_ref <- update_vs_ref <- function(graph) { 28 env <- get_vs_ref(graph) 29 if (!is.null(env)) assign("me", graph, envir = env) 30} 31 32get_es_ref <- get_vs_ref <- function(graph) { 33 if (is_igraph(graph) && !warn_version(graph)) { 34 .Call(C_R_igraph_mybracket, graph, 10L) 35 } else { 36 NULL 37 } 38} 39 40get_es_graph <- get_vs_graph <- function(seq) { 41 at <- attr(seq, "env") 42 if (class(at) == "weakref") { 43 weak_ref_key(at)$me 44 } else if (class(at) == "environment") { 45 get("graph", envir = at) 46 } else { 47 NULL 48 } 49} 50 51has_es_graph <- has_vs_graph <- function(seq) { 52 !is.null(weak_ref_key(attr(seq, "env"))) 53} 54 55get_es_graph_id <- get_vs_graph_id <- function(seq) { 56 new_g <- attr(seq, "graph") 57 if (!is.null(new_g)) { 58 new_g 59 } else if (!is.null(attr(seq, "env"))) { 60 get("graph", envir = attr(seq, "env")) 61 } else { 62 NULL 63 } 64} 65 66#' Decide if two graphs are identical 67#' 68#' This is similar to \code{identical} in the \code{base} package, 69#' but ignores the mutable piece of igraph objects, that might be 70#' different, even if the two graphs are identical. 71#' 72#' @param g1,g2 The two graphs 73#' @return Logical scalar 74#' @export 75 76identical_graphs <- function(g1, g2) { 77 stopifnot(is_igraph(g1), is_igraph(g2)) 78 .Call(C_R_igraph_identical_graphs, g1, g2) 79} 80 81add_vses_graph_ref <- function(vses, graph) { 82 ref <- get_vs_ref(graph) 83 if (!is.null(ref)) { 84 attr(vses, "env") <- make_weak_ref(ref, NULL) 85 attr(vses, "graph") <- get_graph_id(graph) 86 } else { 87 ne <- new.env() 88 assign("graph", graph, envir = ne) 89 attr(vses, "env") <- ne 90 } 91 92 vses 93} 94 95#' Get the id of a graph 96#' 97#' Graph ids are used to check that a vertex or edge sequence 98#' belongs to a graph. If you create a new graph by changing the 99#' structure of a graph, the new graph will have a new id. 100#' Changing the attributes will not change the id. 101#' 102#' @param x A graph or a vertex sequence or an edge sequence. 103#' @param ... Not used currently. 104#' @return The id of the graph, a character scalar. For 105#' vertex and edge sequences the id of the graph they were created from. 106#' 107#' @export 108#' @examples 109#' g <- make_ring(10) 110#' graph_id(g) 111#' graph_id(V(g)) 112#' graph_id(E(g)) 113#' 114#' g2 <- g + 1 115#' graph_id(g2) 116 117graph_id <- function(x, ...) 118 UseMethod("graph_id") 119 120#' @method graph_id igraph 121#' @export 122 123graph_id.igraph <- function(x, ...) { 124 get_graph_id(x) 125} 126 127#' @method graph_id igraph.vs 128#' @export 129 130graph_id.igraph.vs <- function(x, ...) { 131 get_vs_graph_id(x) %||% NA_character_ 132} 133 134#' @method graph_id igraph.es 135#' @export 136 137graph_id.igraph.es <- function(x, ...) { 138 get_es_graph_id(x) %||% NA_character_ 139} 140 141#' Vertices of a graph 142#' 143#' Create a vertex sequence (vs) containing all vertices of a graph. 144#' 145#' @details 146#' A vertex sequence is just what the name says it is: a sequence of 147#' vertices. Vertex sequences are usually used as igraph function arguments 148#' that refer to vertices of a graph. 149#' 150#' A vertex sequence is tied to the graph it refers to: it really denoted 151#' the specific vertices of that graph, and cannot be used together with 152#' another graph. 153#' 154#' At the implementation level, a vertex sequence is simply a vector 155#' containing numeric vertex ids, but it has a special class attribute 156#' which makes it possible to perform graph specific operations on it, like 157#' selecting a subset of the vertices based on graph structure, or vertex 158#' attributes. 159#' 160#' A vertex sequence is most often created by the \code{V()} function. The 161#' result of this includes all vertices in increasing vertex id order. A 162#' vertex sequence can be indexed by a numeric vector, just like a regular 163#' R vector. See \code{\link{[.igraph.vs}} and additional links to other 164#' vertex sequence operations below. 165#' 166#' @section Indexing vertex sequences: 167#' Vertex sequences mostly behave like regular vectors, but there are some 168#' additional indexing operations that are specific for them; 169#' e.g. selecting vertices based on graph structure, or based on vertex 170#' attributes. See \code{\link{[.igraph.vs}} for details. 171#' 172#' @section Querying or setting attributes: 173#' Vertex sequences can be used to query or set attributes for the 174#' vertices in the sequence. See \code{\link{$.igraph.vs}} for details. 175#' 176#' @param graph The graph 177#' @return A vertex sequence containing all vertices, in the order 178#' of their numeric vertex ids. 179#' 180#' @family vertex and edge sequences 181#' @export 182#' @examples 183#' # Vertex ids of an unnamed graph 184#' g <- make_ring(10) 185#' V(g) 186#' 187#' # Vertex ids of a named graph 188#' g2 <- make_ring(10) %>% 189#' set_vertex_attr("name", value = letters[1:10]) 190#' V(g2) 191 192V <- function(graph) { 193 if (!is_igraph(graph)) { 194 stop("Not a graph object") 195 } 196 197 update_vs_ref(graph) 198 199 res <- seq_len(vcount(graph)) 200 if (is_named(graph)) names(res) <- vertex_attr(graph)$name 201 class(res) <- "igraph.vs" 202 add_vses_graph_ref(res, graph) 203} 204 205create_vs <- function(graph, idx, na_ok = FALSE) { 206 if (na_ok) idx <- ifelse(idx < 1 | idx > gorder(graph), NA, idx) 207 res <- simple_vs_index(V(graph), idx, na_ok = na_ok) 208 add_vses_graph_ref(res, graph) 209} 210 211#' Edges of a graph 212#' 213#' An edge sequence is a vector containing numeric edge ids, with a special 214#' class attribute that allows custom operations: selecting subsets of 215#' edges based on attributes, or graph structure, creating the 216#' intersection, union of edges, etc. 217#' 218#' @details 219#' Edge sequences are usually used as igraph function arguments that 220#' refer to edges of a graph. 221#' 222#' An edge sequence is tied to the graph it refers to: it really denoted 223#' the specific edges of that graph, and cannot be used together with 224#' another graph. 225#' 226#' An edge sequence is most often created by the \code{E()} function. The 227#' result includes edges in increasing edge id order by default (if. none 228#' of the \code{P} and \code{path} arguments are used). An edge 229#' sequence can be indexed by a numeric vector, just like a regular R 230#' vector. See links to other edge sequence operations below. 231#' 232#' @section Indexing edge sequences: 233#' Edge sequences mostly behave like regular vectors, but there are some 234#' additional indexing operations that are specific for them; 235#' e.g. selecting edges based on graph structure, or based on edge 236#' attributes. See \code{\link{[.igraph.es}} for details. 237#' 238#' @section Querying or setting attributes: 239#' Edge sequences can be used to query or set attributes for the 240#' edges in the sequence. See \code{\link{$.igraph.es}} for details. 241#' 242#' @param graph The graph. 243#' @param P A list of vertices to select edges via pairs of vertices. 244#' The first and second vertices select the first edge, the third 245#' and fourth the second, etc. 246#' @param path A list of vertices, to select edges along a path. 247#' Note that this only works reliable for simple graphs. If the graph 248#' has multiple edges, one of them will be chosen arbitrarily to 249#' be included in the edge sequence. 250#' @param directed Whether to consider edge directions in the \code{P} 251#' argument, for directed graphs. 252#' @return An edge sequence of the graph. 253#' 254#' @export 255#' @family vertex and edge sequences 256#' @examples 257#' # Edges of an unnamed graph 258#' g <- make_ring(10) 259#' E(g) 260#' 261#' # Edges of a named graph 262#' g2 <- make_ring(10) %>% 263#' set_vertex_attr("name", value = letters[1:10]) 264#' E(g2) 265 266E <- function(graph, P=NULL, path=NULL, directed=TRUE) { 267 if (!is_igraph(graph)) { 268 stop("Not a graph object") 269 } 270 271 update_es_ref(graph) 272 273 if (!is.null(P) && !is.null(path)) { 274 stop("Cannot give both `P' and `path' at the same time") 275 } 276 277 if (is.null(P) && is.null(path)) { 278 ec <- ecount(graph) 279 res <- seq_len(ec) 280 } else if (!is.null(P)) { 281 on.exit( .Call(C_R_igraph_finalizer) ) 282 res <- .Call(C_R_igraph_es_pairs, graph, as.igraph.vs(graph, P)-1, 283 as.logical(directed)) + 1 284 } else { 285 on.exit(.Call(C_R_igraph_finalizer) ) 286 res <- .Call(C_R_igraph_es_path, graph, as.igraph.vs(graph, path)-1, 287 as.logical(directed)) + 1 288 } 289 290 if ("name" %in% edge_attr_names(graph)) { 291 names(res) <- edge_attr(graph)$name[res] 292 } 293 if (is.named(graph)) { 294 el <- ends(graph, es = res) 295 attr(res, "vnames") <- paste(el[,1], el[,2], sep = "|") 296 } 297 298 class(res) <- "igraph.es" 299 add_vses_graph_ref(res, graph) 300} 301 302create_es <- function(graph, idx, na_ok = FALSE) { 303 if (na_ok) idx <- ifelse(idx < 1 | idx > gsize(graph), NA, idx) 304 simple_es_index(E(graph), idx) 305} 306 307simple_vs_index <- function(x, i, na_ok = FALSE) { 308 res <- unclass(x)[i] 309 if (!na_ok && any(is.na(res))) stop('Unknown vertex selected') 310 class(res) <- "igraph.vs" 311 res 312} 313 314#' Indexing vertex sequences 315#' 316#' Vertex sequences can be indexed very much like a plain numeric R vector, 317#' with some extras. 318#' 319#' @details 320#' Vertex sequences can be indexed using both the single bracket and 321#' the double bracket operators, and they both work the same way. 322#' The only difference between them is that the double bracket operator 323#' marks the result for printing vertex attributes. 324#' 325#' @section Multiple indices: 326#' When using multiple indices within the bracket, all of them 327#' are evaluated independently, and then the results are concatenated 328#' using the \code{c()} function (except for the \code{na_ok} argument, 329#' which is special an must be named. E.g. \code{V(g)[1, 2, .nei(1)]} 330#' is equivalent to \code{c(V(g)[1], V(g)[2], V(g)[.nei(1)])}. 331#' 332#' @section Index types: 333#' Vertex sequences can be indexed with positive numeric vectors, 334#' negative numeric vectors, logical vectors, character vectors: 335#' \itemize{ 336#' \item When indexed with positive numeric vectors, the vertices at the 337#' given positions in the sequence are selected. This is the same as 338#' indexing a regular R atomic vector with positive numeric vectors. 339#' \item When indexed with negative numeric vectors, the vertices at the 340#' given positions in the sequence are omitted. Again, this is the same 341#' as indexing a regular R atomic vector. 342#' \item When indexed with a logical vector, the lengths of the vertex 343#' sequence and the index must match, and the vertices for which the 344#' index is \code{TRUE} are selected. 345#' \item Named graphs can be indexed with character vectors, 346#' to select vertices with the given names. 347#' } 348#' 349#' @section Vertex attributes: 350#' When indexing vertex sequences, vertex attributes can be referred 351#' to simply by using their names. E.g. if a graph has a \code{name} vertex 352#' attribute, then \code{V(g)[name == "foo"]} is equivalent to 353#' \code{V(g)[V(g)$name == "foo"]}. See examples below. 354#' 355#' @section Special functions: 356#' There are some special igraph functions that can be used only 357#' in expressions indexing vertex sequences: \describe{ 358#' \item{\code{.nei}}{takes a vertex sequence as its argument 359#' and selects neighbors of these vertices. An optional \code{mode} 360#' argument can be used to select successors (\code{mode="out"}), or 361#' predecessors (\code{mode="in"}) in directed graphs.} 362#' \item{\code{.inc}}{Takes an edge sequence as an argument, and 363#' selects vertices that have at least one incident edge in this 364#' edge sequence.} 365#' \item{\code{.from}}{Similar to \code{.inc}, but only considers the 366#' tails of the edges.} 367#' \item{\code{.to}}{Similar to \code{.inc}, but only considers the 368#' heads of the edges.} 369#' \item{\code{.innei}, \code{.outnei}}{\code{.innei(v)} is a shorthand for 370#' \code{.nei(v, mode = "in")}, and \code{.outnei(v)} is a shorthand for 371#' \code{.nei(v, mode = "out")}. 372#' } 373#' } 374#' Note that multiple special functions can be used together, or with 375#' regular indices, and then their results are concatenated. See more 376#' examples below. 377#' 378#' @param x A vertex sequence. 379#' @param ... Indices, see details below. 380#' @param na_ok Whether it is OK to have \code{NA}s in the vertex 381#' sequence. 382#' @return Another vertex sequence, referring to the same graph. 383#' 384#' @method [ igraph.vs 385#' @name igraph-vs-indexing 386#' @export 387#' @family vertex and edge sequences 388#' @family vertex and edge sequence operations 389#' 390#' @examples 391#' # ----------------------------------------------------------------- 392#' # Setting attributes for subsets of vertices 393#' largest_comp <- function(graph) { 394#' cl <- components(graph) 395#' V(graph)[which.max(cl$csize) == cl$membership] 396#' } 397#' g <- sample_(gnp(100, 2/100), 398#' with_vertex_(size = 3, label = ""), 399#' with_graph_(layout = layout_with_fr) 400#' ) 401#' giant_v <- largest_comp(g) 402#' V(g)$color <- "green" 403#' V(g)[giant_v]$color <- "red" 404#' plot(g) 405#' 406#' # ----------------------------------------------------------------- 407#' # nei() special function 408#' g <- graph( c(1,2, 2,3, 2,4, 4,2) ) 409#' V(g)[ .nei( c(2,4) ) ] 410#' V(g)[ .nei( c(2,4), "in") ] 411#' V(g)[ .nei( c(2,4), "out") ] 412#' 413#' # ----------------------------------------------------------------- 414#' # The same with vertex names 415#' g <- graph(~ A -+ B, B -+ C:D, D -+ B) 416#' V(g)[ .nei( c('B', 'D') ) ] 417#' V(g)[ .nei( c('B', 'D'), "in" ) ] 418#' V(g)[ .nei( c('B', 'D'), "out" ) ] 419 420`[.igraph.vs` <- function(x, ..., na_ok = FALSE) { 421 422 args <- lazy_dots(..., .follow_symbols = FALSE) 423 424 ## If indexing has no argument at all, then we still get one, 425 ## but it is "empty", a name that is "" 426 427 ## Special case, no argument (but we might get an artificial 428 ## empty one 429 if (length(args) < 1 || 430 (length(args) == 1 && class(args[[1]]$expr) == "name" && 431 as.character(args[[1]]$expr) == "")) { 432 return(x) 433 } 434 435 ## Special case: single numeric argument 436 if (length(args) == 1 && class(args[[1]]$expr) == "numeric") { 437 res <- simple_vs_index(x, args[[1]]$expr) 438 return (add_vses_graph_ref(res, get_vs_graph(x))) 439 } 440 441 ## Special case: single symbol argument, no such attribute 442 if (length(args) == 1 && class(args[[1]]$expr) == "name") { 443 graph <- get_vs_graph(x) 444 if (! (as.character(args[[1]]$expr) %in% vertex_attr_names(graph))) { 445 res <- simple_vs_index(x, lazy_eval(args[[1]])) 446 return (add_vses_graph_ref(res, graph)) 447 } 448 } 449 450 .nei <- function(v, mode=c("all", "in", "out", "total")) { 451 ## TRUE iff the vertex is a neighbor (any type) 452 ## of at least one vertex in v 453 mode <- igraph.match.arg(mode) 454 mode <- switch(mode, "out"=1, "in"=2, "all"=3, "total"=3) 455 456 if (is.logical(v)) { 457 v <- which(v) 458 } 459 on.exit(.Call(C_R_igraph_finalizer) ) 460 tmp <- .Call(C_R_igraph_vs_nei, graph, x, as.igraph.vs(graph, v)-1, 461 as.numeric(mode)) 462 tmp[as.numeric(x)] 463 } 464 nei <- function(...) { .nei(...) } 465 .innei <- function(v, mode=c("in", "all", "out", "total")) { 466 .nei(v, mode = mode[1]) 467 } 468 innei <- function(...) { .innei(...) } 469 .outnei <- function(v, mode=c("out", "all", "in", "total")) { 470 .nei(v, mode = mode[1]) 471 } 472 outnei <- function(...) { .outnei(...) } 473 .inc <- function(e) { 474 ## TRUE iff the vertex (in the vs) is incident 475 ## to at least one edge in e 476 if (is.logical(e)) { 477 e <- which(e) 478 } 479 on.exit(.Call(C_R_igraph_finalizer) ) 480 tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1, 481 as.numeric(3)) 482 tmp[as.numeric(x)] 483 } 484 inc <- function(...) { .inc(...) } 485 adj <- function(...) { .inc(...) } 486 .from <- function(e) { 487 ## TRUE iff the vertex is the source of at least one edge in e 488 if (is.logical(e)) { 489 e <- which(e) 490 } 491 on.exit(.Call(C_R_igraph_finalizer) ) 492 tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1, 493 as.numeric(1)) 494 tmp[as.numeric(x)] 495 } 496 from <- function(...) { .from(...) } 497 .to <- function(e) { 498 ## TRUE iff the vertex is the target of at least one edge in e 499 if (is.logical(e)) { 500 e <- which(e) 501 } 502 on.exit(.Call(C_R_igraph_finalizer) ) 503 tmp <- .Call(C_R_igraph_vs_adj, graph, x, as.igraph.es(graph, e)-1, 504 as.numeric(2)) 505 tmp[as.numeric(x)] 506 } 507 to <- function(...) { .to(...) } 508 509 graph <- get_vs_graph(x) 510 511 if (is.null(graph)) { 512 res <- lapply(lazy_eval(args), simple_vs_index, x = x, na_ok = na_ok) 513 514 } else { 515 516 attrs <- vertex_attr(graph) 517 xvec <- as.vector(x) 518 for (i in seq_along(attrs)) attrs[[i]] <- attrs[[i]][xvec] 519 520 res <- lazy_eval( 521 args, 522 data = c(attrs, .nei = .nei, nei = nei, .innei = .innei, 523 innei = innei, .outnei = .outnei, outnei = outnei, adj = adj, 524 .inc = .inc, inc = inc, .from = .from, from = from, .to = .to, 525 to = to) 526 ) 527 res <- lapply(res, function(ii) { 528 if (is.null(ii)) return(NULL) 529 ii <- simple_vs_index(x, ii, na_ok) 530 attr(ii, "env") <- attr(x, "env") 531 attr(ii, "graph") <- attr(x, "graph") 532 class(ii) <- class(x) 533 ii 534 }) 535 } 536 537 res <- drop_null(res) 538 if (length(res)) { 539 do_call(c, res) 540 } else { 541 x[FALSE] 542 } 543} 544 545#' Select vertices and show their metadata 546#' 547#' The double bracket operator can be used on vertex sequences, to print 548#' the meta-data (vertex attributes) of the vertices in the sequence. 549#' 550#' @details 551#' Technically, when used with vertex sequences, the double bracket 552#' operator does exactly the same as the single bracket operator, 553#' but the resulting vertex sequence is printed differently: all 554#' attributes of the vertices in the sequence are printed as well. 555#' 556#' See \code{\link{[.igraph.vs}} for more about indexing vertex sequences. 557#' 558#' @param x A vertex sequence. 559#' @param ... Additional arguments, passed to \code{[}. 560#' @return The double bracket operator returns another vertex sequence, 561#' with meta-data (attribute) printing turned on. See details below. 562#' 563#' @method [[ igraph.vs 564#' @name igraph-vs-indexing2 565#' @family vertex and edge sequences 566#' @family vertex and edge sequence operations 567#' @export 568#' @examples 569#' g <- make_ring(10) %>% 570#' set_vertex_attr("color", value = "red") %>% 571#' set_vertex_attr("name", value = LETTERS[1:10]) 572#' V(g) 573#' V(g)[[]] 574#' V(g)[1:5] 575#' V(g)[[1:5]] 576 577`[[.igraph.vs` <- function(x, ...) { 578 res <- x[...] 579 attr(res, "single") <- TRUE 580 res 581} 582 583#' Select edges and show their metadata 584#' 585#' The double bracket operator can be used on edge sequences, to print 586#' the meta-data (edge attributes) of the edges in the sequence. 587#' 588#' @details 589#' Technically, when used with edge sequences, the double bracket 590#' operator does exactly the same as the single bracket operator, 591#' but the resulting edge sequence is printed differently: all 592#' attributes of the edges in the sequence are printed as well. 593#' 594#' See \code{\link{[.igraph.es}} for more about indexing edge sequences. 595#' 596#' @param x An edge sequence. 597#' @param ... Additional arguments, passed to \code{[}. 598#' @return Another edge sequence, with metadata printing turned on. 599#' See details below. 600#' 601#' @method [[ igraph.es 602#' @name igraph-es-indexing2 603#' @family vertex and edge sequences 604#' @family vertex and edge sequence operations 605#' @export 606#' @examples 607#' g <- make_(ring(10), 608#' with_vertex_(name = LETTERS[1:10]), 609#' with_edge_(weight = 1:10, color = "green")) 610#' E(g) 611#' E(g)[[]] 612#' E(g)[[.inc('A')]] 613 614`[[.igraph.es` <- function(x, ...) { 615 res <- x[...] 616 attr(res, "single") <- TRUE 617 res 618} 619 620simple_es_index <- function(x, i) { 621 if (!is.null(attr(x, "vnames"))) { 622 wh1 <- structure(seq_along(x), names = names(x))[i] 623 wh2 <- structure(seq_along(x), names = attr(x, "vnames"))[i] 624 wh <- ifelse(is.na(wh1), wh2, wh1) 625 res <- unclass(x)[wh] 626 names(res) <- names(x)[wh] 627 attr(res, "vnames") <- attr(x, "vnames")[wh] 628 } else { 629 res <- unclass(x)[i] 630 } 631 if (any(is.na(res))) stop('Unknown edge selected') 632 633 attr(res, "env") <- attr(x, "env") 634 attr(res, "graph") <- attr(x, "graph") 635 class(res) <- "igraph.es" 636 res 637} 638 639#' Indexing edge sequences 640#' 641#' Edge sequences can be indexed very much like a plain numeric R vector, 642#' with some extras. 643#' 644#' @section Multiple indices: 645#' When using multiple indices within the bracket, all of them 646#' are evaluated independently, and then the results are concatenated 647#' using the \code{c()} function. E.g. \code{E(g)[1, 2, .inc(1)]} 648#' is equivalent to \code{c(E(g)[1], E(g)[2], E(g)[.inc(1)])}. 649#' 650#' @section Index types: 651#' Edge sequences can be indexed with positive numeric vectors, 652#' negative numeric vectors, logical vectors, character vectors: 653#' \itemize{ 654#' \item When indexed with positive numeric vectors, the edges at the 655#' given positions in the sequence are selected. This is the same as 656#' indexing a regular R atomic vector with positive numeric vectors. 657#' \item When indexed with negative numeric vectors, the edges at the 658#' given positions in the sequence are omitted. Again, this is the same 659#' as indexing a regular R atomic vector. 660#' \item When indexed with a logical vector, the lengths of the edge 661#' sequence and the index must match, and the edges for which the 662#' index is \code{TRUE} are selected. 663#' \item Named graphs can be indexed with character vectors, 664#' to select edges with the given names. Note that a graph may 665#' have edge names and vertex names, and both can be used to select 666#' edges. Edge names are simply used as names of the numeric 667#' edge id vector. Vertex names effectively only work in graphs without 668#' multiple edges, and must be separated with a \code{|} bar character 669#' to select an edges that incident to the two given vertices. See 670#' examples below. 671#' } 672#' 673#' @section Edge attributes: 674#' When indexing edge sequences, edge attributes can be referred 675#' to simply by using their names. E.g. if a graph has a \code{weight} edge 676#' attribute, then \code{E(G)[weight > 1]} selects all edges with a larger 677#' than one weight. See more examples below. 678#' 679#' @section Special functions: 680#' There are some special igraph functions that can be used 681#' only in expressions indexing edge sequences: \describe{ 682#' \item{\code{.inc}}{takes a vertex sequence, and selects 683#' all edges that have at least one incident vertex in the vertex 684#' sequence.} 685#' \item{\code{.from}}{similar to \code{.inc()}, but only 686#' the tails of the edges are considered.} 687#' \item{\code{.to}}{is similar to \code{.inc()}, but only 688#' the heads of the edges are considered.} 689#' \item{\code{\%--\%}}{a special operator that can be 690#' used to select all edges between two sets of vertices. It ignores 691#' the edge directions in directed graphs.} 692#' \item{\code{\%->\%}}{similar to \code{\%--\%}, 693#' but edges \emph{from} the left hand side argument, pointing 694#' \emph{to} the right hand side argument, are selected, in directed 695#' graphs.} 696#' \item{\code{\%<-\%}}{similar to \code{\%--\%}, 697#' but edges \emph{to} the left hand side argument, pointing 698#' \emph{from} the right hand side argument, are selected, in directed 699#' graphs.} 700#' } 701#' Note that multiple special functions can be used together, or with 702#' regular indices, and then their results are concatenated. See more 703#' examples below. 704#' 705#' @aliases %--% %<-% %->% 706#' @param x An edge sequence 707#' @param ... Indices, see details below. 708#' @return Another edge sequence, referring to the same graph. 709#' 710#' @method [ igraph.es 711#' @name igraph-es-indexing 712#' 713#' @export 714#' @family vertex and edge sequences 715#' @family vertex and edge sequence operations 716#' @examples 717#' # special operators for indexing based on graph structure 718#' g <- sample_pa(100, power = 0.3) 719#' E(g) [ 1:3 %--% 2:6 ] 720#' E(g) [ 1:5 %->% 1:6 ] 721#' E(g) [ 1:3 %<-% 2:6 ] 722#' 723#' # the edges along the diameter 724#' g <- sample_pa(100, directed = FALSE) 725#' d <- get_diameter(g) 726#' E(g, path = d) 727#' 728#' # select edges based on attributes 729#' g <- sample_gnp(20, 3/20) %>% 730#' set_edge_attr("weight", value = rnorm(gsize(.))) 731#' E(g)[[ weight < 0 ]] 732 733`[.igraph.es` <- function(x, ...) { 734 735 args <- lazy_dots(..., .follow_symbols = TRUE) 736 737 ## If indexing has no argument at all, then we still get one, 738 ## but it is "empty", a name that is "" 739 740 if (length(args) < 1 || 741 (length(args) == 1 && class(args[[1]]$expr) == "name" && 742 as.character(args[[1]]$expr) == "")) { 743 return(x) 744 } 745 746 .inc <- function(v) { 747 ## TRUE iff the edge is incident to at least one vertex in v 748 on.exit(.Call(C_R_igraph_finalizer) ) 749 tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1, 750 as.numeric(3)) 751 tmp[ as.numeric(x) ] 752 } 753 adj <- function(...) { .inc(...) } 754 inc <- function(...) { .inc(...) } 755 .from <- function(v) { 756 ## TRUE iff the edge originates from at least one vertex in v 757 on.exit(.Call(C_R_igraph_finalizer) ) 758 tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1, 759 as.numeric(1)) 760 tmp[ as.numeric(x) ] 761 } 762 from <- function(...) { .from(...) } 763 .to <- function(v) { 764 ## TRUE iff the edge points to at least one vertex in v 765 on.exit(.Call(C_R_igraph_finalizer) ) 766 tmp <- .Call(C_R_igraph_es_adj, graph, x, as.igraph.vs(graph, v)-1, 767 as.numeric(2)) 768 tmp[ as.numeric(x) ] 769 } 770 to <- function(...) { .to(...) } 771 772 graph <- get_es_graph(x) 773 774 if (is.null(graph)) { 775 res <- lapply(lazy_eval(args), simple_es_index, x = x) 776 777 } else { 778 779 attrs <- edge_attr(graph) 780 xvec <- as.vector(x) 781 for (i in seq_along(attrs)) attrs[[i]] <- attrs[[i]][xvec] 782 783 res <- lazy_eval( 784 args, 785 data = c(attrs, .inc = .inc, inc = inc, adj = adj, .from = .from, 786 from = from, .to = .to, to = to, 787 .igraph.from = list(.Call(C_R_igraph_mybracket, 788 graph, 3L)[ as.numeric(x) ]), 789 .igraph.to = list(.Call(C_R_igraph_mybracket, 790 graph, 4L)[as.numeric(x)]), 791 .igraph.graph = list(graph), 792 `%--%`=`%--%`, `%->%`=`%->%`, `%<-%`=`%<-%`) 793 ) 794 res <- lapply(res, function(ii) { 795 if (is.null(ii)) return(NULL) 796 ii <- simple_es_index(x, ii) 797 attr(ii, "env") <- attr(x, "env") 798 attr(ii, "graph") <- attr(x, "graph") 799 class(ii) <- class(x) 800 ii 801 }) 802 } 803 804 res <- drop_null(res) 805 if (length(res) == 1) { 806 res[[1]] 807 } else if (length(res)) { 808 do_call(c, res) 809 } else { 810 x[FALSE] 811 } 812} 813 814#' @export 815 816`%--%` <- function(f, t) { 817 from <- get(".igraph.from", parent.frame()) 818 to <- get(".igraph.to", parent.frame()) 819 graph <- get(".igraph.graph", parent.frame()) 820 f <- as.igraph.vs(graph, f)-1 821 t <- as.igraph.vs(graph, t)-1 822 (from %in% f & to %in% t) | (to %in% f & from %in% t) 823} 824 825#' @export 826 827`%->%` <- function(f, t) { 828 from <- get(".igraph.from", parent.frame()) 829 to <- get(".igraph.to", parent.frame()) 830 graph <- get(".igraph.graph", parent.frame()) 831 f <- as.igraph.vs(graph, f)-1 832 t <- as.igraph.vs(graph, t)-1 833 if (is_directed(graph)) { 834 from %in% f & to %in% t 835 } else { 836 (from %in% f & to %in% t) | (to %in% f & from %in% t) 837 } 838} 839 840#' @export 841 842`%<-%` <- function(t, value) { 843 from <- get(".igraph.from", parent.frame()) 844 to <- get(".igraph.to", parent.frame()) 845 graph <- get(".igraph.graph", parent.frame()) 846 value <- as.igraph.vs(graph, value)-1 847 t <- as.igraph.vs(graph, t)-1 848 if (is_directed(graph)) { 849 from %in% value & to %in% t 850 } else { 851 (from %in% value & to %in% t) | (to %in% value & from %in% t) 852 } 853} 854 855#' @param i Index. 856#' @method [[<- igraph.vs 857#' @name igraph-vs-attributes 858#' @export 859 860`[[<-.igraph.vs` <- function(x, i, value) { 861 if (! "name" %in% names(attributes(value)) || 862 ! "value" %in% names(attributes(value))) { 863 stop("invalid indexing") 864 } 865 if (is.null(get_vs_graph(x))) stop("Graph is unknown") 866 value 867} 868 869#' @method [<- igraph.vs 870#' @name igraph-vs-attributes 871#' @export 872 873`[<-.igraph.vs` <- `[[<-.igraph.vs` 874 875#' @param i Index. 876#' @method [[<- igraph.es 877#' @name igraph-es-attributes 878#' @export 879 880`[[<-.igraph.es` <- function(x, i, value) { 881 if (! "name" %in% names(attributes(value)) || 882 ! "value" %in% names(attributes(value))) { 883 stop("invalid indexing") 884 } 885 if (is.null(get_es_graph(x))) stop("Graph is unknown") 886 value 887} 888 889#' @method [<- igraph.es 890#' @name igraph-es-attributes 891#' @export 892 893`[<-.igraph.es` <- `[[<-.igraph.es` 894 895#' Query or set attributes of the vertices in a vertex sequence 896#' 897#' The \code{$} operator is a syntactic sugar to query and set the 898#' attributes of the vertices in a vertex sequence. 899#' 900#' @details 901#' The query form of \code{$} is a shortcut for 902#' \code{\link{vertex_attr}}, e.g. \code{V(g)[idx]$attr} is equivalent 903#' to \code{vertex_attr(g, attr, V(g)[idx])}. 904#' 905#' The assignment form of \code{$} is a shortcut for 906#' \code{\link{set_vertex_attr}}, e.g. \code{V(g)[idx]$attr <- value} is 907#' equivalent to \code{g <- set_vertex_attr(g, attr, V(g)[idx], value)}. 908#' 909#' @param x A vertex sequence. For \code{V<-} it is a graph. 910#' @param name Name of the vertex attribute to query or set. 911#' @return A vector or list, containing the values of 912#' attribute \code{name} for the vertices in the vertex sequence. 913#' For numeric, character or logical attributes, it is a vector of the 914#' appropriate type, otherwise it is a list. 915#' 916#' @method $ igraph.vs 917#' @name igraph-vs-attributes 918#' 919#' @export 920#' @family vertex and edge sequences 921#' @family graph attributes 922#' @examples 923#' g <- make_(ring(10), 924#' with_vertex_( 925#' name = LETTERS[1:10], 926#' color = sample(1:2, 10, replace=TRUE) 927#' ) 928#' ) 929#' V(g)$name 930#' V(g)$color 931#' V(g)$frame.color <- V(g)$color 932#' 933#' # color vertices of the largest component 934#' largest_comp <- function(graph) { 935#' cl <- components(graph) 936#' V(graph)[which.max(cl$csize) == cl$membership] 937#' } 938#' g <- sample_(gnp(100, 2/100), 939#' with_vertex_(size = 3, label = ""), 940#' with_graph_(layout = layout_with_fr) 941#' ) 942#' giant_v <- largest_comp(g) 943#' V(g)$color <- "blue" 944#' V(g)[giant_v]$color <- "orange" 945#' plot(g) 946 947`$.igraph.vs` <- function(x, name) { 948 graph <- get_vs_graph(x) 949 if (is.null(graph)) stop("Graph is unknown") 950 res <- vertex_attr(graph, name, x) 951 if ("single" %in% names(attributes(x)) && attr(x, "single")) { 952 res[[1]] 953 } else { 954 res 955 } 956} 957 958#' Query or set attributes of the edges in an edge sequence 959#' 960#' The \code{$} operator is a syntactic sugar to query and set 961#' edge attributes, for edges in an edge sequence. 962#' 963#' @details 964#' The query form of \code{$} is a shortcut for \code{\link{edge_attr}}, 965#' e.g. \code{E(g)[idx]$attr} is equivalent to \code{edge_attr(g, attr, 966#' E(g)[idx])}. 967#' 968#' The assignment form of \code{$} is a shortcut for 969#' \code{\link{set_edge_attr}}, e.g. \code{E(g)[idx]$attr <- value} is 970#' equivalent to \code{g <- set_edge_attr(g, attr, E(g)[idx], value)}. 971#' 972#' @param x An edge sequence. For \code{E<-} it is a graph. 973#' @param name Name of the edge attribute to query or set. 974#' @return A vector or list, containing the values of the attribute 975#' \code{name} for the edges in the sequence. For numeric, character or 976#' logical attributes, it is a vector of the appropriate type, otherwise 977#' it is a list. 978#' 979#' @method $ igraph.es 980#' @name igraph-es-attributes 981#' 982#' @export 983#' @family vertex and edge sequences 984#' @examples 985#' # color edges of the largest component 986#' largest_comp <- function(graph) { 987#' cl <- components(graph) 988#' V(graph)[which.max(cl$csize) == cl$membership] 989#' } 990#' g <- sample_(gnp(100, 1/100), 991#' with_vertex_(size = 3, label = ""), 992#' with_graph_(layout = layout_with_fr) 993#' ) 994#' giant_v <- largest_comp(g) 995#' E(g)$color <- "orange" 996#' E(g)[giant_v %--% giant_v]$color <- "blue" 997#' plot(g) 998 999`$.igraph.es` <- function(x, name) { 1000 graph <- get_es_graph(x) 1001 if (is.null(graph)) stop("Graph is unknown") 1002 res <- edge_attr(graph, name, x) 1003 if ("single" %in% names(attributes(x)) && attr(x, "single")) { 1004 res[[1]] 1005 } else { 1006 res 1007 } 1008} 1009 1010#' @param value New value of the attribute, for the vertices in the 1011#' vertex sequence. 1012#' 1013#' @method $<- igraph.vs 1014#' @name igraph-vs-attributes 1015#' @export 1016 1017`$<-.igraph.vs` <- function(x, name, value) { 1018 if (is.null(get_vs_graph(x))) stop("Graph is unknown") 1019 attr(x, "name") <- name 1020 attr(x, "value") <- value 1021 x 1022} 1023 1024#' @param value New value of the attribute, for the edges in the edge 1025#' sequence. 1026#' @method $<- igraph.es 1027#' @name igraph-es-attributes 1028#' @export 1029#' @family vertex and edge sequences 1030 1031`$<-.igraph.es` <- function(x, name, value) { 1032 if (is.null(get_es_graph(x))) stop("Graph is unknown") 1033 attr(x, "name") <- name 1034 attr(x, "value") <- value 1035 x 1036} 1037 1038#' @name igraph-vs-attributes 1039#' @export 1040 1041`V<-` <- function(x, value) { 1042 if (!is_igraph(x)) { 1043 stop("Not a graph object") 1044 } 1045 if (! "name" %in% names(attributes(value)) || 1046 ! "value" %in% names(attributes(value))) { 1047 stop("invalid indexing") 1048 } 1049 i_set_vertex_attr(x, attr(value, "name"), index=value, 1050 value=attr(value, "value"), check = FALSE) 1051} 1052 1053#' @param path Select edges along a path, given by a vertex sequence See 1054#' \code{\link{E}}. 1055#' @param P Select edges via pairs of vertices. See \code{\link{E}}. 1056#' @param directed Whether to use edge directions for the \code{path} or 1057#' \code{P} arguments. 1058#' @name igraph-es-attributes 1059#' @export 1060 1061`E<-` <- function(x, path=NULL, P=NULL, directed=NULL, value) { 1062 if (!is_igraph(x)) { 1063 stop("Not a graph object") 1064 } 1065 if (! "name" %in% names(attributes(value)) || 1066 ! "value" %in% names(attributes(value))) { 1067 stop("invalid indexing") 1068 } 1069 i_set_edge_attr(x, attr(value, "name"), index=value, 1070 value=attr(value, "value"), check = FALSE) 1071} 1072 1073#' Show a vertex sequence on the screen 1074#' 1075#' For long vertex sequences, the printing is truncated to fit to the 1076#' screen. Use \code{print} explicitly and the \code{full} argument to 1077#' see the full sequence. 1078#' 1079#' Vertex sequence created with the double bracket operator are 1080#' printed differently, together with all attributes of the vertices 1081#' in the sequence, as a table. 1082#' 1083#' @param x A vertex sequence. 1084#' @param full Whether to show the full sequence, or truncate the output 1085#' to the screen size. 1086#' @param ... These arguments are currently ignored. 1087#' @return The vertex sequence, invisibly. 1088#' 1089#' @method print igraph.vs 1090#' @export 1091#' @family vertex and edge sequences 1092#' @examples 1093#' # Unnamed graphs 1094#' g <- make_ring(10) 1095#' V(g) 1096#' 1097#' # Named graphs 1098#' g2 <- make_ring(10) %>% 1099#' set_vertex_attr("name", value = LETTERS[1:10]) 1100#' V(g2) 1101#' 1102#' # All vertices in the sequence 1103#' g3 <- make_ring(1000) 1104#' V(g3) 1105#' print(V(g3), full = TRUE) 1106#' 1107#' # Metadata 1108#' g4 <- make_ring(10) %>% 1109#' set_vertex_attr("name", value = LETTERS[1:10]) %>% 1110#' set_vertex_attr("color", value = "red") 1111#' V(g4)[[]] 1112#' V(g4)[[2:5, 7:8]] 1113 1114print.igraph.vs <- function(x, full = igraph_opt("print.full"), ...) { 1115 1116 graph <- get_vs_graph(x) 1117 len <- length(x) 1118 id <- graph_id(x) 1119 1120 title <- "+ " %+% chr(len) %+% "/" %+% 1121 (if (is.null(graph)) "?" else chr(vcount(graph))) %+% 1122 (if (len == 1) " vertex" else " vertices") %+% 1123 (if (!is.null(names(x))) ", named" else "") %+% 1124 (if (!is.na(id)) paste(", from", substr(id, 1, 7)) else "") %+% 1125 (if (is.null(graph)) " (deleted)" else "") %+% 1126 ":\n" 1127 cat(title) 1128 1129 if (!is.null(attr(x, "single")) && attr(x, "single") && 1130 !is.null(graph) && length(vertex_attr_names(graph) > 0)) { 1131 ## Double bracket 1132 va <- vertex_attr(graph) 1133 if (all(sapply(va, is.atomic))) { 1134 print(as.data.frame(va, stringsAsFactors = 1135 FALSE)[as.vector(x),, drop = FALSE]) 1136 } else { 1137 print(lapply(va, "[", as.vector(x))) 1138 } 1139 1140 } else { 1141 ## Single bracket 1142 1143 x2 <- if (!is.null(names(x))) names(x) else as.vector(x) 1144 if (length(x2)) { 1145 if (is.logical(full) && full) { 1146 print(x2, quote = FALSE) 1147 } else { 1148 head_print(x2, omitted_footer = "+ ... omitted several vertices\n", 1149 quote = FALSE, max_lines = igraph_opt("auto.print.lines")) 1150 } 1151 } 1152 } 1153 1154 invisible(x) 1155} 1156 1157#' Print an edge sequence to the screen 1158#' 1159#' For long edge sequences, the printing is truncated to fit to the 1160#' screen. Use \code{print} explicitly and the code{full} argument to 1161#' see the full sequence. 1162#' 1163#' Edge sequences created with the double bracket operator are printed 1164#' differently, together with all attributes of the edges in the sequence, 1165#' as a table. 1166#' 1167#' @param x An edge sequence. 1168#' @param full Whether to show the full sequence, or truncate the output 1169#' to the screen size. 1170#' @param ... Currently ignored. 1171#' @return The edge sequence, invisibly. 1172#' 1173#' @method print igraph.es 1174#' @export 1175#' @family vertex and edge sequences 1176#' @examples 1177#' # Unnamed graphs 1178#' g <- make_ring(10) 1179#' E(g) 1180#' 1181#' # Named graphs 1182#' g2 <- make_ring(10) %>% 1183#' set_vertex_attr("name", value = LETTERS[1:10]) 1184#' E(g2) 1185#' 1186#' # All edges in a long sequence 1187#' g3 <- make_ring(200) 1188#' E(g3) 1189#' E(g3) %>% print(full = TRUE) 1190#' 1191#' # Metadata 1192#' g4 <- make_ring(10) %>% 1193#' set_vertex_attr("name", value = LETTERS[1:10]) %>% 1194#' set_edge_attr("weight", value = 1:10) %>% 1195#' set_edge_attr("color", value = "green") 1196#' E(g4) 1197#' E(g4)[[]] 1198#' E(g4)[[1:5]] 1199 1200print.igraph.es <- function(x, full = igraph_opt("print.full"), ...) { 1201 graph <- get_es_graph(x) 1202 ml <- if (identical(full, TRUE)) NULL else igraph_opt("auto.print.lines") 1203 .print.edges.compressed(x = graph, edges = x, max.lines = ml, names = TRUE, 1204 num = TRUE) 1205 invisible(x) 1206} 1207 1208# these are internal 1209 1210as.igraph.vs <- function(graph, v, na.ok=FALSE) { 1211 if (inherits(v, "igraph.vs") && !is.null(graph) && 1212 !warn_version(graph)) { 1213 if (get_graph_id(graph) != get_vs_graph_id(v)) { 1214 stop("Cannot use a vertex sequence from another graph.") 1215 } 1216 } 1217 if (is.character(v) && "name" %in% vertex_attr_names(graph)) { 1218 v <- as.numeric(match(v, V(graph)$name)) 1219 if (!na.ok && any(is.na(v))) { 1220 stop("Invalid vertex names") 1221 } 1222 v 1223 } else { 1224 if (is.logical(v)) { 1225 res <- as.vector(V(graph))[v] 1226 } else if (is.numeric(v) && any(v<0)){ 1227 res <- as.vector(V(graph))[v] 1228 } else { 1229 res <- as.numeric(v) 1230 } 1231 if (!na.ok && any(is.na(res))) { 1232 stop("Invalid vertex name(s)") 1233 } 1234 res 1235 } 1236} 1237 1238as.igraph.es <- function(graph, e) { 1239 if (inherits(e, "igraph.es") && !is.null(graph) 1240 && !warn_version(graph)) { 1241 if (get_graph_id(graph) != get_es_graph_id(e)) { 1242 stop("Cannot use an edge sequence from another graph.") 1243 } 1244 } 1245 if (is.character(e)) { 1246 Pairs <- grep("|", e, fixed=TRUE) 1247 Names <- if (length(Pairs)==0) seq_along(e) else -Pairs 1248 res <- numeric(length(e)) 1249 1250 ## Based on vertex ids/names 1251 if (length(Pairs)!=0) { 1252 vv <- strsplit(e[Pairs], "|", fixed=TRUE) 1253 vl <- sapply(vv, length) 1254 if (any(vl != 2)) { 1255 stop("Invalid edge name: ", e[Pairs][vl!=2][1]) 1256 } 1257 vp <- unlist(vv) 1258 if (! "name" %in% vertex_attr_names(graph)) { 1259 vp <- as.numeric(vp) 1260 } 1261 res[Pairs] <- get.edge.ids(graph, vp) 1262 } 1263 1264 ## Based on edge ids/names 1265 if (length(Names) != 0) { 1266 if ("name" %in% edge_attr_names(graph)) { 1267 res[Names] <- as.numeric(match(e[Names], E(graph)$name)) 1268 } else { 1269 res[Names] <- as.numeric(e[Names]) 1270 } 1271 } 1272 1273 } else { 1274 res <- as.numeric(e) 1275 } 1276 if (any(is.na(res))) { 1277 stop("Invalid edge names") 1278 } 1279 res 1280} 1281 1282 1283is_igraph_vs <- function(x) { 1284 inherits(x, "igraph.vs") 1285} 1286 1287 1288is_igraph_es <- function(x) { 1289 inherits(x, "igraph.es") 1290} 1291 1292 1293parse_op_args <- function(..., what, is_fun, as_fun, check_graph = TRUE) { 1294 1295 args <- list(...) 1296 1297 if (any(! sapply(args, is_fun))) stop("Not ", what, " sequence") 1298 1299 ## get the ids of all graphs 1300 graph_id <- sapply(args, get_vs_graph_id) %>% 1301 unique() 1302 1303 if (length(graph_id) != 1) { 1304 warning("Combining vertex/edge sequences from different graphs.\n", 1305 "This will not work in future igraph versions") 1306 } 1307 1308 graphs <- args %>% 1309 lapply(get_vs_graph) %>% 1310 drop_null() 1311 1312 addresses <- graphs %>% 1313 sapply(function(x) x %&&% address(x)) %>% 1314 unique() 1315 1316 if (check_graph && length(addresses) >= 2) { 1317 warning("Combining vertex/edge sequences from different graphs.\n", 1318 "This will not work in future igraph versions") 1319 } 1320 1321 graph <- if (length(graphs)) graphs[[1]] else NULL 1322 1323 args <- lapply(args, unclass) 1324 1325 list(graph = graph, args = args, id = graph_id) 1326} 1327 1328 1329parse_vs_op_args <- function(...) { 1330 parse_op_args(..., what = "a vertex", is_fun = is_igraph_vs, 1331 as_fun = as.igraph.vs) 1332} 1333 1334 1335parse_es_op_args <- function(...) { 1336 parse_op_args(..., what = "an edge", is_fun = is_igraph_es, 1337 as_fun = as.igraph.es) 1338} 1339 1340 1341create_op_result <- function(parsed, result, class, args) { 1342 result <- add_vses_graph_ref(result, parsed$graph) 1343 class(result) <- class 1344 ## c() drops names for zero length vectors. Why??? 1345 if (! length(result) && 1346 any(sapply(args, function(x) !is.null(names(x))))) { 1347 names(result) <- character() 1348 } 1349 result 1350} 1351 1352 1353#' Remove duplicate vertices from a vertex sequence 1354#' 1355#' @param x A vertex sequence. 1356#' @param incomparables a vector of values that cannot be compared. 1357#' Passed to base function \code{duplicated}. See details there. 1358#' @param ... Passed to base function \code{duplicated()}. 1359#' @return A vertex sequence with the duplicate vertices removed. 1360#' 1361#' @method unique igraph.vs 1362#' @family vertex and edge sequence operations 1363#' @export 1364#' @examples 1365#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1366#' V(g)[1, 1:5, 1:10, 5:10] 1367#' V(g)[1, 1:5, 1:10, 5:10] %>% unique() 1368 1369unique.igraph.vs <- function(x, incomparables = FALSE, ...) { 1370 x[!duplicated(x, incomparables = incomparables, ...)] 1371} 1372 1373 1374#' Remove duplicate edges from an edge sequence 1375#' 1376#' @param x An edge sequence. 1377#' @param incomparables a vector of values that cannot be compared. 1378#' Passed to base function \code{duplicated}. See details there. 1379#' @param ... Passed to base function \code{duplicated()}. 1380#' @return An edge sequence with the duplicate vertices removed. 1381#' 1382#' @method unique igraph.es 1383#' @family vertex and edge sequence operations 1384#' @export 1385#' @examples 1386#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1387#' E(g)[1, 1:5, 1:10, 5:10] 1388#' E(g)[1, 1:5, 1:10, 5:10] %>% unique() 1389 1390unique.igraph.es <- function(x, incomparables = FALSE, ...) { 1391 x[!duplicated(x, incomparables = incomparables, ...)] 1392} 1393 1394 1395#' Concatenate vertex sequences 1396#' 1397#' @param ... The vertex sequences to concatenate. They must 1398#' refer to the same graph. 1399#' @param recursive Ignored, included for S3 compatibility with 1400#' the base \code{c} function. 1401#' @return A vertex sequence, the input sequences concatenated. 1402#' 1403#' @method c igraph.vs 1404#' @family vertex and edge sequence operations 1405#' @export 1406#' @examples 1407#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1408#' c(V(g)[1], V(g)['A'], V(g)[1:4]) 1409 1410c.igraph.vs <- function(..., recursive = FALSE) { 1411 parsed <- parse_vs_op_args(...) 1412 res <- do_call(c, .args = parsed$args) 1413 create_op_result(parsed, res, "igraph.vs", list(...)) 1414} 1415 1416 1417#' Concatenate edge sequences 1418#' 1419#' @param ... The edge sequences to concatenate. They must 1420#' all refer to the same graph. 1421#' @param recursive Ignored, included for S3 compatibility with the 1422#' base \code{c} function. 1423#' @return An edge sequence, the input sequences concatenated. 1424#' 1425#' @method c igraph.es 1426#' @family vertex and edge sequence operations 1427#' @export 1428#' @examples 1429#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1430#' c(E(g)[1], E(g)['A|B'], E(g)[1:4]) 1431 1432c.igraph.es <- function(..., recursive = FALSE) { 1433 parsed <- parse_es_op_args(...) 1434 res <- do_call(c, .args = parsed$args) 1435 res <- create_op_result(parsed, res, "igraph.es", list(...)) 1436 attr(res, "vnames") <- do_call(c, .args = lapply(list(...), attr, "vnames")) 1437 res 1438} 1439 1440 1441#' Union of vertex sequences 1442#' 1443#' @details 1444#' They must belong to the same graph. Note that this function has 1445#' \sQuote{set} semantics and the multiplicity of vertices is lost in the 1446#' result. (This is to match the behavior of the based \code{unique} 1447#' function.) 1448#' 1449#' @param ... The vertex sequences to take the union of. 1450#' @return A vertex sequence that contains all vertices in the given 1451#' sequences, exactly once. 1452#' 1453#' @method union igraph.vs 1454#' @family vertex and edge sequence operations 1455#' @export 1456#' @examples 1457#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1458#' union(V(g)[1:6], V(g)[5:10]) 1459 1460union.igraph.vs <- function(...) { 1461 unique(c(...)) 1462} 1463 1464 1465#' Union of edge sequences 1466#' 1467#' @details 1468#' They must belong to the same graph. Note that this function has 1469#' \sQuote{set} semantics and the multiplicity of edges is lost in the 1470#' result. (This is to match the behavior of the based \code{unique} 1471#' function.) 1472#' 1473#' @param ... The edge sequences to take the union of. 1474#' @return An edge sequence that contains all edges in the given 1475#' sequences, exactly once. 1476#' 1477#' @method union igraph.es 1478#' @family vertex and edge sequence operations 1479#' @export 1480#' @examples 1481#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1482#' union(E(g)[1:6], E(g)[5:9], E(g)['A|J']) 1483 1484union.igraph.es <- union.igraph.vs 1485 1486 1487#' Intersection of vertex sequences 1488#' 1489#' @details 1490#' They must belong to the same graph. Note that this function has 1491#' \sQuote{set} semantics and the multiplicity of vertices is lost in the 1492#' result. 1493#' 1494#' @param ... The vertex sequences to take the intersection of. 1495#' @return A vertex sequence that contains vertices that appear in all 1496#' given sequences, each vertex exactly once. 1497#' 1498#' @method intersection igraph.vs 1499#' @family vertex and edge sequence operations 1500#' @export 1501#' @examples 1502#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1503#' intersection(E(g)[1:6], E(g)[5:9]) 1504 1505intersection.igraph.vs <- function(...) { 1506 ifun <- function(x, y) { 1507 unique(y[match(as.vector(x), y, 0L)]) 1508 } 1509 Reduce(ifun, list(...)) 1510} 1511 1512 1513#' Intersection of edge sequences 1514#' 1515#' @details 1516#' They must belong to the same graph. Note that this function has 1517#' \sQuote{set} semantics and the multiplicity of edges is lost in the 1518#' result. 1519#' 1520#' @param ... The edge sequences to take the intersection of. 1521#' @return An edge sequence that contains edges that appear in all 1522#' given sequences, each edge exactly once. 1523#' 1524#' @method intersection igraph.es 1525#' @family vertex and edge sequence operations 1526#' @export 1527#' @examples 1528#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1529#' intersection(E(g)[1:6], E(g)[5:9]) 1530 1531intersection.igraph.es <- intersection.igraph.vs 1532 1533 1534#' Difference of vertex sequences 1535#' 1536#' @details 1537#' They must belong to the same graph. Note that this function has 1538#' \sQuote{set} semantics and the multiplicity of vertices is lost in the 1539#' result. 1540#' 1541#' @param big The \sQuote{big} vertex sequence. 1542#' @param small The \sQuote{small} vertex sequence. 1543#' @param ... Ignored, included for S3 signature compatibility. 1544#' @return A vertex sequence that contains only vertices that are part of 1545#' \code{big}, but not part of \code{small}. 1546#' 1547#' @method difference igraph.vs 1548#' @family vertex and edge sequence operations 1549#' @export 1550#' @examples 1551#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1552#' difference(V(g), V(g)[6:10]) 1553 1554difference.igraph.vs <- function(big, small, ...) { 1555 if (!length(big)) { 1556 big 1557 } else { 1558 big[ match(big, small, 0L) == 0L ] 1559 } 1560} 1561 1562 1563#' Difference of edge sequences 1564#' 1565#' @details 1566#' They must belong to the same graph. Note that this function has 1567#' \sQuote{set} semantics and the multiplicity of edges is lost in the 1568#' result. 1569#' 1570#' @param big The \sQuote{big} edge sequence. 1571#' @param small The \sQuote{small} edge sequence. 1572#' @param ... Ignored, included for S3 signature compatibility. 1573#' @return An edge sequence that contains only edges that are part of 1574#' \code{big}, but not part of \code{small}. 1575#' 1576#' @method difference igraph.es 1577#' @family vertex and edge sequence operations 1578#' @export 1579#' @examples 1580#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1581#' difference(V(g), V(g)[6:10]) 1582 1583difference.igraph.es <- difference.igraph.vs 1584 1585 1586#' Reverse the order in a vertex sequence 1587#' 1588#' @param x The vertex sequence to reverse. 1589#' @return The reversed vertex sequence. 1590#' 1591#' @method rev igraph.vs 1592#' @family vertex and edge sequence operations 1593#' @export 1594#' @examples 1595#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1596#' V(g) %>% rev() 1597 1598rev.igraph.vs <- function(x) { 1599 x[rev(seq_along(x))] 1600} 1601 1602 1603#' Reverse the order in an edge sequence 1604#' 1605#' @param x The edge sequence to reverse. 1606#' @return The reversed edge sequence. 1607#' 1608#' @method rev igraph.es 1609#' @family vertex and edge sequence operations 1610#' @export 1611#' @examples 1612#' g <- make_(ring(10), with_vertex_(name = LETTERS[1:10])) 1613#' E(g) 1614#' E(g) %>% rev() 1615 1616rev.igraph.es <- rev.igraph.vs 1617 1618#' Convert a vertex or edge sequence to an ordinary vector 1619#' 1620#' @details 1621#' For graphs without names, a numeric vector is returned, containing the 1622#' internal numeric vertex or edge ids. 1623#' 1624#' For graphs with names, and vertex sequences, the vertex names are 1625#' returned in a character vector. 1626#' 1627#' For graphs with names and edge sequences, a character vector is 1628#' returned, with the \sQuote{bar} notation: \code{a|b} means an edge from 1629#' vertex \code{a} to vertex \code{b}. 1630#' 1631#' @param seq The vertex or edge sequence. 1632#' @return A character or numeric vector, see details below. 1633#' 1634#' @export 1635#' @examples 1636#' g <- make_ring(10) 1637#' as_ids(V(g)) 1638#' as_ids(E(g)) 1639#' 1640#' V(g)$name <- letters[1:10] 1641#' as_ids(V(g)) 1642#' as_ids(E(g)) 1643 1644as_ids <- function(seq) 1645 UseMethod("as_ids") 1646 1647#' @method as_ids igraph.vs 1648#' @rdname as_ids 1649#' @export 1650 1651as_ids.igraph.vs <- function(seq) { 1652 names(seq) %||% as.vector(seq) 1653} 1654 1655#' @method as_ids igraph.es 1656#' @rdname as_ids 1657#' @export 1658 1659as_ids.igraph.es <- function(seq) { 1660 attr(seq, "vnames") %||% as.vector(seq) 1661} 1662