How to Use Item Quantity and Basket Analysis in WooCommerce: Step-by-Step Tutorial
Updated October 2026
Introduction to Item Quantity and Basket Analysis
Understanding what customers buy together and in what quantities is crucial for optimizing your WooCommerce store's performance. Item quantity and basket analysis reveals purchasing patterns that point to opportunities in product placement, bundling and cross-selling.
In this tutorial, you'll learn how to analyze your WooCommerce order data to answer critical business questions: What's the typical basket size in your store? Which products are frequently purchased together? Are there opportunities for product bundles that customers are already creating organically?
Unlike traditional analytics that focus solely on individual product performance, basket analysis examines the relationships between products and the aggregate behavior of your customers. This approach, similar to techniques used in AI-first data analysis pipelines, provides actionable insights that can transform your store's profitability.
Prerequisites and What You'll Need
Before You Begin
To successfully complete this tutorial, you'll need:
- Active WooCommerce Store: A functioning WooCommerce installation with enough completed orders that each product you care about appears in many of them. A product that appears in only a handful of orders cannot show a stable pattern (see the Yoga Blocks row in Step 4)
- Database Access or Export Plugin: Ability to export order data with one row per order line item (an order-export plugin, or a database query as in Step 2)
- Basic CSV Understanding: Familiarity with spreadsheet data and CSV file formats
- A CSV file: The export from Step 2, which you can upload at MCP Analytics in Step 3 or analyze yourself with the code in Step 4
Required Data Fields
Your export should include these essential fields:
order_id- Unique identifier for each orderproduct_id- Unique identifier for each productproduct_name- Human-readable product namequantity- Number of items purchasedorder_date- Timestamp of the orderorder_total- Total order value (optional but recommended)
Step 1: Understanding Item Quantity and Basket Analysis Fundamentals
Before diving into the data, it's essential to understand what basket analysis can reveal about your store:
Key Metrics You'll Discover
Average Basket Size
This metric tells you how many items, on average, customers purchase per transaction. Track it over time as a baseline for bundling and promotion changes.
Product Affinity
Product affinity measures how often products appear together in the same order. High affinity between two products suggests a natural bundling opportunity.
Basket Size Distribution
Understanding the distribution of basket sizes (how many customers buy 1 item vs. 2 items vs. 5 items) helps you identify customer segments and tailor marketing strategies accordingly.
Why This Analysis Matters
- Revenue Optimization: Identify cross-sell and upsell opportunities that align with natural customer behavior
- Inventory Management: Stock complementary products appropriately based on purchase patterns
- Marketing Strategy: Create targeted campaigns that promote frequently co-purchased items
- User Experience: Improve product recommendations to match actual purchasing behavior
Step 2: Exporting Your WooCommerce Order Data
The quality of your analysis depends entirely on the quality of your data export. Follow these steps to ensure you capture all necessary information:
Method 1: An Order-Export Plugin
Use an order-export plugin (WP All Export is one example) and follow its own documentation to export WooCommerce orders. Configure the export so that each row is one order line item, with these columns: order ID, product ID, product name, quantity, order date and order total. Filter to completed orders and to the date range you want to analyze. Field names and menu paths differ between plugins, so check the plugin's documentation for the exact placeholders.
Method 2: Direct Database Query
If you have database access, you can export data directly using SQL. The query below reads the classic WordPress storage, where an order is a shop_order row in wp_posts and its line items are rows in wp_woocommerce_order_items, with product ID and quantity in wp_woocommerce_order_itemmeta under the keys _product_id and _qty. WooCommerce 8.2 and later enable High-Performance Order Storage (HPOS) by default for new installations, which keeps orders in dedicated tables (wc_orders and related tables) instead; if your store uses HPOS without posts synchronization, take the order ID, date and total from those tables, or use Method 1.
SELECT
orders.ID AS order_id,
pid.meta_value AS product_id,
order_items.order_item_name AS product_name,
qty.meta_value AS quantity,
DATE(orders.post_date) AS order_date,
order_total.meta_value AS order_total
FROM wp_posts AS orders
INNER JOIN wp_woocommerce_order_items AS order_items
ON orders.ID = order_items.order_id
AND order_items.order_item_type = 'line_item'
INNER JOIN wp_woocommerce_order_itemmeta AS pid
ON order_items.order_item_id = pid.order_item_id
AND pid.meta_key = '_product_id'
INNER JOIN wp_woocommerce_order_itemmeta AS qty
ON order_items.order_item_id = qty.order_item_id
AND qty.meta_key = '_qty'
LEFT JOIN wp_postmeta AS order_total
ON orders.ID = order_total.post_id
AND order_total.meta_key = '_order_total'
WHERE orders.post_type = 'shop_order'
AND orders.post_status = 'wc-completed'
ORDER BY orders.post_date DESC, orders.ID, order_items.order_item_id;
Replace wp_ with your database prefix if it differs. Shipping lines are excluded by the order_item_type = 'line_item' filter. The query was run against a small mock of this table layout to confirm it returns one row per line item.
Data Validation Checklist
Before proceeding, verify your export:
- ✓ No missing order IDs
- ✓ Product names are readable (not just IDs)
- ✓ Quantities are numeric and positive
- ✓ Dates are in consistent format (YYYY-MM-DD recommended)
- ✓ File is saved as CSV with UTF-8 encoding
Expected Output
Your CSV file should look like this. The rows here, and all figures in Step 4, come from a synthetic data set of 1,000 orders generated for this tutorial, not from a real store:
order_id,product_id,product_name,quantity,order_date,order_total
1924,421,Mug Set,2,2024-12-28,44.00
1952,234,Organic Coffee Beans,1,2024-12-28,50.00
1952,567,French Press,1,2024-12-28,50.00
1980,310,Yoga Mat,1,2024-12-28,55.00
1980,311,Yoga Blocks,1,2024-12-28,55.00
Run It on Your Own WooCommerce Export
Export your WooCommerce orders as a CSV and upload it at MCP Analytics. The analysis runs association-rule mining (support, confidence and lift for product pairs) plus a basket size and quantity breakdown on your file and shows the result with the method used. You choose the columns; nothing connects to your WooCommerce account.
Step 4: Interpreting Your Results
Here's how to interpret each of the three views the code below produces:
The code below reads the CSV from Step 2 (saved as export.csv) and computes the three views this step interprets. The data is the synthetic 1,000-order set described above.
Basket Size Distribution
Basket size here is the number of distinct products (order lines) in an order. This view shows what share of orders falls at each size:
import itertools
import pandas as pd
df = pd.read_csv("export.csv")
n_orders = df["order_id"].nunique()
# Basket size = number of distinct products (order lines) in an order
basket = df.groupby("order_id").size()
share = basket.value_counts(normalize=True).sort_index() * 100
print("Basket Size | Percentage of Orders | Cumulative %")
for size, pct in share.items():
print(f"{size} item{'s' if size > 1 else ' '}".ljust(11) + f" | {pct:>5.1f}%".ljust(23) + f"| {share.loc[:size].sum():.1f}%")
print(f"Average basket size: {basket.mean():.2f} items")
print(f"Median basket size: {basket.median():.0f} item")
print(f"Orders with 2 or 3 items: {basket.isin([2, 3]).mean() * 100:.1f}%")
Example Output:
Basket Size | Percentage of Orders | Cumulative %
1 item | 60.6% | 60.6%
2 items | 28.2% | 88.8%
3 items | 9.5% | 98.3%
4 items | 1.7% | 100.0%
Average basket size: 1.52 items
Median basket size: 1 item
Orders with 2 or 3 items: 37.7%
Key Insights:
- In this data set 60.6% of orders contain a single product, so the largest group of orders has no cross-sell in it yet.
- The mean (1.52) is above the median (1) because a minority of larger orders pulls the mean up; 37.7% of orders hold two or three products.
- A common goal is to move single-product orders to two products; compare the distribution before and after a change, not just the mean.
Product Affinity
This section shows which products are purchased together. The table lists five pairs from the same data (1,000 orders):
products = df.groupby("order_id")["product_name"].apply(set)
support = {p: sum(p in s for s in products) / n_orders for p in df["product_name"].unique()}
pairs = [("Organic Coffee Beans", "French Press"), ("Organic Coffee Beans", "Coffee Grinder"),
("Organic Coffee Beans", "Coffee Filters (50pk)"), ("Coffee Filters (50pk)", "Organic Coffee Beans"),
("Yoga Mat", "Yoga Blocks")]
print("Product A | Product B | Orders with both | Confidence | Lift | Orders with A")
for a, b in pairs:
both = sum(a in s and b in s for s in products)
conf = both / (support[a] * n_orders)
lift = conf / support[b]
print(f"{a} | {b} | {both / n_orders * 100:.1f}% ({both}) | {conf * 100:.1f}% | {lift:.1f} | {round(support[a] * n_orders)}")
Example Output:
Product A | Product B | Orders with both | Confidence | Lift | Orders with A
Organic Coffee Beans | French Press | 20.1% (201) | 50.4% | 2.0 | 399
Organic Coffee Beans | Coffee Grinder | 13.8% (138) | 34.6% | 2.0 | 399
Organic Coffee Beans | Coffee Filters (50pk) | 8.9% (89) | 22.3% | 1.5 | 399
Coffee Filters (50pk) | Organic Coffee Beans | 8.9% (89) | 60.1% | 1.5 | 148
Yoga Mat | Yoga Blocks | 4.7% (47) | 40.2% | 8.5 | 117
Understanding the Metrics:
- Orders with both: Share of all orders (and the count) containing both products, so its denominator is every order.
- Confidence: Of the orders that contain Product A, the share that also contain Product B. Its denominator is the orders with A.
- Lift: Confidence divided by the share of all orders that contain Product B. A lift above 1 means B appears with A more often than its overall frequency alone would predict; 1 means no association.
Actionable Insights:
In this data set, 50.4% of orders containing "Organic Coffee Beans" also contain a "French Press" (201 of 399 orders), and French Press appears in 25.0% of all orders, so the lift is 2.0. That makes the pair a candidate for:
- A "Coffee Starter Bundle" with both products
- A French Press recommendation on the Coffee Beans page
- A discount when both items are in the cart
The Yoga Mat and Yoga Blocks row shows why counts matter: its lift of 8.5 rests on only 47 orders, so a few orders more or fewer would move it a lot. Check the counts before acting on a high lift.
Quantity Patterns
Understanding how many units of each product customers buy helps with inventory and pricing. These figures count only orders that contain the product:
qty = df.groupby("product_name")["quantity"].agg(
orders="size", avg="mean", common=lambda s: s.mode()[0], multi=lambda s: (s > 1).mean() * 100)
print("Product Name | Orders | Avg Qty/Order | Most Common Qty | % With Qty > 1")
for name in ["Organic Coffee Beans", "French Press", "Coffee Filters (50pk)", "Coffee Grinder"]:
r = qty.loc[name]
print(f"{name} | {r.orders:.0f} | {r.avg:.2f} | {r.common:.0f} | {r.multi:.1f}%")
Example Output:
Product Name | Orders | Avg Qty/Order | Most Common Qty | % With Qty > 1
Organic Coffee Beans | 399 | 1.88 | 2 | 67.2%
French Press | 250 | 1.04 | 1 | 3.6%
Coffee Filters (50pk) | 148 | 1.73 | 2 | 60.8%
Coffee Grinder | 172 | 1.15 | 1 | 14.5%
Strategic Applications:
- Products with a high average quantity per order, such as Organic Coffee Beans (1.88), are candidates for volume pricing.
- Products usually bought as a single unit, like French Press (3.6% of its orders have more than one), fit "Frequently Bought Together" suggestions better than multi-packs.
- Consider a 2-pack for items where most orders already hold several units (67.2% of Organic Coffee Beans orders).
Temporal Patterns
You can repeat the same calculations on separate date ranges (for example, quarter against quarter) to see how basket behavior changes over time.
Step 5: Applying Insights to Optimize Your Store
Analysis is only valuable when you take action. Here's how to implement your findings:
1. Create Product Bundles
Based on high-lift product pairs with enough orders behind them, create curated bundles (the prices below are illustrative):
// Example: WooCommerce product bundle setup
1. Install "WooCommerce Product Bundles" plugin
2. Create new product: "Coffee Enthusiast Starter Kit"
3. Add components:
- Organic Coffee Beans (Qty: 2)
- French Press (Qty: 1)
- Coffee Grinder (Qty: 1)
4. Set bundle price: $81 (vs $95 individual at the synthetic data set's prices, about 15% off)
5. Configure bundle discount: "Save $14 when purchased together"
2. Optimize Product Recommendations
Update your "Related Products" and "You May Also Like" sections based on actual affinity data rather than categories alone:
// Example: Custom recommendation logic
Product Page: Organic Coffee Beans
Recommended Products (by affinity score):
1. French Press (Lift: 2.0) - "50% of orders with these beans also included this"
2. Coffee Grinder (Lift: 2.0) - "Perfect companion for fresh beans"
3. Coffee Filters (Lift: 1.5) - "Complete your coffee setup"
3. Implement Dynamic Discounting
Encourage basket growth with smart discount rules:
- Threshold Discounts: "Add one more item for 10% off" (targeting customers at 1-2 items)
- Bundle Incentives: "Frequently bought together - Save 15%"
- Quantity Breaks: For products with high multi-unit purchase rates
4. Refine Email Marketing
Use affinity data to create targeted email campaigns:
Email Template:
"We noticed you recently purchased our Organic Coffee Beans. Did you know that half of the orders with our Organic Coffee Beans also include our French Press? Here's an exclusive 20% discount to complete your coffee experience..."
5. Inventory Optimization
Keep orders and units separate. Confidence is a share of orders, not of units:
In the synthetic data set (1,000 orders):
- Organic Coffee Beans appear in 399 orders; French Press in 250 orders
- 201 orders contain both, so 49 of the 250 French Press orders have no beans
- Plan French Press stock from its own total demand (250 orders, 1.04 units
per order), not as a fixed fraction of bean orders
Measuring Success
After implementing changes, track these metrics:
- Average basket size and the basket size distribution, against your own baseline
- Average order value
- Share of orders that contain the bundle
- Click-through and add-to-cart rate on recommendations
Re-run your analysis monthly to track improvements and identify new opportunities. Consider using A/B testing methodologies to test the impact of specific changes.
Ready to Look at Your Own Store's Patterns?
You've learned the fundamentals of item quantity and basket analysis; the figures above come from a synthetic data set, so the patterns in your own orders will differ. Let the data guide your decisions rather than guessing what your customers want.
Next Steps and Advanced Techniques
Expand Your Analysis
Once you've mastered basic basket analysis, consider these advanced approaches:
1. Customer Segmentation
Analyze basket patterns by customer segment (new vs. returning, high-value vs. low-value) to create personalized experiences.
2. Temporal Analysis
Examine how basket composition changes by day of week, season, or in response to marketing campaigns.
3. Sequential Pattern Mining
Go beyond single-order analysis to understand the sequence of purchases across multiple orders (what do customers buy on their second order after buying Product X first?)
4. Price Sensitivity Analysis
Combine basket analysis with pricing data to understand how discounts affect product combinations and basket size.
Related Resources
Troubleshooting Common Issues
Problem: No product pairs with a lift above 1
Cause: Dataset too small or highly fragmented product catalog
Solutions:
- Increase the date range to include more orders; pairs need enough orders behind them to be stable
- Lower the minimum support (the share of orders a pair must appear in) and read the counts before acting on the result
- Group similar products (e.g., different coffee bean varieties) into categories for analysis
- Filter out one-time or seasonal products that create noise
Problem: "Export file too large to upload"
Cause: The file is larger than the upload limit of the tool you are using
Solutions:
- Analyze data in date ranges (e.g., quarter by quarter)
- Remove unnecessary columns (keep only required fields)
- Filter to completed orders only in your export
Problem: "Inconsistent or missing product names"
Cause: Product variations or database encoding issues
Solutions:
// Clean data before upload using spreadsheet formulas
1. Remove special characters: =SUBSTITUTE(A2,CHAR(160)," ")
2. Standardize case: =PROPER(A2)
3. Remove leading/trailing spaces: =TRIM(A2)
4. Consolidate variations: Find/Replace "Coffee - Organic" → "Organic Coffee"
Problem: "Lift scores seem incorrect or counterintuitive"
Cause: Misunderstanding of lift calculation or insufficient sample size
Understanding:
- Lift = 1.0 means products appear together at expected random rate
- Lift > 1.0 means positive correlation (good for bundling)
- Lift < 1.0 means negative correlation (products rarely purchased together)
- A very high lift with few orders behind it (like Yoga Blocks above) is unstable and often reflects too little data
Problem: "Analysis results change dramatically between runs"
Cause: Small sample size or highly volatile purchasing patterns
Solutions:
- Use longer time periods for more stable patterns
- Implement rolling averages (compare 90-day windows)
- Focus on patterns with a high confidence and lift and a large order count behind them
- Consider seasonality: compare same periods year-over-year
Problem: "Can't connect export plugin to database"
Cause: Database permissions or hosting restrictions
Solutions:
- Contact hosting provider to verify database access permissions
- Use phpMyAdmin (available in most cPanel installations) for direct export
- Try alternative export plugins: WP All Export, Advanced Order Export
Your turn
Bring your own data and the question you actually need answered.
MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai
Explore more: WooCommerce Analytics: all tools, tutorials, and guides →