Standard Market Basket
Executive Summary

Executive Summary

What sells together across 1,140 orders.

Orders
1140
Distinct Items
2698
Avg Basket Size
3.05
Qualifying Pairs
272
Top Pairing
CHOCOLATE HOT WATER BOTTLE + HOT WATER BOTTLE TEA AND SYMPATHY
Top Pairing Lift
13.44
Across 1,140 orders with an average basket of 3 items, the strongest product pairing is CHOCOLATE HOT WATER BOTTLE and HOT WATER BOTTLE TEA AND SYMPATHY: shoppers who buy CHOCOLATE HOT WATER BOTTLE also buy HOT WATER BOTTLE TEA AND SYMPATHY 57.8% of the time, a lift of 13.44 — they land in the same basket 13.44 times more often than if the two were bought independently. The most-bought Description overall is POSTAGE, in 11.6% of orders. These are co-purchase patterns to act on for cross-sell, bundling, and layout; they show what sells together, not proof that one item drives the other.
What this means

The short answer

The strongest product pairing is CHOCOLATE HOT WATER BOTTLE with HOT WATER BOTTLE TEA AND SYMPATHY: 57.8% of customers who buy the first also buy the second, with a lift of 13.44. The most-bought item overall is POSTAGE, appearing in 11.6% of orders. Across 1,140 orders, the average basket contains 3.05 items.

The detail

Among 272 qualifying pairs identified across 1,140 orders, CHOCOLATE HOT WATER BOTTLE and HOT WATER BOTTLE TEA AND SYMPATHY show the highest lift at 13.44 and confidence of 57.8%, meaning shoppers buying the chocolate bottle are 13.44 times more likely to also buy the tea bottle than chance would predict. POSTAGE appears in 11.6% of orders, making it the single most-bought Description. These patterns are actionable for bundling (pair the two bottles together), targeted cross-sell prompts ('customers who bought this also bought...'), or shelf adjacency.

What this can't tell you

Association reflects co-purchase behaviour, not causal influence. A high-lift pairing does not establish that one item drives purchase of the other, only that they appear together more often than random selection would suggest.

Overview

Analysis Overview

Market basket analysis of 1,140 orders and 2,698 distinct items.

N Orders1140
N Items2698
N Pairs272
What this means

The short answer

Three measures reveal which products sell together. Support shows how common a pair is across all orders; confidence shows how reliably one item follows another; and lift tells you whether the pairing beats chance. A lift above 1 means the items co-occur more than random, so they're worth bundling, cross-selling, or placing nearby in layout.

The detail

This analysis examined 1,140 orders containing 2,698 distinct items, yielding 272 qualifying pairs. Support is the share of all orders containing both items. Confidence is directional: if item A is bought, the probability item B is also bought. Lift compares observed co-occurrence to independence: a lift of 1 means no association; above 1 means positive association; below 1 means negative. For example, a lift of 13.44 means the pair appears 13.44 times more often together than independence would predict. High-lift pairs are candidates for bundling or 'frequently bought together' prompts; high-confidence rules suggest which item to recommend given the first purchase.

What this can't tell you

This analysis describes co-purchase patterns, not causation. A high-lift pairing shows that customers who buy one item are more likely to buy the other, but does not show that one item drives demand for the other.

Data Preparation

Data Quality

Basket reconstruction, deduplication, and item lumping.

Initial Rows21608
Final Rows21608
Rows Removed0
N Orders1140
Avg Basket Size3.05
What this means

The short answer

All 21,608 item rows were retained and grouped into 1,140 orders with an average basket size of 3 items. Duplicate items within the same order were counted once. The 2,658 items beyond the top 40 most frequent were folded into "Other" to keep pair mining tractable and results interpretable.

The detail

No rows were removed during quality checks—all 21,608 item rows were kept. They reconstructed into 1,140 distinct orders (identified by InvoiceNo) containing 2,698 distinct item descriptions. The average basket size was 3.05 items per order. Items bought multiple times in a single order counted once toward that basket. To prevent the pair list from fragmenting into rare, unstable combinations, the 2,658 items ranked outside the top 40 by frequency were grouped into a single "Other" category and excluded from pair mining. This lumping preserves focus on the strongest, most actionable signals.

What this can't tell you

The grouping of low-frequency items into "Other" means very niche product pairings are not ranked, even if they have high lift within that category. If identifying rare but high-value combinations is a priority, consider a transaction-level export that preserves individual SKUs below the top 40, allowing re-mining at a lower frequency threshold.

Visualization

Most-Bought Items

Share of orders containing each Description (top 15).

What this means

The short answer

POSTAGE is the most-bought item at 11.6% of orders, followed by REGENCY CAKESTAND 3 TIER at 9.6%. The most popular items set a high bar for lift: when two already-common items appear together, they must co-occur much more frequently than chance to show a meaningful lift. Moderately common items that consistently land together stand out faster.

The detail

The top 15 items range from Other at 97.7% (a catch-all category) down to RABBIT NIGHT LIGHT at 4.8%. POSTAGE at 11.6% and REGENCY CAKESTAND 3 TIER at 9.6% anchor the list of branded items. WHITE HANGING HEART T-LIGHT HOLDER (8.2%), JUMBO BAG RED RETROSPOT (7.9%), and ASSORTED COLOUR BIRD ORNAMENT (6.7%) follow. This distribution is the baseline: a pairing of two items each bought in 50% of orders would need to appear together far more than 25% of the time to show positive lift, whereas two items at 6% each need only modest co-occurrence to exceed chance.

What this can't tell you

Popularity alone does not predict which items should be bundled or cross-sold. A common item paired with a rare one may show high lift if they co-occur reliably, while two very common items may show low or negative lift despite appearing together often in absolute terms.

Data Table

Top Product Pairs by Lift

Product pairs ranked by lift, with support and confidence.

Item AItem BSupport PCTConfidence PCTLift
CHOCOLATE HOT WATER BOTTLEHOT WATER BOTTLE TEA AND SYMPATHY2.2857.813.44
LUNCH BAG WOODLANDLUNCH BAG SPACEBOY DESIGN2.7260.812.83
PAPER CHAIN KIT VINTAGE CHRISTMASPAPER CHAIN KIT 50'S CHRISTMAS2.4657.112.77
HOT WATER BOTTLE TEA AND SYMPATHYHOT WATER BOTTLE KEEP CALM2.2853.111.63
ALARM CLOCK BAKELIKE GREENALARM CLOCK BAKELIKE RED3.867111.39
JUMBO BAG ALPHABETJUMBO BAG APPLES2.0251.110.79
CHOCOLATE HOT WATER BOTTLEHOT WATER BOTTLE KEEP CALM1.9348.910.72
LUNCH BAG APPLE DESIGNJUMBO BAG APPLES1.8444.79.43
HEART OF WICKER LARGEHEART OF WICKER SMALL1.7543.59.35
HAND WARMER OWL DESIGNHOT WATER BOTTLE TEA AND SYMPATHY1.5839.19.1
HAND WARMER OWL DESIGNHOT WATER BOTTLE KEEP CALM1.6741.39.06
LUNCH BAG WOODLANDLUNCH BAG RED RETROSPOT2.28518.81
CHOCOLATE HOT WATER BOTTLEHAND WARMER OWL DESIGN1.435.68.81
LUNCH BAG APPLE DESIGNLUNCH BAG WOODLAND1.5838.38.56
LUNCH BAG APPLE DESIGNLUNCH BAG SPACEBOY DESIGN1.4936.27.64
What this means

The short answer

208 product pairs reach a lift of 2 or more, meaning they co-occur at least twice as often as chance would predict. The top pair—CHOCOLATE HOT WATER BOTTLE and HOT WATER BOTTLE TEA AND SYMPATHY—has a lift of 13.44 on support of 2.28%. Read lift and support together: a sky-high lift on a pair seen in only a handful of orders is fragile, whereas a lift of 2 on 3% support is stable enough to act on.

The detail

The table ranks 272 pairs by lift. The top five are: (1) CHOCOLATE HOT WATER BOTTLE + HOT WATER BOTTLE TEA AND SYMPATHY, lift 13.44, support 2.28%, confidence 57.8%; (2) LUNCH BAG WOODLAND + LUNCH BAG SPACEBOY DESIGN, lift 12.83, support 2.72%, confidence 60.8%; (3) PAPER CHAIN KIT VINTAGE CHRISTMAS + PAPER CHAIN KIT 50'S CHRISTMAS, lift 12.77, support 2.46%, confidence 57.1%; (4) HOT WATER BOTTLE TEA AND SYMPATHY + HOT WATER BOTTLE KEEP CALM, lift 11.63, support 2.28%, confidence 53.1%; (5) ALARM CLOCK BAKELIKE GREEN + ALARM CLOCK BAKELIKE RED, lift 11.39, support 3.86%, confidence 71%. The ALARM CLOCK pair stands out for combining high lift with the highest support in the top five (3.86%), making it the most frequent high-lift combination and thus the most reliable for bundling or layout adjacency.

What this can't tell you

Lift is computed at the order level and does not capture whether pairs are driven by a small number of bulk purchases or spread evenly across many customers. A customer-level or order-value export would reveal whether the strongest pairs are concentrated among a few high-volume buyers or distributed across the customer base, affecting the confidence of scaling these recommendations.

Data Table

Cross-Sell Recommendations

Buy-this-recommend-that rules ranked by confidence.

If They BuyRecommendConfidence PCTLift
ROUND SNACK BOXES SET OF4 WOODLANDPOSTAGE71.16.14
ALARM CLOCK BAKELIKE GREENALARM CLOCK BAKELIKE RED7111.39
ALARM CLOCK BAKELIKE REDALARM CLOCK BAKELIKE GREEN6211.39
LUNCH BAG WOODLANDLUNCH BAG SPACEBOY DESIGN60.812.83
JUMBO STORAGE BAG SUKIJUMBO BAG RED RETROSPOT58.77.43
CHOCOLATE HOT WATER BOTTLEHOT WATER BOTTLE TEA AND SYMPATHY57.813.44
LUNCH BAG SPACEBOY DESIGNLUNCH BAG WOODLAND57.412.83
PAPER CHAIN KIT VINTAGE CHRISTMASPAPER CHAIN KIT 50'S CHRISTMAS57.112.77
PAPER CHAIN KIT 50'S CHRISTMASPAPER CHAIN KIT VINTAGE CHRISTMAS54.912.77
HOT WATER BOTTLE TEA AND SYMPATHYCHOCOLATE HOT WATER BOTTLE53.113.44
HOT WATER BOTTLE TEA AND SYMPATHYHOT WATER BOTTLE KEEP CALM53.111.63
JUMBO BAG APPLESJUMBO BAG RED RETROSPOT51.96.57
JUMBO BAG ALPHABETJUMBO BAG APPLES51.110.79
LUNCH BAG WOODLANDLUNCH BAG RED RETROSPOT518.81
ROSES REGENCY TEACUP AND SAUCERREGENCY CAKESTAND 3 TIER515.34
What this means

The short answer

When a shopper buys ALARM CLOCK BAKELIKE GREEN, they also buy ALARM CLOCK BAKELIKE RED 71% of the time—a lift of 11.39, meaning that pairing is 11.39 times more likely than chance. When they buy CHOCOLATE HOT WATER BOTTLE, they buy HOT WATER BOTTLE TEA AND SYMPATHY 57.8% of the time, a lift of 13.44. These high-confidence, high-lift rules are the shortlist for 'frequently bought together' prompts and bundle offers.

The detail

The top 15 cross-sell rules are ranked by confidence. The highest-confidence pairs are: ROUND SNACK BOXES SET OF4 WOODLAND → POSTAGE (71.1%, lift 6.14); ALARM CLOCK BAKELIKE GREEN → ALARM CLOCK BAKELIKE RED (71%, lift 11.39); ALARM CLOCK BAKELIKE RED → ALARM CLOCK BAKELIKE GREEN (62%, lift 11.39); LUNCH BAG WOODLAND → LUNCH BAG SPACEBOY DESIGN (60.8%, lift 12.83); JUMBO STORAGE BAG SUKI → JUMBO BAG RED RETROSPOT (58.7%, lift 7.43); CHOCOLATE HOT WATER BOTTLE → HOT WATER BOTTLE TEA AND SYMPATHY (57.8%, lift 13.44). All rules shown have lift above 1, confirming the second item is genuinely more likely than baseline. The ALARM CLOCK rule combines the highest confidence (71%) with a strong lift (11.39), making it the most reliable cross-sell trigger. The CHOCOLATE HOT WATER BOTTLE rule has lower confidence (57.8%) but the highest lift (13.44), indicating a very strong association even when the first item is not the most common trigger.

What this can't tell you

Confidence reflects the conditional probability within this dataset but does not account for customer segmentation—different customer types may show different propensities to buy the second item after the first. A customer-level or cohort-based export would reveal whether these rules hold uniformly across segments or vary by purchase history, geography, or order value, affecting the precision of targeting and personalization.

Rate this report Was this the answer you needed?
The exact source that produced this report — yours to keep, read, and re-run.
Download PDF
How this was computed method · R source · citation
The code that did it

Market Basket — What Sells Together

Mines classic retail associations from a transaction log (one row per item in each order): the most-bought items, every product pair ranked by lift with support and confidence, and the directional cross-sell rules for bundling and layout.

Why This Method?

Pairwise co-occurrence is the interpretable heart of market basket analysis. Support says how common a combination is, confidence says how reliably one item follows another, and lift says whether that pairing beats chance. Base R counting recovers all three exactly — no arules dependency — and the pair is the unit retailers actually act on.

What This Analysis Covers

  • The most-bought items (share of orders containing each)
  • Product pairs ranked by lift, with support and confidence
  • Directional cross-sell rules: buy this, recommend that

Standard Library

Platform standard-library module (LAT-1441): runs on ANY transaction log via the semantic mapping {order_id, item}. All narrative is derived from the user's own column names and computed values.

suppressPackageStartupMessages(library(DT))
suppressPackageStartupMessages(library(htmlwidgets))
suppressPackageStartupMessages(library(arrow))
suppressPackageStartupMessages(library(knitr))
suppressPackageStartupMessages(library(rmarkdown))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(tidyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(stringr))
suppressPackageStartupMessages(library(lubridate))
suppressPackageStartupMessages(library(broom))
suppressPackageStartupMessages(library(Matrix))
suppressPackageStartupMessages(library(cluster))
suppressPackageStartupMessages(library(data.table))

Core Analysis Pipeline

Step 1: Discover the two mapped columns

initial_rows <- nrow(df)
  for (k in c("order_id", "item")) {
    if (!k %in% names(df)) {
      stop(sprintf("column_mapping must map &#x27;%s' to a column in the transaction log.", k))
    }
  }
  name_order <- humanize_semantic("order_id", col_map)
  name_item  <- humanize_semantic("item", col_map)

Step 2: Clean — drop blank order/item rows, coerce to character

oid <- trimws(as.character(df[["order_id"]]))
  itm <- trimws(as.character(df[["item"]]))
  bad <- is.na(oid) | oid == "" | is.na(itm) | itm == ""
  n_bad_rows <- sum(bad)
  oid <- oid[!bad]; itm <- itm[!bad]
  final_rows <- length(oid)
  rows_removed <- initial_rows - final_rows
  if (final_rows < 1) {
    stop(sprintf("No usable rows: every row was missing an %s or an %s.",
                 name_order, name_item))
  }

Step 3: Reconstruct baskets — unique items per order, drop empties

basket_list <- split(itm, oid)
  basket_list <- lapply(basket_list, unique)
  basket_list <- basket_list[lengths(basket_list) >= 1]
  n_orders <- length(basket_list)
  if (n_orders < 10) {
    stop(sprintf("Only %s reconstructed from %s — market basket analysis needs at least 10 orders.",
                 plural(n_orders, "basket"), name_order))
  }

Step 4: Item frequencies (baskets containing each item)

all_items <- unlist(basket_list, use.names = FALSE)   # each item once per basket (deduped)
  item_freq0 <- sort(table(all_items), decreasing = TRUE)
  n_distinct_items <- length(item_freq0)                # original distinct count
  if (n_distinct_items < 2) {
    stop(sprintf("Only one distinct %s appears across the orders — pairing needs at least two distinct items.",
                 name_item))
  }

Step 5: Lump items beyond the top 40 into "Other" for pair-mining sanity

The 40 most frequent items stay as themselves; everything rarer is folded into a single "Other" bucket that is REPORTED but held out of pair mining (a mixed bucket would pair with everything and mean nothing).

MAX_ITEMS <- 40
  keep_items <- names(item_freq0)[seq_len(min(MAX_ITEMS, n_distinct_items))]
  lumped_items <- setdiff(names(item_freq0), keep_items)
  n_lumped <- length(lumped_items)
  if (n_lumped > 0) {
    basket_list <- lapply(basket_list, function(b) {
      b[b %in% lumped_items] <- "Other"
      unique(b)
    })
  }

Frequencies + average basket recomputed AFTER lumping.

all_items2 <- unlist(basket_list, use.names = FALSE)
  item_freq <- sort(table(all_items2), decreasing = TRUE)
  avg_basket <- mean(lengths(basket_list))

  item_freq_df <- data.frame(
    item = names(item_freq),
    orders_pct = round(100 * as.integer(item_freq) / n_orders, 1),
    stringsAsFactors = FALSE
  )
  top_items_df <- head(item_freq_df, 15)

Most-bought real item (skip the synthetic "Other" bucket if it leads).

real_freq_df <- item_freq_df[item_freq_df$item != "Other", , drop = FALSE]
  top_item <- real_freq_df$item[1]
  top_item_pct <- real_freq_df$orders_pct[1]

Step 6a: The pairable universe — top-40 real items, "Other" excluded

pairable <- setdiff(names(item_freq), "Other")
  pairable <- pairable[seq_len(min(MAX_ITEMS, length(pairable)))]
  k <- length(pairable)

Step 6b: Co-occurrence via a basket-by-item incidence matrix

M <- matrix(0L, nrow = n_orders, ncol = k, dimnames = list(NULL, pairable))
  for (o in seq_len(n_orders)) {
    hit <- basket_list[[o]][basket_list[[o]] %in% pairable]
    if (length(hit)) M[o, hit] <- 1L
  }
  Co <- crossprod(M)            # k x k co-occurrence; diagonal = item basket count
  cnt <- diag(Co)               # baskets containing each pairable item

Support floor: at least 2 baskets, and at least 0.5% of orders.

min_support_orders <- max(2L, as.integer(ceiling(0.005 * n_orders)))

Step 7: Score every qualifying unordered pair

a_vec <- character(0); b_vec <- character(0); co_vec <- integer(0)
  if (k >= 2) {
    for (i in seq_len(k - 1)) {
      for (j in (i + 1):k) {
        co_ij <- Co[i, j]
        if (co_ij >= min_support_orders) {
          a_vec <- c(a_vec, pairable[i])
          b_vec <- c(b_vec, pairable[j])
          co_vec <- c(co_vec, co_ij)
        }
      }
    }
  }
  n_qual <- length(co_vec)
  no_assoc <- n_qual == 0

  empty_rules <- data.frame(item_a = character(0), item_b = character(0),
                            support_pct = numeric(0), confidence_pct = numeric(0),
                            lift = numeric(0), stringsAsFactors = FALSE)
  empty_cross <- data.frame(if_they_buy = character(0), recommend = character(0),
                            confidence_pct = numeric(0), lift = numeric(0),
                            stringsAsFactors = FALSE)

  if (no_assoc) {
    pairs_all <- NULL
    rules_df <- empty_rules
    cross_sell_df <- empty_cross
    top_pair <- NULL
  } else {
    cnt_a <- as.integer(cnt[a_vec]); cnt_b <- as.integer(cnt[b_vec])
    support <- co_vec / n_orders
    conf_ab <- co_vec / cnt_a       # P(item_b | item_a)
    conf_ba <- co_vec / cnt_b       # P(item_a | item_b)
    lift    <- (co_vec * n_orders) / (cnt_a * cnt_b)

Orient each undirected pair so item_a is the stronger antecedent.

swap <- conf_ba > conf_ab
    ia <- ifelse(swap, b_vec, a_vec)
    ib <- ifelse(swap, a_vec, b_vec)
    conf_dir <- pmax(conf_ab, conf_ba)

    pairs_all <- data.frame(
      item_a = ia, item_b = ib,
      support = support,
      support_pct = round(100 * support, 2),
      confidence = conf_dir,
      confidence_pct = round(100 * conf_dir, 1),
      lift = round(lift, 2),
      co = as.integer(co_vec),
      stringsAsFactors = FALSE
    )

Rank by lift, breaking ties by support so common, reliable pairs win.

pairs_all <- pairs_all[order(-pairs_all$lift, -pairs_all$support), , drop = FALSE]
    rownames(pairs_all) <- NULL
    rules_df <- head(pairs_all[, c("item_a", "item_b", "support_pct",
                                   "confidence_pct", "lift")], 15)
    rownames(rules_df) <- NULL

Strongest actionable rule: highest lift among pairs clearing support.

top_pair <- pairs_all[1, , drop = FALSE]

Directional rules for cross-sell — both directions of each pair, kept only when lift > 1 (a genuine positive association), ranked by confidence. This drops "recommend the item that's in every basket".

dir_df <- data.frame(
      if_they_buy = c(a_vec, b_vec),
      recommend   = c(b_vec, a_vec),
      confidence_pct = round(100 * c(conf_ab, conf_ba), 1),
      lift = round(c(lift, lift), 2),
      stringsAsFactors = FALSE
    )
    dir_df <- dir_df[dir_df$lift > 1, , drop = FALSE]
    if (nrow(dir_df) > 0) {
      dir_df <- dir_df[order(-dir_df$confidence_pct, -dir_df$lift), , drop = FALSE]
      cross_sell_df <- head(dir_df, 15)
    } else {
      cross_sell_df <- empty_cross
    }
    rownames(cross_sell_df) <- NULL
  }

Step 8: Metrics + json answer

metrics <- list(
    `Orders`            = as.integer(n_orders),
    `Distinct Items`    = as.integer(n_distinct_items),
    `Avg Basket Size`   = round(avg_basket, 2),
    `Qualifying Pairs`  = as.integer(n_qual),
    `Top Pairing`       = if (no_assoc) "none" else paste0(top_pair$item_a, " + ", top_pair$item_b),
    `Top Pairing Lift`  = if (no_assoc) 0 else top_pair$lift
  )

  json_output <- list(
    answer = if (no_assoc) {
      paste0(
        "Across ", plural(n_orders, "order"), " (average basket ",
        round(avg_basket, 1), " items), no product pair cleared the support floor of ",
        plural(min_support_orders, "basket"),
        ", so no strong associations were found. The most-bought ", name_item,
        " is ", top_item, ", in ", top_item_pct, "% of orders."
      )
    } else {
      paste0(
        "Across ", plural(n_orders, "order"), ", the strongest product pairing is ",
        top_pair$item_a, " and ", top_pair$item_b, ": shoppers who buy ",
        top_pair$item_a, " also buy ", top_pair$item_b, " ", top_pair$confidence_pct,
        "% of the time, a lift of ", top_pair$lift, " (bought together ", top_pair$lift,
        " times more often than chance). ", plural(n_qual, "pair"),
        " cleared the support floor. The most-bought ", name_item, " is ",
        top_item, ", in ", top_item_pct, "% of orders."
      )
    },
    cards = lapply(
      c("tldr", "overview", "preprocessing", "top_items",
        "association_rules", "cross_sell"),
      function(cid) list(id = cid, metrics = metrics)
    )
  )

  list(
    initial_rows = initial_rows, final_rows = final_rows, rows_removed = rows_removed,
    name_order = name_order, name_item = name_item,
    n_orders = n_orders, n_distinct_items = n_distinct_items,
    avg_basket = avg_basket, n_bad_rows = n_bad_rows, n_lumped = n_lumped,
    item_freq_df = item_freq_df, top_items_df = top_items_df,
    top_item = top_item, top_item_pct = top_item_pct,
    pairs_all = pairs_all, rules_df = rules_df, cross_sell_df = cross_sell_df,
    top_pair = top_pair, no_assoc = no_assoc,
    min_support_orders = min_support_orders,
    metrics = metrics, json_output = json_output
  )
}

# Card: tldr (tldr)
card_tldr <- function(shared, df, params) {
  if (shared$no_assoc) {
    text <- paste0(
      "Across ", plural(shared$n_orders, "order"), " (average basket ",
      round(shared$avg_basket, 1), " items), no product pair appeared together in at least ",
      plural(shared$min_support_orders, "basket"),
      ", so no strong co-purchase associations were found. The single most-bought ",
      shared$name_item, " is ", shared$top_item, ", appearing in ",
      shared$top_item_pct, "% of orders. With more transactions or a more ",
      "concentrated catalog, clearer pairings tend to emerge."
    )
  } else {
    tp <- shared$top_pair
    text <- paste0(
      "Across ", plural(shared$n_orders, "order"), " with an average basket of ",
      round(shared$avg_basket, 1), " items, the strongest product pairing is ",
      tp$item_a, " and ", tp$item_b, ": shoppers who buy ", tp$item_a,
      " also buy ", tp$item_b, " ", tp$confidence_pct, "% of the time, a lift of ",
      tp$lift, " — they land in the same basket ", tp$lift,
      " times more often than if the two were bought independently. The most-bought ",
      shared$name_item, " overall is ", shared$top_item, ", in ",
      shared$top_item_pct, "% of orders. These are co-purchase patterns to act on for ",
      "cross-sell, bundling, and layout; they show what sells together, not proof that one item drives the other."
    )
  }
  list(
    title = "Executive Summary",
    description = paste0("What sells together across ", plural(shared$n_orders, "order"), "."),
    metrics = shared$metrics,
    text = text
  )
}
Your data has more stories to tell.Run any analysis on your own data — validated R modules, interactive reports, AI insights, and PDF export. 500 free credits on signup.
Try Free — No SignupSign Up Free

Cite this analysis

Report an Issue

Tell us what's wrong. You'll get a free re-run of this analysis so you can try again with different parameters. If the re-run still doesn't meet your expectations, we'll refund your credits.

Want to run this analysis on your own data? Upload CSV — Free Analysis See Pricing