---
title: "Partial Least Squares Structural Equation Modeling (PLS-SEM) Using R"
subtitle: "Concise R Code and Outputs"

author:
  - name: Joseph F. Hair, Jr.
    affiliation: University of South Alabama
  - name: Tomas M. Hult
    affiliation: Michigan State University
  - name: Christian M. Ringle
    affiliation: Hamburg University of Technology, Germany and University of Waikato, New Zealand 
  - name: Marko Sarstedt
    affiliation: Ludwig-Maximilians-University Munich, Germany and Babeș-Bolyai University, Cluj, Romania
  - name: Nicholas P. Danks
    affiliation: Trinity College Dublin, Ireland
  - name: Susanne J. Adler
    affiliation: Ludwig-Maximilians-University Munich, Germany

format:
  html:
    toc: true
    toc-location: left
    toc-depth: 5
    code-fold: false
    embed-resources: true
    grid:
      body-width: 1000px
    css: styles.css

theme: lumen
editor: visual
bibliography: References.bib
csl: apa.csl
number-sections: true
---

# Introduction

This document accompanies the book Partial Least Squares Structural Equation Modeling (PLS-SEM) Using R by Joseph F. Hair, Jr., Tomas M. Hult, Christian M. Ringle, Marko Sarstedt, Nicholas P. Danks, and Susanne J. Adler.

This document introduces a concise version of the book’s R Code and outputs for the example corporate reputation model.

Due to the concise nature of this document, we refer readers to the book for detailed explanations of the R code and outputs.

`demo("seminr-primer-v2-chap5")`

# Chapter 1 and 2:

Please note that `seminr` must be version 2.4.0 or higher and `seminrExtras` must be 0.9.0 or higher for this tutorial.

```{r}
#| eval: false
# Install the SEMinR package
install.packages(pkgs = "seminr")

# Install the textbook packages from CRAN
install.packages(pkgs = c("learnr", "paran", "psych", "seminrExtras"))
```

# Chapter 3: Introduction to SEMinR {#sec-intro_seminr}

## Installing and loading the package

Download the packages SEMinR [@RayDanksseminr] and seminrExtras [@RayDanksseminrExtras] by calling `install.packages("seminr")` and `install.packages("seminrExtras")` (You only need to do this once to equip RStudio on your computer with SEMinR.)

Load both packages with `library(seminr)` and `library(seminrExtras)`. (You must do this everytime you restart RStudio and wish to use SEMinR.)

```{r}
#| eval: false
# Download and install the SEMinR package
# You only need to do this once to equip RStudio with SEMinR and seminrExtras
install.packages("seminr")
install.packages("seminrExtras")
```

```{r}
# Make the SEMinR library ready to use
# You must do this every time you restart RStudio and wish to use packages
library(seminr)
library(seminrExtras)
```

## Loading the data

We will use the corporate reputation data set () for this example.

Load the corporate reputation data set from the CSV file "Corporate Reputation Data.csv" into RStudio using the `read.csv()` function. You must first download the data file from the book's [companion website](https://www.pls-sem.net/downloads/) and save it to your working directory.

```{r}
# Load the corporate reputation data
corp_rep_data <- read.csv(file = "Corporate Reputation Data.csv", 
  header = TRUE, sep = ";")
```

Inspect the first six rows of the data set using the `head()` function.

```{r}
#| results: 'hide'

head(corp_rep_data)
```

```{r}
#| output: asis
#| echo: false
knitr::kable(head(corp_rep_data))
```

## Specifying the measurement model

Use the below code to create the measurement model for the simple corporate reputation model [@Hair2026]. Figure 1 shows the simple corporate reputation model and Table 1 provides details on the measurement items.

![Figure 1. Simple corporate reputation model](Figures/Fig.3.2.png)

```{r}
#| echo: false

constructs <- c(rep("Competence (COMP)", 3),
                rep("Likeability (LIKE)", 3),
                rep("Customer Satisfaction (CUSA)",1),
                rep("Customer Loyalty (CUSL)",3))

simpleModelMM <- cbind(constructs,
                       c("comp_1","comp_2","comp_3", "like_1","like_2","like_3","cusa","cusl_1","cusl_2","cusl_3"), 
                       c("[The company] is a top competitor in its market.",
                         "As far as I know, [the company] is recognized worldwide.",
                         "I believe that [the company] performs at a premium level.",
                         "[The company] is a company that I can better identify with than other companies.",
                         "[The company] is a company that I would regret more not having if it no longer existed than I would other companies.",
                         "I regard [the company] as a likeable company. ",
                         "I am satisfied with [the company].",
                         "I would recommend [company] to friends and relatives. ",
                         "If I had to choose again, I would choose [company] as my mobile phone services provider.",
                         "I will remain a customer of [company] in the future. "
))
colnames(simpleModelMM) <- c("construct", "variable name", "item")

knitr::kable(simpleModelMM,
             caption = "**Tab. 1** Measurement details for the simple corporate reputation model.")
```

In the code provided below, we create four constructs: COMP (composite with three indicators), LIKE (composite with three indicators), CUSA (single-item composite), and CUSL (composite with three indicators).

The `constructs()` function specifies the list of all construct measurement models. Within this list you can define various constructs:

-   `composite()` specifies the measurement of individual constructs.
-   `interaction_term()` specifies interactions terms.
-   `higher_composite()` specifies hierarchical component models, i.e. higher-order constructs [@sarstedt2019specify].

Thereby, `composite()` specifies the measurement of individual constructs and

-   `multi_items()` creates a vector of multiple measurement items with similar names.
-   `single_item()` describe a single measurement item.

For example, the composite COMP incorporates the items comp_1 to comp_3.

We save the output to the `simple_mm` element.

```{r}
# Create measurement model ----
simple_mm <- constructs(
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3)))
```

The measuremnt model specification above is equivalent to specifying `composite("COMP", multi_items("comp_", 1:3), weights = mode_A)`. That is because, `weights = mode_A` is the default in SEMinR and thus does not need to be specified explicitly.

In principle, SEMinR provides two modes for measurement model estimation. The setup of the measurement model depends on whether the construct under consideration is modeled reflectively or formatively.

-   For reflective measurement models, we use mode A. The mode A estimation produces indicator loadings that correspond to bivariate correlations between the construct and its associated indicators (i.e., correlation weights).

-   For formative measurement models, we use mode B. The mode B estimation produces indicator weights that correspond to regressing the construct on its associated indicators.

## Specifying the structural model

The structural model indicates the sequence of the constructs and the relationships between them.

-   `relationships()` specifies all the structural relationships between all constructs.
-   `paths()` specifies relationships between a specific set of antecedents and outcomes.

For example, to specify the relationships from COMP and LIKE to CUSA and CUSL, we use the `from =` and `to =` arguments in the `paths()` function: `paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL"))`.

We save the output to the `simple_sm` element.

```{r}
# Create structural model ----
simple_sm <- relationships(
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"), to = c("CUSL")))
```

## Estimating the model

We use `estimate_pls()` to estimate the PLS model.

`estimate_pls()` requires the following arguments: - `data`: The data frame containing the corporate reputation data. - `measurement_model`: The measurement model specified earlier `simple_mm`. - `structural_model`: The structural model specified earlier `simple_sm`. - `inner_weights`: The inner weighting scheme to be used for estimating the structural model. - `missing`: The missing data treatment method to be used. - `missing_value`: The value used in the data set to indicate missing data.

For path weighting, SEMinR has two structural model (inner) weighting schemes:

-   Path weighting scheme: The most popular and recommended approach.
-   Factor weighting scheme: Provides the highest R² value for endogenous latent variables.

In this example, we apply mean replacement to handle missing data (`missing = mean_replacement`) and specify that missing values are coded as `-99` in the data set (`missing_value = "-99"`).

```{r}
# Estimate the model
corp_rep_simple_model <- estimate_pls(data = corp_rep_data,
  measurement_model = simple_mm,
  structural_model = simple_sm,
  inner_weights = path_weighting,
  missing = mean_replacement,
  missing_value = "-99")
```

Arguments with default values (e.g. `inner_weights`, `missing`, and `missing_value`) canalso be omitted. The code above therefore is equivalent to `corp_rep_simple <- estimate_pls(data = corp_rep_data, measurement_model = simple_mm, structural_model = simple_sm)`

## Debugging and problem solving

SEMinR has several features which assist users in bug checking and resolution. The majority of issues tyoically arises due to incorrectly entering or specifying item and construct names such as comp_1 and COMP. To address this isseue, SEMinR provides `assess_syntax` within the `estimate_pls()` function. When `assess_syntax = TRUE`, the measurement and structural model will be searched for common mistakes such as misspelled constructs, incorrectly specified interaction terms, or misspelled indicator names.

This code block demonstrates a spelling error in which COMP was erronously spelled COP.

```{r}
#| error: True

## Using the `assess_syntax` argument in `estimate_pls()`
# Specify the measurement model with a bug in the spelling of the construct 
# `COMP`: Here it is `COP` and in the actual model it is `COMP`.
error_mm <- constructs(
  composite("COP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3)))

# Specify the structural model. 
simple_sm <- relationships(
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"), to = c("CUSL")))

# Note that we have specified the argument `assess_syntax` as TRUE
# Now estimating the model should output an error
estimate_pls(data = corp_rep_data,
             measurement_model = error_mm,
             structural_model = simple_sm,
             assess_syntax = TRUE)
```

The `plot()` function can also help to identify bugs. Applied to the un-estimated structural model, measurement model, or the estimated PLS path model, we find that `COMP` is spelled differently in the models. For more info on bug checking and resolving problems, please check `demo(“seminr-debugging-problem-solving”`).

Please note that we use `theme = seminr_theme_academic()` to display constructs as circles or ovals and indicators as rectangles as well as to use a black-and-white plotting scheme.

```{r}
#| error: True

## Plotting a model for debugging
# Plot the measurement model
plot(error_mm, theme = seminr_theme_academic())

# Plot the structural model
plot(simple_sm, theme = seminr_theme_academic())
```

## Summarizing the model

Once the model has been estimated, a summarized report of the results can be generated by using the `summary()` function.

The `summary()` function applied to a SEMinR model object produces a `summary.seminr_model` class object. Its sub-objects (see Tab. 2) serve as basis for the assessment of the measurement and structural model.

```{r, echo = FALSE}
summary.seminr_model_subs <- cbind(c(
  "meta",
  "iterations",
  "paths",
  "total_effects",
  "total_indirect_effects",
  "loadings",
  "weights",
  "validity",
  "reliability",
  "composite_scores",
  "vif_antecedents",
  "fSquare",
  "descriptives",
  "it_criteria",
  "missing_data"), 
  c("The estimation function and version information.",
    "The number of iterations for the PLS algorithm to converge.",
    "The model’s path coefficients and (adjusted) R^2^ values.",
    "The model’s total effects.",
    "The model’s total indirect effects.",
    "The outer loadings for all constructs.",
    "The outer weights for all constructs.",
    "The metrics necessary to evaluate the construct measures’ validity.",
    "The metrics necessary to evaluate the construct measures’ reliability.",
    "The estimated scores for constructs.",
    "The metrics used to evaluate structural model collinearity.",
    "The f^2^ metric for all structural model relationships.",
    "The descriptive statistics of the indicator data.",
    "The information theoretic model selection criteria for the estimated model.",
    "The missing data approach and information. "))
colnames(summary.seminr_model_subs) <- c("Sub-object", "Content")

knitr::kable(summary.seminr_model_subs,
             caption = "**Tab. 2** Elements of the summary.seminr_model object.")
```

For example, by calling `summary_corp_rep$paths` we inspect the model’s path coefficients and the (adjusted) R^2^ values and by calling `summary_corp_rep$reliability`, we inspect the construct reliability metrics.

```{r}
# Summarize the model results
summary_simple_corp_rep <- summary(corp_rep_simple_model)

# Inspect the model’s path coefficients and the (adjusted) R^2 values
summary_simple_corp_rep$paths

# Inspect the construct reliability metrics
summary_simple_corp_rep$reliability
```

## Bootstrapping the model

In PLS-SEM, we need to perform bootstrapping to estimate standard errors and compute confidence intervals.

The rsulting object, i.e. `sum_boot_corp_rep`, contains the following sub-objects (Tab. 3):

```{r, echo = FALSE}
summary.boot_seminr_model_subs <- cbind(c(
  "nboot", "bootstrapped_paths", 
  "bootstrapped_weights", "bootstrapped_loadings", 
  "bootstrapped_HTMT", "bootstrapped_total_paths", "bootstrapped_total_indirect_paths"
),
c(
  "The number of bootstrap subsamples generated during bootstrapping.",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the path coefficients. ",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the indicator weights.",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the indicator loadings.",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the HTMT values.",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the model’s total effects.",
  "The bootstrap estimated standard error, t statistic, and confidence intervals for the model’s total indirect effects."))

colnames(summary.boot_seminr_model_subs) <- c("Sub-object", "Content")

knitr::kable(summary.boot_seminr_model_subs,
             caption = "**Tab. 3** Elements of the summary.boot_seminr_model object.")
```

We run the bootstrapping with the `bootstrap_model()` function with 1,000 subsamples (`nboot = 1000`) and set a seed (`seed = 123`) to obtain reproducible results.

Next, we summarize the bootstrap model with `sum_boot_corp_rep <- summary(boot_corp_rep)` and obtain results on model estimates such as the path coefficients with `sum_boot_corp_rep$bootstrapped_paths`.

```{r}
# Bootstrap the model
boot_simple_corp_rep <- bootstrap_model(
  seminr_model = corp_rep_simple_model,
  nboot = 1000,
  cores = NULL,
  seed = 123)

# Store the summary of the bootstrapped model
sum_boot_simple_corp_rep <- summary(boot_simple_corp_rep)

# Inspect the bootstrapped structural paths
sum_boot_simple_corp_rep$bootstrapped_paths

# Inspect the bootstrapped indicator loadings
sum_boot_simple_corp_rep$bootstrapped_loadings
```

## Plotting, printing, and exporting results to articles

We recommend exporting tables and matrices to .csv files, which can be imported into documents or presentations, and that figures are exported to .pdf or .png files to ensure the best print quality.

For exporting a table or matrix to .csv, we recommend using `write.csv`. For example, to report the bootstrapped paths from the simple model, we use the `write.csv()` function with argument `x = sum_boot_simple_corp_rep$bootstrapped_loadings` and `file = "boot_loadings.csv"`.

```{r}
#| eval: false
# Write the bootstrapped paths object to csv file
write.csv(x = sum_boot_simple_corp_rep$bootstrapped_loadings, 
  file = "boot_loadings.csv")
```

Figures can be first generated in RStudio to be exported in a separate step. To demonstrate this, we use a sub-object of `summary_simple_corp_rep` and plot the constructs’ internal consistency reliability estimates (i.e., Cronbach’s alpha, rhoA, and rhoC) using the `plot()` function.

Figure 2 shows the output (the plot shown in R Studio's "Plots" Window) and highlight the use of "Export" to save the plot as PDF.

```{r}
# Plot the internal consistency consistency reliability estimates
plot(summary_simple_corp_rep$reliability)
```

![Figure 2. Exporting the plot from RStudio using Save as PDF](Figures/Fig.3.11.png)

# Chapter 4: Evaluation of reflective measurement models {#sec-reflective_mm}

## Model set-up

We use the corporate reputation data set and the corporate reputation model specified in Chapter 3.

Below, we conduct the following steps:

1.  Load the packages `seminr`and `seminrExtras`

2.  Load the `crop_rep_data`

3.  Create the measurement model `corp_rep_mm`

4.  Create the structural model `corp_rep_sm`

5.  Estimate the model and store the estimated model in `corp_rep_pls_model`

6.  Summarize the estimated model in `summary_corp_rep`

See @sec-intro_seminr for detailed explanations of the code.

```{r}
# This analysis requires the psych and paran packages (refer to Section 2.6 in the book) 
# Load the SEMinR and seminrExtras libraries
library(seminr)
library(seminrExtras)

# Load the data ----
corp_rep_data <- corp_rep_data

# Create measurement model ----
corp_rep_mm <- constructs(
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3)))

# Create structural model ----
corp_rep_sm <- relationships(
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"), to = c("CUSL")))

# Estimating the model
corp_rep_pls_model <- estimate_pls(
  data = corp_rep_data,
  measurement_model = corp_rep_mm,
  structural_model = corp_rep_sm,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model results
summary_corp_rep <- summary(corp_rep_pls_model)
```

Before evaluating the measurement and the structural model, we check whether the PLS algorithm has converged.

The number resulting from `summary_corp_rep$iterations` indicates the number of iterations the PLS algorithm required to converge. The value should be lower than the value in the `maxIt` argument set in `estimate_pls()`. The default value for `maxIt` is 300. In out example, the algorithm converged after 4 iterations.

```{r}
# Iterations to converge
summary_corp_rep$iterations
```

## Unidimensionality

We initially check unidimensionality for the reflective constructs COMP, LIKE, and CUSL.

To do so, we perform the following steps:

1.  Set up a list of the reflective constructs and their indicators in `con.list`.

2.  Collect the data from the `seminr_model` object `corp_rep_pls_model` in `dim_data`.

3.  Set up helper functions to estimate Revelle’s beta (`est_dim`) and to test for unidimensionality (`test_unidim`).

4.  Perform principal components analysis (PCA) to obtain eigenvalues for the first three principal components (`PCA_results`).

5.  Perform parallel analysis using the `paran` package to obtain adjusted eigenvalues for the first three principal components (`para_results`).

```{r}
# Evaluate the undimensionality of reflective constructs
## Set up the list of reflective constructs
con.list <- list(COMP = multi_items("comp_", 1:3),
                 LIKE = multi_items("like_", 1:3),
                 CUSL = multi_items("cusl_", 1:3))

## Collect the cleaned data from the seminr_model object
## (The missing values have already been treated/imputed)
dim_data <- corp_rep_pls_model$data

## Set up helper functions (using psych package)
est_dim <- function(construct, data) {
  ret <- psych::iclust(data[,construct],
                       nclusters = 1)$beta
}

## Test for unidimensionality
test_unidim <- function(con.list, sum.model, dim_data) {
  dims_results <- lapply(con.list, function(x) est_dim(x, dim_data))
  dims_results <- unlist(dims_results)
  names(dims_results) <- names(con.list)
  alphas <- sum.model$reliability[names(con.list),"alpha"]
  dims_results <- data.frame(
    "Cronbach Alpha" = alphas,
    "Revelle Beta" = dims_results)
  return(dims_results)
}

## Principal Components Analysis Test
PCA_results <- matrix(c(base::eigen(cor(dim_data[,con.list[["COMP"]]]))$values,
                        base::eigen(cor(dim_data[,con.list[["CUSL"]]]))$values,
                        base::eigen(cor(dim_data[,con.list[["LIKE"]]]))$values),
                        nrow = 3, byrow = TRUE,
                        dimnames = list(c("COMP", "CUSL", "LIKE"),
                                        c("PC1 EV", "PC2 EV", "PC3 EV")))

# Parallel analysis (using paran package)
paran_comp <- paran::paran(dim_data[,con.list[["COMP"]]],
                           iterations=1000,
                           centile=95)
paran_cusl <- paran::paran(dim_data[,con.list[["CUSL"]]],
                           iterations=1000,
                           centile=95)
paran_like <- paran::paran(dim_data[,con.list[["LIKE"]]],
                           iterations=1000,
                           centile=95)
para_results <- matrix(c(paran_comp$AdjEv,paran_cusl$AdjEv,paran_like$AdjEv),
                       nrow = 3, byrow = TRUE,
                       dimnames = list(c("COMP", "CUSL", "LIKE"),
                                      c("PC1 EV", "PC2 EV", "PC3 EV")))

## Display the results of the unidimensionality tests
round(test_unidim(con.list, summary_corp_rep, dim_data),2)
round(PCA_results,2)
round(para_results,2)
```

We assess unidimensionality in three steps by checking the following criteria:

1.  Comparing Revelle’s beta with the Cronbach’s alpha values shows that the beta values for COMP, CUSL, and LIKE are 0.08 (i.e., 0.78-0.70), 0.05 (i.e., 0.83-0.78), and 0.02 (i.e., 0.83-0.81) units lower than the corresponding alpha values. ➝ Supporting unidimensionality
2.  The eigenvalues generated from the PCA indicate that all reflective constructs have first eigenvalues larger than one (COMP: 2.08; CUSL: 2.25; and LIKE: 2.24), while all second eigenvalues are lower than one (COMP: 0.57; CUSL: 0.49; and LIKE: 0.41). ➝ Supporting unidimensionality
3.  The parallel analysis confirms the PCA results since the first set of simulated eigenvalues (COMP: 1.92; CUSL: 2.09; and LIKE: 2.09) is smaller than the corresponding empirical eigenvalues. ➝ Supporting unidimensionality

Since all of these criteria are met, we conclude that the reflective constructs COMP, CUSL, and LIKE are unidimensional and continue with the evaluation of the measurement model.

## Indicator reliability

For the reflective measurement model, we need to estimate the relationships between the reflectively measured constructs and their indicators (i.e., loadings). Indicator reliability can be calculated by squaring the loadings.

Low indicator reliability may result in biased construct results. Therefore, we evaluate indicator loadings as follows:

-   Indicator loadings above 0.708 are recommended, since they correspond to an explained variance (indicator reliabilty) of at least 50%.

-   Indicators with loadings between 0.40 and 0.70 should be considered for removal.

-   Indicators with very low loadings (below 0.40) should be removed.

We can get the loadings by inspecting the `summary_corp_rep` object's loadings element (`summary_corp_rep$loadings`) and we can get the indicator reliabilities by squaring the loadings.

```{r}
# Inspect the indicator loadings
summary_corp_rep$loadings 
# Inspect the indicator reliability
summary_corp_rep$loadings^2
```

All indicator loadings of the reflectively measured constructs *COMP*, *CUSL*, and *LIKE* are well above the threshold value of 0.708 and all indicator reliabilities are well above 0.50. Thus we conclude that indicator reliability is established for all reflective constructs in the corporate reputation model.

## Internal consistency reliability

We evaluate internal consistency reliability using three different metrics: Cronbach’s alpha, composite reliability (rhoC), and the exact (or consistent) reliability coefficient (rhoA).

-   Cronbach’s alpha which corresponds to a lower bound of internal consistency reliability [@trizano2016best]

-   Composite reliability $\rho$c [@joreskog1971simultaneous1971] is the upper bound for internal consistency reliability

-   The exact (or consistent) reliability coefficient $\rho$A usually lies between Cronbach’s alpha and $\rho$c, and may serve as a good representation of a construct’s internal consistency reliability [@dijkstra2014pls; @dijkstra2015consistent].

An item is acceptable for inclusion in the model if its internal consistency reliability takes specific values:

-   Recommended value of 0.80 to 0.90.

-   Minimum value of 0.70 (or 0.60 in exploratory research).

-   Maximum value of 0.95 to avoid indicator redundancy, which would compromise content validity [@diamantopoulos2012guidelines].

The reliability indicators can be found in `summary_corp_rep$reliability` and plotted with `plot(summary_corp_rep$reliability)`.

```{r}
# Inspect the composite reliability
summary_corp_rep$reliability
```

```{r}
# Plot the reliabilities of constructs
plot(summary_corp_rep$reliability)
```

All three internal consistency reliability metrics (i.e., Cronbach’s alpha, rhoC, and rhoA) for the reflective constructs COMP, LIKE, and CUSL exceed the recommended threshold of 0.80 ans saty below 0.95. Thus, we conclude that internal consistency reliability is established for all reflective constructs in the corporate reputation model.

## Convergent validity

We asses convergent validity using the average variance extracted (AVE) metric. The AVE is the mean of a construct indicator's squared loadings. An acceptable AVE value is 0.50 or higher [@Hair2026].

AVE values can also be accessed via `summary_corp_rep$reliability`.

```{r}
summary_corp_rep$reliability
```

Since all AVE values for the reflective constructs COMP, LIKE, and CUSL exceed the recommended threshold of 0.50, we conclude that convergent validity is established.

## Discriminant validty

We recommend the heterotrait-monotrait ratio (HTMT) of the correlations to assess discriminant validity [@henseler2015new].

Discriminant validity problems are present when HTMT values

-   exceed 0.90 for constructs that are conceptually very similar.
-   exceed 0.85 for constructs that are conceptually more distinct.

We can get the HTMT matrix by calling `summary_corp_rep$validity$htmt`.

```{r}
summary_corp_rep$validity$htmt
```

All HTMT values are below the threshold of 0.85, indicating that discriminant validity is established for all reflective constructs in the corporate reputation model.

Bootstrap confidence intervals now help test if the HTMT is significantly different from 1.00 [@henseler2015new] or a lower threshold value such as 0.90 or 0.85 [@franke2019heuristics].

We obtain the HTMT 90% bootstrap CI by, first, bootstrapping the PLS model (`corp_rep_pls_model`) using `bootstrap_model()` into the `boot_corp_rep`object and, second, by calling `sum_boot_corp_rep <- summary(boot_corp_rep, alpha = 0.10)` and inspecting the object `sum_boot_corp_rep$bootstrapped_HTMT`.

Please note that we set `alpha = 0.10` to obtain the 90% confidence intervals which is equivalent to running a one-tailed test at 5%, needed for discriminant validity testing.

```{r}
# Bootstrap the model
boot_corp_rep <- bootstrap_model(seminr_model = corp_rep_pls_model, 
                                 nboot = 1000)

# Store the summary of the bootstrapped model
sum_boot_corp_rep <- summary(boot_corp_rep, alpha = 0.10)
```

After estimation, we inspect the bootstrapped HTMT results by calling `sum_boot_corp_rep$bootstrapped_HTMT`.

```{r}
# Extract the bootstrapped HTMT
sum_boot_corp_rep$bootstrapped_HTMT
```

The output displays the following elements:

-   The original HTMT estimates (column: `Original Est.`)

-   The bootstrapped mean ratio estimates (column: `Bootstrap Mean`)

-   The bootstrap standard deviation (column: `Bootstrap SD`)

-   The bootstrap t-values (column: `T Stat.`)

-   The 90% confidence interval (columns: `5% CI` and `95% CI`)

Please note that the results might differ slightly from the results you obtain our your own system.

Since the confidence intervals’ upper bounds are always lower than the threshold value of 0.90 (and even 0.85), we conclude that discriminant validity is established for all reflective constructs in the corporate reputation model.

## Congruence coefficient

As an additional test for discriminant validity, we assess the construct congruence using the `congruence_test()` function.

```{r}
# Calculate the bootstrapped congruence coefficient rc
congruence_test(corp_rep_pls_model, alpha = 0.10)$results
```

We find that all constructs have congruence coefficient that are significantly smaller than 1.0 at the 5% confidence level (column `97.5% CI`). These results provide further show that discriminant validity is strongly supported.

# Chapter 5: Evaluation of formative measurement models {#sec-formative_mm}

Relevant criteria for evaluating formative measurement models include the assessment of:

(1) Convergent validity.
(2) Indicator collinearity.
(3) Statistical significance and relevance of the indicator weights.

## Model set-up and estimation

Here, we work with an extended corporate reputation model as displayed below in Figure 3.

Tab. 4 shows the model's measurement details, i.e. the constructs, variable names and items for the formative constructs:

-   *QUAL*: The quality of a company’s products and services as well as its quality of customer orientation.
-   *PERF*: The company’s economic and managerial performance.
-   *CSOR*: The company’s corporate social responsibility.
-   *ATTR*: The company’s attractivenessa s an employer.

![Figure 3. Extended corporate reputation model](Figures/Fig.5.3.png)

```{r}
constructs_ext <- c(rep("Quality (QUAL)", 8),
                rep("Performance (PERF)", 5),
                rep("Corporate Social Responsibility (CSOR)", 5),
                rep("Attractiveness (ATTR)",3),
                rep("Single-item measure for the redundancy analysis", 4))

ext_ModelMM <- cbind(constructs_ext,
                     c("qual_1","qual_2","qual_3","qual_4","qual_5","qual_6","qual_7","qual_8","perf_1","perf_2","perf_3","perf_4","perf_5","csor_1","csor_2","csor_3","csor_4","csor_5","attr_1","attr_2","attr_3","qual_global","perf_global","csor_global","attr_global"), 
                     c("The products/services offered by [the company] are of high quality.",
                       "[The company] is an innovator, rather than an imitator with respect to [industry].",
                       "[The company]’s products/services offer good value for money.",
                       "The services [the company] offered are good.",
                       "Customer concerns are held in high regard at [the company].",
                       "[The company] is a reliable partner for customers.",
                       "[The company] is a trustworthy company.",
                       "I have a lot of respect for [the company].",
                       "[The company] is a very well-managed company.",
                       "[The company] is an economically stable company.",
                       "The business risk for [the company] is modest compared to its competitors.",
                       "[The company] has growth potential.",
                       "[The company] has a clear vision about the future of the company.",
                       "[The company] behaves in a socially conscious way.",
                       "[The company] is forthright in giving information to the public.",
                       "[The company] has a fair attitude toward competitors.",
                       "[The company] is concerned about the preservation of the environment.",
                       "[The company] is not only concerned about profits.",
                       "[The company] is successful in attracting high-quality employees.",
                       "I could see myself working at [the company].",
                       "I like the physical appearance of [the company] (company, buildings, shops, etc.).",
                       "Please assess the overall quality of [the company’s] activities.",
                       "Please assess [the company’s] overall performance.",
                       "Please assess the extent to which [the company] acts in socially conscious ways.",
                       "Please assess [the company’s] overall attractiveness."
                     ))
colnames(ext_ModelMM) <- c("construct", "variable name", "item")

knitr::kable(ext_ModelMM,
             caption = "**Tab. 4** Measurement details for the extended corporate reputation model.")


```

According to the extended model, we update the measurement and structural model specifications. Note that the four drivers *QUAL*, *PERF*, *CSOR*, and *ATTR* are formative constructs, estimated with `mode_B`, while *COMP* and *LIKE* are reflective, estimated with `mode_A` (default setting in the `composite()` function).

Once the model is set up, we estimate the model with `estimate_pls()` and store it in `corp_rep_pls_model_ext` to summarize it with `summary(corp_rep_pls_model_ext)`.

```{r}
# Create measurement model ----
corp_rep_mm_ext <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3))
)

# Create structural model ----
corp_rep_sm_ext <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"), to = c("CUSL"))
)

# Estimate the model ----
corp_rep_pls_model_ext <- estimate_pls(
  data = corp_rep_data,
  measurement_model = corp_rep_mm_ext,
  structural_model = corp_rep_sm_ext,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model results
summary_corp_rep_ext <- summary(corp_rep_pls_model_ext)
```

We also check the number of iteration until convergence (which should be below the default maximum number of 300 iterations).

```{r}
# Iterations to converge
summary_corp_rep_ext$iterations
```

Next, we bootstrap the model (`bootstrap_model()`), summarize the result with a 90% CI by calling `summary(boot_corp_rep_ext, alpha = 0.10)` and store the result in `sum_boot_corp_rep_ext`.

```{r}
# Bootstrap the model
boot_corp_rep_ext <- bootstrap_model(seminr_model = corp_rep_pls_model_ext,
                                     nboot = 1000,
                                     seed = 123)

# Store the summary of the bootstrapped model
sum_boot_corp_rep_ext <- summary(boot_corp_rep_ext, alpha = 0.10)
```

## Reflective measurement model evaluation

Wwe must ensure that the extended model remains valid and reliable before continuing to evaluate the four new exogenous formative constructs. To do so, we follow the reflective measurement model assessment procedure outlined in Chapter 4 (see @sec-reflective_mm).

The code below conducts the measurement model evaluation for the extended corporate reputation model. (Please note that you only have to reload `seminr` and `seminrExtras` if you detached these packages or started a new R session.)

```{r}
# Load the SEMinR and seminrExtras libraries
library(seminr)
library(seminrExtras)

# Load the corporate repuation data
corp_rep_data <- corp_rep_data

# Create measurement model ----
corp_rep_mm_ext <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3))
)

# Create structural model ----
corp_rep_sm_ext <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"),         to = c("CUSL"))
)

# Estimate the model ----
corp_rep_pls_model_ext <- estimate_pls(
  data = corp_rep_data,
  measurement_model = corp_rep_mm_ext,
  structural_model  = corp_rep_sm_ext,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model results
summary_corp_rep_ext <- summary(corp_rep_pls_model_ext)

# Iterations to converge
summary_corp_rep_ext$iterations

# Bootstrap the model
boot_corp_rep_ext <- bootstrap_model(seminr_model = corp_rep_pls_model_ext,
                                     nboot = 1000,
                                     seed = 123)

# Store the summary of the bootstrapped model
sum_boot_corp_rep_ext <- summary(boot_corp_rep_ext, alpha = 0.10)
```

In the following, we assess indicator loadings (`summary_corp_rep_ext$loadings`), indicator reliability (`summary_corp_rep_ext$loadings^2`), internal consistency reliability (`summary_corp_rep_ext$reliability`), and discriminant validity (`summary_corp_rep_ext$validity$htmt` and `sum_boot_corp_rep_ext$bootstrapped_HTMT`) for the reflective constructs *COMP*, *LIKE*, *CUSA*, and *CUSL* in the extended corporate reputation model.

Please note that the outputs contain the estimates for both reflective and formative constructs although only the reflectively measured constructs need to be evaluated now.

After the addition of the new measurement models and their structural paths, the reflectively measured constructs continue to meet all criteria.

Please see the book's Appendix B for details on the interpretation of the estimates.

```{r}
# Inspect the indicator loadings
summary_corp_rep_ext$loadings

# Inspect the indicator reliability
summary_corp_rep_ext$loadings^2

# Inspect the internal consistency and reliability
summary_corp_rep_ext$reliability

# HTMT criterion
summary_corp_rep_ext$validity$htmt

# Extract the bootstrapped HTMT
sum_boot_corp_rep_ext$bootstrapped_HTMT
```

## Formative measurement model evaluation

### Convergent validity assessment

To examine the formatively measured constructs' convergent validity, we carry out a separate redundancy analysis for each construct (see Fig. 4). The survey contained global single-item measures with generic assessments of the four phenomena—attractiveness, corporate social responsibility, performance, and quality—that we can use as measures of the dependent construct in the redundancy analyses (*attr_global*, *csor_global*, *perf_global*, and *qual_global*).

![Figure 4. Extended corporate reputation model](Figures/Fig.5.4.png){#fig4}

To run the redundancy analysis for a formatively measured construct (e.g. attractiveness), we take the following steps:

1.  **Measurement model:** Assign its multi-item, formative measures to the latent variable `ATTR_F` via `composite("ATTR_F", multi_items("attr_", 1:3), weights = mode_B)` and we assign an alternative measure (*attr_global*) of the same concept to `ATTR_G` via `composite("ATTR_G", single_item("attr_global"))`.

2.  **Structural model:** Assign a single path from `ATTR_F`to `ATTR_G`

3.  **Estimate and summarize the model**: Perform the redundancy analysis by estimating the model with `estimate_pls()` and summarize it.

4.  **Repeat** with all formative constructs separatly.

```{r}
# Redundancy analysis ----
# ATTR ----
# Create measurement model
ATTR_redundancy_mm <- constructs(
  composite("ATTR_F", multi_items("attr_", 1:3), weights = mode_B),
  composite("ATTR_G", single_item("attr_global"))
)

# Create structural model
ATTR_redundancy_sm <- relationships(
  paths(from = c("ATTR_F"), to = c("ATTR_G"))
)

# Estimate the model
ATTR_redundancy_pls_model <- estimate_pls(
  data = corp_rep_data,
  measurement_model = ATTR_redundancy_mm,
  structural_model = ATTR_redundancy_sm,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model
sum_ATTR_red_model <- summary(ATTR_redundancy_pls_model)

# CSOR ----
# Create measurement model
CSOR_redundancy_mm <- constructs(
  composite("CSOR_F", multi_items("csor_", 1:5), weights = mode_B),
  composite("CSOR_G", single_item("csor_global"))
)

# Create structural model
CSOR_redundancy_sm <- relationships(
  paths(from = c("CSOR_F"), to = c("CSOR_G"))
)

# Estimate the model
CSOR_redundancy_pls_model <- estimate_pls(
  data = corp_rep_data,
  measurement_model = CSOR_redundancy_mm,
  structural_model = CSOR_redundancy_sm,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model
sum_CSOR_red_model <- summary(CSOR_redundancy_pls_model)

# PERF ----
# Create measurement model
PERF_redundancy_mm <- constructs(
  composite("PERF_F", multi_items("perf_", 1:5), weights = mode_B),
  composite("PERF_G", single_item("perf_global"))
)

# Create structural model
PERF_redundancy_sm <- relationships(
  paths(from = c("PERF_F"), to = c("PERF_G"))
)

# Estimate the model
PERF_redundancy_pls_model <- estimate_pls(
  data = corp_rep_data,
  measurement_model = PERF_redundancy_mm,
  structural_model  = PERF_redundancy_sm,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model
sum_PERF_red_model <- summary(PERF_redundancy_pls_model)

# QUAL ----
# Create measurement model
QUAL_redundancy_mm <- constructs(
  composite("QUAL_F", multi_items("qual_", 1:8), weights = mode_B),
  composite("QUAL_G", single_item("qual_global"))
)

# Create structural model
QUAL_redundancy_sm <- relationships(
  paths(from = c("QUAL_F"), to = c("QUAL_G"))
)

# Estimate the model
QUAL_redundancy_pls_model <- estimate_pls(
  data = corp_rep_data,
  measurement_model = QUAL_redundancy_mm,
  structural_model  = QUAL_redundancy_sm,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the model
sum_QUAL_red_model <- summary(QUAL_redundancy_pls_model)

# Check the path coefficients for convergent validity
sum_ATTR_red_model$paths
sum_CSOR_red_model$paths
sum_PERF_red_model$paths
sum_QUAL_red_model$paths
```

All path coefficients are above 0.708 , indicating that the formative constructs explain more than 50% of the variance in their respective global measures. This result provides support for the formatively measured construct’s convergent validity.

### Collinearity assessment

To assess collinearity among the indicators of each formative construct, we inspect the variance inflation factor (VIF) values provided in `summary_corp_rep_ext$validity$vif_items`.

```{r}
# Collinearity analysis ----
summary_corp_rep_ext$validity$vif_items
```

All indicator VIF values are below the conservative threshold of 3.0, indicating that collinearity is not a concern for any of the formative constructs in the extended corporate reputation model.

### Statistical significance and relevance of the indicator weights

The next step for evaluating formatively specified constructs is to analyze the indicator weights’ significance and relevance. To do so, we follow the procedure outlined in Figure 5. 

![Figure 5. Decision-making process for keeping or deleting formative indicators](Figures/Fig.5.2.png){#fig5}

#### Indicator weights

We consider the significance of the indicator weights by means of bootstrapping (`bootstrap_model()`).

In `bootstrap_model()`, we use the following parameters:

-   Provide `corp_rep_pls_model_ext` as the seminr model to be evaluated

-   Assign n = 1000 bootstrap runs with `nboot = 1000`. We use 1000 runs for a faster, initial estimation. For reporting the final results, the recommended number of 10,000 bootstrap samples should be used.

-   Use `parallel::detectCores()` to automatically detect the number of cores on your machine and use the maximum cores available. This allows for a faster estimation by using multiple (instead of only one) cores of your CPU.

-   Use `seed` to ensure that the results of a specific bootstrap can be reproduced on your software and hardware. Please note that even with a specified seed, the computations may differ bewteen various configurations of software and hardware meaning that your results might differ from ours.

We assign the output to the `boot_corp_rep_ext` object and apply `summary()` with a significance level of 5% (`alpha = 0.05`) for two-tailed testing.

We extract indicator weights results via `sum_boot_corp_rep_ext$bootstrapped_weights`.

```{r}
# Bootstrap the model ----
# seminr_model is the SEMinR model to be bootstrapped
# nboot is the number of bootstrap iterations to run
# cores is the number of cpu cores to use in multicore bootstrapping
# parallel::detectCores() allows for using the maximum cores on your device
# seed is the seed to be used for making bootstrap replicable
boot_corp_rep_ext <- bootstrap_model(
  seminr_model = corp_rep_pls_model_ext,
  nboot = 1000,
  cores = parallel::detectCores(), 
  seed = 123)

# Summarize the results of the bootstrap
# alpha sets the specified level for significance (i.e., 0.05)
sum_boot_corp_rep_ext <- summary(boot_corp_rep_ext, alpha = 0.05)

# Inspect the bootstrapping results for indicator weights
sum_boot_corp_rep_ext$bootstrapped_weights
```

The output shows the following values which we use to evaluate the significance of the indicators:

- Each row corresponds to one weight. Please note that weights are generated for all indicators (formative and reflective).

- `Original Est.` shows each indicator's original weight as estimated from the original dataset.

- `Bootstrap Mean` shows the mean of the weights from all bootstrap estimation.

- `Bootstrap SD` shows the standard error of the weights from all bootstrap estimation.

- `T. Stat.` shows the empirical t-value which is computed by dividing an indicator’s original weight (`Original Est.`) by its corresponding bootstrap standard error (`Bootstrap SD`). 

- `2.5% CI`and `97.5% CI` show the lower boundary (2.5% CI) and the upper boundary (97.5% CI) of the 95% confidence interval. Confidence intervals can be used for significance testing. Specifically, a null hypothesis H~0~ that a certain parameter, such as an indicator weight w~1~, equals zero (i.e., H~0~: w~1~ = 0) in the population is rejected at a given level alphs (α), if the corresponding (1 – α) % bootstrap confidence interval does not include zero. In other words, if a confidence interval does not include zero, we assume a significant effect. 

- `Bootstrap P Val` shows the p-value.

The results indicate that all formative indicators are significant at a 5% level, except *csor_2*, *csor_4*, *qual_2*, *qual_3*, and *qual_4*. 

For  *csor_2*, *csor_4*, *qual_2*, *qual_3*, and *qual_4*, we can see that their 95% confidence interval includes zero and the p-value, correspondingly, is larger than 0.05. For example, for *csor_2*, the confidence interval ranges from `-0.097` to `0.173` which includes 0 and the p-value of `0.626` also is non-significant at a 5% alpha level.

#### Indicator loadings

Following the procedure outlined in Figure 5 (@fig5), we now check the indicator loadings for the indicators with non-significant weights (i.e.,  *csor_2*, *csor_4*, *qual_2*, *qual_3*, and *qual_4*) by calling `sum_boot_corp_rep_ext$bootstrapped_loadings`.

```{r}
# Inspect the bootstrapping results for indicator loadings
sum_boot_corp_rep_ext$bootstrapped_loadings
```

The resulting table is structurally similar to the table from previous estimation of indicator weights (`sum_boot_corp_rep_ext$bootstrapped_weights`) but shows results for the indicator loadings.

For *csor_2*, *csor_4*, *qual_2*, *qual_3*, and *qual_4*, all loadings are above the threshold value of 0.5 and are significant. Thus, we retain all indicators in the formatively measured constructs, even though not every indicator weight is significant.

# Chapter 6: Evaluation of the structural model {#sec-structural}

In this chapter, we continue evaluating the extended corporate reputation model introduced in Chap. 5.

To initiate the analysis, we reload the necessary libraries and re-estimate the extended corporate reputation model.

```{r}
# Load the SEMinR, SEMinRExtras libraries 
library(seminr)
library(seminrExtras)

# Setup the plotting environment
oldpar <- par(mfrow = c(1,1))

# Load the data
corp_rep_data <- corp_rep_data

# Create measurement model ----
corp_rep_mm_ext <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3))
)

# Create structural model ----
corp_rep_sm_ext <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"), to = c("CUSL"))
)

# Estimate the model ----
corp_rep_pls_model_ext <- estimate_pls(
  data = corp_rep_data,                            
  measurement_model = corp_rep_mm_ext,
  structural_model = corp_rep_sm_ext,
  missing = mean_replacement,
  missing_value = "-99")

# Summarize the results of the model estimation
summary_corp_rep_ext <- summary(corp_rep_pls_model_ext)

# Bootstrap the model ----
boot_corp_rep_ext <- bootstrap_model(
  seminr_model = corp_rep_pls_model_ext,
  nboot = 1000,
  cores = parallel::detectCores(),
  seed = 123)

# Summarize the results of the bootstrap
summary_boot_corp_rep_ext <- summary(boot_corp_rep_ext,
                                     alpha = 0.05)
```

## Assess collinearity issues in the structural model

We start by assessing collinearity issues in the structural model by inspecting the variance inflation factor (VIF) values for the structural model's antecedents provided in `summary_corp_rep_ext$vif_antecedents`.

```{r}
# Inspect the structural model collinearity VIF
summary_corp_rep_ext$vif_antecedents
```

We find that all VIFs are below the threshold of 5. *Qual* shows a value slighty above 3 (specifically `{r}summary_corp_rep_ext$vif_antecedents$COMP["QUAL"]`) which we consider unproblematic since it is a single occurence. Thus, we conclude that collinearity is not a concern for the structural model of the extended corporate reputation model.

## Assess the model’s explanatory power

We next turn to the structural model assessment to evaluate the relevance and significance of the structural paths. 

The following codes provides codes provides the following information about the structural model:

-  The structural **path coefficients** can be accessed by inspecting the `bootstrapped_paths` element nested in the `summary_corp_rep_ext` object.

- The **total effects** (i.e., the sum of all direct and indirect effects that link to constructs) can be accessed by inspecting the `bootstrapped_total_paths` element nested in the `summary_corp_rep_ext` object.

- The model's **explanatory power (R^2^)** can be accessed by inspecting the `paths` element nested in the `summary_corp_rep_ext` object.

- The model's **effect sizes (f^2^)** can be accessed by inspecting the `fSquare` element nested in the `summary_corp_rep_ext` object.

```{r}
#| results: 'hide'
# Inspect the structural paths
summary_boot_corp_rep_ext$bootstrapped_paths

# Inspect the total effects
summary_boot_corp_rep_ext$bootstrapped_total_paths

# Inspect the model RSquares
summary_corp_rep_ext$paths

# Inspect the effect sizes
summary_corp_rep_ext$fSquare
```

```{r}
#| echo: false
# Inspect the structural paths
summary_boot_corp_rep_ext$bootstrapped_paths
```

The path coefficient output of `summary_boot_corp_rep_ext$bootstrapped_paths` shows the following metrics which we use to evaluate the significance of the path coefficient:

- Each row corresponds to a specific path coefficient.

- `Original Est.` shows each path coefficient as estimated from the original dataset.

- `Bootstrap Mean` shows the mean of a path coefficient from all bootstrap estimation.

- `Bootstrap SD` shows the standard error of a path coefficient from all bootstrap estimation.

- `T. Stat.` shows the empirical t-value which is computed by dividing the original path coefficient (`Original Est.`) by its corresponding bootstrap standard error (`Bootstrap SD`). 

- `2.5% CI`and `97.5% CI` show the lower boundary (2.5% CI) and the upper boundary (97.5% CI) of the 95% confidence interval that can be used for significance testing. Specifically, if a confidence interval does not include zero, we assume a significant effect. 

- `Bootstrap P Val` shows the p-value.

For the paths from the exogenous constructs, we find that:

- *QUAL* has a strong positive impact on both COMP (0.430) and LIKE (0.380), which are also both significant on a 5% alpha-level with p-values of p < .001. Please note that SEMinR displays such low p-values as 0.000. Such values should be reported as p < .001 since a p-value only approximatly reaches 0.

- *PERF* shows a similar pattern of relationships with COMP (0.295) and LIKE (0.117). The path from PERF TO COMP is significant at a 5% alpha level with a p-value of p < .001 while the path from PERF to LIKE is not significant (p = .092)

- *CSOR* has a positive, but comparatively small impact on COMP (0.059) and LIKE (0.178). The path from CSOR to COMP is not significant (p = .274) while the path from CSOR to LIKE is significant at a 5% alpha level with a p-value of p = .002.

- *ATTR* has a positive but also a small impact on COMP (0.086) and LIKE (0.167). The path from ATTR to COMP is not significant (p = .102) while the path from ATTR to LIKE is significant at a 5% alpha level with a p-value of p = .016.

Similar interpretations can be reached if we interpret the confidence intervals instead of the p-values. For example, for the effect of QUAL to COMP 0 is clearly below the lower bound of the CI (CI95% = [0.303, 0.552]). Estimates that are not significant, such as the path coefficient for PERF to LIKE have a CI that includes 0 (CI95% = [-0.011, 0.261]).


```{r}
#| echo: false
# Inspect the total effects
summary_boot_corp_rep_ext$bootstrapped_total_paths
```

It is also important to consider the total effects to gain an idea of the impact of the four exogenous driver constructs on the outcome constructs *CUSA* and *CUSL*.

Of the four driver constructs, *QUAL* has the strongest total effect on *CUSL* (0.248), followed by *CSOR* (0.105), *ATTR* (0.101), and *PERF* (0.089). Please note that all of exogenous driver constructs' total effects are significant at a 5% level (since all p-values are below the 5% alpha level).

```{r}
#| echo: false
# Inspect the model RSquares
summary_corp_rep_ext$paths

# Inspect the effect sizes
summary_corp_rep_ext$fSquare
```

We next consider the model’s explanatory power by analyzing the R^2^ of the endogenous constructs and the f^2^ effect size of the predictor constructs.

The R^2^ values of *COMP* (0.631), *CUSL* (0.562), and *LIKE* (0.558) can be considered moderate, whereas the R^2^ value of *CUSA* (0.292) is weak.

The f^2^ values for all combinations of endogenous constructs (represented by the columns) and corresponding predictor constructs (represented by the rows) show, for example, that *LIKE* has a medium effect size of 0.159 on *CUSA* and of 0.138 on *CUSL*. On the contrary, *COMP* has no effect on *CUSA* (0.018) or *CUSL* (0.000). 

## Assess the model’s predictive power 

To assess the model's predictive power, we can use the cross‐validated predictive ability test (CVPAT)  which is a holdout-based procedure for comparing the predictive performance of a PLS path model with that of a linear regression model (LM) and an indicator averages model (IA) [@Liengaard2021].

We use the `assess_cvpat()` function from the `seminrExtras` package to conduct the CVPAT assessment of the extended corporate reputation model (`corp_rep_pls_model_ext`). 

The `assess_cvpat()` function requires the following parameters:

- `seminr_model`: The SEMinR model to be assessed (here: `corp_rep_pls_model_ext`).

- `testtype`: The type of test to be performed. We use a one-tailed test with the alternative hypothesis that the PLS model has a greater predictive performance than the LM and IA models (i.e., `testtype = "greater"`).

- `nboot`: The number of bootstrap iterations to be performed. We use 2000 iterations for a faster, initial estimation. For reporting the final results, the recommended number of 10,000 bootstrap samples should be used.

- `seed`: The seed to be used for making the bootstrap replicable. Please note that even with a specified seed, the computations may differ bewteen various configurations of software and hardware meaning that your results might differ from ours.

- `technique`: The technique to be used for generating predictions. We use the default technique of `predict_DA` which generates predictions based on the direct antecedents (DA) approach.

- `noFolds`: The number of folds to be used in the cross-validation procedure. We use 10 folds.

- `reps`: The number of repetitions to be performed in the cross-validation procedure. We use 10 repetitions.

After estimating the CVPAT, we inspect the results for the comparison of the PLS model with the LM and IA models by calling `assess_results$CVPAT_compare_LM` and `assess_results$CVPAT_compare_IA`, respectively.

```{r}
# Conduct CVPAT assessment of the established model
assess_results <- assess_cvpat(seminr_model = corp_rep_pls_model_ext,
                               testtype = "greater",
                               nboot = 2000,
                               seed = 123,
                               technique = predict_DA,
                               noFolds = 10,
                               reps = 10)

print(assess_results$CVPAT_compare_LM,
      digits = 3)
print(assess_results$CVPAT_compare_IA,
      digits = 3)
```

The results show that the PLS model has a significantly greater predictive performance than the IA model (p = 0.000).

For the comparision against the LM however, we find more mixed results. The global model also yields significantly better predictive power than the LM, indicating that the structural model contributes significant predictive power over and above the measurement model only. In terms of individual constructs, both *COMP* and *CUSA*, however, are not better predicted by our model than the LM with Δ: -0.034 (p = 0.124) and Δ: 0.015 (p = 0.729) for *COMP* and *CUSA*, respectively.

## Model comparisons

We next turn to a model comparison. This step is optional and the analysis should be conducted if we have multiple theoretically or managerially justified candidate models.

In our demonstration, we compre the following models (Figure 6 (@fig6))

![Figure 6. Three competing models for predictive model comparison (source: @Hair2026)](Figures/Fig.6.10.png){#fig6}

The following code builds the models:

```{r}
# Conduct predictive model comparison
# Estimate alternative models
# Create measurement model ----
measurement_model <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3))
)

# Create structural models
# Model 1
structural_model1 <- relationships(
  paths(from = c("QUAL","PERF","CSOR","ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP","LIKE"),  to = c("CUSA", "CUSL")),
  paths(from = "CUSA", to = c("CUSL"))
)
# Model 2
structural_model2 <- relationships(
  paths(from = c("QUAL","PERF","CSOR","ATTR"), to = c("COMP", "LIKE", "CUSA")),
  paths(from = c("COMP","LIKE"),  to = c("CUSA", "CUSL")),
  paths(from = "CUSA", to = c("CUSL"))
)
# Model 3	
structural_model3 <- relationships(
  paths(from = c("QUAL","PERF","CSOR","ATTR"), 
        to = c("COMP", "LIKE", "CUSA", "CUSL")),
  paths(from = c("COMP","LIKE"),  to = c("CUSA", "CUSL")),
  paths(from = "CUSA", to = c("CUSL"))
)

# Estimate and summarize the models ----
pls_model1 <- estimate_pls(
  data  = corp_rep_data,
  measurement_model = measurement_model,
  structural_model  = structural_model1,
  missing_value = "-99"
)
sum_model1 <- summary(pls_model1)

pls_model2 <- estimate_pls(
  data  = corp_rep_data,
  measurement_model = measurement_model,
  structural_model  = structural_model2,
  missing_value = "-99"
)
sum_model2 <- summary(pls_model2)

pls_model3 <- estimate_pls(
  data  = corp_rep_data,
  measurement_model = measurement_model,
  structural_model  = structural_model3,
  missing_value = "-99"
)
sum_model3 <- summary(pls_model3)
```

For model comparison, we use three approaches:

- **Information-theoretic model selection criteria (IT criteria)**: IT criteria facilitate the comparison of models in terms of model fit and predictive power without requiring a holdout sample. Here, Schwarz's [-@schwarz1978estimating] Bayesian information criterion (BIC) achieves a good tradeoff between model fit and predictive power. Researchers should  select the model that minimizes the value of BIC for a specific target construct. Differences in BIC values can, however, be small in practice, leading to model selection uncertainty. Akaike weights resolve this issue, by determining a model’s relative likelihood of being the data generation model, given the data and a set of competing models (Danks, Sharma, & Sarstedt, 2020). The higher the Akaike weights, the more likely it is that the selected model better represents the data generation model.

- **Predictive power**: CVPAT can help assess whether an alternative model offers significantly higher out‐of‐sample predictive power than an established model.

- **Overfit ratio (OFR)**: The OFR is calculated as the ratio of in-sample predictive power to out-of-sample predictive power and indicates the relative loss in predictive power on training data versus unseen data. In other words, the OFR checks whether additional model complexity has affected model overfit. We recommend that the OFR should not be larger than 10%  [@Danks2024_OFR].

We focus our analysis on the CUSA construct as the immediate consequence of the two dimensions of corporate reputation (LIKE and COMP).

The following code assesses the **IT criteria** BIC and AIC by calling the `it_criteria` for each of the condidate models.

The results indicate that Model 1 is superior in terms of model fit. That is becausem the BIC values are lowest for Model 1 (-102.206) versus Model 2 (-93.965) and Model 3 (-97.401). The Akaike weight is also very high for Model 1 (0.904) compared to Model 2 (0.015) and Model 3 (0.082).

```{r}
# Inspect the IT Criteria matrix of Model1
sum_model1$it_criteria

# Subset the matrix to only return the BIC row and CUSA column
sum_model1$it_criteria["BIC", "CUSA"]

# Collect the vector of BIC values for CUSA
itcriteria_vector <- c(sum_model1$it_criteria["BIC","CUSA"],
                       sum_model2$it_criteria["BIC","CUSA"],
                       sum_model3$it_criteria["BIC","CUSA"])

# Assign the model names to IT Criteria vector
names(itcriteria_vector) <- c("Model1", "Model2", "Model3")

# Inspect the IT Criteria vector for competing models
itcriteria_vector

# Calculate the model BIC Akaike weights
compute_itcriteria_weights(itcriteria_vector)
```

Next we compare Model1 and Model 3 in a **CVPAT**.

Please note that we use the function `assess_cvpat_compare()` and specify `pls_model1` as the established_model and `pls_model3` as an alternative model.

We however find that the established model does not yield significantly lower predictive error versus the alternative model overall (Δ-0.004. p = 0.359).

```{r}
# Conduct CVPAT analysis ----
# Select the models for analysis and comparison
established_model <- pls_model1
alternative_model <- pls_model3

# Conduct CVPAT model comparison
compare_results <- assess_cvpat_compare(established_model = established_model,
                                        alternative_model = alternative_model,
                                        testtype = "greater",
                                        nboot = 2000,
                                        technique = predict_DA,
                                        seed = 123,
                                        noFolds = 10,
                                        reps = 10)

print(compare_results,
      digits = 3)
```

As a last step, we assess the **OFR** for model 1 and model 3.

To do so, we use the `predict_pls()` function to each model object and apply `summary()` to get the objects that contain the OFR (`predict_established`, `predict_alternative`]. To display the OFR, we output the contents of `predict_established$construct_error`, respectively `predict_alternative$construct_error` and focus on the `overfit` row in the output.


```{r}
# Calculate overfit ratio ----
predict_established <- summary(predict_pls(model = established_model))
predict_alternative <- summary(predict_pls(model = alternative_model))

# Overfit ratio for established model
predict_established$construct_error

# Overfit ratio for alternative model
predict_alternative$construct_error
```

The results also favor Model 1 since its OFR is notably smaller (0.014, i.e. 1.4%) than model 3's OFR (0.050, i.e. 5%) and both OFRs do not exceed the stricter of the recommended thresholds of 10%.

# Chapter 7: Moderation {#sec-moderation}

Moderation describes a situation in which the relationship between two constructs is not constant but depends on the values of a third variable, referred to as a moderator variable. The moderator variable (or construct) changes the strength or even the direction of a relationship between two constructs in the model.

Specifically, we introduce perceived *switching costs (SC)* as a moderator variable that can be assumed to negatively influence the relationship between *CUSA* and *CUSL* (Fig. 7). We assume that the higher *SC*, the weaker the relationship between these two constructs. We use an extended form of Jones, Mothersbaugh, and Beatty’s [-@jones2000switching] scale and measure switching costs reflectively using four indicators (Tab. 5).

![Figure 7. Corporate reputation model with the added moderator switching costs (SC) and the interaction term (CUSA * SC) (source: authors’ own figure)](Figures/Fig.7.6.png){#fig7}

```{r}
constructs_extMod <- rep("Switching Cost (SC)", 4)

ext_ModModelMM <- cbind(constructs_extMod,
                        c("switch_1","switch_2", "switch_3","switch_4"), 
                        c("It takes me a great deal of time to switch to another company.",
                          "It costs me too much to switch to another company.",
                          "It takes a lot of effort to get used to a new company with its specific 'rules' and practices.",
                          "In general, it would be a hassle switching to another company."
                        ))
colnames(ext_ModModelMM) <- c("construct", "variable name", "item")

knitr::kable(ext_ModModelMM,
             caption = "**Tab. 5** Switching costs indicators")
```

## Creating the interaction term

Interaction terms are described in the measurement model function` constructs()` via `interaction_term`. We recommend using	`two-stage` method which specifies the interaction term as the product of the latent scores of the independent construct and moderator variable. For specifying the measurement model, thus, we add the interaction term between the independent variable *CUSA* and the moderator variable *SC* and apply the two stage approach as follows: `interaction_term(iv = "CUSA", moderator = "SC", method = two_stage)`.

```{r}
# Load the SEMinR library
library(seminr)

# Load the data
corp_rep_data <- corp_rep_data

# Create the measurement model ----
corp_rep_mm_mod <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("SC", multi_items("switch_", 1:4)),
  composite("CUSL", multi_items("cusl_", 1:3)),
  interaction_term(iv = "CUSA", moderator = "SC", method = two_stage))
```

Further, we add the path corresponding to the simple effect (i.e., a path from *CS* to *CUSl*) and the path corresponding to the interaction effect (i.e., `CUSA*SC` to *CUSL*) to the structural model. For this, we use `paths(from = c("CUSA", "SC", "CUSA*SC"), to = c("CUSL"))`.

Next, we estimate the model with `estimate_pls()`, store it to `corp_rep_pls_model_mod` and summarize it to `sum_corp_rep_mod`. Further, we also apply bootstrapping and summarize the bootstrapped model with an alpha level of 5% (`summary(boot_corp_rep_mod, alpha = 0.05)`).

```{r}
# Create the structural model ----
corp_rep_sm_mod <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA", "SC", "CUSA*SC"), to = c("CUSL"))
)

# Estimate the new model with moderator ----
corp_rep_pls_model_mod <- estimate_pls(
  data = corp_rep_data,
  measurement_model = corp_rep_mm_mod,
  structural_model = corp_rep_sm_mod,
  missing = mean_replacement,
  missing_value = "-99"
)

# Extract the summary
sum_corp_rep_mod <- summary(corp_rep_pls_model_mod)

# Bootstrap the model ----
boot_corp_rep_mod <- bootstrap_model(
  seminr_model = corp_rep_pls_model_mod,
  nboot = 1000)

# Summarize the results of the bootstrap
sum_boot_corp_rep_mod <- summary(boot_corp_rep_mod, alpha = 0.05)
```

## Model evaluation

In terms of the measurement model evaluation, al constructs still need to match the requirements outlined in Sections @sec-reflective_mm, @sec-formative_mm, @structural. However, there is no requirement to assess the measurement model of the interaction term (i.e., CUSA * SC) itself since it represents an auxiliary measurement and, thus, does not represent a distinct theoretical entity.
Plase note that all measurement models, including the newly introduced *SC* construct, exhibit sufficient levels of reliability and validity.

In order to evaluate the moderating effect, we inspect the `bootstrapped_paths` element in the `sum_boot_corp_rep_mod` object and, to assess the moderating effect's size, we inspect the paths f^2^ via `sum_corp_rep_mod$fSquare`.

```{r}
# Inspect the bootstrapped structural paths
sum_boot_corp_rep_mod$bootstrapped_paths

# Inspect the fsquare
sum_corp_rep_mod$fSquare
```

We find a negative interaction term for CUSA*SC on CUSL of –0.071 that is significant with a t-value of -2.191 and a p-value of 0.008. A corresponding simple effect of CUSA on CUSL has a path coefficient of 0.467 (t = 10.071, p < 0.001). These results suggest that a higher level of switching costs decrease the relationship between CUSA and CUSL. In terms of the effect size f², the moderating effect is small with f² = 0.014.

To better comprehend the results of the moderator analysis, we use the `slope_analysis()` function to visualize the two-way interaction effect. Therefore, we specify the model (`moderated_model = corp_rep_pls_model_mod`), the dependent variable (`dv = "CUSL"`), the moderator (`moderator = "SC"`), and the independent variable (`iv = "CUSA"`) as well as the legend's position (`leg_place = "bottomright"`).

```{r}
# Simple slope analysis plot
slope_analysis(
  moderated_model = corp_rep_pls_model_mod,
  dv = "CUSL",
  moderator = "SC",
  iv = "CUSA",
  leg_place = "bottomright")
```

The graph shows that the relationship between *CUSA* and *CUSL* is positive for all three lines. The negative moderating effect, however, shows itself at the slopes for the different levels of the moderator *SC*. Specifically:

- higher levels of the moderator *SC* (i.e., the *SC at +1SD* line) correspond to a weaker effect of CUSA on CUSL. 

- lower levels of the moderator *SC* (i.e., the *SC at -1SD* line) correspond to a higher effect of CUSA on CUSL.

The results thus support the idea that higher switching costs (*SC*) decrease the effect of customer satisfaction (*CUSA* on customer loyalty (*CUSL*).

# Chapter 8: Mediation {#sec-mediation}

Mediation occurs when a construct, referred to as mediator construct, intervenes between two other related constructs. Direct effects are the relationships linking two constructs with a single arrow (p~1~, p~2~, and p~3~). Indirect effects represent a sequence of relationships with at least one intervening construct. Thus, an indirect effect is a sequence of two or more direct effects (p~1~ and p~2~) and is represented visually by multiple arrows. 

Fig. 8 shows two mediation effects in the corporate reputation model, i.e. from *COMP* through *CUSA* to *CUSL* and from *LIKE* through *CUSA* to *CUSL*.

![Figure 8. Extended corporate reputation model with highlighted mediation effects (source: authors’ own figure).](Figures/Fig.8.6.png){#fig8}

To initiate the analysis, we reload the necessary libraries and re-estimate the extended corporate reputation model.

```{r}
# Load the SEMinR and seminrExtras libraries
library(seminr)
library(seminrExtras)

# Load the data
corp_rep_data <- corp_rep_data

# Create measurement model ----
corp_rep_mm_ext <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("CUSL", multi_items("cusl_", 1:3))
)

# Create structural model ----
corp_rep_sm_ext <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA"),         to = c("CUSL"))
)

# Estimate the model ----
corp_rep_pls_model_ext <- estimate_pls(
  data = corp_rep_data,                            
  measurement_model = corp_rep_mm_ext,
  structural_model = corp_rep_sm_ext,
  missing = mean_replacement,
  missing_value = "-99"
)

# Summarize the results of the model estimation
summary_corp_rep_ext <- summary(corp_rep_pls_model_ext)

# Bootstrap the model ----
boot_corp_rep_ext <- bootstrap_model(
  seminr_model = corp_rep_pls_model_ext,
  nboot = 1000,
  cores = parallel::detectCores(),
  seed = 123
)

# Summarize the results of the bootstrap
summary_boot_corp_rep_ext <- summary(boot_corp_rep_ext, 
                                     alpha = 0.05)
```

## Testing mediating effects

The results for the indirect effects ($\small p~1~*p~2~$) can be found by inspecting the `total_indirect_effects` element within the `summary_corp_rep_ext` object.

Specific indirect paths can be evaluated for significance by using the `specific_effect_significance()` function.
This function takes a bootstrapped model object (`boot_corp_rep_ext`), an antecedent construct name (`from = "COMP`, respectively `from = "LIKE`), a mediator construct name (`through = "CUSA"`) and an outcome construct name (`to = "CUSL"`) as arguments and returns the confidence interval (e.g. with `alpha = 0.05`) for the total indirect paths from the antecedent to the outcome construct.

Note that for serial mediation models, the `through` argument can take multiple mediating constructs as arguments (e.g. `through = c("construct1", "construct2”)`). 

For example, the specific indirect effects for the paths from *COMP* through *CUSA* to *CUSL* and *LIKE* through *CUSA* to *CUSL* are:

```{r}
# Inspect total indirect effects
summary_corp_rep_ext$total_indirect_effects

# Inspect indirect effects
specific_effect_significance(boot_corp_rep_ext, 
  from = "COMP", 
  through = "CUSA", 
  to = "CUSL", 
  alpha = 0.05)
specific_effect_significance(boot_corp_rep_ext, 
  from = "LIKE", 
  through = "CUSA", 
  to = "CUSL", 
  alpha = 0.05)
```

We find that the indirect effects are significant with

- for *COMP* on *CUSL*, the coefficient for the indirect effect being 0.074 (p = .034)

- for *LIKE* on *CUSL* , the coefficient for the indirect effect being 0.220 (p < .001)

Next, we consider both the indirect ($\small p~1~*p~2~$) and the direct effects ($\small p~3~$) to determine the mediation type, respectively the non-mediation type [@Zhao.2010]. Zhao et al. [-@Zhao.2010] identify three types of mediation:

- 	**Complementary mediation**: The indirect effect as well as the direct effect are significant and point in the same direction. The product of indirect and direct effects ($\small p~1~*p~2~*p~3~$) has a positive sign.
- 	**Competitive mediation**: The indirect effect as well as the direct effect are significant, but point in opposite directions. The product of indirect and direct effects ($\small p~1~*p~2~*p~3~$) has a negative sign.
- 	**Indirect-only mediation**: The indirect effect is significant but not the direct effect.

In addition, they characterize two types of non-mediation:

- 	**Direct-only non-mediation**: The direct effect is significant but not the indirect effect.
- 	**No-effect non-mediation**: Neither the direct nor the indirect effect are significant.

The direct effects (e.g. *COMP* on *CUSL* and *LIKE* on *CUSL*) can be accessed by the inspecting `summary_corp_rep_ext$paths`. The confidence intervals for the direct effects are stored in `summary_boot_corp_rep_ext$bootstrapped_paths`.

```{r}
# Inspect the direct effects
summary_corp_rep_ext$paths

# Inspect the confidence intervals for direct effects
summary_boot_corp_rep_ext$bootstrapped_paths
```

The direct effect from *COMP* to *CUSL* is 0.006 (p = 0.894). Given the significant indirect effect and the non-significant direct effect, we conclude that the relationship between *COMP* and *CUSL* is fully mediated by *CUSA*.

Since the direct effect from *LIKE* to *CUSL* has a 0.344 path coefficient (p < .001) and a significant indirect effect, we conclude that *CUSA* partially mediates the effect of *LIKE* on *CUSL*. 

To evaluate if *CUSA* acts as a complementary or competitive mediator for the effect of *LIKE*, respectively *COMP*, on *CUSL*, we determine whether the product of indirect and direct effects ($\small p~1~*p~2~*p~3~$) has a positive or negative sign.

We can subset the path matrix to access the path coefficients for p~1~, p~2~, and p~3~ (e.g. from *LIKE* to *CUSA*: `summary_corp_rep_ext$paths[“LIKE”, “CUSA”]`.

```{r}
# Calculate the sign of p1*p2*p3 for LIKE->CUSA->CUSL
summary_corp_rep_ext$paths["LIKE", "CUSL"] *
  summary_corp_rep_ext$paths["LIKE","CUSA"] *
  summary_corp_rep_ext$paths["CUSA","CUSL"]

# Calculate the sign of p1*p2*p3 for COMP->CUSA->CUSL
summary_corp_rep_ext$paths["COMP", "CUSL"] *
  summary_corp_rep_ext$paths["COMP","CUSA"] *
  summary_corp_rep_ext$paths["CUSA","CUSL"]
```

All three paths for both mediation relationships are positive. Therefore CUSA acts as a complementary partial mediator between *LIKE* and *CUSL*.

## Examining the mediation effect size

Next, we calculate the effect size υ by multiplying the squared path coefficients for each of the mediation relationships.

```{r}
# Calculate the effect size v for LIKE->CUSA->CUSL
summary_corp_rep_ext$paths["LIKE","CUSA"]^2 *
  summary_corp_rep_ext$paths["CUSA","CUSL"]^2

# Calculate the effect size v for COMP->CUSA->CUSL
summary_corp_rep_ext$paths["COMP","CUSA"]^2 *
  summary_corp_rep_ext$paths["CUSA","CUSL"]^2
```

We find a small effect size (υ = 0.048) for *LIKE* to *CUSL* and a very small effect size (υ = 0.005) for *COMP* to *CUSL*.

## Moderated (conditional) mediation

In the following section, we demonstrate a moderated mediation analysis as displayed in Fig. 9.

![Fig. 9. Extended corporate reputation model with moderated mediation effects (source: authors’ own figure)](Figures/Fig.8.11.png){#fig9}

To set up the moderated mediation, we first rerun the code setting up the moderating effect (see @sec-moderation).

```{r}
# Conduct a moderated mediation analysis ----

# Create the measurement model ----
corp_rep_mm_mod <- constructs(
  composite("QUAL", multi_items("qual_", 1:8), weights = mode_B),
  composite("PERF", multi_items("perf_", 1:5), weights = mode_B),
  composite("CSOR", multi_items("csor_", 1:5), weights = mode_B),
  composite("ATTR", multi_items("attr_", 1:3), weights = mode_B),
  composite("COMP", multi_items("comp_", 1:3)),
  composite("LIKE", multi_items("like_", 1:3)),
  composite("CUSA", single_item("cusa")),
  composite("SC", multi_items("switch_", 1:4)),
  composite("CUSL", multi_items("cusl_", 1:3)),
  interaction_term(iv = "CUSA", moderator = "SC", method = two_stage))

# Create the structural model ----
corp_rep_sm_mod <- relationships(
  paths(from = c("QUAL", "PERF", "CSOR", "ATTR"), to = c("COMP", "LIKE")),
  paths(from = c("COMP", "LIKE"), to = c("CUSA", "CUSL")),
  paths(from = c("CUSA", "SC", "CUSA*SC"), to = c("CUSL"))
)

# Estimate the new model with moderator ----
corp_rep_pls_model_mod <- estimate_pls(
  data = corp_rep_data,
  measurement_model = corp_rep_mm_mod,
  structural_model = corp_rep_sm_mod,
  missing = mean_replacement,
  missing_value = "-99"
)

# Extract the summary
sum_corp_rep_mod <- summary(corp_rep_pls_model_mod )

# Bootstrap the model ----
boot_corp_rep_mod <- bootstrap_model(
  seminr_model = corp_rep_pls_model_mod,
  seed = 123,
  nboot = 1000)
```

Next, we use the code below to calculate and assess the indices of moderated mediation.

First, we calculate the point estimates for the relationship between *COMP* and *CUSL* by multiplying the path coefficnt from *COMP* to *CUSA* with the path coefficient from the interaction term *CUSA**SC* to *CUSA* via `sum_corp_rep_mod$paths["COMP","CUSA"] * sum_corp_rep_mod$paths["CUSA*SC","CUSL"]`.

Using the same setup we also calculate the point estimates for the moderated mediation involving *LIKE* via `sum_corp_rep_mod$paths["LIKE","CUSA"] * sum_corp_rep_mod$paths["CUSA*SC","CUSL"]`

Second, we assign a helper function to get the p-value of the indes of moderated mediation.

In the third step, we compute the confidence interval for the index of moderated mediation and get its p-value.

To get the 95% confidence interval, we calculate the index of moderated mediation for each of the bootstrap runs via `1_p5_comp <- boot_corp_rep_mod$boot_path["COMP", "CUSA", ] *  boot_corp_rep_mod$boot_path["CUSA*SC", "CUSL", ]`. This operation yields a distribution from which we retrieve the 2.5% and 97.5% percentiles (in our case, we have `nboot = 1000` estimates in this distribution). `quantile(p1_p5_comp, probs = c(0.025, 0.975))` yields the corresponding confidence interval.

To get the p-value, we call our previously assigned function `p_val(p1_p5_comp)`.

For *LIKE*, we can repeat this sequence.


```{r}
# Index of moderated mediation ----
## Get the point estimate for the index of moderated mediation
# Compute p1*p5 for COMP
sum_corp_rep_mod$paths["COMP","CUSA"] *
  sum_corp_rep_mod$paths["CUSA*SC","CUSL"]

# Compute p1*p5 for LIKE
sum_corp_rep_mod$paths["LIKE","CUSA"] *
  sum_corp_rep_mod$paths["CUSA*SC","CUSL"]

## Get confidence intervals and p-values for the index of moderated mediation
# create a helper function to compute p-values
p_val <- function(x){
  2*min(mean(x<=0), mean(x>=0))
}

# Compute p1*p5 and 95% confidence interval for COMP
p1_p5_comp <- boot_corp_rep_mod$boot_path["COMP", "CUSA", ] *
  boot_corp_rep_mod$boot_path["CUSA*SC", "CUSL", ]
quantile(p1_p5_comp, probs = c(0.025, 0.975))

# Compute the p-value of path p1*p5 for COMP
p_val(p1_p5_comp)

# Compute p1*p5 and 95% confidence interval for LIKE
p1_p5_like <- boot_corp_rep_mod$boot_path["LIKE", "CUSA", ] *
  boot_corp_rep_mod$boot_path["CUSA*SC", "CUSL", ]
quantile(p1_p5_like, probs = c(0.025, 0.975))

# Compute the p-value of path p1*p5 for LIKE
p_val(p1_p5_like)
```

For *COMP*, we find an index of moderated mediation of -0.010 that is (barely) significant with a confidence interval from -0.026 to -0.0002 and the bootstrap p-value of 0.048. 

For *LIKE* the 95% confidence interval for the index of moderated mediation ranges from -0.064 to -0.006  and the bootstrap p-value 0.014.

# Session info

```{r}
sessionInfo()
```

# References
