Introduction

1.Load packages

# install.packages("seminr")
library(seminr)
# install.packages("readxl")   # run once
library(readxl)

2. Load and clean data

my_data <- read_excel(path  = "WL_TPB_Poland_Survey_2025.xlsx", sheet = 3)
my_data

3.Review data

head(my_data)
colnames(my_data)
##  [1] "ID"                      "Time"                   
##  [3] "Age_group"               "Residence"              
##  [5] "Med_city_size"           "large_city_size"        
##  [7] "Province"                "Climate_change_belief"  
##  [9] "Wft"                     "ATT1"                   
## [11] "ATT2"                    "ATT3"                   
## [13] "SN1"                     "SN2"                    
## [15] "SN3"                     "PBC1"                   
## [17] "PBC2"                    "PBC3"                   
## [19] "PI1"                     "PI2"                    
## [21] "PI3"                     "WL1"                    
## [23] "WL2"                     "WL3"                    
## [25] "WL4"                     "Education"              
## [27] "Food_choice_willingness"

4. Specify measurement model (mm)

  1. composite(): measurement of individual constructs. ii)interaction_term(): specifies interaction terms.
  2. higher_composite(): specifies hierarchial component models.
# 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("WL", multi_items("WL",1:4))
)
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] "WL"  "WL1" "A"   "WL"  "WL2" "A"   "WL"  "WL3" "A"   "WL"  "WL4" "A"  
## attr(,"class")
## [1] "character" "construct" "composite"
## 
## attr(,"class")
## [1] "list"              "measurement_model" "seminr_model"

5. Specify structural model (sm)

  1. relationships(): specifies all the structural relationships between constructs.
  2. paths(): specifies relationship between sets of antecedents and outcomes.
# to create sm
simple_sm <- relationships(
  paths(from = c("ATT","SN","PBC","WL"), to = "PI")
)
simple_sm
##      source target
## [1,] "ATT"  "PI"  
## [2,] "SN"   "PI"  
## [3,] "PBC"  "PI"  
## [4,] "WL"   "PI"  
## attr(,"class")
## [1] "matrix"           "array"            "structural_model" "seminr_model"

6. Estimate the structural 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.718
## AdjR^2 0.717
## ATT    0.182
## SN     0.152
## PBC    0.157
## WL     0.493
## 
## 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
## WL  0.850 0.898 0.689 0.879
## 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

7.Check if the algorithm of estimated sm converge

summary_simple$iterations  # should be lower than 300
## [1] 3

8. Bootstrap the estimated model

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.182          0.183        0.028   6.409 0.136  0.230
## SN  ->  PI          0.152          0.152        0.032   4.764 0.099  0.204
## PBC  ->  PI         0.157          0.156        0.033   4.787 0.103  0.210
## WL  ->  PI          0.493          0.493        0.033  14.719 0.437  0.547
## 
## Bootstrapped Weights:
##               Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## PI1  ->  PI           0.346          0.346        0.003 137.451 0.342  0.350
## PI2  ->  PI           0.352          0.352        0.003 137.163 0.348  0.356
## PI3  ->  PI           0.351          0.351        0.003 108.499 0.346  0.357
## ATT1  ->  ATT         0.369          0.369        0.007  55.755 0.358  0.380
## ATT2  ->  ATT         0.365          0.365        0.007  49.609 0.353  0.377
## ATT3  ->  ATT         0.339          0.339        0.007  46.059 0.327  0.351
## SN1  ->  SN           0.361          0.362        0.006  62.066 0.352  0.371
## SN2  ->  SN           0.345          0.345        0.004  77.384 0.338  0.353
## SN3  ->  SN           0.366          0.366        0.006  58.053 0.356  0.377
## PBC2  ->  PBC         0.562          0.563        0.014  39.221 0.540  0.587
## PBC3  ->  PBC         0.558          0.558        0.014  40.431 0.536  0.581
## WL1  ->  WL           0.235          0.235        0.010  23.279 0.218  0.251
## WL2  ->  WL           0.342          0.342        0.009  39.198 0.328  0.357
## WL3  ->  WL           0.256          0.256        0.009  29.737 0.242  0.270
## WL4  ->  WL           0.360          0.360        0.010  34.994 0.344  0.377
## 
## Bootstrapped Loadings:
##               Original Est. Bootstrap Mean Bootstrap SD T Stat. 5% CI 95% CI
## PI1  ->  PI           0.954          0.954        0.005 202.816 0.945  0.961
## PI2  ->  PI           0.961          0.961        0.004 258.310 0.955  0.967
## PI3  ->  PI           0.946          0.946        0.006 156.868 0.935  0.955
## ATT1  ->  ATT         0.951          0.951        0.004 223.992 0.944  0.958
## ATT2  ->  ATT         0.939          0.939        0.009 109.967 0.924  0.952
## ATT3  ->  ATT         0.905          0.905        0.010  92.791 0.888  0.920
## SN1  ->  SN           0.929          0.929        0.006 145.913 0.918  0.939
## SN2  ->  SN           0.943          0.943        0.006 158.865 0.932  0.952
## SN3  ->  SN           0.926          0.926        0.007 132.919 0.914  0.937
## PBC2  ->  PBC         0.893          0.893        0.010  93.707 0.877  0.908
## PBC3  ->  PBC         0.891          0.891        0.010  87.299 0.873  0.906
## WL1  ->  WL           0.743          0.742        0.024  30.779 0.701  0.780
## WL2  ->  WL           0.899          0.899        0.008 117.553 0.885  0.910
## WL3  ->  WL           0.796          0.796        0.019  41.784 0.762  0.826
## WL4  ->  WL           0.873          0.873        0.009  96.143 0.858  0.888
## 
## 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  ->  WL          0.668          0.668        0.030 0.616  0.716
## 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  ->  WL           0.664          0.664        0.028 0.618  0.708
## SN  ->  PI           0.696          0.696        0.024 0.656  0.734
## PBC  ->  WL          0.787          0.787        0.031 0.735  0.837
## PBC  ->  PI          0.804          0.804        0.025 0.763  0.844
## WL  ->  PI           0.864          0.864        0.016 0.836  0.889
## 
## Bootstrapped Total Paths:
##             Original Est. Bootstrap Mean Bootstrap SD 5% CI 95% CI
## ATT  ->  PI         0.182          0.183        0.028 0.136  0.230
## SN  ->  PI          0.152          0.152        0.032 0.099  0.204
## PBC  ->  PI         0.157          0.156        0.033 0.103  0.210
## WL  ->  PI          0.493          0.493        0.033 0.437  0.547
# use tstat and CI from Bootstrapped Structural Paths results to check which is significant

9. Evaluating estimated structural models (can be done for either formative/reflective model)

STEP 1: Assess multi-collinearity issues (VIF < 5: No multicollinearity problems)

# 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 
## 
## WL :
##   WL1   WL2   WL3   WL4 
## 1.638 2.747 1.841 2.386 
## 
## 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    WL 
## 1.973 1.976 2.141 2.034

STEP 2: Assess significance and relevance of sm relationships (path coefficient)

# 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.182          0.183        0.028   6.409 0.136  0.230
## SN  ->  PI          0.152          0.152        0.032   4.764 0.099  0.204
## PBC  ->  PI         0.157          0.156        0.033   4.787 0.103  0.210
## WL  ->  PI          0.493          0.493        0.033  14.719 0.437  0.547
# 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.954        0.005 202.816 0.945  0.961
## PI2  ->  PI           0.961          0.961        0.004 258.310 0.955  0.967
## PI3  ->  PI           0.946          0.946        0.006 156.868 0.935  0.955
## ATT1  ->  ATT         0.951          0.951        0.004 223.992 0.944  0.958
## ATT2  ->  ATT         0.939          0.939        0.009 109.967 0.924  0.952
## ATT3  ->  ATT         0.905          0.905        0.010  92.791 0.888  0.920
## SN1  ->  SN           0.929          0.929        0.006 145.913 0.918  0.939
## SN2  ->  SN           0.943          0.943        0.006 158.865 0.932  0.952
## SN3  ->  SN           0.926          0.926        0.007 132.919 0.914  0.937
## PBC2  ->  PBC         0.893          0.893        0.010  93.707 0.877  0.908
## PBC3  ->  PBC         0.891          0.891        0.010  87.299 0.873  0.906
## WL1  ->  WL           0.743          0.742        0.024  30.779 0.701  0.780
## WL2  ->  WL           0.899          0.899        0.008 117.553 0.885  0.910
## WL3  ->  WL           0.796          0.796        0.019  41.784 0.762  0.826
## WL4  ->  WL           0.873          0.873        0.009  96.143 0.858  0.888

STEP 3: Assess the model’s explanatory power

# to inspect the r^square
summary_simple$paths # check for direct effects
##           PI
## R^2    0.718
## AdjR^2 0.717
## ATT    0.182
## SN     0.152
## PBC    0.157
## WL     0.493
# CONCLUSION: PI model,R2 =0.718; paths coefficients: ATT=0.182, PBC=0.157, SN=0.157,WFL = 0.493
# 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 (WFL),medium (), small(ATT,SN,PBC), no/very weak()
##       ATT    SN   PBC    WL    PI
## ATT 0.000 0.000 0.000 0.000 0.059
## SN  0.000 0.000 0.000 0.000 0.041
## PBC 0.000 0.000 0.000 0.000 0.041
## WL  0.000 0.000 0.000 0.000 0.422
## PI  0.000 0.000 0.000 0.000 0.000

STEP 4: Assess the model’s predictive power

  1. if majority of PLS out-of-sample indicators have RMSE/MAE values< LM benchmark =medium predictive power
  2. if minority of PLS out-of-sample indicators have RMSE/MAE values< LM benchmark =low predictive power # STEP 4a: to generate the model predictions based on PLS estimate
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.591 0.573 0.575
## MAE  0.431 0.418 0.419
## 
## PLS out-of-sample metrics:
##        PI1   PI2   PI3
## RMSE 0.596 0.577 0.579
## MAE  0.434 0.420 0.421
## 
## LM in-sample metrics:
##        PI1   PI2   PI3
## RMSE 0.567 0.554 0.559
## MAE  0.411 0.401 0.406
## 
## LM out-of-sample metrics:
##        PI1   PI2   PI3
## RMSE 0.582 0.570 0.572
## MAE  0.421 0.409 0.413
## 
## Construct Level metrics:
##             PI
## IS_MSE  1.7326
## IS_MAE  1.0274
## OOS_MSE 1.7375
## OOS_MAE 1.0282
## overfit 0.0028
# Conclusion PLSpredict indicates that while the model explains purchase intention well in-sample, it shows limited out-of-sample predictive accuracy and does not outperform the linear benchmark, which is typical for attitudinal intention models.

STEP 4b: to analyse the distribution of prediction error

  1. -0.5 to 0.5 → roughly symmetric (acceptable for normality).
  2. -1 to -0.5 → moderate skew.
  3. < -1 → highly skewed.
library(moments)
# All indicators
indicators <- c(paste0("PI",1:3), paste0("ATT",1:3), paste0("SN",1:3),
                paste0("PBC",2:3), paste0("WL",1:4))
# 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.5820863 1.437934e-27
## PI2        PI2 -0.5771280 2.198353e-27
## PI3        PI3 -0.6367268 6.417538e-28
## ATT1      ATT1 -0.5219598 1.734718e-28
## ATT2      ATT2 -0.5580303 1.969654e-28
## ATT3      ATT3 -0.3746609 2.241768e-29
## SN1        SN1 -0.1590990 1.315768e-27
## SN2        SN2 -0.1878545 2.464034e-29
## SN3        SN3 -0.2787749 3.100212e-30
## PBC2      PBC2 -0.1807918 5.620343e-29
## PBC3      PBC3 -0.2006717 3.553573e-27
## WL1        WL1 -0.5945917 5.461892e-27
## WL2        WL2 -0.6531834 4.989517e-28
## WL3        WL3 -0.4675625 6.031396e-26
## WL4        WL4 -0.6202800 5.381864e-28
#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))

10. ASSESSING RELIABILITY AND VALIDITY OF ESTIMATED MODEL

STEP 1: Assess the indicator reliability: how much of each indicator’s variance is explained by construct

# 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    WL    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
## WL1  0.000 0.000 0.000 0.743 0.000
## WL2  0.000 0.000 0.000 0.899 0.000
## WL3  0.000 0.000 0.000 0.796 0.000
## WL4  0.000 0.000 0.000 0.873 0.000
# Conclusion: all indicators are >0.708
summary_simple$loadings^2 #(indicator reliability should be>0.5)
##        ATT    SN   PBC    WL    PI
## PI1  0.000 0.000 0.000 0.000 0.909
## PI2  0.000 0.000 0.000 0.000 0.924
## PI3  0.000 0.000 0.000 0.000 0.895
## 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.795 0.000 0.000
## WL1  0.000 0.000 0.000 0.552 0.000
## WL2  0.000 0.000 0.000 0.807 0.000
## WL3  0.000 0.000 0.000 0.634 0.000
## WL4  0.000 0.000 0.000 0.762 0.000
# Conclusion: all indicators are > 0.5. No issues of non-reliability of indicators

STEP 2: Assess the internal consistency (construct) reliability

# 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
## WL  0.850 0.898 0.689 0.879
## 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
## WL  0.850 0.898 0.689 0.879
## PI  0.950 0.968 0.909 0.950
## 
## Alpha, rhoC, and rhoA should exceed 0.7 while AVE should exceed 0.5

STEP 4: Assess the discriminant validity

  1. AVE must be higher than the squared interconstruct correlation.
summary_simple$validity$fl_criteria #first value in each column should be higher than other values below
##       ATT    SN   PBC    WL    PI
## ATT 0.932     .     .     .     .
## SN  0.595 0.933     .     .     .
## PBC 0.616 0.626 0.892     .     .
## WL  0.608 0.598 0.634 0.830     .
## PI  0.669 0.653 0.676 0.794 0.954
## 
## FL Criteria table reports square root of AVE on the diagonal and construct correlations on the lower triangle.

STEP 4b: Heterotrait-monotrait ratio (HTMT) [Henseler et al., 2015]is alternative to above criticised measure

# to inspect Henseler proposal
summary_simple$validity$htmt #all of the values should be <0.90
##       ATT    SN   PBC    WL PI
## ATT     .     .     .     .  .
## SN  0.643     .     .     .  .
## PBC 0.743 0.754     .     .  .
## WL  0.668 0.664 0.787     .  .
## PI  0.713 0.696 0.804 0.864  .

STEP 4c: Crossloadings for assessment of discriminant validity

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    WL    PI
## PI1  0.630 0.614 0.631 0.751 0.954
## PI2  0.628 0.624 0.647 0.768 0.961
## PI3  0.655 0.630 0.655 0.752 0.946
## ATT1 0.951 0.561 0.574 0.585 0.642
## ATT2 0.939 0.571 0.569 0.570 0.635
## ATT3 0.905 0.533 0.580 0.545 0.591
## SN1  0.592 0.929 0.593 0.559 0.615
## SN2  0.524 0.943 0.564 0.545 0.588
## SN3  0.548 0.926 0.593 0.568 0.623
## PBC2 0.584 0.542 0.893 0.587 0.605
## PBC3 0.515 0.575 0.891 0.543 0.601
## WL1  0.371 0.395 0.429 0.743 0.508
## WL2  0.582 0.550 0.571 0.899 0.739
## WL3  0.409 0.453 0.487 0.796 0.554
## WL4  0.603 0.559 0.591 0.873 0.777

STEP 5: Bootstrapping HTMT results (to see if HTMT values are significantly different from 1 or a lower threshold like 0.9/0.85)

# 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  ->  WL          0.668          0.668        0.030  21.966 0.616  0.716
## 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  ->  WL           0.664          0.664        0.028  24.060 0.618  0.708
## SN  ->  PI           0.696          0.696        0.024  29.249 0.656  0.734
## PBC  ->  WL          0.787          0.787        0.031  25.409 0.735  0.837
## PBC  ->  PI          0.804          0.804        0.025  32.760 0.763  0.844
## WL  ->  PI           0.864          0.864        0.016  52.547 0.836  0.889

11. Plotting, Printing and Exporting Results

# 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

MEDIATING EFFECT ANALYSIS

# 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("WL", multi_items("WL",1:4))
)
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] "WL"  "WL1" "A"   "WL"  "WL2" "A"   "WL"  "WL3" "A"   "WL"  "WL4" "A"  
## attr(,"class")
## [1] "character" "construct" "composite"
## 
## attr(,"class")
## [1] "list"              "measurement_model" "seminr_model"

2. Create structural model

# to create sm for indirect efffects
simple_sm <- relationships(
  paths(from = c("ATT","SN","PBC","WL"), to = "PI"),
  paths(from = "WL", to = c("ATT","SN","PBC"))
)
simple_sm
##      source target
## [1,] "ATT"  "PI"  
## [2,] "SN"   "PI"  
## [3,] "PBC"  "PI"  
## [4,] "WL"   "PI"  
## [5,] "WL"   "ATT" 
## [6,] "WL"   "SN"  
## [7,] "WL"   "PBC" 
## attr(,"class")
## [1] "matrix"           "array"            "structural_model" "seminr_model"

3. Estimate the structural 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

4. Check for indirect effects

summary_simple$total_indirect_effects
##       ATT    SN   PBC    WL    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
## WL  0.000 0.000 0.000 0.000 0.302
## PI  0.000 0.000 0.000 0.000 0.000
# Conclusion: Only AFF shows a non-zero indirect effect on Purchase Intention (PI) (β = 0.269), indicating the presence of mediation

5. Bootstrap estimated model

boot_simple <- bootstrap_model(
  seminr_model = simple_model,
  nboot = 10000,
  cores = NULL,
  seed = 123
)
## Bootstrapping model using seminr...
## SEMinR Model successfully bootstrapped

6. Inspect significance of indirect effects

distal <- "WL"
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="WL", through=m, to="PI", alpha=0.05))
## [[1]]
##  Original Est. Bootstrap Mean   Bootstrap SD        T Stat.        2.5% CI 
##     0.11120463     0.11193332     0.01900359     5.85176806     0.07574051 
##       97.5% CI 
##     0.15075745 
## 
## [[2]]
##  Original Est. Bootstrap Mean   Bootstrap SD        T Stat.        2.5% CI 
##     0.09139521     0.09130059     0.01940342     4.71026182     0.05385709 
##       97.5% CI 
##     0.12908554 
## 
## [[3]]
##  Original Est. Bootstrap Mean   Bootstrap SD        T Stat.        2.5% CI 
##     0.09947129     0.09919270     0.02109589     4.71519833     0.05763380 
##       97.5% CI 
##     0.14023741
# 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.183          0.184        0.029   6.406   0.128    0.242
## SN  ->  PI          0.153          0.153        0.032   4.775   0.091    0.215
## PBC  ->  PI         0.157          0.157        0.033   4.780   0.092    0.220
## WL  ->  ATT         0.607          0.607        0.027  22.567   0.552    0.658
## WL  ->  SN          0.598          0.598        0.025  23.644   0.547    0.646
## WL  ->  PBC         0.633          0.634        0.025  25.201   0.583    0.681
## WL  ->  PI          0.490          0.490        0.034  14.504   0.422    0.555

7. Inspect significance of direct effects

# to inspect direct effects
summary_simple$paths
##           PI   ATT    SN   PBC
## R^2    0.717 0.368 0.357 0.401
## AdjR^2 0.716 0.368 0.356 0.401
## ATT    0.183     .     .     .
## SN     0.153     .     .     .
## PBC    0.157     .     .     .
## WL     0.490 0.607 0.598 0.633
# 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.182          0.183        0.028   6.409 0.136  0.230
## SN  ->  PI          0.152          0.152        0.032   4.764 0.099  0.204
## PBC  ->  PI         0.157          0.156        0.033   4.787 0.103  0.210
## WL  ->  PI          0.493          0.493        0.033  14.719 0.437  0.547

8.Inspect the sign of the mediation

# FOR AFF
summary_simple$paths["WL","ATT"]*summary_simple$paths["WL","PI"]*summary_simple$paths["WL","PI"]
## [1] 0.1457166
summary_simple$paths["WL","SN"]*summary_simple$paths["WL","PI"]*summary_simple$paths["WL","PI"]
## [1] 0.1435085
summary_simple$paths["WL","PBC"]*summary_simple$paths["WL","PI"]*summary_simple$paths["WL","PI"]
## [1] 0.1521046

9.Plot estimated and bootstrapped model

plot(simple_model)
plot(boot_simple)

MODERATION ANALYSIS (2 stage approach- Chin, Marcolin adn Newsted’s 20033)

# 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("WL", multi_items("WL",1:4)),
  interaction_term(iv = "ATT",moderator = "WL",method = two_stage),
  interaction_term(iv = "SN",moderator = "WL",method = two_stage),
  interaction_term(iv = "PBC",moderator = "WL",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] "WL"  "WL1" "A"   "WL"  "WL2" "A"   "WL"  "WL3" "A"   "WL"  "WL4" "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: 0x0000018446d54518>
## <environment: 0x0000018446d52e70>
## 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: 0x0000018446d54518>
## <environment: 0x0000018446d41f80>
## 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: 0x0000018446d54518>
## <environment: 0x0000018446d43120>
## attr(,"class")
## [1] "function"              "interaction"           "two_stage_interaction"
## 
## attr(,"class")
## [1] "list"              "measurement_model" "seminr_model"

2. Create the sm

simple_sm <- relationships(
  paths(from = c("ATT","SN","PBC","WL","ATT*WL","SN*WL","PBC*WL"), to = "PI")
)
simple_sm
##      source   target
## [1,] "ATT"    "PI"  
## [2,] "SN"     "PI"  
## [3,] "PBC"    "PI"  
## [4,] "WL"     "PI"  
## [5,] "ATT*WL" "PI"  
## [6,] "SN*WL"  "PI"  
## [7,] "PBC*WL" "PI"  
## attr(,"class")
## [1] "matrix"           "array"            "structural_model" "seminr_model"

3. Estimate the 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.720
## AdjR^2  0.718
## ATT     0.174
## SN      0.149
## PBC     0.162
## WL      0.486
## ATT*WL -0.038
## SN*WL   0.005
## PBC*WL  0.001
## 
## 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
## WL     0.850 0.898 0.689 0.879
## ATT*WL 1.000 1.000 1.000 1.000
## SN*WL  1.000 1.000 1.000 1.000
## PBC*WL 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

4. Bootstrap moderating model

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)
summary_mod_boot$bootstrapped_paths
##                Original Est. Bootstrap Mean Bootstrap SD T Stat. 2.5% CI
## ATT  ->  PI            0.174          0.176        0.028   6.172   0.122
## SN  ->  PI             0.149          0.149        0.032   4.676   0.086
## PBC  ->  PI            0.162          0.162        0.033   4.949   0.098
## WL  ->  PI             0.486          0.486        0.033  14.590   0.419
## ATT*WL  ->  PI        -0.038         -0.037        0.025  -1.504  -0.086
## SN*WL  ->  PI          0.005          0.004        0.024   0.190  -0.044
## PBC*WL  ->  PI         0.001          0.001        0.022   0.043  -0.042
##                97.5% CI
## ATT  ->  PI       0.232
## SN  ->  PI        0.210
## PBC  ->  PI       0.225
## WL  ->  PI        0.550
## ATT*WL  ->  PI    0.012
## SN*WL  ->  PI     0.052
## PBC*WL  ->  PI    0.043
#significant ATT*WFL ->  PI has -0.033 and ATT  ->  PI  has 0.174
#Interpretation: The interaction term-ATT*WFL has a -ve effect on PI, While the simple effect ATT is +VE for average level of WFL
#Lower levels of WFL (for every standard deviation unit decrease of WFL), the relationship between ATT and PI increases the size of the interaction term(i.e 0.174-(-0.033)=0.207)

5. Compute f2 for each endogenous construct

# 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

6.Create simple slope analysis plot

slope_analysis(moderated_model = simple_mod_model,
               dv="PI",
               moderator = "WL",
               iv="ATT",
               leg_place = "bottomright")

# check the steepness of the curve
slope_analysis(moderated_model = simple_mod_model,
               dv="PI",
               moderator = "WL",
               iv="SN",
               leg_place = "bottomright")

# check the steepness of the curve
slope_analysis(moderated_model = simple_mod_model,
               dv="PI",
               moderator = "WL",
               iv="PBC",
               leg_place = "bottomright")

# check the steepness of the curve

MONTE CARLO SIMULATION

Step 1: Set seed and simulations

set.seed(123)
n_sim <- 10000
n_sim
## [1] 10000

Step 2: Insert bootstrapped coefficients

✔ 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 WL → 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
# Using 5%–95% bootstrap CIs (no normality assumption)

boot_beta_ATT <- runif(n_sim, min = 0.136, max = 0.230)  # ATT -> PI
boot_beta_SN  <- runif(n_sim, min = 0.099, max = 0.204)  # SN  -> PI
boot_beta_PBC <- runif(n_sim, min = 0.103, max = 0.210)  # PBC -> PI
boot_beta_WFL <- runif(n_sim, min = 0.437, max = 0.547)  # WL  -> PI

Step 3: Monte Carlo resampling from bootstrap distributions

✔ 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_WFL <- sample(boot_beta_WFL, n_sim, replace = TRUE)

Step 4: Simulate Purchase Intention (PI)

  1. MC-H5 (Joint TPB + WL effect):
  2. ATT, SN, PBC, and WL jointly produce a positive purchase intention.
# Step 4: Simulate Purchase Intention
PI_sim <- beta_ATT +beta_PBC +beta_SN + beta_WFL

Step 5: Summarise Monte Carlo results

  1. MC-H5 (robustness under uncertainty)
  2. Why: Shows that PI remains positive across the uncertainty range.
# Step 5: Summary statistics
summary(PI_sim)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.7960  0.9402  0.9818  0.9823  1.0245  1.1737
# Uncertainty range
quantile(PI_sim, probs = c(0.05, 0.50, 0.95))
##        5%       50%       95% 
## 0.8839176 0.9818230 1.0819442
# Conclusion: Monte Carlo simulations based on bootstrap confidence intervals indicate that the combined effects of attitude, subjective norms, perceived behavioral control, and water footprint labelling on purchase intention remain positive and stable, with a 90% uncertainty interval of [0.884, 1.084].

Step 6: Probability statement (VERY IMPORTANT)

  1. MC-H6 (Policy-relevant probability): ii)There is a high probability that purchase intention is positive.
# 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.