File: coercion.R

package info (click to toggle)
r-cran-network 1.19.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,500 kB
  • sloc: ansic: 2,491; sh: 13; makefile: 2
file content (636 lines) | stat: -rw-r--r-- 25,615 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
######################################################################
#
# coercion.R
#
# Written by Carter T. Butts <buttsc@uci.edu>; portions contributed by
# David Hunter <dhunter@stat.psu.edu> and Mark S. Handcock
# <handcock@u.washington.edu>.
#
# Last Modified 06/08/21
# Licensed under the GNU General Public License version 2 (June, 1991)
# or greater
#
# Part of the R/network package
#
# This file contains various routines for coercion to/from network
# class objects.
#
# Contents:
#
#   as.matrix.network
#   as.matrix.network.adjacency
#   as.matrix.network.edgelist
#   as.matrix.network.incidence
#   as.network
#   as.network.default
#   as.network.network
#   as.network.matrix
#   as.sociomatrix
#
######################################################################


# Method for general coercion of network class objects into matrices.
# Matrix type is indicated by the eponymous argument; note that some
# types may not be supported for certain networks.  Where
# attrname!=NULL, an edge attribute of name attrname is used to supply
# edge values.  Otherwise, edges are assumed to be unvalued.
#


#' Coerce a Network Object to Matrix or Table Form
#' 
#' The \code{as.matrix} methods attempt to coerce their input to a matrix in
#' adjacency, incidence, or edgelist form.  Edge values (from a stored
#' attribute) may be used if present. \code{\link[tibble:as_tibble]{as_tibble}}
#' coerces into an edgelist in \code{\link[tibble]{tibble}} (a type of
#' \code{\link{data.frame}}) form; this can be especially useful if extrecting
#' a character-type edge attribute.
#' 
#' If no matrix type is specified, \code{\link{which.matrix.type}} will be used
#' to make an educated guess based on the shape of \code{x}.  Where edge values
#' are not specified, a dichotomous matrix will be assumed.
#' 
#' Edgelists returned by the \code{as.matrix} methods are by default in a
#' slightly different form from the \code{sna} edgelist standard, but do
#' contain the \code{sna} extended matrix attributes (see
#' \code{\link{as.network.matrix}}).  They should typically be compatible with
#' \code{sna} library functions.  To ensure compatibility, the
#' \code{as.sna.edgelist} argument can be set (which returns an exact
#' \code{sna} edgelist). The \code{\link{as.edgelist}} function also returns a
#' similar edgelist matrix but with an enforced sorting.
#' 
#' For the \code{as.matrix} methods, if the \code{attrname} attribute is used
#' to include a charcter attribute, the resulting edgelist matrix will be
#' character rather than numeric. The \code{as_tibble} methods never coerce.
#' 
#' Note that adjacency matrices may also be obtained using the extraction
#' operator.  See the relevant man page for details. Also note that which
#' attributes get returned by the \code{as_tibble} method by default depends on
#' \code{unit}: by default no edge attributes are returned but all vertex
#' attributes are.
#' 
#' @param x an object of class \code{network}
#' @param matrix.type one of \code{"adjacency"}, \code{"incidence"},
#' \code{"edgelist"}, or \code{NULL}
#' @param attrname optionally, the name of an edge attribute to use for edge
#' values
#' @param attrnames optionally, either a character vector of the names of edge
#' attributes to use for edge values, or a numerical or logical vector to use
#' as indices for selecting them from \code{\link{list.edge.attributes}(x)} or
#' \code{\link{list.vertex.attributes}(x)} (depending on \code{unit}); passing
#' \code{TRUE} therefore returns all edge attributes as columns
#' @param expand.bipartite logical; if \code{x} is bipartite, should we return
#' the full adjacency matrix (rather than the abbreviated, two-mode form)?
#' @param as.sna.edgelist logical; should the edgelist be returned in sna
#' edglist form?
#' @param na.rm logical; should missing edges/vertices be included in the
#' edgelist formats? Ignored if \code{as.sna.edgelist=TRUE}.
#' @param unit whether a \code{\link[tibble]{tibble}} of edge or vertex attributes
#' should be returned.
#' @param ...  additional arguments.
#' @return For \code{as.matrix} methods, an adjacency, incidence, or edgelist
#' matrix. For the \code{as_tibble} method, a \code{tibble} whose first two
#' columns are \code{.head} and \code{.tail}, whose third column \code{.eid} is
#' the edge ID, and whose subsequent columns are the requested edge attributes.
#' @author Carter T. Butts \email{buttsc@@uci.edu} and David Hunter
#' \email{dhunter@@stat.psu.edu}
#' @seealso \code{\link{which.matrix.type}}, \code{\link{network}},
#' \code{\link{network.extraction}},\code{\link{as.edgelist}}
#' @references Butts, C. T.  (2008).  \dQuote{network: a Package for Managing
#' Relational Data in R.} \emph{Journal of Statistical Software}, 24(2).
#' \doi{10.18637/jss.v024.i02}
#' @keywords classes graphs
#' @examples
#' 
#' # Create a random network
#' m <- matrix(rbinom(25,4,0.159),5,5) # 50% density
#' diag(m) <- 0
#' g <- network(m, ignore.eval=FALSE, names.eval="a") # With values
#' g %e% "ac" <- letters[g %e% "a"]
#' 
#' # Coerce to matrix form
#' # No attributes:
#' as.matrix(g,matrix.type="adjacency")
#' as.matrix(g,matrix.type="incidence")
#' as.matrix(g,matrix.type="edgelist")
#' # Attributes:
#' as.matrix(g,matrix.type="adjacency",attrname="a")
#' as.matrix(g,matrix.type="incidence",attrname="a")
#' as.matrix(g,matrix.type="edgelist",attrname="a")
#' as.matrix(g,matrix.type="edgelist",attrname="ac")
#' 
#' # Coerce to a tibble:
#' library(tibble)
#' as_tibble(g)
#' as_tibble(g, attrnames=c("a","ac"))
#' as_tibble(g, attrnames=TRUE)
#' # Get vertex attributes instead:
#' as_tibble(g, unit = "vertices")
#' 
#' # Missing data handling:
#' g[1,2] <- NA
#' as.matrix(g,matrix.type="adjacency") # NA in the corresponding cell
#' as.matrix(g,matrix.type="edgelist", na.rm=TRUE) # (1,2) excluded
#' as.matrix(g,matrix.type="edgelist", na.rm=FALSE) # (1,2) included
#' as_tibble(g, attrnames="na", na.rm=FALSE) # Which edges are marked missing?
#' 
#' # Can also use the extraction operator
#' g[,]                            # Get entire adjacency matrix
#' g[1:2,3:5]                      # Obtain a submatrix
#' 
#' @export as.matrix.network
#' @export
as.matrix.network<-function(x,matrix.type=NULL,attrname=NULL,...){
  #Get the matrix type
  if(is.null(matrix.type))
    matrix.type<-"adjacency"
  else
    matrix.type<-match.arg(matrix.type,c("adjacency","incidence","edgelist"))
  #Dispatch as needed
  switch(matrix.type,
    adjacency=as.matrix.network.adjacency(x=x,attrname=attrname,...),
    incidence=as.matrix.network.incidence(x=x,attrname=attrname,...),
    edgelist=as.matrix.network.edgelist(x=x,attrname=attrname,...)
  )
}


# Coerce a network object to an adjacency matrix (where possible).  If
# provided, attrname is used to identify an attribute to use for edge
# values.
#
#' @rdname as.matrix.network
#' @usage \method{as.matrix.network}{adjacency}(x, attrname=NULL, 
#'    expand.bipartite = FALSE, ...)
#' @export as.matrix.network.adjacency
#' @rawNamespace S3method(as.matrix.network,adjacency)
as.matrix.network.adjacency<-function(x,attrname=NULL,expand.bipartite=FALSE,...){
  #Check to make sure this is a supported network type
  if(is.hyper(x))
    stop("Hypergraphs not currently supported in as.matrix.network.adjacency.  Exiting.\n")
  if(is.multiplex(x))
    stop("Multigraphs not currently supported in as.matrix.network.adjacency.  Exiting.\n")
  #Generate the adjacency matrix 
  m<-matrix(0,nrow=network.size(x),ncol=network.size(x))
  if(network.size(x)==0)
    return(m)
  tl<-unlist(sapply(x$mel,"[[","outl")) #Can unlist b/c no hyperedges
  hl<-unlist(sapply(x$mel,"[[","inl"))
  nal<-as.logical(get.edge.attribute(x$mel,"na",unlist=TRUE))
  if(!is.null(attrname)){
    val<-unlist(get.edge.attribute(x$mel,attrname,unlist=FALSE))
    if(is.null(val)){
     warning(paste("There is no edge attribute named", attrname))
     val<-rep(1,length(tl))
    }
  }else{
    val<-rep(1,length(tl))
  }
  if(length(hl[!nal])>0){
    m[tl[!nal]+(hl[!nal]-1)*network.size(x)]<-val[!nal]
  }
  if(length(hl[ nal])>0){
   m[tl[ nal]+(hl[ nal]-1)*network.size(x)]<-NA
  }
  #If undirected, symmetrize
  if(!is.directed(x)){
# changed by MSH to allow non binary values
#   m<-pmax(m,t(m))
    sel<-m
    sel[is.na(m)]<-1
    m[sel==0] <- t(m)[sel==0]
  }
  #Set row/colnames to vertex names
  xnames <- network.vertex.names(x)
  dimnames(m) <- list(xnames, xnames)
  #If bipartite and !expand.bipartite, return in two-mode form
  if(is.bipartite(x)&(!expand.bipartite)){
    nactors <- get.network.attribute(x, "bipartite")
    nevents <- network.size(x) - nactors
    m <- m[1:nactors, nactors+(1:nevents), drop=FALSE]
  }
  #Return the result
  m
}


# Coerce a network object to an edgelist matrix.  If provided, attrname is 
# used to identify an attribute to use for edge values.  Setting as.sna.edgelist
# results in output in the sna edgelist format (including missing edge handling)
# and is used by the sna package for coercion.
#
#' @rdname as.matrix.network
#' @usage \method{as.matrix.network}{edgelist}(x, attrname=NULL, 
#'    as.sna.edgelist = FALSE, na.rm = TRUE, ...)
#' @export as.matrix.network.edgelist
#' @rawNamespace S3method(as.matrix.network,edgelist)
as.matrix.network.edgelist<-function(x,attrname=NULL,as.sna.edgelist=FALSE,na.rm=TRUE,...){
  #Check to make sure this is a supported network type
  if(is.hyper(x))
    stop("Hypergraphs not currently supported in as.matrix.network.edgelist.  Exiting.\n")
  #Find the missing edges
  nal<-as.logical(get.edge.attribute(x$mel,"na"))
  #Generate the edgelist matrix
  m<-cbind(unlist(sapply(x$mel,"[[","outl")), unlist(sapply(x$mel,"[[","inl")))
  #Add edge values, if needed
  if(!is.null(attrname))
    m<-cbind(m,get.edge.attribute(x$mel,attrname,na.omit=FALSE,null.na=TRUE,deleted.edges.omit=TRUE))
  else if(as.sna.edgelist)
    m<-cbind(m,rep(1,NROW(m)))
  #Set additional attributes and return the result
  if(as.sna.edgelist && nrow(m) > 0) # check that there are actually edges
    m[nal,3]<-NA
  else if(na.rm) m<-m[!nal,,drop=FALSE]
  
  if(length(m)==0)
    m<-matrix(numeric(0),ncol=2+as.sna.edgelist+!is.null(attrname))
  else if((!is.directed(x))&&as.sna.edgelist){    #sna uses directed form
    m<-rbind(m,m[m[,2]!=m[,1],c(2:1,3)])
  }
  attr(m,"n")<-network.size(x)
  attr(m,"vnames")<-network.vertex.names(x)
  if(is.bipartite(x))
    attr(m,"bipartite")<-x%n%"bipartite"
  m
}

# Coerce a network object to an edgelist tibble.  If provided, attrnames is 
# used to identify a list of attributes to use for edge values.
#
#' @rdname as.matrix.network
#' @param store.eid whether the edge ID should be stored in the third column (`.eid`).
#' @export
as_tibble.network<-function(x,attrnames=(match.arg(unit)=="vertices"),na.rm=TRUE,..., unit=c("edges", "vertices"), store.eid=FALSE){
  df <- as.data.frame(x, unit = unit, store_eid = store.eid, na.rm = na.rm, attrs_to_ignore = c(), name_vertices = FALSE, sort_attrs=TRUE, ...)

  unit <- match.arg(unit)
  if(is.logical(attrnames) || is.numeric(attrnames))
    attrnames <- na.omit(setdiff(names(df), c(".tail", ".head", ".eid"))[attrnames])

  # Keep only requested columns, but make sure all named columns are present.
  df <- df[intersect(c(".tail", ".head", ".eid", attrnames), names(df))]
  for(a in setdiff(attrnames, names(df))) df[[a]] <- rep(list(), nrow(df))

  structure(as_tibble(df),
            n = network.size(x),
            vnames = network.vertex.names(x),
            bipartite = x %n% "bipartite")
}

#' @rdname as.matrix.network
#' @rawNamespace S3method(as.tibble,network)
as.tibble.network <- as_tibble.network

# Coerce a network object to an incidence matrix (where possible).  If
# provided, attrname is used to identify an attribute to use for edge
# values.
#
#' @rdname as.matrix.network
#' @usage \method{as.matrix.network}{incidence}(x, attrname=NULL, ...)
#' @export as.matrix.network.incidence
#' @rawNamespace S3method(as.matrix.network,incidence)
as.matrix.network.incidence<-function(x,attrname=NULL,...){
  #Perform preprocessing
  n<-network.size(x)
  nulledge<-sapply(x$mel,is.null)
  inl<-lapply(x$mel,"[[","inl")[!nulledge]
  outl<-lapply(x$mel,"[[","outl")[!nulledge]
  if(!is.null(attrname))
    evals<-unlist(get.edge.attribute(x$mel,attrname))[!nulledge]
  else
    evals<-rep(1,length(x$mel))[!nulledge]
  ena<-as.logical(get.edge.attribute(x$mel,"na"))[!nulledge]
  #If called with an empty graph, return a degenerate matrix
  if(length(ena)==0)
    return(matrix(numeric(0),nrow=n))
  #Generate the incidence matrix
  dir<-is.directed(x)
  f<-function(a,m,k){y<-rep(0,m); y[a]<-k; y}
  im<-sapply(inl,f,n,1)+sapply(outl,f,n,ifelse(dir,-1,1))
  if(!dir)
    im<-pmin(im,1)
  im<-sweep(im,2,evals,"*")              #Fill in edge values
  im[(sapply(ena,rep,n)*(im!=0))>0]<-NA      #Add NAs, if needed
  #Return the result
  im
}

#' @rdname network
#' @export
as.network<-function(x,...)
  UseMethod("as.network")

#' @name as.network.matrix
#'
#' @title Coercion from Matrices to Network Objects
#' 
#' @description \code{as.network.matrix} attempts to coerce its first argument to an object
#' of class \code{network}.
#' 
#' @details Depending on \code{matrix.type}, one of three edgeset constructor methods
#' will be employed to read the input matrix (see
#' \code{\link{edgeset.constructors}}).  If \code{matrix.type==NULL},
#' \code{\link{which.matrix.type}} will be used to guess the appropriate matrix
#' type.
#' 
#' The coercion methods will recognize and attempt to utilize the \code{sna}
#' extended matrix attributes where feasible.  These are as follows: \itemize{
#' \item\code{"n"}: taken to indicate number of vertices in the network.
#' \item\code{"bipartite"}: taken to indicate the network's \code{bipartite}
#' attribute, where present.  \item\code{"vnames"}: taken to contain vertex
#' names, where present.  } These attributes are generally used with edgelists,
#' and indeed data in \code{sna} edgelist format should be transparently
#' converted in most cases.  Where the extended matrix attributes are in
#' conflict with the actual contents of \code{x}, results are no guaranteed
#' (but the latter will usually override the former). For an edge list, the
#' number of nodes in a network is determined by the number of unique nodes
#' specified. If there are isolate nodes not in the edge list, the "n"
#' attribute needs to be set. See example below.
#' 
#' @param x a matrix containing an adjacency structure
#' @param matrix.type one of \code{"adjacency"}, \code{"edgelist"},
#' \code{"incidence"}, or \code{NULL}
#' @param directed logical; should edges be interpreted as directed?
#' @param hyper logical; are hyperedges allowed?
#' @param loops logical; should loops be allowed?
#' @param multiple logical; are multiplex edges allowed?
#' @param bipartite count; should the network be interpreted as bipartite? If
#' present (i.e., non-NULL) it is the count of the number of actors in the
#' bipartite network. In this case, the number of nodes is equal to the number
#' of actors plus the number of events (with all actors preceding all events).
#' The edges are then interpreted as nondirected.
#' @param ignore.eval logical; ignore edge values?
#' @param names.eval optionally, the name of the attribute in which edge values
#' should be stored
#' @param na.rm logical; ignore missing entries when constructing the network?
#' @param edge.check logical; perform consistency checks on new edges?
#' @param ... additional arguments
#' @return An object of class \code{network}
#' @author Carter T. Butts \email{buttsc@@uci.edu} and David Hunter
#' \email{dhunter@@stat.psu.edu}
#' @seealso \code{\link{edgeset.constructors}}, \code{\link{network}},
#' \code{\link{which.matrix.type}}
#' @references Butts, C. T.  (2008).  \dQuote{network: a Package for Managing
#' Relational Data in R.} \emph{Journal of Statistical Software}, 24(2).
#' \doi{10.18637/jss.v024.i02}
#' @keywords classes graphs
#' @examples
#' 
#' #Draw a random matrix
#' m<-matrix(rbinom(25,1,0.5),5)
#' diag(m)<-0
#' 
#' #Coerce to network form
#' g<-as.network.matrix(m,matrix.type="adjacency")
#' 
#' # edge list example. Only 4 nodes in the edge list.
#' m = matrix(c(1,2, 2,3, 3,4), byrow = TRUE, nrow=3)
#' attr(m, 'n') = 7
#' as.network(m, matrix.type='edgelist')
#' 
#' @export as.network.default
#' @export
as.network.default<-function(x,...)
  as.network.matrix(x,...)

#' @export as.network.network
#' @export
as.network.network<-function(x,...)
  x


#
# MSH modified for bipartite
#

#' @rdname as.network.matrix
#' @export as.network.matrix
#' @export
as.network.matrix<-function(x, matrix.type=NULL,
        directed=TRUE, hyper=FALSE, loops=FALSE, multiple=FALSE,
        bipartite=FALSE,
        ignore.eval=TRUE, names.eval=NULL, na.rm=FALSE, edge.check=FALSE, ...){
  #Before doing anything else, pull any attributes from the matrix that we
  #might need....
  nattr<-attr(x,"n")             #Currently, only using sna edgelist attributes
  battr<-attr(x,"bipartite")
  vattr<-attr(x,"vnames")
  #Convert logicals to numeric form
  if(is.logical(x)){x <- 1*x}
  #Get the matrix type
  if(is.null(matrix.type))
    matrix.type<-which.matrix.type(x)
  else
    matrix.type<-match.arg(matrix.type,c("adjacency","incidence","edgelist",
                                         "bipartite"))
  if(is.logical(bipartite)&&bipartite)
    matrix.type<-"bipartite"
  #Patch adj->bipartite case
  if((bipartite>0)&&(matrix.type=="adjacency")&&(NROW(x)==bipartite))  
    matrix.type<-"bipartite"
  
  # Add names if available
  unames <- NULL
  if(matrix.type=="edgelist"){
    if(dim(x)[2]>2)
      vals<-x[,-(1:2),drop=FALSE]
    else
      vals<-NULL
    if(is.character(x<-as.matrix(x[,1:2,drop=FALSE]))){
      unames <- sort(unique(as.vector(x)))
      x <- cbind(match(x[,1],unames),match(x[,2],unames))
    }
    if(!is.null(vals)){
      x<-cbind(x,vals)
      
      if (is.null(colnames(vals))){
        colnames(x)<-NULL  #R creates these, and they are annoying later
      } else {
        # leave colnames for vals intact so they can be used for edge attributes
        colnames(x)<-c(NA,NA,colnames(vals))
      }
    }
  }
  if(matrix.type=="adjacency" && !is.null(colnames(x))){
    unames <- colnames(x)
  }
  if(matrix.type=="bipartite"){
   directed <- FALSE
   bipartite <- dim(x)[1]
   unames <- 1:sum(dim(x))
   if(!is.null(rownames(x))){
     unames[1:(dim(x)[1])] <- rownames(x)
   }
   if(!is.null(colnames(x))){
     unames[(dim(x)[1])+(1:(dim(x)[2]))] <- colnames(x)
   }
  }
  if(!is.null(vattr))                        #If given names, use 'em
    unames<-vattr
  #Initialize the network object
  if(is.numeric(nattr)){                     #If given n, use it
    n<-nattr
  }else{  
    if((matrix.type=="edgelist")&&(NROW(x)==0))
      stop("Cannot determine network size from zero-length edgelist; assign an n attribute to use data of this type.\n")
    n<-switch(matrix.type,	#Extract n based on matrix type
      adjacency=dim(x)[1],
      incidence=dim(x)[1],
      bipartite=sum(dim(x)),
      edgelist=max(x[,1:2]),
    )
  }
  if(is.numeric(battr))                      #If given bipartite info, use it
    bipartite<-battr
  
  # if we are going to build an adjacency matrix and it doesn't match the nattr, give an error, because otherwise will crash
  # this may happen if a square edgelist with attribute information is passed in
  if (is.numeric(nattr) & matrix.type=='adjacency'){
    if (nattr != ncol(x)){
      stop('the dimensions of the matrix argument (',nrow(x),' by ', ncol(x),') do not match the network size indicated by the attached n attribute (',nattr,'), perhaps matrix.type argument is not correct')
    }
  }
  
  g<-network.initialize(n,directed=directed, hyper=hyper, loops=loops, multiple=multiple,bipartite=bipartite)
  #Call the specific coercion routine, depending on matrix type
  g<-switch(matrix.type,
    adjacency=network.adjacency(x,g,
     ignore.eval,names.eval,na.rm,edge.check),
    incidence=network.incidence(x,g,
     ignore.eval,names.eval,na.rm,edge.check),
    bipartite=network.bipartite(x,g,
     ignore.eval,names.eval,na.rm,edge.check),
    edgelist=network.edgelist(x,g, 
     ignore.eval,names.eval,na.rm,edge.check)
  )

  if(!is.null(unames)){
   g <- set.vertex.attribute(g,"vertex.names", unames)
  }
  #Return the result
  g
}


#Force the input into sociomatrix form.  This is a shortcut to 
#as.matrix.network.adjacency, which ensures that a raw matrix is
#passed through as-is.


#' Coerce One or More Networks to Sociomatrix Form
#' 
#' \code{as.sociomatrix} takes adjacency matrices, adjacency arrays,
#' \code{\link{network}} objects, or lists thereof, and returns one or more
#' sociomatrices (adjacency matrices) as appropriate.  This routine provides a
#' useful input-agnostic front-end to functions which process adjacency
#' matrices.
#' 
#' \code{as.sociomatrix} provides a more general means of coercing input into
#' adjacency matrix form than \code{\link{as.matrix.network}}. In particular,
#' \code{as.sociomatrix} will attempt to coerce all input networks into the
#' appropriate form, and return the resulting matrices in a regularized manner.
#' If \code{simplify==TRUE}, \code{as.sociomatrix} attempts to return the
#' matrices as a single adjacency array.  If the input networks are of variable
#' size, or if \code{simplify==FALSE}, the networks in question are returned as
#' a list of matrices.  In any event, a single input network is always returned
#' as a lone matrix.
#' 
#' If \code{attrname} is given, the specified edge attribute is used to extract
#' edge values from any \code{\link{network}} objects contained in \code{x}.
#' Note that the same attribute will be used for all networks; if no attribute
#' is specified, the standard dichotomous default will be used instead.
#' 
#' @param x an adjacency matrix, array, \code{\link{network}} object, or list
#' thereof.
#' @param attrname optionally, the name of a network attribute to use for
#' extracting edge values (if \code{x} is a \code{\link{network}} object).
#' @param simplify logical; should \code{as.sociomatrix} attempt to combine its
#' inputs into an adjacency array (\code{TRUE}), or return them as separate
#' list elements (\code{FALSE})?
#' @param expand.bipartite logical; if \code{x} is bipartite, should we return
#' the full adjacency matrix (rather than the abbreviated, two-mode form)?
#' @param ...  additional arguments for the coercion routine.
#' @return One or more adjacency matrices.  If all matrices are of the same
#' dimension and \code{simplify==TRUE}, the matrices are joined into a single
#' array; otherwise, the return value is a list of single adjacency matrices.
#' @author Carter T. Butts \email{buttsc@@uci.edu}
#' @seealso \code{\link{as.matrix.network}}, \code{\link{network}}
#' @references Butts, C. T.  (2008).  \dQuote{network: a Package for Managing
#' Relational Data in R.} \emph{Journal of Statistical Software}, 24(2).
#' \doi{10.18637/jss.v024.i02}
#' @keywords graphs manip
#' @examples
#' 
#' #Generate an adjacency array
#' g<-array(rbinom(100,1,0.5),dim=c(4,5,5))
#' 
#' #Generate a network object
#' net<-network(matrix(rbinom(36,1,0.5),6,6))
#' 
#' #Coerce to adjacency matrix form using as.sociomatrix
#' as.sociomatrix(g,simplify=TRUE)   #Returns as-is
#' as.sociomatrix(g,simplify=FALSE)  #Returns as list
#' as.sociomatrix(net)               #Coerces to matrix
#' as.sociomatrix(list(net,g))       #Returns as list of matrices
#' 
#' @export as.sociomatrix
as.sociomatrix<-function(x, attrname=NULL, simplify=TRUE, expand.bipartite=FALSE, ...){
  if(is.network(x)){ #If network, coerce to adjacency matrix
    g<-as.matrix.network.adjacency(x,attrname=attrname, expand.bipartite=expand.bipartite,...)
  }else if(is.matrix(x)||is.array(x)){ #If an array/matrix, use as-is
    g<-x
  }else if(is.list(x)){  #If a list, recurse on list elements
    g<-lapply(x,as.sociomatrix,attrname=attrname,simplify=simplify, expand.bipartite=expand.bipartite,...)
  }else{
    stop("as.sociomatrix input must be an adjacency matrix/array, network, or list.")
  }
  #Convert into the appropriate return format
  if(is.list(g)){   #Collapse if needed
    if(length(g)==1){
      g<-g[[1]]
      if((!simplify)&&(length(dim(g))==3)){  #Coerce to a list of matrices?
        out<-list()
        for(i in 1:dim(g)[1])
          out[[i]]<-g[i,,]
      }else{
        out<-g
      }
    }else{
      #Coerce to array form?
      if(simplify){
        dims<-sapply(g,dim)
        if(is.list(dims)){      #Dims must not be of equal length
          mats<-sapply(dims,length)
          mats[mats==1]<-0
          mats[mats==2]<-1
          mats[mats==3]<-sapply(dims[mats==3],"[[",1)
          mats<-cumsum(mats)
          dims<-sapply(dims,"[",2)
        }else{                  #Dims are of equal length
          if(NROW(dims)==3)      #Determine number of matrices per entry
            mats<-cumsum(dims[1,])
          else
            mats<-1:NCOL(dims)
          dims<-dims[2,]         #Get ncols
        }
        if((!any(is.null(dims)))&&(length(unique(dims))==1)&&(all(mats>0))){
          out<-array(dim=c(mats[length(mats)],dims[1],dims[1]))
          for(i in 1:length(mats))
            out[(c(0,mats)[i]+1):(mats[i]),,]<-g[[i]]
        }else
          out<-g
      }else
        out<-g
    }
  }else{
    if((!simplify)&&(length(dim(g))==3)){  #Coerce to a list of matrices?
      out<-list()
      for(i in 1:dim(g)[1])
        out[[i]]<-g[i,,]
    }else
      out<-g
  }
  #Return the result
  out
}