mcpField
P
planogram-optimization

planogram-optimization

github.com/kishorkukreja营销与增长8452 次安装3 周前更新
代码仓库

关于 planogram-optimization

面向零售商的货架图优化技能,帮助依据销售、利润、商品属性和门店条件分配货架空间与商品排面。内容涵盖空间生产率、陈列位置、商品邻接及实施规划。

主要功能

  1. 01按销售与利润分配货架空间
  2. 02分析货架空间与销售关系
  3. 03规划商品排面和陈列位置
  4. 04优化互补商品邻接
  5. 05制定调整与实施方案

适用场景

  1. 01提升品类每平方英尺销售额
  2. 02根据商品周转率调整排面
  3. 03规划互补商品的交叉陈列
  4. 04为门店陈列调整制定分阶段计划

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill planogram-optimization

安装后在 Claude Code 中输入/planogram-optimization确认已加载。

详细介绍

货架陈列图优化

你是零售货架陈列图优化与空间管理方面的专家。你的目标是帮助零售商通过优化货架空间分配、确定商品排面数,以及设计高效的门店布局,在每平方英尺的空间内实现销售额和盈利能力最大化,同时兼顾商品可见度、顾客体验和运营效率。

初步评估

优化货架陈列图之前,请先了解:

  1. 门店背景

    • 门店业态是什么?(杂货、服装、电子产品、药房)
    • 门店面积和布局如何?(平方英尺、货架数量)
    • 客流模式如何?(入口位置、收银台位置)
    • 目标顾客的人口统计特征是什么?
    • 门店位于哪类地点?(市区、郊区、购物中心)
  2. 品类特征

    • 需要优化哪个品类/部门?
    • 品类中有多少个 SKU?
    • 商品尺寸是多少?(高度、宽度、深度)
    • 单位销量如何?(畅销品与滞销品)
    • 各 SKU 的利润率如何?
    • 是否需要考虑保质期?(易腐商品、季节性商品)
  3. 当前表现

    • 当前每平方英尺的销售额是多少?
    • 缺货频率如何?
    • 各货架的空间生产力如何?
    • 顾客对布局的满意度如何?
    • 补货的人工成本是多少?
  4. 业务目标

    • 目标是最大化收入还是利润?
    • 目标服务水平是多少?(商品有货率)
    • 是否有交叉陈列目标?
    • 是否有品牌或促销要求?
    • 是否有运营限制?(补货频率、人工)

货架陈列图优化框架

空间生产力原则

1. 空间弹性

  • 货架空间与销售额之间的关系
  • 边际效益递减:空间增加并不总能带来更多销售
  • 各 SKU 的最佳排面数因商品而异

2. 空间分配规则

  • 高周转商品:增加排面,放置在视线高度
  • 高利润率商品:放置在优质位置
  • 冲动购买商品:放在端架和收银台附近
  • 目的性购买商品:可放在店铺深处(以吸引顾客经过更多区域)
  • 互补商品:组成交叉陈列组合

3. 货架高度的影响

  • 视线高度(4-5 ft):黄金位置,占销售额的 40%
  • 胸部高度(3-4 ft):次优位置,占销售额的 30%
  • 腰部高度(2-3 ft):第三优位置,占销售额的 20%
  • 地面高度(0-2 ft):可见度低,占销售额的 10%
  • 视线以上(5-6 ft):用于溢出陈列,满足偶尔购买需求

4. 商品邻接

  • 将相关商品放在一起(意面 + 酱料)
  • 通过颜色分区提升视觉吸引力
  • 按尺寸递增排列(从小到大)
  • 按价格递增排列(从低到高)

空间与销售额分析

空间弹性建模
import numpy as np
import pandas as pd
from scipy.optimize import minimize
import matplotlib.pyplot as plt

class SpaceElasticityAnalyzer:
    """
    Analyze space elasticity - relationship between shelf space and sales

    Space Elasticity = % change in sales / % change in shelf space
    """

    def __init__(self, historical_data):
        """
        Parameters:
        - historical_data: DataFrame with space/sales experiments
          columns: ['sku', 'period', 'facings', 'sales_units', 'sales_dollars']
        """
        self.data = historical_data

    def calculate_space_elasticity(self, sku):
        """
        Calculate space elasticity coefficient

        Using log-log regression: log(Sales) = a + b * log(Facings)
        b is the space elasticity
        """

        sku_data = self.data[self.data['sku'] == sku].copy()

        if len(sku_data) < 5:
            return {'error': 'Insufficient data'}

        # Log transformation
        sku_data['log_facings'] = np.log(sku_data['facings'])
        sku_data['log_sales'] = np.log(sku_data['sales_units'] + 1)

        # Linear regression in log-log space
        X = sku_data['log_facings'].values.reshape(-1, 1)
        y = sku_data['log_sales'].values

        from sklearn.linear_model import LinearRegression
        model = LinearRegression()
        model.fit(X, y)

        elasticity = model.coef_[0]
        r_squared = model.score(X, y)

        # Interpretation
        if elasticity > 0.8:
            interpretation = 'High elasticity - sales very responsive to space'
        elif elasticity > 0.4:
            interpretation = 'Moderate elasticity - typical'
        elif elasticity > 0.1:
            interpretation = 'Low elasticity - limited response to space'
        else:
            interpretation = 'Very low elasticity - sales not space-dependent'

        return {
            'sku': sku,
            'elasticity': elasticity,
            'r_squared': r_squared,
            'interpretation': interpretation,
            'model': model
        }

    def estimate_sales_at_facings(self, sku, target_facings):
        """
        Estimate sales at a given number of facings

        Uses fitted elasticity model
        """

        elasticity_result = self.calculate_space_elasticity(sku)

        if 'error' in elasticity_result:
            return None

        model = elasticity_result['model']
        log_facings = np.log(target_facings)

        log_sales_pred = model.predict([[log_facings]])[0]
        estimated_sales = np.exp(log_sales_pred) - 1

        return max(0, estimated_sales)

    def find_optimal_facings(self, sku, cost_per_facing, profit_per_unit,
                            max_facings=20):
        """
        Find optimal number of facings to maximize profit

        Balance: More facings = more sales but higher space cost
        """

        elasticity_result = self.calculate_space_elasticity(sku)

        if 'error' in elasticity_result:
            return {'error': 'Cannot optimize without elasticity data'}

        facings_range = range(1, max_facings + 1)
        results = []

        for facings in facings_range:
            estimated_sales = self.estimate_sales_at_facings(sku, facings)

            # Calculate profit
            revenue = estimated_sales * profit_per_unit
            space_cost = facings * cost_per_facing

            profit = revenue - space_cost

            results.append({
                'facings': facings,
                'estimated_sales': estimated_sales,
                'revenue': revenue,
                'space_cost': space_cost,
                'profit': profit,
                'profit_per_facing': profit / facings if facings > 0 else 0
            })

        results_df = pd.DataFrame(results)

        # Find optimal
        optimal_idx = results_df['profit'].idxmax()
        optimal = results_df.iloc[optimal_idx]

        return {
            'optimal_facings': optimal['facings'],
            'expected_sales': optimal['estimated_sales'],
            'expected_profit': optimal['profit'],
            'elasticity': elasticity_result['elasticity'],
            'all_scenarios': results_df
        }

# Example usage
np.random.seed(42)

# Generate sample data - simulate space elasticity
historical_data = []

for sku_id in range(1, 6):
    # Each SKU has different elasticity
    base_sales = np.random.uniform(50, 200)
    elasticity = np.random.uniform(0.2, 0.7)

    for period in range(20):
        facings = np.random.randint(2, 15)

        # Sales = base * (facings ^ elasticity) + noise
        sales = base_sales * (facings ** elasticity) + np.random.normal(0, 10)
        sales = max(0, sales)

        historical_data.append({
            'sku': f'SKU{sku_id:03d}',
            'period': period,
            'facings': facings,
            'sales_units': sales,
            'sales_dollars': sales * np.random.uniform(3, 8)
        })

historical_df = pd.DataFrame(historical_data)

# Analyze elasticity
analyzer = SpaceElasticityAnalyzer(historical_df)
elasticity = analyzer.calculate_space_elasticity('SKU001')

print(f"Space Elasticity: {elasticity['elasticity']:.3f}")
print(f"Interpretation: {elasticity['interpretation']}")
print(f"R-squared: {elasticity['r_squared']:.3f}")

# Find optimal facings
optimization = analyzer.find_optimal_facings(
    sku='SKU001',
    cost_per_facing=2.5,  # Cost per facing per week
    profit_per_unit=4.0,   # Profit margin per unit
    max_facings=15
)

print(f"\nOptimal facings: {optimization['optimal_facings']}")
print(f"Expected weekly sales: {optimization['expected_sales']:.0f} units")
print(f"Expected weekly profit: ${optimization['expected_profit']:.2f}")

货架陈列图优化模型

货架级空间分配
class PlanogramOptimizer:
    """
    Optimize product placement and facings on a fixture

    Maximize sales/profit per square foot
    """

    def __init__(self, fixture_config, products_data):
        """
        Parameters:
        - fixture_config: Dict with fixture dimensions
          {'shelves': 5, 'width_inches': 48, 'depth_inches': 12}
        - products_data: DataFrame with product info
          columns: ['sku', 'width_inches', 'depth_inches', 'height_inches',
                   'weekly_sales', 'profit_per_unit', 'min_facings', 'max_facings']
        """
        self.fixture = fixture_config
        self.products = products_data

    def calculate_space_productivity(self, allocation):
        """
        Calculate sales and profit per square foot for an allocation

        allocation: Dict {sku: {'shelf': shelf_num, 'facings': count}}
        """

        total_sales = 0
        total_profit = 0
        space_used = {}  # Track space used per shelf

        for sku, placement in allocation.items():
            product = self.products[self.products['sku'] == sku].iloc[0]

            # Calculate space consumption
            facings = placement['facings']
            width_per_facing = product['width_inches']
            total_width = facings * width_per_facing

            shelf = placement['shelf']

            # Update space tracking
            if shelf not in space_used:
                space_used[shelf] = 0
            space_used[shelf] += total_width

            # Calculate sales (with space elasticity effect)
            base_sales = product['weekly_sales']
            elasticity = product.get('space_elasticity', 0.4)

            # Diminishing returns: sales = base * (facings ^ elasticity)
            adjusted_sales = base_sales * (facings ** elasticity)

            total_sales += adjusted_sales
            total_profit += adjusted_sales * product['profit_per_unit']

        # Calculate space utilization
        total_space_sqft = (
            self.fixture['shelves'] *
            self.fixture['width_inches'] *
            self.fixture['depth_inches'] / 144  # Convert to sqft
        )

        sales_per_sqft = total_sales / total_space_sqft if total_space_sqft > 0 else 0
        profit_per_sqft = total_profit / total_space_sqft if total_space_sqft > 0 else 0

        # Check constraints
        valid = True
        for shelf, width_used in space_used.items():
            if width_used > self.fixture['width_inches']:
                valid = False

        return {
            'total_sales': total_sales,
            'total_profit': total_profit,
            'sales_per_sqft': sales_per_sqft,
            'profit_per_sqft': profit_per_sqft,
            'space_utilization': space_used,
            'valid': valid
        }

    def greedy_allocation(self, objective='profit'):
        """
        Greedy algorithm to allocate products to fixture

        Prioritize by profit per square foot (or sales per sqft)
        """

        # Calculate priority score for each product
        self.products['priority'] = self.products.apply(
            lambda row: self._calculate_priority(row, objective),
            axis=1
        )

        # Sort by priority
        sorted_products = self.products.sort_values('priority', ascending=False)

        # Allocate
        allocation = {}
        shelf_space_remaining = {
            i: self.fixture['width_inches'] for i in range(self.fixture['shelves'])
        }

        for idx, product in sorted_products.iterrows():
            sku = product['sku']
            width = product['width_inches']
            min_facings = product.get('min_facings', 1)
            max_facings = product.get('max_facings', 10)

            # Try to allocate to best shelf position
            # Eye level (middle shelves) are most valuable
            shelf_priority = self._get_shelf_priority_order(self.fixture['shelves'])

            allocated = False

            for shelf in shelf_priority:
                max_facings_possible = int(shelf_space_remaining[shelf] / width)

                if max_facings_possible >= min_facings:
                    # Allocate optimal number of facings
                    facings = min(max_facings, max_facings_possible)

                    allocation[sku] = {
                        'shelf': shelf,
                        'facings': facings,
                        'position': 'center'  # Simplified
                    }

                    # Update remaining space
                    shelf_space_remaining[shelf] -= facings * width
                    allocated = True
                    break

            if not allocated:
                # Could not fit this product
                pass

        return allocation

    def _calculate_priority(self, product, objective):
        """Calculate priority score for product placement"""

        if objective == 'profit':
            # Profit per unit of space
            space_per_unit = product['width_inches'] * product['depth_inches']
            return (product['weekly_sales'] * product['profit_per_unit']) / space_per_unit

        else:  # sales
            space_per_unit = product['width_inches'] * product['depth_inches']
            return product['weekly_sales'] / space_per_unit

    def _get_shelf_priority_order(self, num_shelves):
        """
        Get shelf allocation priority

        Eye level (middle) is most valuable
        """

        if num_shelves <= 3:
            return list(range(num_shelves))

        # Middle shelves first
        middle = num_shelves // 2
        priority = [middle]

        # Alternate above and below middle
        for offset in range(1, num_shelves):
            if middle + offset < num_shelves:
                priority.append(middle + offset)
            if middle - offset >= 0:
                priority.append(middle - offset)

        return priority

    def optimize_with_constraints(self, objective='profit',
                                  category_constraints=None):
        """
        Optimize with business constraints

        Constraints:
        - Minimum facings per SKU
        - Maximum facings per SKU
        - Product grouping (keep related products together)
        - Brand requirements
        """

        # Use greedy as baseline
        allocation = self.greedy_allocation(objective)

        # Calculate performance
        performance = self.calculate_space_productivity(allocation)

        return allocation, performance

    def create_visual_planogram(self, allocation):
        """
        Create visual representation of planogram

        Returns ASCII art / simple visualization
        """

        # Group by shelf
        shelves = {}
        for sku, placement in allocation.items():
            shelf = placement['shelf']
            if shelf not in shelves:
                shelves[shelf] = []

            product = self.products[self.products['sku'] == sku].iloc[0]
            width = product['width_inches'] * placement['facings']

            shelves[shelf].append({
                'sku': sku,
                'facings': placement['facings'],
                'width': width
            })

        # Print planogram
        print(f"\nPLANOGRAM - {self.fixture['width_inches']}\" wide x {self.fixture['shelves']} shelves")
        print("=" * 60)

        for shelf in range(self.fixture['shelves'] - 1, -1, -1):  # Top to bottom
            print(f"Shelf {shelf + 1}:", end=" ")

            if shelf in shelves:
                for item in shelves[shelf]:
                    # Display SKU with facings
                    display = f"[{item['sku']}x{item['facings']}]"
                    print(display, end=" ")
            else:
                print("(empty)", end="")

            print()

        print("=" * 60)

# Example
fixture_config = {
    'shelves': 5,
    'width_inches': 48,
    'depth_inches': 12
}

products_data = pd.DataFrame({
    'sku': [f'SKU{i:03d}' for i in range(1, 16)],
    'width_inches': np.random.uniform(3, 8, 15),
    'depth_inches': np.random.uniform(4, 10, 15),
    'height_inches': np.random.uniform(6, 12, 15),
    'weekly_sales': np.random.uniform(10, 100, 15),
    'profit_per_unit': np.random.uniform(2, 8, 15),
    'space_elasticity': np.random.uniform(0.3, 0.6, 15),
    'min_facings': 1,
    'max_facings': np.random.randint(4, 12, 15)
})

optimizer = PlanogramOptimizer(fixture_config, products_data)

# Optimize
allocation, performance = optimizer.optimize_with_constraints(objective='profit')

print(f"Total weekly sales: ${performance['total_sales']:.0f}")
print(f"Total weekly profit: ${performance['total_profit']:.0f}")
print(f"Profit per sqft: ${performance['profit_per_sqft']:.2f}")

# Visualize
optimizer.create_visual_planogram(allocation)

品类管理整合

商品组合与空间优化
class CategorySpaceManager:
    """
    Manage category-level space allocation

    Decide how much space each category/subcategory gets
    """

    def __init__(self, store_data):
        self.store = store_data

    def allocate_space_to_categories(self, categories_data,
                                     total_space_sqft):
        """
        Allocate store space across categories

        Methods:
        - Sales-based: Proportional to sales
        - Profit-based: Proportional to profit
        - Hybrid: Balance sales and profit
        """

        # Calculate each category's contribution
        categories_data['sales_contribution'] = (
            categories_data['annual_sales'] /
            categories_data['annual_sales'].sum()
        )

        categories_data['profit_contribution'] = (
            categories_data['annual_profit'] /
            categories_data['annual_profit'].sum()
        )

        # Hybrid allocation (60% sales, 40% profit)
        categories_data['allocation_weight'] = (
            categories_data['sales_contribution'] * 0.6 +
            categories_data['profit_contribution'] * 0.4
        )

        # Allocate space
        categories_data['allocated_space_sqft'] = (
            categories_data['allocation_weight'] * total_space_sqft
        )

        # Calculate expected productivity
        categories_data['current_sales_per_sqft'] = (
            categories_data['annual_sales'] /
            categories_data['current_space_sqft']
        )

        categories_data['expected_sales_per_sqft'] = (
            categories_data['annual_sales'] /
            categories_data['allocated_space_sqft']
        )

        return categories_data[[
            'category', 'current_space_sqft', 'allocated_space_sqft',
            'annual_sales', 'annual_profit', 'current_sales_per_sqft',
            'expected_sales_per_sqft'
        ]]

    def recommend_space_adjustments(self, categories_data):
        """
        Identify categories that need more or less space

        Based on:
        - Sales per sqft vs. store average
        - Growth trends
        - Profit margins
        """

        store_avg_sales_per_sqft = (
            categories_data['annual_sales'].sum() /
            categories_data['current_space_sqft'].sum()
        )

        recommendations = []

        for idx, category in categories_data.iterrows():
            current_productivity = category['annual_sales'] / category['current_space_sqft']
            ratio_to_avg = current_productivity / store_avg_sales_per_sqft

            if ratio_to_avg > 1.3:
                recommendation = 'Expand space'
                reason = f"High productivity ({ratio_to_avg:.1f}x store average)"
                change_pct = '+15 to +25%'

            elif ratio_to_avg < 0.7:
                recommendation = 'Reduce space'
                reason = f"Low productivity ({ratio_to_avg:.1f}x store average)"
                change_pct = '-15 to -25%'

            else:
                recommendation = 'Maintain space'
                reason = 'Productivity in line with average'
                change_pct = '±5%'

            recommendations.append({
                'category': category['category'],
                'recommendation': recommendation,
                'reason': reason,
                'suggested_change': change_pct,
                'productivity_ratio': ratio_to_avg
            })

        return pd.DataFrame(recommendations)

# Example
categories_data = pd.DataFrame({
    'category': ['Dairy', 'Bakery', 'Produce', 'Meat', 'Frozen', 'Beverages'],
    'current_space_sqft': [800, 600, 1200, 900, 1500, 2000],
    'annual_sales': [1_200_000, 800_000, 1_500_000, 1_800_000, 1_400_000, 2_200_000],
    'annual_profit': [180_000, 240_000, 300_000, 360_000, 210_000, 330_000]
})

manager = CategorySpaceManager({})

# Allocate space
allocation = manager.allocate_space_to_categories(categories_data, total_space_sqft=7000)
print("Category Space Allocation:")
print(allocation)

# Recommendations
recommendations = manager.recommend_space_adjustments(categories_data)
print("\nSpace Adjustment Recommendations:")
print(recommendations)

交叉陈列与邻接

class CrossMerchandisingOptimizer:
    """
    Optimize product adjacencies for cross-selling

    Place complementary products near each other
    """

    def __init__(self, products_data, affinity_matrix):
        """
        Parameters:
        - products_data: Product information
        - affinity_matrix: Cross-purchase patterns
          affinity_matrix[i][j] = likelihood customer buys j given they buy i
        """
        self.products = products_data
        self.affinity = affinity_matrix

    def identify_product_clusters(self, min_affinity=0.3):
        """
        Cluster products with high affinity

        Products that are frequently bought together
        """

        from sklearn.cluster import AgglomerativeClustering

        # Use affinity matrix for clustering
        clustering = AgglomerativeClustering(
            n_clusters=None,
            distance_threshold=1 - min_affinity,
            affinity='precomputed',
            linkage='average'
        )

        # Convert affinity to distance
        distance_matrix = 1 - self.affinity
        clusters = clustering.fit_predict(distance_matrix)

        self.products['cluster'] = clusters

        return self.products[['sku', 'cluster']]

    def score_adjacency(self, sku1, sku2):
        """
        Score how beneficial it is to place two products adjacent

        Higher score = more beneficial
        """

        idx1 = self.products[self.products['sku'] == sku1].index[0]
        idx2 = self.products[self.products['sku'] == sku2].index[0]

        # Bidirectional affinity
        affinity_1_to_2 = self.affinity[idx1, idx2]
        affinity_2_to_1 = self.affinity[idx2, idx1]

        # Combined score
        adjacency_score = (affinity_1_to_2 + affinity_2_to_1) / 2

        return adjacency_score

    def recommend_adjacencies(self, sku, top_n=5):
        """
        Recommend products to place next to a given SKU

        Based on cross-purchase affinity
        """

        idx = self.products[self.products['sku'] == sku].index[0]

        # Get affinities for this product
        affinities = self.affinity[idx, :]

        # Sort and get top N
        top_indices = np.argsort(affinities)[::-1][:top_n + 1]  # +1 to exclude self

        recommendations = []
        for other_idx in top_indices:
            other_sku = self.products.iloc[other_idx]['sku']

            if other_sku == sku:
                continue  # Skip self

            recommendations.append({
                'sku': other_sku,
                'affinity_score': affinities[other_idx],
                'rationale': 'Frequently purchased together'
            })

        return recommendations[:top_n]

# Example
n_products = 20
products_data = pd.DataFrame({
    'sku': [f'SKU{i:03d}' for i in range(1, n_products + 1)]
})

# Generate sample affinity matrix
# (in practice, this comes from transaction data)
np.random.seed(42)
affinity_matrix = np.random.rand(n_products, n_products) * 0.5
np.fill_diagonal(affinity_matrix, 1.0)  # Product always purchased with itself

# Make it symmetric (for simplicity)
affinity_matrix = (affinity_matrix + affinity_matrix.T) / 2

optimizer = CrossMerchandisingOptimizer(products_data, affinity_matrix)

# Identify clusters
clusters = optimizer.identify_product_clusters(min_affinity=0.3)
print("Product Clusters:")
print(clusters.groupby('cluster')['sku'].apply(list))

# Get adjacency recommendations
recommendations = optimizer.recommend_adjacencies('SKU001', top_n=5)
print(f"\nRecommended adjacencies for SKU001:")
for rec in recommendations:
    print(f"  {rec['sku']}: {rec['affinity_score']:.2f}")

工具与库

Python 库

优化:

  • scipy.optimize:非线性优化
  • pulp、pyomo:用于空间分配的线性规划
  • ortools:用于货架陈列图的约束规划

机器学习:

  • scikit-learn:用于商品分组的聚类
  • mlxtend:关联规则挖掘(购物篮分析)

可视化:

  • matplotlib、seaborn:货架陈列图可视化
  • plotly:交互式布局
  • PIL(Pillow):基于图像的货架陈列图
商业软件

货架陈列图软件:

  • JDA/Blue Yonder Intactix:企业级空间规划
  • RELEX Solutions:空间与商品组合优化
  • Galleria by Movista:视觉陈列
  • Apollo by Shelf Logic:AI 驱动的货架陈列图
  • SCORPION by ESL:货架陈列图自动化

品类管理:

  • Nielsen Spaceman:空间规划与优化
  • IRI ProSpace:空间生产力分析
  • Symphony RetailAI:AI 驱动的品类管理

专用工具:

  • SmartDraw:基础货架陈列图创建
  • PlanoHero:云端货架陈列图软件
  • Quant:零售空间智能分析

常见挑战与解决方案

挑战:产品尺寸差异

问题:

  • 产品尺寸各不相同
  • 不规则形状难以整齐摆放
  • 摆放不当造成空间浪费

解决方案:

  • 模块化货架高度
  • 可调节隔板
  • 按尺寸对产品分组
  • 小件商品垂直堆叠
  • 为异形商品定制货架配件
挑战:商品组合频繁变动

问题:

  • 频繁引入新产品
  • 季节性轮换
  • 重新规划商品陈列图耗费大量人力

解决方案:

  • 灵活的商品陈列图区域
  • 为新产品设置“热点”区域
  • 采用微型品类方式(更易更换)
  • 数字化商品陈列图(易于更新)
  • 自动化商品陈列图合规性检查
挑战:门店业态多样

问题:

  • 门店规模各不相同
  • 布局存在差异
  • 一种商品陈列图无法适用于所有门店

解决方案:

  • 对门店进行分类(A/B/C 类业态)
  • 采用模块化商品陈列图方案
  • 设置核心区域与灵活区域
  • 按门店自动生成商品陈列图
  • 在指导原则范围内进行本地化定制
挑战:运营复杂

问题:

  • 补货困难
  • 执行陈列调整耗费人力和时间
  • 难以监控合规情况

解决方案:

  • 评估运营可行性并打分
  • 调整陈列时尽量减少 SKU 移动
  • 分阶段实施
  • 使用照片合规检查应用
  • 简化商品陈列图
挑战:平衡多重目标

问题:

  • 销售额最大化与利润最大化之间的取舍
  • 客户体验与效率之间的取舍
  • 品牌要求与优化之间的取舍
  • 视觉吸引力与空间利用效率之间的取舍

解决方案:

  • 多目标优化
  • 加权评分系统
  • 对品牌和体验要求设置约束
  • 对不同方案进行 A/B 测试
  • 与品类负责人协作

输出格式

货架图优化报告

执行摘要:

  • 品类:饮料(碳酸饮料区)
  • 当前表现:$450/平方英尺/周
  • 优化后表现:$580/平方英尺/周(+29%)
  • 货架数量:12 个货架(每段 4 英尺)
  • SKU 数量:85 个 SKU

当前与优化后的表现对比:

指标当前优化后改善幅度
每平方英尺每周销售额$450$580+29%
每平方英尺每周利润$95$135+42%
空间利用率78%94%+16 个百分点
SKU 数量8572-13 个 SKU
每个 SKU 的平均排面数3.24.5+41%

主要调整 — SKU 级别:

SKU产品当前排面数优化后排面数变化原因
SKU001Coke 12pk48+4销量高,对空间较敏感
SKU015Pepsi 2L64-2空间弹性低,占用空间过多
SKU023LaCroix variety26+4品类持续增长,空间配置不足
SKU045Generic cola30-3(停售)单个排面销量不佳

货架层级规划:

PLANOGRAM - Soft Drinks Section (48" x 5 shelves)
============================================================
Shelf 5: [Coke12pkx8] [Pepsi12pkx6] [Sprite12pkx5]
Shelf 4: [Coke2Lx6] [Pepsi2Lx4] [DrPepper2Lx4] [Sprite2Lx4]
Shelf 3: [LaCroixX6] [BublyX5] [Perrier6pkx4]
Shelf 2: [CokeCansx4] [PepsiCansx4] [Energy6pkx5]
Shelf 1: [2LSparkling1] [2LSparkling2] [Juice4pkx3]
============================================================

各货架的空间产出:

货架当前 $/平方英尺/周优化后 $/平方英尺/周改善幅度
货架 1(视线高度)$620$780+26%
货架 2(视线高度)$580$750+29%
货架 3(胸部高度)$480$610+27%
货架 4(腰部高度)$380$490+29%

交叉陈列机会:

  1. 薯片 + 蘸酱组合:在薯片旁增加莎莎酱(年度销售额增加 $8K)
  2. 意面 + 意面酱:集中陈列,方便顾客选购(年度销售额增加 $12K)
  3. 烘焙用品:将面粉、糖、小苏打集中陈列(年度销售额增加 $6K)

实施计划:

阶段行动涉及 SKU 数量工时时间安排
阶段 1调整排面数(不移动商品)258 小时第 1 周
阶段 2将高销量商品移至视线高度1512 小时第 2 周
阶段 3停售表现不佳的商品134 小时第 3 周
阶段 4最终调整与整理全部6 小时第 4 周

风险与缓解措施:

风险影响概率缓解措施
调整期间缺货中中调整前增加库存,分阶段实施
顾客困惑低高设置清晰标识,向员工说明
执行错误中中拍照检查执行情况,走访门店

待询问的问题

如需了解更多背景:

  1. 需要优化哪个品类/部门?
  2. 该品类有多少个 SKU?
  3. 当前每平方英尺的销售额是多少?
  4. 使用哪些货架?(搁架、岛式货架、端架)
  5. 是否有商品尺寸数据?
  6. 是否有按 SKU 划分的历史销售数据?
  7. 是否有空间弹性数据?(测试不同排面数)
  8. 有哪些限制条件?(品牌要求、最少排面数)
  9. 这是针对一家门店还是整个连锁体系?

相关技能

  • retail-allocation:门店初始库存分配
  • retail-replenishment:补货策略
  • demand-forecasting:按 SKU/门店进行需求预测
  • inventory-optimization:安全库存与服务水平
  • supply-chain-analytics:空间产出指标
  • warehouse-slotting-optimization:适用于仓库的类似概念

评论

支持 Markdown