| name | warehouse-slotting-optimization |
| description | When the user wants to optimize warehouse slot assignments, improve pick efficiency, or design warehouse layouts. Also use when the user mentions "slotting optimization," "slot assignment," "ABC slotting," "pick path optimization," "storage location assignment," "warehouse layout optimization," or "forward pick locations." For picker routing, see picker-routing-optimization. For warehouse design, see warehouse-design. |
Warehouse Slotting Optimization
You are an expert in warehouse slotting optimization and storage location assignment. Your goal is to help optimize the placement of SKUs in warehouse locations to minimize travel distance, improve pick efficiency, maximize space utilization, and reduce labor costs.
Initial Assessment
Before optimizing slotting, understand:
-
Warehouse Characteristics
- Warehouse layout? (zones, aisles, levels)
- Total storage locations and capacity?
- Pick zones (forward pick, reserve, bulk)?
- Equipment types? (forklifts, pickers, automated systems)
- Current slotting method? (manual, ABC analysis, random)
-
Product Profile
- Number of SKUs?
- Product dimensions and weight?
- Velocity (picks per day/week)?
- Seasonality and demand patterns?
- Cube movement (cubic feet × picks)?
-
Operational Constraints
- FIFO/FEFO requirements?
- Hazmat or compatibility restrictions?
- Temperature zones?
- Max weight per location/level?
- Replenishment frequency?
-
Business Objectives
- Primary goal? (minimize travel, balance workload, maximize throughput)
- Current pick efficiency metrics?
- Expected order profile changes?
- Slotting refresh frequency?
Slotting Optimization Framework
Slotting Principles
1. Golden Zone Principle
- Place fast movers in most accessible locations
- Eye level (waist to shoulder height) is optimal
- Minimize bending and reaching
- Reduce picker fatigue
2. Cube-per-Order Index (COI)
- Formula: COI = Cubic Volume / Orders per Period
- Lower COI = More picks per cubic foot = Better slot candidate
- Prioritize high-pick, low-cube items in forward pick
3. ABC Velocity Analysis
- A items: Top 20% SKUs, 80% of picks → Prime locations
- B items: Next 30% SKUs, 15% of picks → Secondary locations
- C items: Bottom 50% SKUs, 5% of picks → Reserve locations
4. Correlated Picks
- Items frequently ordered together should be close
- Reduces travel between picks
- Cluster analysis on order history
5. Product Affinity
- Group similar products (size, category, handling)
- Improves replenishment efficiency
- Facilitates cross-training
Mathematical Formulation
Quadratic Assignment Problem (QAP)
Slotting optimization is fundamentally a Quadratic Assignment Problem:
Decision Variables:
- x[i,j] = 1 if SKU i is assigned to location j, 0 otherwise
Objective:
Minimize total travel distance weighted by pick frequency
minimize: Σ Σ Σ Σ (f[i,k] × d[j,l] × x[i,j] × x[k,l])
i k j l
where:
- f[i,k] = frequency SKU i and k are picked together
- d[j,l] = distance between location j and l
- x[i,j] = 1 if SKU i assigned to location j
Constraints:
for i in SKUs:
Σ x[i,j] = 1 for all j in Locations
for j in Locations:
Σ x[i,j] ≤ 1 for all i in SKUs
for j in Locations:
Σ (size[i] × x[i,j]) ≤ capacity[j] for all i
for i in SKUs:
for j in Locations:
if not compatible(i, j):
x[i,j] = 0
Slotting Algorithms
ABC Slotting (Basic)
import pandas as pd
import numpy as np
def abc_slotting_analysis(sku_data):
"""
Perform ABC analysis for warehouse slotting
Parameters:
-----------
sku_data : DataFrame with columns
- sku_id: Product identifier
- picks_per_month: Number of picks
- cube_per_unit: Cubic feet per unit
- units_per_pick: Average units per pick
Returns:
--------
DataFrame with ABC classification and slotting recommendations
"""
df = sku_data.copy()
df['cube_moved'] = (df['picks_per_month'] *
df['units_per_pick'] *
df['cube_per_unit'])
df['coi'] = df['cube_per_unit'] / (df['picks_per_month'] + 1)
df = df.sort_values('picks_per_month', ascending=False)
df['cumulative_picks'] = df['picks_per_month'].cumsum()
total_picks = df['picks_per_month'].sum()
df['pick_percentage'] = (df['cumulative_picks'] / total_picks) * 100
df['abc_class'] = pd.cut(
df['pick_percentage'],
bins=[0, 80, 95, 100],
labels=['A', 'B', 'C']
)
():
row[] == :
row[] < :
:
row[] == :
:
df[] = df.apply(get_zone_recommendation, axis=)
df[] = (
df[].rank() * +
df[].rank(ascending=) * +
df[].rank(ascending=) *
)
df.sort_values()
sku_data = pd.DataFrame({
: [ i (, )],
: np.random.randint(, , ),
: np.random.uniform(, , ),
: np.random.randint(, , )
})
slotting_analysis = abc_slotting_analysis(sku_data)
()
(slotting_analysis.head()[[, ,
, , ]])
()
(slotting_analysis[].value_counts())
Correlated Picks Analysis
from sklearn.cluster import KMeans
from scipy.spatial.distance import pdist, squareform
def analyze_pick_correlation(order_lines_data):
"""
Analyze which SKUs are frequently picked together
Parameters:
-----------
order_lines_data : DataFrame with columns
- order_id: Order identifier
- sku_id: Product identifier
- quantity: Units picked
Returns:
--------
Correlation matrix and clustered SKU groups
"""
pivot = order_lines_data.pivot_table(
index='order_id',
columns='sku_id',
values='quantity',
fill_value=0,
aggfunc='sum'
)
binary_matrix = (pivot > 0).astype(int)
correlation_matrix = binary_matrix.corr()
n_orders = len(pivot)
co_occurrence = binary_matrix.T.dot(binary_matrix)
affinity_matrix = co_occurrence / n_orders
n_clusters = min(10, len(pivot.columns) // 5)
if len(pivot.columns) > 1:
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
clusters = kmeans.fit_predict(binary_matrix.T)
sku_clusters = pd.DataFrame({
'sku_id': pivot.columns,
'cluster': clusters
})
cluster_sizes = sku_clusters['cluster'].value_counts()
sku_clusters[] = sku_clusters[].(cluster_sizes)
:
sku_clusters = pd.DataFrame({
: pivot.columns,
: ,
: (pivot.columns)
})
{
: correlation_matrix,
: affinity_matrix,
: sku_clusters,
: binary_matrix
}
():
mask = np.triu(np.ones_like(correlation_matrix), k=).astype()
upper_tri = correlation_matrix.where(mask)
correlations = upper_tri.stack().sort_values(ascending=)
correlations.head(top_n)
order_lines = pd.DataFrame({
: [, , , , , , , , , , , , , ],
: [, , , , , , , , , , , , , ],
: [, , , , , , , , , , , , , ]
})
correlation_analysis = analyze_pick_correlation(order_lines)
()
(correlation_analysis[].sort_values())
top_corr = get_top_correlations(correlation_analysis[])
()
(top_corr)
Optimization Model: Slotting Assignment
from pulp import *
import numpy as np
def optimize_slotting(skus, locations, pick_frequencies, distances,
sku_sizes, location_capacities, constraints=None):
"""
Optimize warehouse slotting using Mixed-Integer Programming
Parameters:
-----------
skus : list
SKU identifiers
locations : list
Location identifiers
pick_frequencies : dict
{sku: picks_per_period}
distances : dict
{location: distance_from_depot} or distance matrix
sku_sizes : dict
{sku: cubic_feet}
location_capacities : dict
{location: max_cubic_feet}
constraints : dict, optional
Additional constraints (compatibility, etc.)
Returns:
--------
Optimal slotting assignment
"""
prob = LpProblem("Warehouse_Slotting", LpMinimize)
x = LpVariable.dicts("assign",
[(i, j) for i in skus for j in locations],
cat='Binary')
prob += lpSum([
pick_frequencies.get(i, 0) * distances.get(j, 0) * x[i, j]
for i in skus for j in locations
]), "Total_Weighted_Distance"
for i in skus:
prob += lpSum([x[i, j] for j in locations]) == 1,
j locations:
prob += lpSum([x[i, j] i skus]) <= ,
j locations:
prob += lpSum([
sku_sizes.get(i, ) * x[i, j] i skus
]) <= location_capacities.get(j, ()),
constraints constraints:
(sku, loc) constraints[]:
sku skus loc locations:
prob += x[sku, loc] == ,
constraints constraints:
(sku, loc) constraints[]:
sku skus loc locations:
prob += x[sku, loc] == ,
prob.solve(PULP_CBC_CMD(msg=))
assignments = {}
i skus:
j locations:
x[i, j].varValue > :
assignments[i] = j
total_distance = (
pick_frequencies.get(sku, ) * distances.get(loc, )
sku, loc assignments.items()
)
utilization = {}
j locations:
used = (
sku_sizes.get(i, )
i, assigned_loc assignments.items()
assigned_loc == j
)
capacity = location_capacities.get(j, )
utilization[j] = (used / capacity * ) capacity >
{
: LpStatus[prob.status],
: assignments,
: total_distance,
: utilization,
: value(prob.objective)
}
skus = [ i (, )]
locations = [ i (, )]
np.random.seed()
pick_frequencies = {sku: np.random.randint(, ) sku skus}
distances = {loc: np.random.uniform(, ) loc locations}
sku_sizes = {sku: np.random.uniform(, ) sku skus}
location_capacities = {loc: loc locations}
constraints = {
: [(, ), (, )],
}
result = optimize_slotting(
skus, locations, pick_frequencies, distances,
sku_sizes, location_capacities, constraints
)
()
()
()
sorted_skus = (skus, key= s: pick_frequencies[s], reverse=)[:]
sku sorted_skus:
loc = result[][sku]
()
Hungarian Algorithm for Simple Assignment
from scipy.optimize import linear_sum_assignment
import numpy as np
def hungarian_slotting(skus, locations, cost_matrix):
"""
Use Hungarian algorithm for simple one-to-one slotting
Parameters:
-----------
skus : list
SKU identifiers
locations : list
Location identifiers
cost_matrix : 2D array
Cost[i,j] = cost of assigning sku i to location j
Returns:
--------
Optimal assignment (when # SKUs = # locations)
"""
n_skus = len(skus)
n_locs = len(locations)
if n_skus != n_locs:
max_dim = max(n_skus, n_locs)
padded_cost = np.full((max_dim, max_dim), np.max(cost_matrix) * 10)
padded_cost[:n_skus, :n_locs] = cost_matrix
cost_matrix = padded_cost
row_ind, col_ind = linear_sum_assignment(cost_matrix)
assignments = {}
total_cost = 0
for i, j in zip(row_ind, col_ind):
if i < n_skus and j < n_locs:
assignments[skus[i]] = locations[j]
total_cost += cost_matrix[i, j]
return {
'assignments': assignments,
'total_cost': total_cost,
'row_indices': row_ind[:n_skus],
'col_indices': col_ind[:n_skus]
}
skus = [f'SKU{i}' for i in range(1, )]
locations = [ i (, )]
np.random.seed()
pick_freq = np.random.randint(, , )
distances = np.random.uniform(, , )
cost_matrix = np.outer(pick_freq, distances)
result = hungarian_slotting(skus, locations, cost_matrix)
()
()
()
sku, loc result[].items():
i = skus.index(sku)
j = locations.index(loc)
()
Advanced Slotting Techniques
Dynamic Slotting
import pandas as pd
from datetime import datetime, timedelta
class DynamicSlottingOptimizer:
"""
Dynamic slotting that adapts to changing demand patterns
"""
def __init__(self, warehouse_layout, refresh_frequency='weekly'):
self.warehouse_layout = warehouse_layout
self.refresh_frequency = refresh_frequency
self.slotting_history = []
self.current_slotting = {}
def calculate_velocity_trend(self, historical_picks, window_days=30):
"""
Calculate velocity trends for each SKU
Returns trending up, stable, or trending down
"""
df = historical_picks.copy()
df['date'] = pd.to_datetime(df['date'])
recent_end = df['date'].max()
recent_start = recent_end - timedelta(days=window_days)
older_start = recent_start - timedelta(days=window_days)
recent_picks = df[df['date'] >= recent_start].groupby('sku_id')['picks'].sum()
older_picks = df[
(df['date'] >= older_start) & (df['date'] < recent_start)
].groupby('sku_id')['picks'].sum()
velocity_change = (recent_picks - older_picks) / (older_picks + 1)
trends = {}
for sku in velocity_change.index:
change = velocity_change[sku]
if change > :
trends[sku] =
change < -:
trends[sku] =
:
trends[sku] =
trends
():
seasonal_factors = {
: {: , : , : },
: {: , : , : , : },
}
seasonal_factors.get(sku_id, {}).get(current_month, )
():
reslot_candidates = []
sku, current_zone current_slotting.items():
trend = velocity_trends.get(sku, )
trend == current_zone [, ]:
reslot_candidates.append({
: sku,
: ,
: ,
: current_zone,
:
})
trend == current_zone == :
reslot_candidates.append({
: sku,
: ,
: ,
: current_zone,
:
})
pd.DataFrame(reslot_candidates)
():
moves_needed = []
sku new_slotting:
old_loc = old_slotting.get(sku)
new_loc = new_slotting.get(sku)
old_loc != new_loc:
impact = .calculate_move_impact(sku, old_loc, new_loc)
moves_needed.append({
: sku,
: old_loc,
: new_loc,
: impact
})
moves_df = pd.DataFrame(moves_needed)
(moves_df) > :
moves_df = moves_df.sort_values(, ascending=)
moves_df.head(max_moves)
moves_df
():
np.random.uniform(, )
optimizer = DynamicSlottingOptimizer(warehouse_layout={})
historical_data = pd.DataFrame({
: pd.date_range(, periods=),
: np.random.choice([, , , , ], ),
: np.random.randint(, , )
})
velocity_trends = optimizer.calculate_velocity_trend(historical_data)
()
(velocity_trends)
current_slotting = {
: ,
: ,
: ,
: ,
:
}
recommendations = optimizer.recommend_reslotting(current_slotting, velocity_trends)
()
(recommendations)
Forward Pick Location Sizing
def calculate_forward_pick_size(sku_data, replenishment_frequency='daily'):
"""
Determine optimal forward pick location size for each SKU
Balance between:
- Minimizing forward pick space (expensive)
- Minimizing replenishment trips (labor cost)
Parameters:
-----------
sku_data : DataFrame with columns
- sku_id
- daily_picks (average)
- units_per_pick
- cube_per_unit
- replenishment_cost ($ per trip)
Returns:
--------
Recommended forward pick capacity for each SKU
"""
df = sku_data.copy()
df['daily_cube_picked'] = (df['daily_picks'] *
df['units_per_pick'] *
df['cube_per_unit'])
replen_days = {
'daily': 1,
'every_2_days': 2,
'weekly': 7
}
days = replen_days.get(replenishment_frequency, 1)
df['forward_pick_cube'] = df['daily_cube_picked'] * days * 1.2
standard_sizes = [1, 2, 4, 8, 16, 32]
def round_to_standard(size):
for std_size in standard_sizes:
if size <= std_size:
return std_size
standard_sizes[-]
df[] = df[].apply(round_to_standard)
df[] = ( / days) * (df[] > )
df[] = df[] * df[]
space_cost_per_cuft = /
df[] = df[] * space_cost_per_cuft
df[] = df[] + df[]
df[[
, , ,
, ,
,
]]
sku_data = pd.DataFrame({
: [ i (, )],
: np.random.randint(, , ),
: np.random.uniform(, , ),
: np.random.uniform(, , ),
: np.full(, )
})
forward_pick_sizing = calculate_forward_pick_size(sku_data, )
()
(forward_pick_sizing)
Tools & Libraries
Slotting Software
Commercial Solutions:
- Manhattan WMS: Advanced slotting module
- Blue Yonder (JDA) WMS: AI-driven slotting
- SAP EWM: Extended warehouse management slotting
- HighJump WMS: Task-based slotting optimization
- Körber Supply Chain: Dynamic slotting
- Infor WMS: Velocity-based slotting
Specialized Slotting Tools:
- EasySlot: Standalone slotting optimizer
- SlotSmart: Cloud-based slotting
- Warehousing Efficiency Solutions: Slotting consulting + software
Python Libraries
from pulp import *
from scipy.optimize import linear_sum_assignment
from ortools.linear_solver import pywraplp
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
Common Challenges & Solutions
Challenge: Seasonal Demand Shifts
Problem:
- Summer vs. winter products have different velocities
- Holiday season drastically changes pick patterns
- Back-to-school surge
Solutions:
- Implement seasonal slotting profiles (3-4 per year)
- Use rolling 30-day velocity for classification
- Reserve flex zones for seasonal items
- Pre-slot before season starts (proactive)
- Use forward pick overflow areas
- Monitor velocity weekly during transitions
Challenge: Product Proliferation
Problem:
- Too many SKUs for available forward pick slots
- New SKUs constantly added
- Long tail of slow movers
Solutions:
- Stricter ABC cutoffs (only top 15-20% in forward pick)
- Implement each-pick for C items from reserve
- Consolidate similar SKUs
- Phase out slow movers
- Use dynamic slotting (continuous reoptimization)
- Multi-SKU per location for small items
Challenge: Physical Constraints
Problem:
- Weight limits on upper shelves
- Incompatible products (hazmat, temperature)
- Oversized items don't fit standard slots
Solutions:
- Add weight constraints to optimization model
- Pre-assign zones by product type
- Dedicated bulk pick areas
- Floor stacking for heavy/large items
- Use equipment-specific zones
- Multi-deep pallet racking for reserves
Challenge: Replenishment Disruption
Problem:
- Replenishment congestion during pick waves
- Forklift traffic interferes with pickers
- Running out of forward pick inventory
Solutions:
- Schedule replenishment between pick waves
- Use separate replenishment aisles
- Implement min/max replenishment triggers
- Right-size forward pick locations
- Two-deep forward pick (A/B positions)
- Automated replenishment (AS/RS, shuttles)
Challenge: Resistance to Change
Problem:
- Pickers resist new slotting
- Muscle memory disrupted
- Short-term productivity drop
Solutions:
- Implement gradually (zone by zone)
- Communicate benefits clearly
- Provide updated pick face maps
- Allow 2-week learning curve
- Use voice/RF directed picking (tells location)
- Track and celebrate improvements
- Involve experienced pickers in design
Challenge: Data Quality
Problem:
- Inaccurate pick history
- Missing product dimensions
- Unknown product velocity for new items
Solutions:
- Clean historical data (remove outliers, returns)
- Physical audit of product dimensions
- Cube scan on receiving
- Proxy velocity from similar products
- Use category averages for new SKUs
- Start with conservative slotting, adjust after 30 days
Output Format
Slotting Optimization Report
Executive Summary:
- Current picking efficiency: 120 lines/hour
- Projected improvement: 145 lines/hour (+21%)
- Total SKUs analyzed: 2,450
- SKUs requiring relocation: 347 (14%)
- Estimated implementation time: 3 weeks
- ROI: 6 months
ABC Analysis Results:
| Category | # SKUs | % of SKUs | % of Picks | Avg Pick/Month | Zone Assignment |
|---|
| A | 245 | 10% | 75% | 850 | Golden Zone |
| B | 612 | 25% | 20% | 180 | Forward Pick |
| C | 1,593 | 65% | 5% | 12 | Reserve Storage |
Top 25 SKUs - Golden Zone Placement:
| SKU | Description | Picks/Month | COI | Current Loc | Optimal Loc | Travel Savings |
|---|
| SKU1234 | Widget A | 1,245 | 0.05 | C-15-3 | A-01-2 | 3,850 ft/mo |
| SKU2345 | Gadget B | 1,123 | 0.08 | B-08-1 | A-01-3 | 3,200 ft/mo |
| ... | ... | ... | ... | ... | ... | ... |
Product Affinity Clusters:
Cluster 1 (Office Supplies): 45 SKUs
- Frequently ordered together
- Recommend: Aisle A1-A2
Cluster 2 (Electronics): 32 SKUs
- High correlation in order patterns
- Recommend: Aisle B1-B2
...
Implementation Plan:
Phase 1 - Week 1: Golden Zone (A items)
- Move top 50 SKUs to optimal locations
- Expected: 50% of total benefit
- Minimal disruption
Phase 2 - Week 2: Forward Pick (B items)
- Optimize 200 SKU locations
- Expected: 30% additional benefit
Phase 3 - Week 3: Reserve Storage (C items)
- Consolidate and organize
- Expected: 20% additional benefit
Expected Benefits:
| Metric | Current | Optimized | Improvement |
|---|
| Avg Pick Travel (ft) | 2,850 | 2,100 | -26% |
| Picks per Hour | 120 | 145 | +21% |
| Daily Labor Hours | 180 | 150 | -17% |
| Annual Labor Cost | $540K | $450K | -$90K |
| Forward Pick Utilization | 68% | 89% | +21 pts |
Maintenance Plan:
- Weekly velocity monitoring
- Monthly ABC reclassification
- Quarterly full slotting review
- Seasonal profile changes (4x/year)
- Continuous improvement (1% moves/week)
Questions to Ask
If you need more context:
- How many SKUs and storage locations do you have?
- What's your current pick efficiency (lines/hour)?
- Do you have forward pick and reserve zones?
- How often do you currently re-slot?
- What WMS or slotting software do you use?
- Are there seasonal demand patterns?
- What are physical constraints (weight, temperature, hazmat)?
- How is replenishment currently scheduled?
Related Skills
- picker-routing-optimization: For optimizing pick path given slotting
- warehouse-design: For overall layout and zone design
- order-batching-optimization: For batching orders to pick together
- wave-planning-optimization: For planning pick waves
- task-assignment-problem: For assigning pickers to zones
- inventory-optimization: For forward pick inventory levels
- demand-forecasting: For predicting future velocity changes
- abc-analysis: For product classification