File: sedist.R

package info (click to toggle)
r-cran-blockmodeling 1.1.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 912 kB
  • sloc: ansic: 2,024; f90: 952; sh: 13; makefile: 5
file content (243 lines) | stat: -rw-r--r-- 12,852 bytes parent folder | download | duplicates (2)
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
#' @encoding UTF-8
#' @title Computes distances in terms of Structural equivalence (Lorrain & White, 1971)
#' 
#' @description
#' The functions compute the distances in terms of Structural equivalence (Lorrain and White, 1971) between the units of a one-mode network.
#' Several options for treating the diagonal values are supported.
#' 
# #' @usage
# #' sedist(M, method = "default", fun = "default",
# #' fun.on.rows = "default", handle.interaction = "switch",
# #' use = "pairwise.complete.obs", ...)
#'
#' @param M A matrix representing the (usually valued) network. For now, only one-relational networks are supported. The network must be one-mode.
#' @param method The method used to compute distances - any of the methods allowed by functions dist, \code{"cor"} or \code{"cov"} (all \code{package::stats}) or just \code{"cor"} or \code{"cov"} (given as a character).
#' @param fun Which function should be used to compute distances (given as a character).
#' @param fun.on.rows For non-standard function - does the function compute measure on rows (such as \code{"cor"}, \code{"cov"},...) of the data matrix (as opposed to computing measure on columns (such as \code{dist}).
#' @param handle.interaction How should the interaction between the vertices analysed be handled:\cr
#'        \code{"switch"} (the default) - assumes that when comparing units i and j, M[i,i] should be compared with M[j,j] and M[i,j] with M[j,i]. These two comparisons are weighted by 2. This should be used with Euclidean distance to get the corrected Euclidean distance with p = 2.\cr
#'        \code{"switch2"} - the same (alias)\cr
#'        \code{"switch1"} - the same as above, only that the two comparisons are weighted by 1. This should be used with Euclidean distance to get the corrected Wuclidean distance with p = 1.\cr
#'        \code{"ignore"} (diagonal) - Diagonal is ignored. This should be used with Euclidean distance to get the corrected Euclidean distance with p = 0.\cr
#'        \code{"none"} - the matrix is used "as is"
#' @param use For use with methods \code{"cor"} and \code{"cov"}, for other methods (the default option should be used if \code{handle.interaction == "ignore"}), \code{"pairwise.complete.obs"} are always used, if \code{stats.dist.cor.cov = TRUE}.
#' @param \dots Additional arguments to \code{fun}
#' 
#' @details
#' If both \code{method} and \code{fun} are \code{"default"}, the Euclidean distances are computed.
#' The \code{"default"} method for \code{fun = "dist"} is "euclidean" and for \code{fun  = "cor"} "pearson".  
#'
#' @return A matrix (usually of class dist) is returned.
#' 
#' @references
#' Batagelj, V., Ferligoj, A., & Doreian, P. (1992). Direct and indirect methods for structural equivalence. Social Networks, 14(1-2), 63-90. doi: 10.1016/0378-8733(92)90014-X
#' 
#' Lorrain, F., & White, H. C. (1971). Structural equivalence of individuals in social networks. Journal of Mathematical Sociology, 1(1), 49-80. doi: 10.1080/0022250X.1971.9989788
#' 
#' @author \enc{Aleš Žiberna}{Ales Ziberna}
#' @seealso \code{\link{dist}}, \code{\link{hclust}}, \code{\link{REGE}}, \code{\link{optParC}}, \code{\link{optParC}}, \code{\link{optRandomParC}}
#' 
#' @examples
#' # Generating a simple network corresponding to the simple Sum of squares
#' # Structural equivalence with blockmodel:
#' # null com
#' # null null
#' n <- 20
#' net <- matrix(NA, ncol = n, nrow = n)
#' clu <- rep(1:2, times = c(5, 15))
#' tclu <- table(clu)
#' net[clu == 1, clu == 1] <- rnorm(n = tclu[1] * tclu[1], mean = 0, sd = 1)
#' net[clu == 1, clu == 2] <- rnorm(n = tclu[1] * tclu[2], mean = 4, sd = 1)
#' net[clu == 2, clu == 1] <- rnorm(n = tclu[2] * tclu[1], mean = 0, sd = 1)
#' net[clu == 2, clu == 2] <- rnorm(n = tclu[2] * tclu[2], mean = 0, sd = 1)
#'
#' D <- sedist(M = net)
#' plot.mat(net, clu = cutree(hclust(d = D, method = "ward"), k = 2))
#' 
#' @keywords cluster graphs
#' @importFrom stats as.dist cor cov na.omit
#' 
#' @export

"sedist" <-
function(
	M,	#matrix (of a network)
	method="default", 	# the a method used to compute distances - any of the methods alloed by functions dist, cor or cov {all package::stats} or just "cor" or "cov" (given as character)
	fun="default",	#which function should be used to comput distacnes (given as character),
	fun.on.rows="default", # for non-standard function - does it compute measure on rows (such as cor, cov,...) of the data matrix.
#	stats.dist.cor.cov=TRUE,	#call "stats::dist", "stats::cor" or "stats::cov", not "dist", "cor" or "cov", if nonstandard functions are used, they should exemp the same arguments as those in package stats
	handle.interaction="switch",	#how should the interaction between the vertices analysed be handled:
						# "switch" (the default) - assumes that when comparing units i and j, M[i,i] should be compared with M[j,j] and M[i,j] with M[j,i]
						# "switch1" - the same as above, only that each pair occurs only once
						# "switch2" - an alias for switch
						# "ignore" (diagonal) - Diagonal is ignored
						# "none" - the matrix is used "as is"
	use = "pairwise.complete.obs",	#for use with methods "cor" and "cov", for other methods (the default option should be used if handle.interaction=="ignore"), "pairwise.complete.obs" are always used, if stats.dist.cor.cov=TRUE
	#p=2	,#The power of the Minkowski distance in functin dist if stats.dist.cor.cov=TRUE
	... #other argumets passed to fun
)
{

	method<-match.arg(method, choices=c("euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski","pearson", "kendall", "spearman","dist","cor", "cov", "default"))
	handle.interaction<-match.arg(handle.interaction, choices=c("switch", "switch1", "switch2", "ignore", "none"))
	if(handle.interaction=="switch2")handle.interaction<-"switch"
	if(any(method=="default", fun=="default")){
		if(all(method=="default", fun=="default")){
			fun<-"dist"
			method<-"euclidean"
		} else if(fun=="default"){
			if(method %in% c("pearson", "kendall", "spearman")) fun<-"cor"
			if(method %in% c("cor", "cov")){
				fun<-method
				method<-"pearson"
			}
			if(method %in% c("euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski")) fun<-"dist"
		} else {
			if(fun %in% c("cor","cov")) method<-"pearson"
			if(fun=="dist") method<-"euclidean"
		}
	}

	if(handle.interaction=="ignore"&& fun %in% c("cor","cov") && use != "pairwise.complete.obs")warning("The option use='pairwise.complete.obs' should be used with handle.interaction=='ignore' && fun %in% c('cor','cov')")

#	if(fun %in% c("dist", "cor" or "cov") && stats.dist.cor.cov) fun<-paste("stats::",fun,sep="")
	if(fun.on.rows=="default") if(fun %in% c("cor","cov")){
		fun.on.rows<-TRUE
	} else fun.on.rows<-FALSE

	n<-dim(M)[1]
	if(n!=dim(M)[2]) stop("This function is suited for one-mode networks only")
    if(fun %in% c("cor", "cov")) usearg<-list(use=use) else usearg<-NULL #usearg

	if(handle.interaction %in% c("switch","switch1")){
		if(fun=="cor"){
			cor1<-function(...)cor(...)[1,2]
			fun<-"cor1"
		}
		if(fun=="cov"){
			cor1<-function(...)cov(...)[1,2]
			fun<-"cov1"
		}
		X<-cbind(M,t(M))
		res<-matrix(NA,ncol=n,nrow=n)
		for(i in 2:n)for(j in seq(length=(i-1))){
			jind<-seq(length=2*n)
			jind[i]<-j
			jind[j]<-i
			jind[n+i]<-ifelse(handle.interaction=="switch",n+j,NA)
			jind[n+j]<-ifelse(handle.interaction=="switch",n+i,NA)
			Xij<-rbind(X[i,],X[j,jind])
			if(fun.on.rows)Xij<-t(Xij)
			res[i,j]<-do.call(fun,args=c(list(x=Xij, method=method,...),usearg))
		}
		if(handle.interaction=="switch1" & fun=="dist" & !(method%in%c("maximum","binary"))) res<-res*sqrt((n-1)/n)
		res<-as.dist(res)
	}else{
		if(handle.interaction=="ignore") diag(M)<-NA
		X<-cbind(M,t(M))
		if(fun.on.rows)X<-t(X)
		res<-do.call(fun,args=c(list(x=X, method=method,...),usearg))
	}
	if(inherits(res,"dist"))attr(res,"Labels")<-rownames(M)
	if(is.matrix(res))dimnames(res)<-dimnames(M)
	return(res)	
}



"sedistX" <-    function(
    X,	#a matrix composed of network and network transposed
    method="default", 	# the a method used to compute distances - any of the methods alloed by functions dist, cor or cov {all package::stats} or just "cor" or "cov" (given as character)
    fun="default",	#which function should be used to comput distacnes (given as character),
    fun.on.rows="default", # for non-standard function - does it compute measure on rows (such as cor, cov,...) of the data matrix.
    #	stats.dist.cor.cov=TRUE,	#call "stats::dist", "stats::cor" or "stats::cov", not "dist", "cor" or "cov", if nonstandard functions are used, they should exemp the same arguments as those in package stats
    handle.interaction="switch",	#how should the interaction between the vertices analysed be handled:
    # "switch" (the default) - assumes that when comparing units i and j, M[i,i] should be compared with M[j,j] and M[i,j] with M[j,i]
    # "switch1" - the same as above, only that each pair occurs only once
    # "switch2" - an alias for switch
    # "ignore" (diagonal) - Diagonal is ignored
    # "none" - the matrix is used "as is"
    use = "pairwise.complete.obs",	#for use with methods "cor" and "cov", for other methods (the default option should be used if handle.interaction=="ignore"), "pairwise.complete.obs" are always used, if stats.dist.cor.cov=TRUE
    #p=2	,#The power of the Minkowski distance in functin dist if stats.dist.cor.cov=TRUE
    ... #other argumets passed to fun
){
    
    method<-match.arg(method, choices=c("euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski","pearson", "kendall", "spearman","dist","cor", "cov", "default"))
    handle.interaction<-match.arg(handle.interaction, choices=c("switch", "switch1", "switch2", "ignore", "none"))
    if(handle.interaction=="switch2")handle.interaction<-"switch"
    if(any(method=="default", fun=="default")){
        if(all(method=="default", fun=="default")){
            fun<-"dist"
            method<-"euclidean"
        } else if(fun=="default"){
            if(method %in% c("pearson", "kendall", "spearman")) fun<-"cor"
            if(method %in% c("cor", "cov")){
                fun<-method
                method<-"pearson"
            }
            if(method %in% c("euclidean", "maximum", "manhattan", "canberra", "binary", "minkowski")) fun<-"dist"
        } else {
            if(fun %in% c("cor","cov")) method<-"pearson"
            if(fun=="dist") method<-"euclidean"
        }
    }
    
    if(handle.interaction=="ignore"&& fun %in% c("cor","cov") && use != "pairwise.complete.obs")warning("The option use='pairwise.complete.obs' should be used with handle.interaction=='ignore' && fun %in% c('cor','cov')")
    
    #	if(fun %in% c("dist", "cor" or "cov") && stats.dist.cor.cov) fun<-paste("stats::",fun,sep="")
    if(fun.on.rows=="default") if(fun %in% c("cor","cov")){
        fun.on.rows<-TRUE
    } else fun.on.rows<-FALSE
    
    n<-dim(X)[1]
    if(dim(X)[2]%%n!=0) stop("The columns must be a multiple of the rows")
    k<-dim(X)[2]/n
    if(fun %in% c("cor", "cov")) usearg<-list(use=use) else usearg<-NULL #usearg
    
    if(handle.interaction %in% c("switch","switch1")){
        if(fun=="cor"){
            cor1<-function(...)cor(...)[1,2]
            fun<-"cor1"
        }
        if(fun=="cov"){
            cor1<-function(...)cov(...)[1,2]
            fun<-"cov1"
        }
        res<-matrix(NA,ncol=n,nrow=n)
        for(i in 2:n)for(j in seq(length=(i-1))){
            jind<-seq(length=k*n)
            for(l in seq(0,k-1,by = 2)){
                jind[l*n+i]<-j
                jind[l*n+j]<-i
                if((l+1)<k){
                    if(handle.interaction=="switch"){
                        jind[(l+1)*n+i]<-(l+1)*n+j
                        jind[(l+1)*n+j]<-(l+1)*n+i
                    }else{
                        jind[(l+1)*n+i]<-NA
                        jind[(l+1)*n+j]<-NA
                    }
                } 
            }
            Xij<-rbind(X[i,],X[j,jind])
            if(fun.on.rows)Xij<-t(Xij)
            res[i,j]<-do.call(fun,args=c(list(x=Xij, method=method,...),usearg))
        }
        if(handle.interaction=="switch1" & fun=="dist" & !(method%in%c("maximum","binary"))) res<-res*sqrt((n-1)/n)
        res<-as.dist(res)
    }else{
        for(i in 1:n){
            for(l in 0:(k-1)){
                X[i,l*n+i]<-NA
            }
        }
        if(fun.on.rows)X<-t(X)
        res<-do.call(fun,args=c(list(x=X, method=method,...),usearg))
        if(fun.on.rows)X<-t(X)
    }
    if(inherits(res,"dist"))attr(res,"Labels")<-rownames(X)
    if(is.matrix(res))colnames(res)<-rownames(res)<-rownames(X)
    return(res)	
}