network : A Package for Managing Relational Data in R

Eﬀective memory structures for relational data within R must be capable of representing a wide range of data while keeping overhead to a minimum. The network package provides an class which may be used for encoding complex relational structures composed a vertex set together with any combination of undirected/directed, valued/unvalued, dyadic/hyper, and single/multiple edges; storage requirements are on the order of the number of edges involved. Some simple constructor, interface, and visualization functions are provided, as well as a set of operators to facilitate employment by end users. The package also supports a C -language API, which allows developers to work directly with network objects within backend code.

PLEASE NOTE: This document has been modified from the original paper to form a package vignette.It has been compiled with the version of the network package it is bundled with, and has been partially updated to reflect some changes in the package.The original paper is: network: A Package for Managing Relational Data in R. Journal of Statistical Software 24:2, 2008.http://www.jstatsoft.org/v24/i02/paper

Background and introduction
In early 2002, the author and several other members of what would ultimately become the statnet project (Handcock, et al. 2003) came to the conclusion that the simple, matrix-based approach to representation of relational data utilized by early versions of packages such as sna were inadequate for the next generation of relational analysis tools in R. Rather, what was required was a customized class structure to support relational data.This class structure would be used for all statnet packages, thus insuring interoperability; ideally, it would also be possible to port this structure to other languages, thereby further enhancing compatibility.
The requirements which were posed for a network data class were as follows, in descending order of priority: 1.The class had to be sufficiently general to encode all major types of network data collected presently or in the foreseeable future; 2. Class storage needed to be of sufficient efficiency to permit representation of large networks (in particular, storage which was sub-quadratic in graph order for sparse networks); and

A very quick note on notation
Throughout this paper we will use "graph" or "network" (G) generically to refer to any relational structure on a given vertex set (V ), and "edge" to refer to a generalized edge (i.e., an ordered pair (T, H) where T is the "tail set" of the edge and H is the corresponding "head set," and where T, H ⊆ V (G)).The cardinality of the vertex set we denote |V (G)| = n, and the cardinality of the corresponding edge set we likewise denote |E(G)| = m.When discussing storage/computational complexity we will often use a loose order notation, where O f (x) is intended to indicate that the quantity in question grows more slowly than f (x) as x → ∞.A general familiarity with the R statistical computing system (and related syntax/terminology) is assumed.Those unfamiliar with R may wish to peruse a text such as those of Venables & Ripley (2000, 2002) or Chambers (1998).

The network class
The network class is a (reasonably) simple object structure designed to store a single relation on a vertex set of arbitrary size.The relation stored by a network class object is based on a generalized edge model; thus, edges may be directed, arbitrarily valued (with multiple values per edge), multiplex (i.e., multiple edges per directed dyad), hyper (i.e., multiple head/tail vertices per edge), etc. Storage requirements for the network class are on the order of the number of nodes plus the total number of edges (which is substantially sub-n 2 for sparse graphs), and retrieval of edge values has a time complexity which is no worse than O(n). 1 For example, a network with 100,000 vertices and 100,000 edges currently consumes approximately 74MB of RAM (R 2.6.1),versus approximately 40GB for a full sociomatrix (a savings of approximately 99.8%).When dealing with extremely large, sparse graphs it therefore follows that network objects are substantially more efficient than simpler representations such as adjacency matrices.The class also provides for the storage of arbitrary metadata at the edge, vertex, and network level.Thus, network objects may be preferred to matrix representations for reasons of generality, performance, or integrative capability; while alternative means exist of obtaining these goals separately, network provides a single toolkit which is designed to be effective across a wide range of applications.
In this section, we provide a basic introduction to the network class, from a user's point of view.We describe the conditions which are necessary for network to be employed, and the properties of network objects (and their components).This serves as background for a discussion of the use of network methods in practical settings, which is given in the section which follows.

Identification of vertices and edges
For purposes of storage, we presume that each vertex and each edge can be uniquely identified.
(For partially labeled or unlabeled graphs, observe that this internal labeling is essentially arbitrary.See Butts & Carley 2005, for a discussion.)Vertices are labeled by positive integers in the order of entry, with edges likewise; it is further assumed that this is maintained for vertices (e.g., removing a vertex requires relabeling) but not for edges.(This last has to do with how edges are handled internally, but has the desirable side effect of making edge changes less expensive.)Vertices and edges are always stored by label.In the text that follows, any reference to a vertex or edge "ID" refers to these labeling numbers, and not to any other (external) identification that a vertex or edge may have.

Basic class structure
Functionally, a network object can be thought as a collection of vertices and edges, together with metadata regarding those vertices and edges (as well as the network itself).As noted above, each vertex is assumed to be identifiable, and the number of vertices is fixed.Here, we discuss the way in which edges are defined within network, as well as the manner in which associated metadata is stored.

Edge structure
Edges within a network object consist of three essential components.First, each edge contains two vectors of vertex IDs, known respectively as the head and tail lists of the edge.In addition to these lists, each edge also contains a list of attribute information.This is discussed in more detail below.The content and interpretation of the head and tail lists are dependent on the type of network in which they reside.In a directed network, an edge connects the elements of its tail list with those of its head list, but not vice versa: i is adjacent to j iff there exists some edge, e = (T, H), such that i ∈ T, j ∈ H.In an undirected network, by contrast, the head and tail sets of an edge are regarded as exchangeable.Thus, i is adjacent to j in an undirected network iff there exists an edge such that i ∈ T, j ∈ H or i ∈ H, j ∈ T .network methods which deal with adjacency and incidence make this distinction transparently, based on the network object's directedness attribute (see below).
Note that in the familiar case of dyadic networks (the focus of packages such as sna (Butts 2007)), the head and tail lists of any given edge must have exactly one element.This need not be true in general, however.An edge with a head or tail list containing more than one element is said to be hypergraphic, reflecting a one-to-many, many-to-one, or many-to-many relationship.Hyperedges are permitted natively within network, although some methods may not support them -a corresponding network attribute is used by network methods to determine whether these edges are present, as explained below.Finally, another fundamental distinction is made between edges in which H and T are disjoint, versus those in which these endpoint lists have one or more elements in common.Edges of the latter type are said to be loop-like, generalizing the familiar notion of "loop" (self-tie) from the theory of dyadic graphs.Loop-like edges allow vertices to relate to themselves, and are disallowed in many applications.Applicable methods are expected to interpret such edges intelligently, where present.

network attributes
As we have already seen, each network object contains a range of metadata in addition to relational information.This metadata -in the form of attributes -is divided into information stored at the network, vertex, and edge levels.In all three cases, attributes are stored in lists, and are expected to be named.While there is no limit to the user-defined attributes which may be stored in this manner, certain attributes are required of all network objects.At the network level, such attributes describe general properties of the network as a whole; specifically, they may be enumerated as follows: bipartite This is a logical or numeric attribute, which is used to indicate the presence of an intrinsic bipartition in the network object.Formally, a bipartition is a partition of a network's vertices into two classes, such that no vertex in either class is adjacent to any vertex in the same class.While such partitions occur naturally, they may also be specifically enforced by the nature of the data in question.(This is the case, for instance, with two-mode networks (Wasserman & Faust 1994), in which edges represent connections between two distinct classes of entities.)In order to allow for bipartite networks with a partition size of zero, non-bipartite networks are marked as bipartite=FALSE.Where the value of bipartite is numeric, network methods will automatically assume that vertices with IDs less than or equal to bipartite belong to one such class, with those with IDs greater than bipartite belonging to the other.This information may be used in selecting default modes for data display, calculating numbers of possible edges, etc.When bipartite == FALSE or NULL, by contrast, no such bipartition is assumed.
Because of the dual logical/numeric nature of the attribute, it is safest to check it using the is.bipartite method.It should be emphasized that bipartite is intended to reflect bipartitions which are required ex ante, rather than those which happen to arise empirically.There is also no performance advantage to the use of bipartite, since network only stores edges which are defined; it can make data processing more convenient, however, when working with intrinsically bipartite structures.
directed This is a logical attribute, which should be set to TRUE iff edges are to be interpreted as directed.As explained earlier, network methods will regard edge endpoint lists as exchangeable when directed is FALSE, allowing for automatic handling of both directed and undirected networks.For obvious reasons, misspecification of this attribute may lead to surprising results; it is generally set when a network object is created, and considered fixed thereafter.
hyper This attribute is a logical variable which is set to TRUE iff the network is allowed to contain hyperedges.Since the vast majority of network data is dyadic, this attribute defaults to FALSE for must construction methods.The setting of hyper to TRUE has potentially serious implications for edge retrieval, and so methods should not activate this option unless hypergraphic edges are explicitly to be permitted.
loops As noted, loop-like edges are frequently undefined in practical settings.The loops attribute is a logical which should be set to TRUE iff such edges are permitted within the network.
multiple In most settings, an edge is uniquely defined by its head and tail lists.In other cases, however, one must represent data in which multiple edges are permitted between the same endpoints.("Same" here includes the effect of directedness; an edge from set H to set T is not the same as an edge from set T to set H, unless the network is undirected.)The multiple attribute is a logical variable which is set to TRUE iff such multiplex edges are permitted within the network.Where multiple is FALSE, network methods will assume all edges to be unique -like directed, the possibility of multiplex edges thus can substantially impact both behavior and performance.For this reason, multiple is generally set to FALSE by default, and should not be set to TRUE unless it is specifically necessary to permit multiple edges between the same endpoint sets.
n Finally, n is a numeric attribute containing the number of elements in the vertex set.Applicable methods are expected to adjust this attribute up or down, should vertices be added or deleted from the network.Note that as of network v1.8, networks of size zero are permitted.
While these attributes are clearly reserved, any number of others may be added.Attributes specifically pertaining to edges and/or vertices can be stored at the network level, but this is generally non-optimal -such attributes would have to be manually updated to reflect edge or vertex changes, and would require the creation of custom access methods.The preferred approach is to store such information directly at the edge or vertex level, as we discuss below.

Vertex attributes
As with the network as a whole, it is often useful to be able to supply attribute data for individual vertices (e.g., names, attributes, etc.).Each vertex thus has a list of named attributes, which can be used to store arbitrary information on a per-vertex basis; there is no restriction on the type of information which may be stored in this fashion, nor are all vertices constrained to carry information regarding the same attributes.Each vertex does carry two special attributes, however, which are assumed to be available to all class methods.These are vertex.names,which must be a character containing the name of the vertex, and the logical attribute na.Where TRUE, na indicates that the associated vertex is unobserved; this is useful in cases for which individual entities are known to belong to a given network, but where data regarding those entities is unavailable.By default, na is set to FALSE and vertex.names is set equal to the corresponding vertex ID.

Edge attributes
Just as vertices can carry attributes, so too can edges.Each edge is endowed with a list of named attributes, which can be used to carry arbitrary information (e.g., tie strength, onset and termination times, etc.).As with vertex attributes, any information type may be employed and there is no requirement that all edges carry the same attributes.The one attribute required to be carried by each edge is na, a logical which (like the vertex case) is used to indicate the missingness of a given edge.Many network methods provide the option of filtering out missing edges when retrieving information, and/or returning the associated information (e.g., adjacency) as missing.

Using the network class
In addition to the class itself, network provides a range of tools for creating, manipulating, and visualizing network objects.2Here, we provide an overview of some of these tools, with a focus on the basic tasks most frequently encountered by end users.Additional information on these functions is also provided within the package manual.For the examples below, we begin by loading the network package into memory; we also set the random seed, to ensure that examples using random data match the output shown here.Within R, this may be accomplished via the following: > library(network) > set.seed(1702) Throughout, we will represent R code in the above format.Readers may wish to try the demonstrations listed here for themselves, to get a better feel for how the package operates.

Importing data
It almost goes without saying that an important aspect of network functionality is the ability to import data from external sources.network includes functionality for the importation of Pajek project files (Batagelj 2007), a popular and versatile network data format, via the read.pajroutine.Other formats supported by sna can be used as well, by importing to adjacency matrix form (using the relevant sna routines) and then coercing the result into a network object as described below.The foreign package can be used to import adjacency, edgelist, or incidence matrices from other computing environments in much the same way.Future package versions may include support for converting to and from other related classes, e.g., those of RBGL (Carey, et al. 2007) and Rgraphviz (Gentry, et al. 2007).
In addition to these methods, network objects can be loaded into R using native tools such as load (for saved objects) or data (for packaged data sets).With respect to the latter, network contains two sample data sets: flo, John Padgett's Florentine wedding data (from Wasserman & Faust 1994); and emon, a set of interorganizational networks from search and rescue operations collected by Drabek, et al. (1981).flo consists of a single adjacency matrix, and is useful for illustrating the process of converting data from adjacency matrix to network form.emon, on the other hand, consists of a list of seven network objects with vertex and edge metadata.emon is thus especially useful for illustrating the use of network objects for rich data storage (in addition to being an interesting data set in its own right).Loading these data sets is as simple as invoking the data command, like so: Further information on each of these data sets is given in the network manual.We shall also use these data sets as illustrative examples at various points within this paper.

Creating and viewing network objects
While importation is sometimes possible, in other cases we must create our own network objects.network supports two basic approaches to this task: create the object from scratch, or build it from existing relational data via coercion.Both methods are useful, and we illustrate each here.
In the most minimal case, we begin by creating an empty network to which edges may be added.This task is performed by the network.initializeroutine, which serves as a constructor for the network class.network.initializetakes the order of the desired graph (i.e., n) as a required argument, and the required network attributes discussed in Section 2.2.2 may be passed as well.In the event that these are unspecified, it is assumed that a simple digraph (directed, no loops, hyperedges, multiplexity, or bipartitions) is desired.For example, one may create and print an empty digraph like so: No edge attributes network has default print and summary methods, as well as low-level operators for assignment and related operations.These do not show much in the above case, since the network in question caries little information.To create a network along with a specified set of edges, the preferred high-level constructor is the eponymous network.Like network.initialize,this function returns a newly allocated network object having specified properties.Unlike the former, however, network may be called with adjacency and/or attribute information.Adjacency information may be passed by using a full or bipartite adjacency matrix, incidence matrix, or edgelist as the function's first argument.These input types are defined as follows: Adjacency matrix: This must consist of a square matrix or two-dimensional array, whose i, jth cell contains the value of the edge from i to j; as such, adjacency matrices may only be used to specify dyadic networks.By default, edges are assumed to exist for all non-zero matrix values, and are constructed accordingly.Edge values may be retained by passing ignore.eval= FALSE, as described in the manual page for the network.adjacencyconstructor.The matrix.type for an adjacency matrix is "adjacency".
Bipartite adjacency matrix: This must consist of a rectangular matrix or two-dimensional array whose row and column elements reflect vertices belonging to the lower and upper sets of a bipartition (respectively).Otherwise, the matrix is interpreted as per a standard adjacency matrix.(Thus, a bipartite adjacency matrix is simply the upper off-diagonal block of the full adjacency matrix for a bipartite graph, where vertices have been ordered by partition membership.See also Doreian, et al. (2005).)The matrix.type for a bipartite adjacency matrix is "bipartite".
Incidence matrix: This must consist of a rectangular matrix or two-dimensional array whose row elements represent vertices, and whose column elements represent edges.A non-zero value is placed in the i, jth cell if vertex i is an endpoint of edge j.In the directed case, negative values signify membership in the tail set of the corresponding edge, while positive values signify membership in the edge's head set.Unlike adjacency matrices, incidence matrices can thus be used to describe hypergraphic edges (directed or otherwise).Note, however, that an undirected hypergraph composed of two-endpoint edges is not the same as a simple graph, since the edges of the former are necessarily loop-like.When loops, hyper, and directed are all FALSE, therefore, the two positive row-elements of an incidence matrix for each column are taken to signify the head and tail elements of a dyadic edge.(This is without loss of generality, since such an incidence matrix would otherwise be inadmissible.)When specifying that an incidence matrix is to be used, matrix.typeshould be set to "incidence".
Edge list: This must consist of a rectangular matrix or two-dimensional array whose row elements represent edges.The i, 1st cell of this structure is taken to be the ID of the tail vertex for the edge with ID i, with the i, 2st cell containing the ID of the edge's head vertex.(Only dyadic networks may be input in this fashion.)Additional columns, if present, are taken to contain edge attribute values.The matrix.type for an edge list is "edgelist".
As one might suspect, the network function actually operates by first calling network.initialize to create the required object, and then calling an appropriate edge set constructor based on the input type.This fairly modular design allows for the eventual inclusion of a wider range of input formats (although the above covers the formats currently in widest use within the social network community).Although network attempts to infer the matrix type from context, is wise to fix the function's behavior via the matrix.typeargument when passing information which is not in the default, adjacency matrix form.As a simple example of the network constructor in action, consider the following:  1 2 3 4 5  1 1 0 1 0 1  2 1 0 0 1 0  3 1 0 1 1 Here, we have generated a random adjacency matrix (permitting diagonal elements) and used this to construct a digraph (with loops) in network object form.Since we employed an adjacency matrix, there was no need to set the matrix type explicitly; had we failed to set loops = TRUE, however, the diagonal entries of nmat would have been ignored.The above example also demonstrates the use of an important form of operator overloading which can be used with dyadic network objects: specifically, dyadic network objects respond to the use of the subset and subset assignment operators [ and [<-as if they were conventional adjacency matrices.Thus, in the above case, net[,] returns net's adjacency matrix (a fact we verify by comparing it with nmat).This is an extremely useful "shorthand" which can be used to simplify otherwise cumbersome network operations, especially on small networks.
The use of network function to create objects from input matrices has a functional parallel in the use of coercion methods to transform other objects into network form.These operate in the same manner as the above, but follow the standard R syntax for coercion, e.g.: By default, as.network assumes that square input matrices should be treated as adjacency matrices, and that diagonal entries should be ignored; here we have overridden the latter behavior by invoking the additional argument loops = TRUE.Matrix-based input can also be given in edgelist or incidence matrix form, as selected by the matrix.typeargument.This and other options are described in greater detail within the package documentation.
The above methods can be used in conjunction with data, load, or read functions to convert imported relational data into network form.For example, we may apply this to the Florentine data mentioned in the previous section:

No edge attributes
Although the network's adjacency structure is summarized here in edgelist form, it may be queried in other ways.For instance, the following example demonstrates three simple methods for examining the neighborhood of a particular vertex: [1] TRUE > is.adjacent(nflo, 9, 4) [1] FALSE As the example shows, overloading can be used to extract partial as well as complete adjacency information from a network object.A more cumbersome (but slightly faster) method is to use a direct call to is.adjacent, the general indicator method for network adjacency.Calling the indicator method avoids the call parsing required by the extraction operator, which is the source of the performance difference.In practice, however, the impact of call parsing is quite minimal, and users are unlikely to detect a difference between the two approaches.(Where such overhead is an issue, it will generally be more efficacious to conduct adjacency queries directly from the backend code; this will be discussed below, in the context of the C-language API.) In addition to adjacency, network supplies methods to query many basic properties of network objects.Although complex structural descriptives (e.g., centrality scores Wasserman & Faust 1994) are the province of other packages, network's built-in functionality is sufficient to determine the types of edges allowed within a network object and constraints such as enforced bipartitions, as well as essential quantities such as size (number of vertices), edge count, and density (the ratio of observed to potential edges

Coercing network objects to other forms
Just as one may often seek to coerce data from other forms into network object, so to does one sometimes need to coerce network objects into other data types.network currently supports several such coercion functions, all of which take network objects as input and produce matrices of one type or another.The class method for as.matrix performs this task, converting network objects to adjacency, incidence, or edgelist matrices as desired (adjacency being the default).Scalar-valued edge attributes, where present, may be used to set edge values using the appropriate functional arguments.Similar functionality is provided by as.sociomatrix and the extraction operator, although these are constrained to produce adjacency matrices.These equivalent approaches may be illustrated with application to the Florentine data as follows: [1] TRUE > all(as.matrix(nflo)==as.sociomatrix(nflo)) [1] TRUE > as.matrix(nflo,matrix.type="edgelist") "Albizzi" "Barbadori" "Bischeri" "Castellani" [6] "Ginori" "Guadagni" "Lamberteschi" "Medici" "Pazzi" [11] "Peruzzi" "Pucci" "Ridolfi" "Salviati" "Strozzi" [16] "Tornabuoni" Note that vertex names (per the vertex.namesattribute) are used by as.sociomatrix to set adjacency matrix row/column names where present.
The less-flexible as.sociomatrix function also plays an important role with respect to coercion in the sna package; the latter's as.sociomatrix.snadispatches to network's as.sociomatrix routine when network is loaded and a network object is given.The intent in both packages is to maintain an interoperable and uniform mechanism for guaranteeing adjacency matrix representations of input data (which are necessary for backward compatibility with some legacy functions).

Creating and modifying edges and vertices
In addition to coercion of data to network form, the network package contains many mechanisms for creating, modifying, and removing edges and vertices from network objects.The simplest means of manipulating edges for most users is the use of the overloaded extraction and assignment operators, which (as noted previously) simulate the effects of working with an adjacency matrix.Thus, a statement such as g[i,j] <-1 adds an edge between i and j (if one is not already present), g[i,j] <-0 removes an existing edge, and g[i,j] itself is a dichotomous indicator of adjacency.Subset selection and assignment otherwise works in the same fashion as for R matrices, including the role of logicals and element lists.(One minor exception involves the effects of assignment on undirected and/or loopless graphs: network will enforce symmetry and/or empty diagonal entries, and will ignore any assignments which are contrary to this.)The uses of assignment by overloading are hence legion, as partially illustrated by the following: #When will it all end??
The use of is.adjacent (and friends) to perform adjacency testing has been shown above.
While this is adequate for many purposes, it is sometimes necessary to examine an edge's contents in detail.As we have seen, each edge can be thought of as a list made up of a vector of tail vertex IDs, a vector of head vertex IDs, and a vector of attributes.The utility function get.edges retrieves edges in this form, returning them as lists with elements inl (tail), outl (head), and atl (attributes).get.edges allows for edges to be retrieved by endpoint(s), and is usable even on multiplex networks.Incoming or outgoing edges (or both) can be selected, as per the following example: The alter argument in the last case tells get.edges to supply only edges from vertex 1 to vertex 2. As with other applications of get.edges, this will return all applicable edges in the multiplex case.
Retrieving edges themselves is useful, but does not provide the edges' ID information -particularly in multiplex networks, such information is needed to delete or modify edges.For that purpose, we employ a parallel routine called get.edgeIDs: > #Retrieving edge IDs > get.edgeIDs(net,1) #Same as above, but gets ID numbers [1] 4 > get.edgeIDs(net,2,neighborhood="in") [1] 7 5 4 > get.edgeIDs(net,1,alter=2) [1] 4 By the same token, it is sometimes the vertex neighborhood (rather than edge neighborhood) which is of interest.The get.neighborhood function can be used in these cases to obtain vertex neighborhoods directly, without having to first query edges.(Since this operation is implemented in the underlying compiled code, it is considerably faster than an R-level front end would be.) Finally, we note that edge deletion can be performed either by assignment operators (as noted above) or by the delete.edgesfunction.delete.edgesremoves edges by ID, and hence is not primarily employed by end users.In conjunction with tools such as get.edgeIDs, however, it can be seen to be quite versatile.A typical example is as follows: #Should be TRUE [1] TRUE > delete.edges(net,get.edgeIDs(net,2,neighborhood="in"))#Remove all->2 > net [,] 1 2 3 5 <NA> <NA> 1 0 0 0 0 0 0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 5 0 0 0 0 0 0 <NA> 0 0 0 0 0 0 <NA> 0 0 0 0 0 0 Since it works by IDs, it should be noted that delete.edgescan be used to selectively remove edges from multiplex networks.The operator-based approach automatically removes any edges connecting the selected pair, and is not recommended for use with multiplex networks.

Working with attributes
A major advantage of network objects over simple matrix or list based data representations is the ability to store meta-information regarding vertices, edges, or the network as a whole.
For each such attribute type, network contains access functions to manage the creation, modification, and extraction of such information.Here, we briefly introduce the primary functions used for these tasks, by attribute type.

Network attributes
As indicated previously, network-level attributes are those attached to the network object as a whole.Such attributes are created via the set.network.attributefunction, which takes as arguments the object to which the attribute should be attached, the name of the attribute, and the value of the attribute in question.Network attributes may contain arbitrary data, as they are stored internally via generalized vectors (lists).To streamline the creation of such attributes, the network attribute operator, %n%, has also been provided.Assignment using the operator is performed via the syntax network %n% "attrname" <-value, as in the second portion of the example below (which assigns the first seven lowercase letters to an attribute called "hoo" in net).

Vertex attributes
Vertex attributes are manipulated in the same general manner as network attributes, with the caveat that each vertex can have its own attributes.There is no requirement that all vertices have the same attributes, or that all attributes of a given name contain the same data type; however, not all extraction methods work well in the latter case.Complete functionality for arbitrary vertex creation, listing, retrieval, and deletion is provided by the set.vertex.attribute, list.vertex.attributes,get.vertex.attribute,and delete.vertex.attributemethods (respectively).These allow attribute data to be passed in list form (permitting arbitrary contents) and to be assigned to specific vertices.While the generality of these functions is helpful, they are cumbersome to use for simple tasks such as assigning scalar or character values to each vertex (or retrieving the same).To facilitate such routine tasks, network provides a vertex attribute operator, %v%.The operator may be used either for extraction or assignment, treating the right-hand value as a vector of attribute values (with the ith element corresponding to the ith vertex).By passing a list with a list for each element, one may assign arbitrary vertex values in this manner; however, the vertex operator will vectorize these values upon retrieval (and hence one must use get.vertex.attributewith unlist = FALSE to recover the full list structure).If a requested attribute is unavailable for a particular vertex, an NA is returned.Typical use of the vertex attribute methods is illustrated via the following example.Note that more complex usage is also possible, as detailed in the package manual.> #Add vertex attributes > set.vertex.attribute(net,"boo",1:5) #Create a numeric attribute > net %v% "hoo" <-letters[1:5] #Now, a character attribute > #Listing attributes > list.vertex.attributes(net)#List all vertex attributes [1] "boo" "hoo" "na" "vertex.names"> #Retrieving attributes > get.vertex.attribute(net,"boo")#Retrieve ✬em [1] 1 2 3 4 5 > net %v% "hoo" [1] "a" "b" "c" "d" "e" > #Deleting attributes > delete.vertex.attribute(net,"boo")#Remove one > list.vertex.attributes(net) #Check to see that it✬s gone [1] "hoo" "na" "vertex.names"

Edge attributes
Finally, we come to edge attributes.The operations involved here are much like those for the network and vertex cases.List, set, get, and delete methods exist for edge attributes (list.edge.attributes,set.edge.attribute, get.edge.attribute,and delete.edge.attribute),as does an edge attribute operator (%e%).Operations with edges are rendered somewhat more complex, however, because of the need to employ edge IDs in referencing the edges themselves.These can be obtained via the get.edgeIDs function (as described above), but this adds complexity which is unnecessary in the case of simple attribute assignment on non-multiplex, dyadic graphs (where edges are uniquely identifiable by a pair of endpoints).For such cases, the convenience function set.edge.value allows edge values to be specified in adjacency matrix form.Also useful is the bracket operator, which can be used to assign values as well as to create edges.For network net, net[sel, names.eval= "attrname"] <-value will set the attribute named by "attrname" on the edges selected by sel (which follows standard R syntax for selection of cells from square matrices) to the values in value.By default, values for non-existent edges are ignored (although new edges can be created by adding add.edges = TRUE to the included arguments).Reasonable behavior for non-scalar values using this method is not guaranteed.
In addition to the above, methods such as as.sociomatrix allow for edge attributes to be employed in some settings.These provide a more convenient (if less flexible) interface for the common case of scalar attributes on the edges of non-multiplex, dyadic networks.The following is a typical example of these routines in action, although much more exotic scenarios are certainly possible.

From attributes to networks
In addition to simply storing covariate information, it should be noted that one can actively use attributes to construct new networks.For instance, consider the emon data set used above.Among other variables, each vertex carries an attribute called "Location" which contains information on whether the corresponding organization had headquarters or command post installations which were local, non-local, or both with respect to the operation from which the network was drawn.We may thus use this information to construct a very simple hypergraph, in which locations constitute edges and edge membership is defined as having an installation at the respective location.For the Mt.St. Helens network, such a network may be constructed as follows.First, we extract the location information from the relevant network object, and use this to build an incidence matrix based on location.Then we convert this incidence matrix to a hypergraphic network object (setting vertex names from the original network object for convenience).Obviously, the simple location coding used here cannot lead to a very complex structure.Nevertheless, this case serves to illustrate the flexibility of the network tools in allowing attribute information to be used in creative ways.In addition to constructing networks from attributes, one can use attributes to store networks (useful for joint representation of cognitive and behavioral structures such as those of Krackhardt 1988;Killworth & Bernard 1976), edge timing information (for dynamic structures, as in the package networkDynamic (Butts, et all. 2014)), etc. Appropriate use of network, edge, and vertex attributes allows a wide range of complex relational data structures to be supported without the need for a cumbersome array of of custom data classes.

Visualizing network objects
In addition to manipulating network objects, the network package provides built-in support for network visualization.This capability is supplied by the package plot method (ported from sna's gplot), which is dispatched transparently when plot is called with a network object.The plot method supports a range of layout and display options, which are specified through additional arguments.For instance, to visualize the Florentine marriage data we might use commands such as the following: > plot(nflo, displaylabels = TRUE, boxed.labels= FALSE) > plot(nflo, displaylabels = TRUE, mode = "circle") Typical results of these commands are shown in Figure 1.Note that the plot method automatically determines whether the network being visualized is directed, and adds or suppresses arrowheads accordingly.For instance, compare the above with the Mt.Si communication network (Figure 2): The default layout algorithm for the plot method is that of Fruchterman & Reingold (1991), a force-directed display with good overall performance.Other layout methods are available (including the well-known energy-minimization algorithm of Kamada& Kawai 1989), and support is included for user-added functions.To create a custom layout method, one need only create a function with the prefix network.layoutwhich supplies the appropriate formal arguments (see the network manual for details).The plot method can then be directed to utilize the custom layout function, as in this simple example (shown in Figure 3):  > plot(emon$MtStHelens, mode = "degree", displaylabels = TRUE, + boxed.labels= FALSE, suppress.axes= FALSE, label.cex= 0.5, + xlab = "Indegree", ylab = "Outdegree", label.col= 3) As this example illustrates, most properties of the visualization can be adjusted where necessary.This is especially helpful when visualizing structures such as hypergraphs: Note that the plot method automatically recognizes that the network being passed is hypergraphic, an employs a two-mode representation for visualization purposes (see Figure 4).Supplying custom labeling and vertex coloring helps clarify the interpretation.For instance, here we can immediately see the division between organizations who maintained headquarters exclusively at local or remote locations during the Mount St. Helens search and rescue operation, as well as those organizations (e.g. the Salvation Army and Red Cross) which bridged the two.Though simple, examples such as this demonstrate how the default plot settings can be adjusted to produce effective visualizations of even complex relational data.

C-language API
While the functionality described thus far has been aimed at users working within an interpreted R environment, many network package features can also be accessed through a C-language application programming interface (API).Although this API still makes use of R data structures, it provides mechanisms for direct manipulation of those structures via compiled code.While invisible to most end users, the API has a number of attractions for developers.Chief among these is performance: in the author's experience, a reasonably network: Managing Relational Data in R well-designed C function can run as much as one to two orders of magnitude faster than an equivalent R implementation.For many day-to-day applications, such gains are unlikely to be worth the considerable increase in implementation and maintenance costs associated with choosing C over R; however, they may prove vital when performing computationally demanding tasks such as Markov chain Monte Carlo simulation, large-graph computations, and small-N solutions for non-polynomial time problems (e.g., cycle counting).Another useful feature of the C API is its ability to make the complex data storage capabilities of network objects accessible to developers whose projects involve existing backend code, or developing packages such as networkDynamic which extend network's functionality at the C level.
Instead of performing data extraction on a network object and passing the result to the compiled routine, the network API allows for such routines to work with such objects directly.Finally, a third useful asset of the network API is the capacity it provides for generating usertransparent functionality which transcends what is feasible with R's pass-by-value semantics.The use of compiled code to directly modify objects without copying has been fundamental to the functionality of the package since version 1.0, as can be gleaned from an examination of the package source code4 .
The mechanism by which the API is currently implemented is fairly simple.A shared header file (which must be included in the user's application) defines a series of macros which point to the package's internal routines.During program execution, a global registration function is used to map these macros to their internal symbols; following this, the macros may be called normally.Other then ensuring that the network library is loaded prior to invoking the registration function, no other measures are necessary.In particular, the calling routine does not have to be linked against the network library, although the aforementioned header/registration routines must be included at compile time.5 In addition, network versions 1.11.1 and higher implement R's template for registering native C routines6 so that packages may compile against network's code by declaring a LinkingTo: network in the DESCRIPTION file.The listing of exported functions are in the file src/Rinit.c.

Using the network API
To use the network API within one's own code, the following steps are necessary: 1.The required network header and function registration files must be added to the developer's source tree.
2. The network header file must be included during compilation.
3. The netRegisterFunctions function must be invoked at the entry point to any C program using the API.
4. The network API functions must be used as required.
The command netRegisterFunctions takes and returns no arguments, being invoked solely for its side effect.Although it must be called at each entry to the C backend (i.e., each invocation of .Call or .External from R), its effects persist until the calling routine exits.
The API is designed for use with the .Call interface, although wrappers for conversion to .External are in principle possible.Object references are maintained through SEXP pointers, as is standard for R's C language interface.Because references (rather than copies of the objects themselves) are passed to C via the interface, C routines may directly alter the objects with which they are called.network has many routines for creating and modifying networks, as well as for accessing object contents within compiled code.To illustrate the use of the network API in practical settings, we here provide a walk-through for a relatively simple (but non-trivial) example.Consider a C function which generates an undirected network from a homogeneous Bernoulli graph distribution, tagging each edge with random "onset" and "termination" times based on a piecewise-exponential process with fixed onset/termination hazards.Such a function might also keep track of the first and last edge times for each vertex (and for the network as a whole), storing these within the network object via appropriately named attributes.
To implement our sample function, we begin with the standard header for a .Call function, which both takes and receives arguments of type SEXP (S-expression pointers).In this case, the parameters to be passed consist of an initialized network object, the probability of an edge between any two vertices, and the hazards for edge onset and termination (respectively).Note that we do not need to tell the function about properties such as network size, since it can determine these itself using the API's interface methods.
SEXP rnbernexp_R(SEXP g, SEXP ep, SEXP oh, SEXP th) /* C-Language code for a simple random dynamic network generator.Arguments are as follows: g -a pre-initialized network object ep -the edge probability parameter oh -the edge onset hazard parameter th -the edge termination hazard parameter */ { int n, i, w; double u, fet, let, *vfet, *vlet, ot, tt; SEXP tail, head, atl, atlnam, sot, stt, ec; /*Verify that we were called properly, and set things up*/ netRegisterFunctions(); if(!netIsNetwork(g)) error("rnbernexp_R must be called with a network object.\n");if(netIsDir(g)) error("Network passed to rnbernexp_R should be undirected.\n");n = netNetSize(g); PROTECT(ep = coerceVector(ep, REALSXP)); PROTECT(oh = coerceVector(oh, REALSXP)); PROTECT(th = coerceVector(th, REALSXP)); REAL(stt)[0] = let; g = netSetNetAttrib(g, "FirstOnsetTime", sot); g = netSetNetAttrib(g, "LastTerminationTime", stt); /*Clear protection stack and return*/ PutRNGstate(); UNPROTECT(6); return g; } To use the rnbernexp_R function, it must be invoked from R using the .Call interface.A simple wrapper function (whose behavior is similar to R's built-in random number generation routines) might look like the following: > rnbernexp <-function (n, nv, p = 0.5, onset.hazardIn actual use, the PACKAGE setting would be changed to the name of the shared object file in which the rnbernexp_R symbol resides.(This file would need to be linked against the networkapi file, and dynamically loaded after network is in memory.Linking against the entire network library is not required, however.)Although the specific distribution simulated is too simplistic to serve as a very good model of social dynamics, it nevertheless illustrates how the network API can be used to efficiently simulate and store the results of non-trivial processes within compiled code.

Final comments
For several decades, tools for social network analysis were essentially isolated from those supporting conventional statistical analyses.A major reason for this isolation was the difficulty in manipulating -or even representing -relational data within standard statistical packages.
In recent years, the emergence of flexible statistical computing environments such as R have helped to change this situation.Platforms like R allow for the creation of the complex data structures needed to represent rich relational data, while also facilitating the development of tools to make such structures accessible to the end user.The network package represents one attempt to leverage these capabilities in order to create a low-level infrastructure for the analysis of relational data.Together with packages like sna, ergm, and the rest of the statnet suite, it is hoped that network will provide a useful resource for scientists both inside and outside of the social network community.

>Figure 1 :
Figure 1: Sample displays of the Florentine marriage data; the left panel depicts the default Fruchterman-Reingold layout, while the right panel depicts a circular layout.

Figure 2 :
Figure 2: Sample display of the Mt.Si EMON data, using the default Fruchterman-Reingold layout.

Figure 4 :
Figure 4: Sample display of the Mt.St. Helens location hypergraph, showing division between locally, non-locally, and dual headquartered organizations.
). Use of these indicator methods is straightforward, as illustrated by the following examples.