# install.packages("seminr")
library(seminr)
# install.packages("readxl") # run once
library(readxl)
my_data <- read_excel(path = "AFF_TPB_Poland_Survey_2025.xlsx", sheet = 3)
my_data
head(my_data)
colnames(my_data)
## [1] "ID" "Time" "Age_group" "Age" "Province" "ATT1"
## [7] "ATT2" "ATT3" "SN1" "SN2" "SN3" "PBC1"
## [13] "PBC2" "PBC3" "PI1" "PI2" "PI3" "AF1"
## [19] "AF2" "AF3" "Marital" "Fam_size" "Job" "Income"
# to create reflective mm (no need to write mode_A)
simple_mm<- constructs(
composite("PI", multi_items("PI",1:3)),
composite("ATT", multi_items("ATT",1:3)),
composite("SN", multi_items("SN",1:3)),
composite("PBC", multi_items("PBC",2:3)), #PBC1 was removed because loading=0.540 and alpha< 0.7its removal has a better effect on reliability and validity metrics/content validity (<0.708 recommendation limit)
composite("AF", multi_items("AF",1:3))
)
simple_mm
## $composite
## [1] "PI" "PI1" "A" "PI" "PI2" "A" "PI" "PI3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "ATT" "ATT1" "A" "ATT" "ATT2" "A" "ATT" "ATT3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "SN" "SN1" "A" "SN" "SN2" "A" "SN" "SN3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "PBC" "PBC2" "A" "PBC" "PBC3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "AF" "AF1" "A" "AF" "AF2" "A" "AF" "AF3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## attr(,"class")
## [1] "list" "measurement_model" "seminr_model"
# to create sm
simple_sm <- relationships(
paths(from = c("ATT","SN","PBC","AF"), to = "PI")
)
simple_sm
## source target
## [1,] "ATT" "PI"
## [2,] "SN" "PI"
## [3,] "PBC" "PI"
## [4,] "AF" "PI"
## attr(,"class")
## [1] "matrix" "array" "structural_model" "seminr_model"
# to estimate the model
simple_model <- estimate_pls(data = my_data,
measurement_model = simple_mm,
structural_model = simple_sm,
inner_weights = path_weighting,
missing = mean_replacement,
missing_value = "-99")
## Generating the seminr model
## All 999 observations are valid.
# summary() [gives info about sub-meta]can be used for estimate_pls(), bootstrap_model() and predict_pls() functions
summary_simple <- summary(simple_model) #shows the path coefficients and reliability
summary_simple
##
## Results from package seminr (2.3.7)
##
## Path Coefficients:
## PI
## R^2 0.746
## AdjR^2 0.745
## ATT 0.194
## SN 0.130
## PBC 0.112
## AF 0.555
##
## Reliability:
## alpha rhoC AVE rhoA
## ATT 0.924 0.952 0.869 0.927
## SN 0.925 0.952 0.870 0.926
## PBC 0.744 0.886 0.796 0.744
## AF 0.737 0.851 0.657 0.745
## PI 0.950 0.968 0.909 0.950
##
## Alpha, rhoC, and rhoA should exceed 0.7 while AVE should exceed 0.5
# plot the see estimated model
plot(simple_model) #β IS PATH COEFFICIENT ; λ IS outer loadings
summary_simple$iterations # should be lower than 300
## [1] 4
boot_simple <- bootstrap_model(
seminr_model = simple_model,
nboot = 10000,
cores = NULL,
seed = 123
)
## Bootstrapping model using seminr...
## SEMinR Model successfully bootstrapped
# store the summary of the bootstrapped model
# A path is significant when its **t-value exceeds 1.96 and its 95% confidence interval does not include zero
summary_boot <- summary(boot_simple,alpha=0.10)
summary_boot
##
## Results from Bootstrap resamples: 10000
##
## Bootstrapped Structural Paths:
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## ATT -> PI 0.194 0.194 0.025 7.706 0.152 0.236
## SN -> PI 0.130 0.130 0.028 4.670 0.085 0.176
## PBC -> PI 0.112 0.112 0.028 4.042 0.066 0.157
## AF -> PI 0.555 0.554 0.031 18.133 0.503 0.604
##
## Bootstrapped Weights:
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## PI1 -> PI 0.346 0.346 0.003 132.067 0.342 0.350
## PI2 -> PI 0.348 0.349 0.002 141.424 0.345 0.353
## PI3 -> PI 0.354 0.354 0.003 108.293 0.349 0.360
## ATT1 -> ATT 0.369 0.369 0.007 55.779 0.358 0.380
## ATT2 -> ATT 0.365 0.365 0.007 49.638 0.353 0.377
## ATT3 -> ATT 0.339 0.339 0.007 46.078 0.327 0.351
## SN1 -> SN 0.361 0.362 0.006 62.071 0.352 0.372
## SN2 -> SN 0.345 0.345 0.004 77.382 0.338 0.353
## SN3 -> SN 0.366 0.366 0.006 58.066 0.356 0.377
## PBC2 -> PBC 0.563 0.563 0.014 39.223 0.540 0.587
## PBC3 -> PBC 0.558 0.558 0.014 40.433 0.536 0.581
## AF1 -> AF 0.433 0.433 0.015 29.864 0.410 0.458
## AF2 -> AF 0.454 0.454 0.012 38.700 0.436 0.474
## AF3 -> AF 0.348 0.347 0.011 31.639 0.329 0.365
##
## Bootstrapped Loadings:
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## PI1 -> PI 0.954 0.953 0.005 204.402 0.945 0.961
## PI2 -> PI 0.961 0.961 0.004 252.291 0.954 0.967
## PI3 -> PI 0.946 0.946 0.006 158.747 0.936 0.955
## ATT1 -> ATT 0.951 0.951 0.004 224.009 0.944 0.958
## ATT2 -> ATT 0.939 0.939 0.009 109.963 0.924 0.952
## ATT3 -> ATT 0.905 0.905 0.010 92.792 0.888 0.920
## SN1 -> SN 0.929 0.929 0.006 145.914 0.918 0.939
## SN2 -> SN 0.943 0.943 0.006 158.861 0.932 0.952
## SN3 -> SN 0.926 0.926 0.007 132.918 0.914 0.937
## PBC2 -> PBC 0.893 0.893 0.010 93.733 0.877 0.908
## PBC3 -> PBC 0.891 0.891 0.010 87.279 0.873 0.906
## AF1 -> AF 0.738 0.738 0.019 38.030 0.704 0.768
## AF2 -> AF 0.884 0.884 0.009 103.643 0.869 0.897
## AF3 -> AF 0.803 0.803 0.018 44.274 0.771 0.831
##
## Bootstrapped HTMT:
## Original Est. Bootstrap Mean Bootstrap SD 5% CI 95% CI
## ATT -> SN 0.643 0.643 0.025 0.601 0.683
## ATT -> PBC 0.743 0.744 0.029 0.695 0.790
## ATT -> AF 0.704 0.705 0.032 0.651 0.756
## ATT -> PI 0.713 0.713 0.023 0.673 0.750
## SN -> PBC 0.754 0.754 0.028 0.707 0.799
## SN -> AF 0.733 0.734 0.029 0.684 0.780
## SN -> PI 0.696 0.696 0.024 0.656 0.734
## PBC -> AF 0.882 0.882 0.032 0.828 0.934
## PBC -> PI 0.804 0.804 0.025 0.763 0.844
## AF -> PI 0.971 0.971 0.014 0.948 0.994
##
## Bootstrapped Total Paths:
## Original Est. Bootstrap Mean Bootstrap SD 5% CI 95% CI
## ATT -> PI 0.194 0.194 0.025 0.152 0.236
## SN -> PI 0.130 0.130 0.028 0.085 0.176
## PBC -> PI 0.112 0.112 0.028 0.066 0.157
## AF -> PI 0.555 0.554 0.031 0.503 0.604
# use tstat and CI from Bootstrapped Structural Paths results to check which is significant
# inspect the items VIF
summary_simple$validity$vif_items # CONCLUSION: PI1 and PI2 has VIF > 5, So presence of multicollinearity problems (not good)
## ATT :
## ATT1 ATT2 ATT3
## 4.792 4.197 2.798
##
## SN :
## SN1 SN2 SN3
## 3.473 4.223 3.270
##
## PBC :
## PBC2 PBC3
## 1.54 1.54
##
## AF :
## AF1 AF2 AF3
## 1.226 2.179 1.974
##
## PI :
## PI1 PI2 PI3
## 5.277 5.977 4.389
summary_simple$vif_antecedents # CONCLUSION: all constructs VIF < 5, So absence of multicollinearity problems (good)
## PI :
## ATT SN PBC AF
## 1.924 1.992 2.212 2.089
# to inspect specific significance direct effects
summary_boot$bootstrapped_paths #path coefficent is sig at 5% if there is no zeros in between the CI
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## ATT -> PI 0.194 0.194 0.025 7.706 0.152 0.236
## SN -> PI 0.130 0.130 0.028 4.670 0.085 0.176
## PBC -> PI 0.112 0.112 0.028 4.042 0.066 0.157
## AF -> PI 0.555 0.554 0.031 18.133 0.503 0.604
# to inspect the Bootstrapped Structural loadings
summary_boot$bootstrapped_loadings
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## PI1 -> PI 0.954 0.953 0.005 204.402 0.945 0.961
## PI2 -> PI 0.961 0.961 0.004 252.291 0.954 0.967
## PI3 -> PI 0.946 0.946 0.006 158.747 0.936 0.955
## ATT1 -> ATT 0.951 0.951 0.004 224.009 0.944 0.958
## ATT2 -> ATT 0.939 0.939 0.009 109.963 0.924 0.952
## ATT3 -> ATT 0.905 0.905 0.010 92.792 0.888 0.920
## SN1 -> SN 0.929 0.929 0.006 145.914 0.918 0.939
## SN2 -> SN 0.943 0.943 0.006 158.861 0.932 0.952
## SN3 -> SN 0.926 0.926 0.007 132.918 0.914 0.937
## PBC2 -> PBC 0.893 0.893 0.010 93.733 0.877 0.908
## PBC3 -> PBC 0.891 0.891 0.010 87.279 0.873 0.906
## AF1 -> AF 0.738 0.738 0.019 38.030 0.704 0.768
## AF2 -> AF 0.884 0.884 0.009 103.643 0.869 0.897
## AF3 -> AF 0.803 0.803 0.018 44.274 0.771 0.831
# to inspect the r^square
summary_simple$paths # check for direct effects
## PI
## R^2 0.746
## AdjR^2 0.745
## ATT 0.194
## SN 0.130
## PBC 0.112
## AF 0.555
# CONCLUSION: PI model,R2 =0.746; paths coefficients: ATT=0.194, PBC=0.112, SN=0.130,AF = 0.555
# inspect the effect size
### 0.00 - 0.02 = No/very weak effect
### 0.02 to 0.15 = Small effect
### 0.15 to 0.35 = Medium effect
### 0.35 to 0.50 = Large effect
summary_simple$fSquare #CONCLUSION: large effect (AF),medium (), small(ATT,SN,PBC), no/very weak()
## ATT SN PBC AF PI
## ATT 0.000 0.000 0.000 0.000 0.077
## SN 0.000 0.000 0.000 0.000 0.033
## PBC 0.000 0.000 0.000 0.000 0.022
## AF 0.000 0.000 0.000 0.000 0.580
## PI 0.000 0.000 0.000 0.000 0.000
predict_model<- predict_pls(
model=simple_model,
technique = predict_DA,
noFolds = 10,
reps = 10
)
# compare PLS out-of-sample metrics: to LM out-of-sample metrics
# summarise the prediction results
summary_predict<- summary(predict_model)
summary_predict
##
## PLS in-sample metrics:
## PI1 PI2 PI3
## RMSE 0.569 0.562 0.543
## MAE 0.424 0.419 0.405
##
## PLS out-of-sample metrics:
## PI1 PI2 PI3
## RMSE 0.575 0.567 0.547
## MAE 0.427 0.422 0.407
##
## LM in-sample metrics:
## PI1 PI2 PI3
## RMSE 0.559 0.548 0.518
## MAE 0.405 0.396 0.376
##
## LM out-of-sample metrics:
## PI1 PI2 PI3
## RMSE 0.572 0.562 0.529
## MAE 0.412 0.404 0.383
##
## Construct Level metrics:
## PI
## IS_MSE 1.8344
## IS_MAE 1.0436
## OOS_MSE 1.8384
## OOS_MAE 1.0443
## overfit 0.0022
# Conclusion: Both PLS and LM demonstrate consistent predictive accuracy, with negligible overfitting and strong out-of-sample performance.
library(moments)
# All indicators
indicators <- c(paste0("PI",1:3), paste0("ATT",1:3), paste0("SN",1:3),
paste0("PBC",2:3), paste0("AF",1:3))
# Skewness
skew_values <- sapply(my_data[indicators], skewness, na.rm = TRUE)
# mostly moderate negative skew.
# Shapiro-Wilk p-values
shapiro_p <- sapply(my_data[indicators], function(x) shapiro.test(x)$p.value)
# All p-values are extremely small (< 0.05). all indicators are “statistically non-normal”, but this is normal in large samples.
# Combine results
results <- data.frame(Indicator = indicators, Skewness = skew_values, Shapiro_p = shapiro_p)
print(results) #Shapiro-Wilk is not reliable for large N — rely on skewness and practical judgment.
## Indicator Skewness Shapiro_p
## PI1 PI1 -0.58208631 1.437934e-27
## PI2 PI2 -0.57712795 2.198353e-27
## PI3 PI3 -0.63672682 6.417538e-28
## ATT1 ATT1 -0.52195984 1.734718e-28
## ATT2 ATT2 -0.55803035 1.969654e-28
## ATT3 ATT3 -0.37466091 2.241768e-29
## SN1 SN1 -0.15909899 1.315768e-27
## SN2 SN2 -0.18785450 2.464034e-29
## SN3 SN3 -0.27877487 3.100212e-30
## PBC2 PBC2 -0.18079183 5.620343e-29
## PBC3 PBC3 -0.20067175 3.553573e-27
## AF1 AF1 -0.81989060 2.737072e-29
## AF2 AF2 -0.27560308 3.580737e-25
## AF3 AF3 -0.04715377 5.882178e-24
#RESULTS:All indicators showed slight negative skewness. Shapiro–Wilk tests indicated non-normal distributions (p < .001)
# Recommendation: Use MAE for PLSpredict metrics and interpret with skewness in mind.
####OR PLOT TO CHECK SKEWNESS#####
## to plot and see for each indicators
par(mfrow=c(1,2))
plot(summary_predict,
indicator = "PI2")
plot(summary_predict,
indicator = "PI3")
par(mfrow=c(1,1))
# inspect the construct loadings metrics:ideally be ≥ 0.70, with 0.60–0.70 acceptable and < 0.60 considered problematic.
summary_simple$loadings #(indicator loading should be > 0.708)
## ATT SN PBC AF PI
## PI1 0.000 0.000 0.000 0.000 0.954
## PI2 0.000 0.000 0.000 0.000 0.961
## PI3 0.000 0.000 0.000 0.000 0.946
## ATT1 0.951 0.000 0.000 0.000 0.000
## ATT2 0.939 0.000 0.000 0.000 0.000
## ATT3 0.905 0.000 0.000 0.000 0.000
## SN1 0.000 0.929 0.000 0.000 0.000
## SN2 0.000 0.943 0.000 0.000 0.000
## SN3 0.000 0.926 0.000 0.000 0.000
## PBC2 0.000 0.000 0.893 0.000 0.000
## PBC3 0.000 0.000 0.891 0.000 0.000
## AF1 0.000 0.000 0.000 0.738 0.000
## AF2 0.000 0.000 0.000 0.884 0.000
## AF3 0.000 0.000 0.000 0.803 0.000
# Conclusion: all indicators are > 0.708
summary_simple$loadings^2 #(indicator reliability should be>0.5)
## ATT SN PBC AF PI
## PI1 0.000 0.000 0.000 0.000 0.909
## PI2 0.000 0.000 0.000 0.000 0.923
## PI3 0.000 0.000 0.000 0.000 0.896
## ATT1 0.905 0.000 0.000 0.000 0.000
## ATT2 0.882 0.000 0.000 0.000 0.000
## ATT3 0.819 0.000 0.000 0.000 0.000
## SN1 0.000 0.863 0.000 0.000 0.000
## SN2 0.000 0.889 0.000 0.000 0.000
## SN3 0.000 0.857 0.000 0.000 0.000
## PBC2 0.000 0.000 0.798 0.000 0.000
## PBC3 0.000 0.000 0.794 0.000 0.000
## AF1 0.000 0.000 0.000 0.544 0.000
## AF2 0.000 0.000 0.000 0.781 0.000
## AF3 0.000 0.000 0.000 0.645 0.000
# Conclusion: all indicators are > 0.5. No issues of non-reliability of indicators
# to inspect composite reliability rhoc of estimated model
summary_simple$reliability ### Note: only PBC has alpha<0.7 in the original model (not good);so PBC1 was removed
## alpha rhoC AVE rhoA
## ATT 0.924 0.952 0.869 0.927
## SN 0.925 0.952 0.870 0.926
## PBC 0.744 0.886 0.796 0.744
## AF 0.737 0.851 0.657 0.745
## PI 0.950 0.968 0.909 0.950
##
## Alpha, rhoC, and rhoA should exceed 0.7 while AVE should exceed 0.5
# rhoa values should be between alpha and rhoc
# Conclusion : all are relaible
# to plot reliability chart
plot(summary_simple$reliability) #horizontal blue line is the threshold
# STEP 3: Assess the convergent validity - Validity assessment is AVE. -
Average variance extracted (AVE) = SUM OF SQUARED LOADINGS/NO OF
INDICATORS (>=0.50 is acceptable).
summary_simple$reliability # Conclusion: all AVE values are > 0.5:good
## alpha rhoC AVE rhoA
## ATT 0.924 0.952 0.869 0.927
## SN 0.925 0.952 0.870 0.926
## PBC 0.744 0.886 0.796 0.744
## AF 0.737 0.851 0.657 0.745
## PI 0.950 0.968 0.909 0.950
##
## Alpha, rhoC, and rhoA should exceed 0.7 while AVE should exceed 0.5
summary_simple$validity$fl_criteria #first value in each column should be higher than other values below
## ATT SN PBC AF PI
## ATT 0.932 . . . .
## SN 0.595 0.933 . . .
## PBC 0.616 0.626 0.892 . .
## AF 0.593 0.609 0.656 0.810 .
## PI 0.669 0.653 0.676 0.822 0.954
##
## FL Criteria table reports square root of AVE on the diagonal and construct correlations on the lower triangle.
# to inspect Henseler proposal
summary_simple$validity$htmt #all of the values should be <0.90
## ATT SN PBC AF PI
## ATT . . . . .
## SN 0.643 . . . .
## PBC 0.743 0.754 . . .
## AF 0.704 0.733 0.882 . .
## PI 0.713 0.696 0.804 0.971 .
summary_simple$validity$cross_loadings #loadings values for each indicators should be the highest at its own construct and not other constructs
## ATT SN PBC AF PI
## PI1 0.630 0.614 0.631 0.778 0.954
## PI2 0.628 0.624 0.647 0.782 0.961
## PI3 0.655 0.630 0.655 0.792 0.946
## ATT1 0.951 0.561 0.574 0.568 0.642
## ATT2 0.939 0.571 0.569 0.565 0.635
## ATT3 0.905 0.533 0.580 0.522 0.591
## SN1 0.592 0.929 0.593 0.570 0.615
## SN2 0.524 0.943 0.564 0.552 0.588
## SN3 0.548 0.926 0.593 0.581 0.623
## PBC2 0.584 0.542 0.893 0.548 0.606
## PBC3 0.515 0.575 0.891 0.623 0.601
## AF1 0.594 0.434 0.474 0.738 0.692
## AF2 0.480 0.570 0.609 0.884 0.725
## AF3 0.338 0.466 0.501 0.803 0.555
# extract bootstrapped htmt
summary_boot$bootstrapped_HTMT #there should be no 1 in the CI=NO ISSUES OF DISCRIMINALITY
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## ATT -> SN 0.643 0.643 0.025 25.724 0.601 0.683
## ATT -> PBC 0.743 0.744 0.029 25.601 0.695 0.790
## ATT -> AF 0.704 0.705 0.032 22.237 0.651 0.756
## ATT -> PI 0.713 0.713 0.023 30.341 0.673 0.750
## SN -> PBC 0.754 0.754 0.028 26.780 0.707 0.799
## SN -> AF 0.733 0.734 0.029 25.175 0.684 0.780
## SN -> PI 0.696 0.696 0.024 29.249 0.656 0.734
## PBC -> AF 0.882 0.882 0.032 27.178 0.828 0.934
## PBC -> PI 0.804 0.804 0.025 32.760 0.763 0.844
## AF -> PI 0.971 0.971 0.014 69.453 0.948 0.994
# exporitng
# write.csv(x=summary_boot$bootstrapped_loadings, file = "boot_loadings.csv")
# to plot the constructs' internal consistency reliabilities
plot(summary_simple$reliability)
# to plot pls estimated model
plot(simple_model)
# to plot bootstrapped pls estimated model
plot(boot_simple) #shows the significant variables
# to create reflective mm (no need to write mode_A)
simple_mm<- constructs(
composite("PI", multi_items("PI",1:3)),
composite("ATT", multi_items("ATT",1:3)),
composite("SN", multi_items("SN",1:3)),
composite("PBC", multi_items("PBC",2:3)), #PBC1 was removed because loading=0.540 and alpha< 0.7its removal has a better effect on reliability and validity metrics/content validity (<0.708 recommendation limit)
composite("AF", multi_items("AF",1:3))
)
simple_mm
## $composite
## [1] "PI" "PI1" "A" "PI" "PI2" "A" "PI" "PI3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "ATT" "ATT1" "A" "ATT" "ATT2" "A" "ATT" "ATT3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "SN" "SN1" "A" "SN" "SN2" "A" "SN" "SN3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "PBC" "PBC2" "A" "PBC" "PBC3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "AF" "AF1" "A" "AF" "AF2" "A" "AF" "AF3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## attr(,"class")
## [1] "list" "measurement_model" "seminr_model"
# to create sm for indirect efffects
simple_sm <- relationships(
paths(from = c("ATT","SN","PBC","AF"), to = "PI"),
paths(from = "AF", to = c("ATT","SN","PBC"))
)
simple_sm
## source target
## [1,] "ATT" "PI"
## [2,] "SN" "PI"
## [3,] "PBC" "PI"
## [4,] "AF" "PI"
## [5,] "AF" "ATT"
## [6,] "AF" "SN"
## [7,] "AF" "PBC"
## attr(,"class")
## [1] "matrix" "array" "structural_model" "seminr_model"
# to estimate the model
simple_model <- estimate_pls(data = my_data,
measurement_model = simple_mm,
structural_model = simple_sm,
inner_weights = path_weighting,
missing = mean_replacement,
missing_value = "-99"
)
## Generating the seminr model
## All 999 observations are valid.
# summary()
summary_simple <- summary(simple_model) #shows the path coefficients and reliability
plot(simple_model) #β IS PATH COEFFICIENT ; λ IS outer loadings
summary_simple$total_indirect_effects
## ATT SN PBC AF PI
## ATT 0.000 0.000 0.000 0.000 0.000
## SN 0.000 0.000 0.000 0.000 0.000
## PBC 0.000 0.000 0.000 0.000 0.000
## AF 0.000 0.000 0.000 0.000 0.269
## PI 0.000 0.000 0.000 0.000 0.000
# Conclusion: Only AF shows a non-zero indirect effect on Purchase Intention (PI) (β = 0.269), indicating the presence of mediation
boot_simple <- bootstrap_model(
seminr_model = simple_model,
nboot = 10000,
cores = NULL,
seed = 123
)
## Bootstrapping model using seminr...
## SEMinR Model successfully bootstrapped
distal <- "AF"
mediators <- c("ATT","SN","PBC")
target <- "PI"
for (f in distal) for (m in mediators) {
specific_effect_significance(boot_simple, from = f, through = m, to = target, alpha = 0.05)
}
lapply(c("ATT","SN","PBC"), function(m) specific_effect_significance(boot_simple, from="AF", through=m, to="PI", alpha=0.05))
## [[1]]
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI
## 0.11728110 0.11780374 0.01716785 6.83143626 0.08533884
## 97.5% CI
## 0.15208106
##
## [[2]]
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI
## 0.07925994 0.07923070 0.01761300 4.50008152 0.04557675
## 97.5% CI
## 0.11483830
##
## [[3]]
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI
## 0.07233320 0.07254192 0.01884230 3.83887228 0.03606383
## 97.5% CI
## 0.10981183
# to inspect indirect effects
summary_boot_med<-summary(boot_simple)
summary_boot_med$bootstrapped_paths
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI 97.5% CI
## ATT -> PI 0.199 0.200 0.026 7.694 0.149 0.250
## SN -> PI 0.130 0.130 0.028 4.641 0.075 0.185
## PBC -> PI 0.110 0.110 0.028 3.955 0.056 0.165
## AF -> ATT 0.589 0.590 0.027 21.835 0.534 0.640
## AF -> SN 0.609 0.609 0.025 23.973 0.558 0.657
## AF -> PBC 0.657 0.657 0.024 27.197 0.607 0.703
## AF -> PI 0.551 0.550 0.031 17.805 0.488 0.609
# to inspect direct effects
summary_simple$paths
## PI ATT SN PBC
## R^2 0.744 0.347 0.371 0.432
## AdjR^2 0.743 0.346 0.370 0.431
## ATT 0.199 . . .
## SN 0.130 . . .
## PBC 0.110 . . .
## AF 0.551 0.589 0.609 0.657
# to inspect the confidence intervals for direct effects
summary_boot$bootstrapped_paths #if significant if t-stat >1.96 and there is no zeros between CI, then it shows partial mediation
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## ATT -> PI 0.194 0.194 0.025 7.706 0.152 0.236
## SN -> PI 0.130 0.130 0.028 4.670 0.085 0.176
## PBC -> PI 0.112 0.112 0.028 4.042 0.066 0.157
## AF -> PI 0.555 0.554 0.031 18.133 0.503 0.604
# Note:
# In PLS-SEM, the total effect of an independent variable on a dependent variable
# is the sum of the direct effect and all indirect effects.
# The percentage of the total effect transmitted via a mediator can be calculated as:
# Indirect Contribution (%) = (Indirect Effect / Total Effect) * 100
# Example:
# Indirect effects via ATT, SN, and PBC: 0.551 × 0.194 ≈ 0.107, 0.551 × 0.130 ≈ 0.072, 0.551 × 0.112 ≈ 0.062
# Direct effect of AF on PI: 0.555
# Total effect = 0.555 + 0.107 + 0.072 + 0.062 ≈ 0.796
# % contribution via ATT = 0.107 / 0.796 × 100 ≈ 13.4%, via SN =0.072 / 0.796 × 100 ≈ 9.0%, via PBC = 0.062 / 0.796 × 100 ≈ 7.8%
# Cumulative indirect contribution = 0.107 + 0.072 + 0.062 ≈ 0.241 (≈ 30.3% of total effect)
# FOR AFF
summary_simple$paths["AF","ATT"]*summary_simple$paths["AF","PI"]*summary_simple$paths["AF","PI"]
## [1] 0.1787081
summary_simple$paths["AF","SN"]*summary_simple$paths["AF","PI"]*summary_simple$paths["AF","PI"]
## [1] 0.184843
summary_simple$paths["AF","PBC"]*summary_simple$paths["AF","PI"]*summary_simple$paths["AF","PI"]
## [1] 0.1994204
plot(simple_model)
plot(boot_simple)
# create interaction (two-stage recommended for reflective composites)
simple_mm <- constructs(
composite("PI", multi_items("PI",1:3)),
composite("ATT", multi_items("ATT",1:3)),
composite("SN", multi_items("SN",1:3)),
composite("PBC", multi_items("PBC",2:3)),
composite("AF", multi_items("AF",1:3)),
interaction_term(iv = "ATT",moderator = "AF",method = two_stage),
interaction_term(iv = "SN",moderator = "AF",method = two_stage),
interaction_term(iv = "PBC",moderator = "AF",method = two_stage)
)
simple_mm
## $composite
## [1] "PI" "PI1" "A" "PI" "PI2" "A" "PI" "PI3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "ATT" "ATT1" "A" "ATT" "ATT2" "A" "ATT" "ATT3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "SN" "SN1" "A" "SN" "SN2" "A" "SN" "SN3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "PBC" "PBC2" "A" "PBC" "PBC3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $composite
## [1] "AF" "AF1" "A" "AF" "AF2" "A" "AF" "AF3" "A"
## attr(,"class")
## [1] "character" "construct" "composite"
##
## $two_stage_interaction
## function (data, mmMatrix, structural_model, ints, estimate_first_stage,
## ...)
## {
## interaction_name <- paste(iv, moderator, sep = "*")
## structural_model <- structural_model[!grepl("\\*", structural_model[,
## "source"]), ]
## measurement_mode_scheme <- sapply(unique(c(structural_model[,
## 1], structural_model[, 2])), get_measure_mode, mmMatrix,
## USE.NAMES = TRUE)
## first_stage <- estimate_first_stage(data = data, smMatrix = structural_model,
## mmMatrix = mmMatrix, measurement_mode_scheme = measurement_mode_scheme,
## ...)
## interaction_term <- as.matrix(first_stage$construct_scores[,
## iv] * first_stage$construct_scores[, moderator], ncol = 1)[,
## , drop = FALSE]
## colnames(interaction_term) <- c(paste(interaction_name, "_intxn",
## sep = ""))
## intxn_mm <- matrix(measure_interaction(interaction_name,
## interaction_term, weights), ncol = 3, byrow = TRUE)
## return(list(name = interaction_name, data = interaction_term[,
## 1, drop = FALSE], mm = intxn_mm))
## }
## <bytecode: 0x0000016f0b3159e0>
## <environment: 0x0000016f0b30b1b0>
## attr(,"class")
## [1] "function" "interaction" "two_stage_interaction"
##
## $two_stage_interaction
## function (data, mmMatrix, structural_model, ints, estimate_first_stage,
## ...)
## {
## interaction_name <- paste(iv, moderator, sep = "*")
## structural_model <- structural_model[!grepl("\\*", structural_model[,
## "source"]), ]
## measurement_mode_scheme <- sapply(unique(c(structural_model[,
## 1], structural_model[, 2])), get_measure_mode, mmMatrix,
## USE.NAMES = TRUE)
## first_stage <- estimate_first_stage(data = data, smMatrix = structural_model,
## mmMatrix = mmMatrix, measurement_mode_scheme = measurement_mode_scheme,
## ...)
## interaction_term <- as.matrix(first_stage$construct_scores[,
## iv] * first_stage$construct_scores[, moderator], ncol = 1)[,
## , drop = FALSE]
## colnames(interaction_term) <- c(paste(interaction_name, "_intxn",
## sep = ""))
## intxn_mm <- matrix(measure_interaction(interaction_name,
## interaction_term, weights), ncol = 3, byrow = TRUE)
## return(list(name = interaction_name, data = interaction_term[,
## 1, drop = FALSE], mm = intxn_mm))
## }
## <bytecode: 0x0000016f0b3159e0>
## <environment: 0x0000016f0b304888>
## attr(,"class")
## [1] "function" "interaction" "two_stage_interaction"
##
## $two_stage_interaction
## function (data, mmMatrix, structural_model, ints, estimate_first_stage,
## ...)
## {
## interaction_name <- paste(iv, moderator, sep = "*")
## structural_model <- structural_model[!grepl("\\*", structural_model[,
## "source"]), ]
## measurement_mode_scheme <- sapply(unique(c(structural_model[,
## 1], structural_model[, 2])), get_measure_mode, mmMatrix,
## USE.NAMES = TRUE)
## first_stage <- estimate_first_stage(data = data, smMatrix = structural_model,
## mmMatrix = mmMatrix, measurement_mode_scheme = measurement_mode_scheme,
## ...)
## interaction_term <- as.matrix(first_stage$construct_scores[,
## iv] * first_stage$construct_scores[, moderator], ncol = 1)[,
## , drop = FALSE]
## colnames(interaction_term) <- c(paste(interaction_name, "_intxn",
## sep = ""))
## intxn_mm <- matrix(measure_interaction(interaction_name,
## interaction_term, weights), ncol = 3, byrow = TRUE)
## return(list(name = interaction_name, data = interaction_term[,
## 1, drop = FALSE], mm = intxn_mm))
## }
## <bytecode: 0x0000016f0b3159e0>
## <environment: 0x0000016f0b305928>
## attr(,"class")
## [1] "function" "interaction" "two_stage_interaction"
##
## attr(,"class")
## [1] "list" "measurement_model" "seminr_model"
simple_sm <- relationships(
paths(from = c("ATT","SN","PBC","AF","ATT*AF","SN*AF","PBC*AF"), to = "PI")
)
simple_sm
## source target
## [1,] "ATT" "PI"
## [2,] "SN" "PI"
## [3,] "PBC" "PI"
## [4,] "AF" "PI"
## [5,] "ATT*AF" "PI"
## [6,] "SN*AF" "PI"
## [7,] "PBC*AF" "PI"
## attr(,"class")
## [1] "matrix" "array" "structural_model" "seminr_model"
simple_mod_model <- estimate_pls(data = my_data,simple_mm,simple_sm
)
## Generating the seminr model
## All 999 observations are valid.
# to summarise
summary_mod_simple <- summary(simple_mod_model)
summary_mod_simple
##
## Results from package seminr (2.3.7)
##
## Path Coefficients:
## PI
## R^2 0.750
## AdjR^2 0.748
## ATT 0.193
## SN 0.125
## PBC 0.115
## AF 0.544
## ATT*AF 0.005
## SN*AF -0.020
## PBC*AF -0.034
##
## Reliability:
## alpha rhoC AVE rhoA
## ATT 0.924 0.952 0.869 0.927
## SN 0.925 0.952 0.870 0.926
## PBC 0.744 0.886 0.796 0.744
## AF 0.737 0.851 0.657 0.745
## ATT*AF 1.000 1.000 1.000 1.000
## SN*AF 1.000 1.000 1.000 1.000
## PBC*AF 1.000 1.000 1.000 1.000
## PI 0.950 0.968 0.909 0.950
##
## Alpha, rhoC, and rhoA should exceed 0.7 while AVE should exceed 0.5
boot_mod_simple <- bootstrap_model(
seminr_model = simple_mod_model,
nboot = 10000,
cores = NULL,
seed = 123
)
## Bootstrapping model using seminr...
## SEMinR Model successfully bootstrapped
# to inspect bootstrapped paths
summary_mod_boot<-summary(boot_mod_simple, alpha=0.05)
plot(boot_mod_simple)
summary_mod_boot$bootstrapped_paths
## Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI
## ATT -> PI 0.193 0.194 0.025 7.800 0.146
## SN -> PI 0.125 0.125 0.028 4.467 0.071
## PBC -> PI 0.115 0.116 0.027 4.211 0.062
## AF -> PI 0.544 0.543 0.031 17.793 0.482
## ATT*AF -> PI 0.005 0.006 0.021 0.240 -0.036
## SN*AF -> PI -0.020 -0.020 0.022 -0.925 -0.061
## PBC*AF -> PI -0.034 -0.035 0.018 -1.867 -0.072
## 97.5% CI
## ATT -> PI 0.243
## SN -> PI 0.181
## PBC -> PI 0.169
## AF -> PI 0.601
## ATT*AF -> PI 0.049
## SN*AF -> PI 0.025
## PBC*AF -> PI -0.000
#main effects significant, interactions mostly not significant except PBC*AF borderline.
# Extract R2 values
r2_included <- summary_mod_simple$paths
# = 0.750
r2_excluded <- summary_simple$paths
# =0.744
# Calculate f2
f2 = (0.750 - 0.744) / (1 - 0.750)
f2 #0.024
## [1] 0.024
#use kenny 2018 proposition to determine effects
slope_analysis(moderated_model = simple_mod_model,
dv="PI",
moderator = "AF",
iv="ATT",
leg_place = "bottomright")
# check the steepness of the curve
slope_analysis(moderated_model = simple_mod_model,
dv="PI",
moderator = "AF",
iv="SN",
leg_place = "bottomright")
# check the steepness of the curve
slope_analysis(moderated_model = simple_mod_model,
dv="PI",
moderator = "AF",
iv="PBC",
leg_place = "bottomright")
# check the steepness of the curve
set.seed(123)
n_sim <- 10000
n_sim
## [1] 10000
✔ These preserve skewness and non-normality ✔ No distributional assumptions - Hypotheses addressed: i) MC-H1: Stability of ATT → PI ii) MC-H2: Stability of SN → PI iii) MC-H3: Stability of PBC → PI iv) MC-H4: Stability of AF → PI - Why: Defines the estimated TPB and labelling effects to be tested for robustness.
# Since we only have the bootstrap means, create vectors by repeating the mean
# This avoids assuming normality, but note: ideally we would use full 10,000 bootstrap resamples
# Create empirical bootstrap-based distributions
# check summary_boot$bootstrapped_paths and use the values of the CI for each of the constructs (no normality assumption)
boot_beta_ATT <- runif(n_sim, min = 0.152, max = 0.236) # ATT -> PI
boot_beta_SN <- runif(n_sim, min = 0.085, max = 0.176) # SN -> PI
boot_beta_PBC <- runif(n_sim, min = 0.066, max = 0.157) # PBC -> PI
boot_beta_AF <- runif(n_sim, min = 0.503, max = 0.604) # AF -> PI
✔ This is the key correction for non-normal data ✔ Hypotheses addressed: i) MC-H1 to MC-H4 (all) - Why: Tests whether the estimated effects remain stable under repeated resampling (Monte Carlo logic).
beta_ATT <- sample(boot_beta_ATT, n_sim, replace = TRUE)
beta_PBC <- sample(boot_beta_PBC, n_sim, replace = TRUE)
beta_SN <- sample(boot_beta_SN, n_sim, replace = TRUE)
beta_AF <- sample(boot_beta_AF, n_sim, replace = TRUE)
# Step 4: Simulate Purchase Intention
PI_sim <- beta_ATT +beta_PBC +beta_SN + beta_AF
# Step 5: Summary statistics
summary(PI_sim)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.8242 0.9516 0.9885 0.9889 1.0263 1.1576
# Uncertainty range
quantile(PI_sim, probs = c(0.05, 0.50, 0.95))
## 5% 50% 95%
## 0.9023636 0.9884834 1.0774605
# Conclusion: Monte Carlo simulations based on bootstrap confidence intervals indicate that the combined effects of attitude, subjective norms, perceived behavioral control, and affordability on purchase intention remain positive and stable, with a 90% uncertainty interval of [0.901, 1.076].
# Probability that Purchase Intention is positive
mean(PI_sim > 0)
## [1] 1
# Monte Carlo simulations indicate a 100% probability that purchase intention remains positive, confirming the robustness of the extended TPB model under non-normal data conditions.
# Measure contribution of each path to PI variability
sens_ATT <- sd(beta_ATT)
sens_SN <- sd(beta_SN)
sens_PBC <- sd(beta_PBC)
sens_AF <- sd(beta_AF)
# Combine results
sensitivity_results <- data.frame(
Path = c("ATT → PI", "SN → PI", "PBC → PI", "AF → PI"),
Sensitivity_SD = c(sens_ATT, sens_SN, sens_PBC, sens_AF)
)
sensitivity_results
# Relative sensitivity (% contribution)
sensitivity_results$Relative_Contribution <-
sensitivity_results$Sensitivity_SD / sum(sensitivity_results$Sensitivity_SD)
sensitivity_results