Nothing
#' Create the segment sequence container
#'
#' This function combines rules into a sequence container.
#'
#' @param context Defines the level that the segment logic should operate on.
#' Valid values for sequential segments is visitors and visits. See Details
#' @param rules List of rules created using [`seg_rule()`] function. Must be wrapped
#' in a list() function.
#' @param sequence How should the sequence of items be considered.
#' Options: `in_order` (default), `before`, `after`, `and`, `or`
#' @param exclude Excludes the entire sequence container which will include all rules.
#' @param exclude_checkpoint Which checkpoints (rules) should be excluded.
#' Example `c(1, 4)`. See Details
#'
#' @details
#'
#' **Context**
#'
#' The rules in a segment have a context that specify the level of operation.
#' The context can be "visitors", "visits" or "hits."
#' As an example, let's build a segment rule where revenue is greater than 0
#' (meaning a purchase took place) and change the context to see how things change.
#' If the context is set to "visitors", the segment includes all hits from visitors
#' that have a purchase of some kind during a visit. This is useful in analyzing
#' customer behavior in visits leading up to a purchase and possibly behavior
#' after a purchase.
#' If the context is set to "visits", the segment includes all hits from visits where
#' a purchase occurred. This is useful for seeing the behavior of a visitor in
#' immediate page views leading up to the purchase.
#' If the context is set to "hits", the segment only includes hits where a purchase
#' occurred, and no other hits. This is useful in seeing which products were most
#' popular.
#' In the above example, the context for the container listed is hits. This means
#' that the container only evaluates data at the hit level, (in contrast to visit
#' or visitor level). The rows in the container are also at the hit level.
#'
#' **Exclude checkpoint**
#'
#' Ensures the next checkpoint doesn't happen between the
#' preceding checkpoint and the subsequent checkpoint. If there is no subsequent
#' checkpoint then the excluded checkpoint must not occur at any point after
#' the preceding checkpoint. If there is no preceding checkpoint then the
#' excluded checkpoint must not have occurred at any point preceding the
#' subsequent checkpoint.
#'
#' **More Information**
#'
#' Sequential segments can be difficult to get right. Referencing this article
#' can help: https://experienceleague.adobe.com/docs/analytics/components/segmentation/segmentation-workflow/seg-sequential-build.html?lang=en
#'
#' @return a structured list of containers to be used to build the segment
#'
#' @import dplyr assertthat stringr
#'
#' @export
#'
#'
seg_seq <- function(context = 'visits',
rules = NULL,
sequence = 'in_order',
exclude = FALSE,
exclude_checkpoint = NULL) {
# validate 'sequence' argument value
if (!sequence %in% c("in_order", "after", "before", "and", "or")) {
stop("Make sure the `sequence` argument is either 'in_order' (default), 'after', 'before', 'and', or 'or'.")
}
# validate 'context' argument value
if (!context %in% c("hits", "visits", "visitors")) {
stop("Make sure the `context` argument is either 'hits', 'visits' (default), or 'visitors'")
}
# validate 'exclude' argument value
if (!is.logical(exclude)) {
stop("The argument `exclude` must be either True or False.")
}
# validate 'exclude_checkpoint' argument value
if (!is.null(exclude_checkpoint) & !is.numeric(exclude_checkpoint)) {
stop("The argument 'exclude_checkpoint' needs to be NULL, a single number, or a vector of numbers defining the rules you want to exclude.")
}
#define the sequence direction
f.seq_dir <- function(sequence){
dplyr::case_when(sequence == 'in_order' ~ 'sequence',
sequence == 'after' ~ 'sequence-prefix',
sequence == 'before' ~ 'sequence-suffix',
sequence == 'and' ~ 'sequence-and',
sequence == 'or' ~ 'sequence-or')
}
sequence_dir <- f.seq_dir(sequence)
## Append the exclusion list item
if (!is.null(exclude_checkpoint)) {
new_checkpoints <- (exclude_checkpoint-1) + (seq(length(exclude_checkpoint)) - 1)
for (i in new_checkpoints) {
rules <- append(rules,
list(list(func = 'exclude-next-checkpoint')),
after = i)
}
}
## Add in the necessary 'container' and 'hits' variables to each rule for sequence to work
pred_items <- list()
for (i in seq_along(rules)) {
if (!is.null(rules[[i]]$val)) {
pred_items <- append(pred_items, list(
list(context = 'hits',
func = 'container',
pred = rules[[i]])
))
} else if (length(rules[[i]]) == 2) {
if (is.list(rules[[i]][[1]])){
for(item in seq(length(rules[[i]]))) {
pred_items <- append(pred_items, rules[[i]][item])
}
} else {
pred_items <- append(pred_items, list(
list(context = 'hits',
func = 'container',
pred = rules[[i]])
))
}
}
}
##Create the different json strings
f.sequence <- function(context, sequence_dir, pred_items, exclude) {
if (!exclude) {
list(
func = 'container',
context = context,
pred = list(
func = sequence_dir,
stream = pred_items
)
)
} else if (exclude) {
list(
func = 'container',
context = context,
pred = list(
func = 'without',
pred = list(
func = sequence_dir,
stream = pred_items
)
)
)
}
}
f.pref_suff <- function(context, sequence_dir, pred_items, exclude) {
if (exclude == FALSE) {
list(
func = 'container',
context = context,
pred = list(
func = sequence_dir,
context = context,
stream = pred_items
)
)
} else if (exclude == TRUE) {
list(
func = 'container',
context = context,
pred = list(
func = 'without',
pred = list(
func = sequence_dir,
context = context,
stream = pred_items
)
)
)
}
}
f.and_or <- function(context, sequence_dir, pred_items, exclude) {
if (exclude == FALSE) {
list(
func = 'container',
context = context,
pred = list(
func = sequence_dir,
checkpoints = pred_items
)
)
} else if (exclude == TRUE){
list(
func = 'container',
context = context,
pred = list(
func = 'without',
pred = list(
func = sequence_dir,
checkpoints = pred_items
)
)
)
}
}
sequence_items <- if (sequence_dir == 'sequence') {
f.sequence(context, sequence_dir, pred_items, exclude)
} else if (sequence_dir %in% c('sequence-prefix', 'sequence-suffix')) {
f.pref_suff(context, sequence_dir, pred_items, exclude)
} else if (sequence_dir %in% c('sequence-and', 'sequence-or')) {
f.and_or(context, sequence_dir = pred_items, exclude)
}
sequence_items
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.