@@ -80,15 +80,22 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
80
80
delta = 0 , verbose = FALSE , conv_crit = 1e-4 ,
81
81
maxiter = 500 , check_installed = T ,
82
82
order_by = seq(1 , nrow(data )),
83
- order_type = c(" cosine " , " side " , " align " )[ 3 ] ,
84
- volume_ratio = c(" t_ratio" , " variance_ratio" , " none" )[ 1 ] ,
83
+ order_type = c(" align " , " cosine " , " side " ) ,
84
+ volume_ratio = c(" t_ratio" , " variance_ratio" , " none" ),
85
85
converge_else_fail = TRUE ,
86
86
var_in_dims = FALSE , normalise_var = TRUE ,
87
87
method = c(" pcha" , " kmeans" ), method_options = list ()) {
88
88
89
- if (check_installed ) .py_pcha_installed()
89
+ # check arguments
90
+ method = match.arg(method )
91
+ volume_ratio = match.arg(volume_ratio )
92
+ order_type = match.arg(order_type )
93
+
94
+
95
+ if (method == " pcha" ){
96
+
97
+ if (check_installed ) .py_pcha_installed()
90
98
91
- if (isTRUE(method [1 ] == " pcha" )){
92
99
# run PCHA -------------------------------------------------------------------
93
100
94
101
# coerce to matrix
@@ -112,8 +119,10 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
112
119
})
113
120
# ---------------------------------------------------------------------------
114
121
115
- } else if (isTRUE(method [1 ] == " kmeans" )) {
122
+ } else if (method == " kmeans" ) {
123
+
116
124
# run k-means --------------------------------------------------------------
125
+
117
126
# set defaults or replace them with provided options
118
127
default = list (iter.max = 10 , nstart = 1 ,
119
128
algorithm = c(" Hartigan-Wong" , " Lloyd" , " Forgy" ,
@@ -122,19 +131,26 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
122
131
options = c(default [default_retain ], method_options )
123
132
124
133
res = kmeans(Matrix :: t(data ), centers = as.integer(noc ),
125
- iter.max = default $ iter.max , nstart = default $ nstart ,
126
- algorithm = default $ algorithm , trace = default $ trace )
127
- res = list (XC = t(res $ centers ),
128
- S = Matrix :: sparseMatrix(i = res $ cluster ,
129
- j = seq_len(length(res $ cluster )), x = 1 ),
130
- C = Matrix :: sparseMatrix(i = res $ cluster ,
131
- j = seq_len(length(res $ cluster )), x = 0 ),
134
+ iter.max = options $ iter.max , nstart = options $ nstart ,
135
+ algorithm = options $ algorithm , trace = options $ trace )
136
+
137
+ # Create S as binary cluster membership matrix
138
+ S = Matrix :: sparseMatrix(i = res $ cluster ,
139
+ j = seq_len(length(res $ cluster )), x = 1 )
140
+ # compute C so that X %*% C gives cluster averages
141
+ C = S / matrix (Matrix :: rowSums(S ), nrow = nrow(S ), ncol = ncol(S ), byrow = FALSE )
142
+ C = Matrix :: t(C )
143
+
144
+ # create pch_fit object to be returned
145
+ res = list (XC = t(res $ centers ), S = S , C = C ,
132
146
SSE = res $ tot.withinss , varexpl = res $ betweenss / res $ totss )
133
147
if (! is.null(rownames(data ))) rownames(res $ XC ) = rownames(data )
134
148
colnames(res $ XC ) = NULL
135
149
class(res ) = " pch_fit"
150
+
136
151
# ---------------------------------------------------------------------------
137
- } else stop(" method should be pcha or kmeans" )
152
+
153
+ } else stop(" method should be pcha or kmeans" )
138
154
139
155
if (is.null(res )) return (NULL )
140
156
@@ -145,7 +161,7 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
145
161
arch_order = .find_archetype_order(XC2 , noc , order_type = order_type )
146
162
res $ XC = res $ XC [, arch_order ]
147
163
res $ S = res $ S [arch_order , ]
148
- res $ C = res $ C [arch_order , ]
164
+ res $ C = res $ C [, arch_order ]
149
165
} else {
150
166
# when only one archetype make sure data is still in the matrix form
151
167
res $ XC = matrix (res $ XC , length(res $ XC ), 1 )
@@ -158,7 +174,7 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
158
174
# when calculating convex hull and volume of the polytope
159
175
# adjust number of dimensions to noc
160
176
data_dim = seq(1 , noc - 1 )
161
- if (volume_ratio == " t_ratio" & nrow(data ) > = length(data_dim ) & noc > 2 ){
177
+ if (volume_ratio == " t_ratio" & nrow(data ) > = length(data_dim ) & noc > 2 & method != " poisson_aa " ){
162
178
# calculate volume or area of the polytope only when number of archetypes (noc) > number of dimenstions which means
163
179
# find volume of the convex hull of the data
164
180
hull_vol = fit_convhulln(data [data_dim , ], positions = FALSE )
@@ -201,7 +217,7 @@ fit_pch = function(data, noc = as.integer(3), I = NULL, U = NULL,
201
217
202
218
} else {
203
219
204
- # 3 none
220
+ # 3 NA
205
221
206
222
if (volume_ratio == " t_ratio" & isTRUE(converge_else_fail )) message(paste0(" Convex hull and t-ratio not computed for noc: " , noc ," and nrow(data) = " , nrow(data )," . fit_pch() can calculate volume or area of the polytope only when\n the number of archetypes (noc) > the number of dimensions (when polytope is convex):\n check that noc > nrow(data),\n select only revelant dimensions or increase noc" ))
207
223
res $ hull_vol = NA
@@ -1095,3 +1111,20 @@ plot_dim_var = function(rand_arch,
1095
1111
}
1096
1112
res
1097
1113
}
1114
+
1115
+ # #' @rdname fit_pch
1116
+ # #' @name pch_fit
1117
+ # #' @description \code{pch_fit()} a constructor function for the "pch_fit" class
1118
+ # #' @export pch_fit
1119
+ pch_fit = function (XC , S , C , SSE , varexpl , arc_vol , hull_vol , t_ratio , var_vert ,
1120
+ var_dim , total_var , summary , call ) {
1121
+
1122
+ # add integrity checks
1123
+
1124
+ # create object
1125
+ structure(list (XC = XC , S = S , C = C , SSE = SSE , varexpl = varexpl ,
1126
+ arc_vol = arc_vol , hull_vol = hull_vol , t_ratio = t_ratio ,
1127
+ var_vert = var_vert , var_dim = var_dim , total_var = total_var ,
1128
+ summary = summary , call = call ),
1129
+ class = " pch_fit" )
1130
+ }
0 commit comments