@@ -958,42 +958,33 @@ ClassicProcess <- function(jaspResults, dataset = NULL, options) {
958
958
return (modOut )
959
959
}
960
960
961
- .procMedEffectsSyntaxModPars <- function (pathEdge , sourceNode , contrFacVars , graph , modProbes ) {
961
+ .procMedEffectsSyntaxModPars <- function (pathEdge , sourceNode , contrFacVars , graph ) {
962
962
# Get moderator parameters for two-way interactions
963
963
modPars <- lapply(pathEdge $ modVars [[1 ]], function (v ) {
964
964
# Get edge for two way interaction between X and M
965
965
twoWayEdge <- igraph :: E(graph )[paste(sourceNode , v , sep = " __" ) %-- % pathEdge $ target ]$ parName
966
966
967
- # Concatenate two way edge parName with moderator probes
968
- return (apply(expand.grid(
969
- twoWayEdge ,
970
- format(modProbes [[v ]], digits = 3 )
971
- ), 1 , paste , collapse = " *" ))
967
+ # Concatenate two way edge parName with moderator
968
+ return (paste(twoWayEdge , v , sep = " *" ))
972
969
})
973
970
974
971
# Concatenate dummy variables for factor moderators
975
972
modPars <- .procMedEffectsSyntaxMergeFacMods(pathEdge $ modVars [[1 ]], contrFacVars , modPars , sep = " + " )
976
973
977
- # Get all possible combinations of probes from different moderators
978
- modPars <- apply(expand.grid(modPars ), 1 , paste , collapse = " + " )
979
-
980
974
# Get name of potential three-way interaction
981
975
threeWayInt <- paste(c(pathEdge $ source , pathEdge $ modVars [[1 ]]), collapse = " __" )
982
976
983
977
if (length(pathEdge $ modVars [[1 ]]) > 1 && threeWayInt %in% igraph :: E(graph )$ source ) { # If three-way int
984
978
# Get edge of three way interaction X x M1 x M2
985
979
threeWayEdge <- igraph :: E(graph )[threeWayInt %-- % pathEdge $ target ]
986
- # Combine three way int parName with moderator probes
980
+ # Combine three way int parName with moderator
987
981
threeWayModPars <- paste(
988
982
threeWayEdge $ parName ,
989
- apply(
990
- expand.grid(lapply(pathEdge $ modVars [[1 ]], function (v ) format(modProbes [[v ]], digits = 3 ))),
991
- 1 , paste , collapse = " *"
992
- ),
983
+ paste(pathEdge $ modVars [[1 ]], collapse = " *" ),
993
984
sep = " *"
994
985
)
995
- # Add to previous moderator probes
996
- modPars <- paste (modPars , threeWayModPars , sep = " + " )
986
+ # Add to previous moderator terms
987
+ modPars <- append (modPars , threeWayModPars )
997
988
}
998
989
return (modPars )
999
990
}
@@ -1067,8 +1058,11 @@ ClassicProcess <- function(jaspResults, dataset = NULL, options) {
1067
1058
sourceFacVars <- numeric (0 )
1068
1059
}
1069
1060
1061
+ rhs <- c()
1062
+ uniqueMods <- c()
1063
+
1070
1064
# Right hand side of lavaan syntax
1071
- rhs <- lapply( 2 : length(path ), function ( i ) {
1065
+ for ( i in 2 : length(path )) {
1072
1066
# Get edge from path[-1] to path[i]
1073
1067
if (any(sourceInContrFacVars ) && names(path )[i - 1 ] %in% sourceFacVars ) {
1074
1068
# Get all vars with factor levels as source nodes
@@ -1081,24 +1075,141 @@ ClassicProcess <- function(jaspResults, dataset = NULL, options) {
1081
1075
# Edge from previous node to current node
1082
1076
pathEdge <- igraph :: E(graph )[sourceNode %-- % names(path )[i ]]
1083
1077
1078
+ modVars <- unlist(pathEdge $ modVars )
1079
+
1084
1080
# If no moderators on edge, return only parName
1085
- if (any(is.na(unlist(pathEdge $ modVars )))) return (pathEdge $ parName )
1081
+ if (any(is.na(modVars ))) {
1082
+ pathEdgeRhs <- pathEdge $ parName
1086
1083
1087
- # Get pars from moderators
1088
- modPars <- .procMedEffectsSyntaxModPars(pathEdge , sourceNode , contrFacVars , graph , modProbes )
1089
-
1090
- # If indirect path add parentheses
1091
- if (i > 1 ) {
1092
- return (paste0(" (" , pathEdge $ parName , " + " , modPars , " )" ))
1084
+ if (length(rhs ) > 0 ) {
1085
+ # Multiply effects on indirect paths
1086
+ rhs <- paste(rhs , pathEdgeRhs , sep = " *" )
1087
+ } else {
1088
+ rhs <- pathEdgeRhs
1089
+ }
1090
+ } else {
1091
+ # Get pars from moderators
1092
+ modPars <- .procMedEffectsSyntaxModPars(pathEdge , sourceNode , contrFacVars , graph )
1093
+
1094
+ # Get sum of moderator terms
1095
+ modPars <- .doCallPaste(modPars , sep = " + " )
1096
+
1097
+ # If indirect path add parentheses
1098
+ if (i > 1 ) {
1099
+ pathEdgeRhs <- paste0(" (" , pathEdge $ parName , " + " , unlist(modPars ), " )" )
1100
+ } else {
1101
+ pathEdgeRhs <- paste(pathEdge $ parName , unlist(modPars ), sep = " + " )
1102
+ }
1103
+
1104
+ if (length(rhs ) > 0 ) {
1105
+ # Get all possible combinations with new moderators
1106
+ if (all(! modVars %in% uniqueMods )) {
1107
+ rhs <- apply(expand.grid(rhs , pathEdgeRhs ), 1 , paste , collapse = " *" )
1108
+ } else {
1109
+ rhs <- paste(rhs , pathEdgeRhs , sep = " *" )
1110
+ }
1111
+ } else {
1112
+ rhs <- pathEdgeRhs
1113
+ }
1114
+ uniqueMods <- unique(c(uniqueMods , modVars ))
1093
1115
}
1094
- # Concanenate path edge parName with moderator probes
1095
- return (paste(pathEdge $ parName , modPars , sep = " + " ))
1096
- })
1097
-
1098
- # If indirect paths, multiply their steps
1099
- rhs <- .doCallPaste(rhs , sep = " *" )
1116
+ }
1100
1117
1101
- return (rhs )
1118
+ modProbes <- modProbes [uniqueMods ]
1119
+
1120
+ # Substitute moderator variables with probe values
1121
+ rhsProbes <- c()
1122
+
1123
+ if (any(sourceInContrFacVars ) && length(modProbes ) > 0 ) {
1124
+ # Identify which modProbes are factor dummies
1125
+ modIdx <- sapply(names(modProbes ), function (nm ) {
1126
+ idx <- which(sapply(contrFacVars , function (fac ) nm %in% fac ))
1127
+
1128
+ if (length(idx ) == 0 ) return (FALSE )
1129
+
1130
+ return (idx )
1131
+ })
1132
+ # Remove non-factor moderators that have index zero
1133
+ facModIdx <- modIdx [modIdx > 0 ]
1134
+
1135
+ facProbeList <- list ()
1136
+ facGroups <- list ()
1137
+ if (any(facModIdx )) {
1138
+ # Group factor dummies by factor (with >2 levels)
1139
+ facGroups <- contrFacVars [unique(facModIdx )]
1140
+ names(facGroups ) <- as.character(unique(facModIdx ))
1141
+ # Concatenate levels of the same factor
1142
+ facProbeList <- lapply(facGroups , function (grp ) {
1143
+ probes <- Reduce(cbind , modProbes [grp ])
1144
+
1145
+ if (is.null(dim(probes ))) return (probes )
1146
+
1147
+ return (apply(probes , 1 , paste , collapse = " _" ))
1148
+ })
1149
+ }
1150
+ # Now, collect all non-factor moderators
1151
+ nonFacMods <- names(modProbes )[modIdx == 0 ]
1152
+ nonFacProbeList <- modProbes [nonFacMods ]
1153
+ # Combine all for expansion
1154
+ expandList <- c(facProbeList , nonFacProbeList )
1155
+ modGrid <- expand.grid(expandList , stringsAsFactors = FALSE )
1156
+ # Assign column names for substitution
1157
+ colNames <- c(names(facProbeList ), nonFacMods )
1158
+ colnames(modGrid ) <- colNames
1159
+
1160
+ # Split concatenated factor columns back into original dummy columns
1161
+ if (length(facProbeList ) > 0 ) {
1162
+ for (k in 1 : length(facProbeList )) {
1163
+ facCol <- names(facProbeList )[k ]
1164
+ # Get the original dummy variable names for this factor
1165
+ origDummies <- facGroups [[facCol ]]
1166
+ if (length(origDummies ) == 1 ) {
1167
+ splitVals <- matrix (modGrid [[facCol ]], ncol = 1 )
1168
+ } else {
1169
+ # Split the concatenated string into separate columns
1170
+ splitVals <- do.call(rbind , strsplit(modGrid [[facCol ]], " _" , fixed = TRUE ))
1171
+ # If only one dummy, keep as vector
1172
+ }
1173
+
1174
+ # Assign columns with original dummy names
1175
+ for (d in 1 : length(origDummies )) {
1176
+ modGrid [[origDummies [d ]]] <- splitVals [, d ]
1177
+ }
1178
+ # Remove the concatenated column
1179
+ modGrid [[facCol ]] <- NULL
1180
+ }
1181
+ }
1182
+
1183
+ for (j in 1 : nrow(modGrid )) { # Probe combinations
1184
+ for (i in 1 : length(rhs )) { # Path edges
1185
+ rowRhs <- rhs [i ]
1186
+ # Replace factor dummies as a group
1187
+ for (p in 1 : ncol(modGrid )) {
1188
+ rowRhs <- gsub(colnames(modGrid )[p ], format(modGrid [j , p ], digits = 3 ), rowRhs )
1189
+ }
1190
+ rhsProbes <- c(rhsProbes , rowRhs )
1191
+ }
1192
+ }
1193
+ } else {
1194
+ # Default: iterate over rhs first, then probes
1195
+ for (i in 1 : length(rhs )) { # Path edges
1196
+ modInPath <- sapply(names(modProbes ), function (v ) grepl(v , rhs [i ]))
1197
+ if (any(modInPath )) {
1198
+ modGrid <- expand.grid(modProbes [modInPath ])
1199
+ for (j in 1 : nrow(modGrid )) { # Probe combinations
1200
+ rowRhs <- rhs [i ]
1201
+ for (p in 1 : ncol(modGrid )) { # Moderator vars
1202
+ rowRhs <- gsub(colnames(modGrid )[p ], format(modGrid [j , p ], digits = 3 ), rowRhs )
1203
+ }
1204
+ rhsProbes <- c(rhsProbes , rowRhs )
1205
+ }
1206
+ } else {
1207
+ rhsProbes <- c(rhsProbes , rhs [i ])
1208
+ }
1209
+ }
1210
+ }
1211
+
1212
+ return (rhsProbes )
1102
1213
}
1103
1214
1104
1215
.procMedEffectsSyntaxSinglePath <- function (path , graph , modProbes , contrasts ) {
0 commit comments