A significant question in applying the KPM is how to categorize
purchased products and services within the matrix. Organizations vary in
how they measure supply risk and profit impact. Furthermore, some
organizations may choose to measure their purchases against different
dimensions; however, the fundamental purpose of the matrix remains. For
our purpose, we assume that the organization has developed a means to
condense their measurement of each dimension to a single value. For
example, many organizations use an index (i.e. IBISWorld Buyer
Power Score) to measure one of the dimensions. Or some organizations
develop an indexed value function that generates a single value score
for many attributes (i.e. profit impact can be a function of volume
purchased, expected growth in demand, percent of total purchase cost,
impact on product quality and business growth, etc.). However, once you
have a single value that represents each dimension, subjectivity still
largely drives how they are positioned in the KPM. The
kraljicMatrix
package was designed to assist with this
concern and the examples that follow walk you through how to implement
kraljicMatrix
functions.
Implementation of kraljicMatrix
The x and y attributes are simply evaluation measures. They enable
each product and service to obtain a score for each dimension being
measured. For example, the x attribute score (1-5 in .01 increments)
could be the IBISWorld Buyer Power Score measuring supply
market complexity. However, to plot these attributes on the KPM matrix
we need to normalize the value scores such that the values are between
0-1. To do this we can use an exponential single attribute value
function (SAVF). For example, let vx(xi)
represent the normalized value of the x attribute such that x0 and x* are the lowest and
highest preferred value of attribute x respectively. Thus, vx(x0) = 0
and vx(x*) = 1.
Consequently, let vx(xi)
be the SAVF of exponential form whereby each xi is an input
and ρx is
the exponential constant for vx(xi):
$$v_x(x_i)=\frac{1-e^{[-(x_i-x^0)/\rho_x]}}{1-e^{[-(x^*-x^0)/\rho_x]}}
\forall i \in PSC$$
However, prior to applying the SAVF to our x and y attributes we must
first identify the appropriate ρ value. The benefit of applying an
exponential SAVF is that it can take on many forms of increasing rates,
along with aligning to a linear value function. Consequently, if certain
x attribute values are valued more than other values an exponential SAVF
will capture this utility curve. To identify the appropriate exponential
rate, subject matter expert (SME) inputs are typically evaluated and an
exponential rate that most closely matches the preffered values provided
by the SMEs is chosen. Thus, let’s assume for our given x attribute the
SME inputs suggest that x attribute values of 3, 4, & 5 provide a
utility score of .75, .90 & 1.0 respectively (this represents a
decreasing rate of return utility curve). Knowing that our x attribute
is bounded between 1 and 5 we can search for a rho value between 0-1
that provides the best fit utility function using the
SAVF_preferred_rho
function.
SAVF_preferred_rho(desired_x = c(3, 4, 5),
desired_v = c(.8, .9, 1),
x_low = 1,
x_high = 5,
rho_low = 0,
rho_high = 1)
## [1] 0.6531
Thus, we can see that ρ = 0.6531 provides the best fit
exponential SAVF. We can illustrate this two ways. First, we can use
SAVF_plot
to plot the single attribute utility curve
compared to the subject matter desired values.
SAVF_plot(desired_x = c(3, 4, 5),
desired_v = c(.8, .9, 1),
x_low = 1,
x_high = 5,
rho = 0.6531)
We can also visualize the errors of the ρ search space with
SAVF_plot_rho_error
, which plots the squared error terms
for all ρ values within the
ρ search space to illustrate
the preferred rho that minimizes the squared error between subject
matter desired values and exponentially fitted scores.
SAVF_plot_rho_error(desired_x = c(3, 4, 5),
desired_v = c(.75, .9, 1),
x_low = 1,
x_high = 5,
rho_low = 0,
rho_high = 1)
## Warning in ggplot2::geom_point(ggplot2::aes(true_rho, min(delta)), shape = 23, : All aesthetics have length 1, but the data has 10000 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
## a single row.
Once we’ve identified the preferred ρ value, we can now apply the
exponential SAVF with SAVF_score
to normalize our
attributes based on our utility curve.
# using dplyr to add a new variable while preserving existing data
library(dplyr)
# here we are assuming we found the appropriate rho value for the y attribute using
# the same process as mentioned above
psc <- psc %>%
mutate(x_SAVF_score = SAVF_score(x_attribute, 1, 5, .653),
y_SAVF_score = SAVF_score(y_attribute, 1, 10, .70))
psc
## # A tibble: 200 × 5
## PSC x_attribute y_attribute x_SAVF_score y_SAVF_score
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 D233 3.01 4.84 0.789 0.934
## 2 F352 4.34 5.64 0.957 0.963
## 3 T713 3.37 4.3 0.850 0.902
## 4 K833 2.67 5.53 0.717 0.960
## 5 Q121 3.48 4.33 0.866 0.904
## 6 C791 3.32 7.32 0.842 0.990
## 7 Y207 3.48 5.42 0.866 0.956
## 8 W439 2.47 3.35 0.666 0.808
## 9 N290 1.66 4.02 0.378 0.881
## 10 C251 1 7.47 0 0.991
## # ℹ 190 more rows
Now that we have the normalized x and y attribute utility scores we
can proceed with plotting each PSC within the Kraljic matrix with
kraljic_matrix
.
kraljic_matrix(psc, x_SAVF_score, y_SAVF_score)
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
This illustrates that most of our PSCs fall in the “Leverage” (upper
left) quadrant while a few fall in the “Strategic” (upper right) and
“Non-critical” (lower left) quadrants and no PSCs fall in the
“Bottleneck” quadrant. Keep in mind that each category benefits from a
different strategic sourcing approach. So decision-makers benefit from
understanding specifically which products and services align to each so
that they can coordinate the appropriate sourcing strategy for that
particular product or service. We can easily do this with the
kraljic_quadrant
function.
psc %>%
mutate(quadrant = kraljic_quadrant(x_SAVF_score, y_SAVF_score))
## # A tibble: 200 × 6
## PSC x_attribute y_attribute x_SAVF_score y_SAVF_score quadrant
## <chr> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 D233 3.01 4.84 0.789 0.934 Leverage
## 2 F352 4.34 5.64 0.957 0.963 Leverage
## 3 T713 3.37 4.3 0.850 0.902 Leverage
## 4 K833 2.67 5.53 0.717 0.960 Leverage
## 5 Q121 3.48 4.33 0.866 0.904 Leverage
## 6 C791 3.32 7.32 0.842 0.990 Leverage
## 7 Y207 3.48 5.42 0.866 0.956 Leverage
## 8 W439 2.47 3.35 0.666 0.808 Leverage
## 9 N290 1.66 4.02 0.378 0.881 Strategic
## 10 C251 1 7.47 0 0.991 Strategic
## # ℹ 190 more rows
Lastly, it is important to keep in mind that decision-makers may
weight the importance of each attribute differently. Consequently, due
to certain market environments, decision-makers may weight the x
attribute (i.e. supply risk) of greater importance than the y attribute
(i.e. profit impact). Thus, we can prioritize PSCs based on this
preference by applying a multi-attribute value function (MAVF) with
swing weights. Swing weight values for x and y attributes (wx and wy respectively)
are typically elicited from SMEs. This allows for calculation of the
interaction swing weight wxy = 1 − wx − wy.
Thus, we can calculate the MAVF as outlined by Keeney and Raiffa
(1993):
V(x, y) = wxvx(x) + wyvy(y) + wxyvx(x)vy(y)
Thus, we can apply the MAVF_score
function to compute
the multi-attribute value score based on x
and
y
attribute utility scores and their respective swing
weights. So if through discussions with decision-makers we identify
swing weight values of 0.65 and 0.35 for the x and y attributes
respectively, we can obtain the computed MAVF score for each PSC:
psc %>%
mutate(MAVF = MAVF_score(x_SAVF_score, y_SAVF_score, 0.65, 0.35))
## # A tibble: 200 × 6
## PSC x_attribute y_attribute x_SAVF_score y_SAVF_score MAVF
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 D233 3.01 4.84 0.789 0.934 0.839
## 2 F352 4.34 5.64 0.957 0.963 0.959
## 3 T713 3.37 4.3 0.850 0.902 0.868
## 4 K833 2.67 5.53 0.717 0.960 0.802
## 5 Q121 3.48 4.33 0.866 0.904 0.879
## 6 C791 3.32 7.32 0.842 0.990 0.894
## 7 Y207 3.48 5.42 0.866 0.956 0.897
## 8 W439 2.47 3.35 0.666 0.808 0.716
## 9 N290 1.66 4.02 0.378 0.881 0.554
## 10 C251 1 7.47 0 0.991 0.347
## # ℹ 190 more rows
This allows us to quickly dissect our PSCs. For example, if
decision-makers are most concerned with the “Leverage” quadrant but want
to assess the top 10 PSCs based on the decision-makers preferences of
the attributes we can efficiently make this assessment. This identifies
the top 10 PSCs that are most likely to benefit from a strategic
sourcing approach specifically designed for “Leverage” PSCs.
psc %>%
mutate(MAVF = MAVF_score(x_SAVF_score, y_SAVF_score, 0.65, 0.35),
quadrant = kraljic_quadrant(x_SAVF_score, y_SAVF_score)) %>%
filter(quadrant == "Leverage") %>%
top_n(10, wt = MAVF)
## # A tibble: 10 × 7
## PSC x_attribute y_attribute x_SAVF_score y_SAVF_score MAVF quadrant
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 Y357 5 6.55 1 0.981 0.993 Leverage
## 2 E103 4.46 7.71 0.967 0.993 0.976 Leverage
## 3 C432 4.65 6.05 0.980 0.973 0.977 Leverage
## 4 P402 5 5.82 1 0.968 0.989 Leverage
## 5 Q255 4.95 6.41 0.997 0.979 0.991 Leverage
## 6 H426 5 5.58 1 0.961 0.986 Leverage
## 7 E908 4.75 7.11 0.986 0.988 0.987 Leverage
## 8 X634 5 5.19 1 0.949 0.982 Leverage
## 9 O288 5 5 1 0.941 0.979 Leverage
## 10 V870 5 5.88 1 0.969 0.989 Leverage
And finally, since our swing weight inputs are subjective in nature
we may wish to perform a senstivity analysis on these swing weights to
see their impact on MAVF scores. The MAVF_sensitivity
function executes a sensitivity analysis by performing a Monte Carlo
simulation with 1000 trials for each product or service (row). Each
trial randomly selects a weight from a uniform distribution between
lower and upper bound swing weight parameters and calculates the
mult-attribute utility score. From these trials, summary statistics for
each product or service (row) are calculated and reported for the final
output.
MAVF_sensitivity(psc,
x = x_SAVF_score,
y = y_SAVF_score,
x_wt_min = .55,
x_wt_max = .75,
y_wt_min = .25,
y_wt_max = .45) %>%
select(PSC, starts_with("MAVF"))
## # A tibble: 200 × 8
## PSC MAVF_Min MAVF_1st_Q MAVF_Median MAVF_Mean MAVF_3rd_Q MAVF_Max
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 D233 0.815 0.830 0.841 0.840 0.850 0.863
## 2 F352 0.952 0.957 0.960 0.959 0.962 0.967
## 3 T713 0.847 0.862 0.869 0.868 0.876 0.889
## 4 K833 0.772 0.789 0.804 0.803 0.816 0.831
## 5 Q121 0.859 0.873 0.880 0.879 0.886 0.898
## 6 C791 0.878 0.887 0.895 0.894 0.902 0.910
## 7 Y207 0.881 0.891 0.898 0.898 0.904 0.913
## 8 W439 0.677 0.703 0.717 0.717 0.730 0.754
## 9 N290 0.496 0.530 0.558 0.556 0.583 0.612
## 10 C251 0.248 0.304 0.355 0.352 0.399 0.446
## # ℹ 190 more rows
## # ℹ 1 more variable: MAVF_Range <dbl>