AMQL Docs
User-defined functions

ABC classification

Classify items into A, B and C by their share of a total, such as products by sales, with reusable AQL functions.

ABC classification sorts items by a measure, biggest first, and groups them by how much of the total they cover together. It's the 80/20 idea: a few items make up most of the volume.

  • A: the top items that together cover the first 70% of the total.
  • B: the next items, up to 90%.
  • C: the rest, the long tail.

For example, it shows which products bring in most of your sales, so you know where to focus stock and promotion.

How it works

A product's class doesn't depend on its own sales alone. It depends on how much the products bigger than it already cover.

ProductSalesRunning total, biggest firstShare of totalClass
P1505050%A
P2207070%A
P3158585%B
P4109595%C
P55100100%C

The functions compute this in two parts:

  • Running total, biggest first: window_sum(m, order: m | desc()) adds this item and every item bigger than it.
  • Total: m | of_all(products, keep_filters: true) is the total over all items, with your filters kept. If you filter to one year, the classes are computed for that year.

Step 1: Add the function library

Add this AqlFunctionModule to your project.

library/aql-functions/abc.aml
AqlFunctionModule Abc {
  // Cumulative share of the total, items sorted by pMeasure descending.
  function CumulatedPct {
    param pMeasure {
      type: 'metric'
    }
    param pItemModel {
      type: 'model'
    }
    return_type: 'number'
    definition: @aql
      const pct = safe_divide(
        window_sum(pMeasure, order: pMeasure | desc()),
        pMeasure | of_all(pItemModel, keep_filters: true)
      );
      case(when: pct > 1, then: 1, else: pct)
    ;;
  }

  // 'A' up to pA, 'B' up to pB, 'C' after. null bounds use 0.7 and 0.9.
  function Class {
    param pMeasure {
      type: 'metric'
    }
    param pItemModel {
      type: 'model'
    }
    param pA {
      type: 'number'
    }
    param pB {
      type: 'number'
    }
    return_type: 'text'
    definition: @aql
      case(
        when: CumulatedPct(pMeasure, pItemModel) <= coalesce(pA, 0.7), then: 'A',
        when: CumulatedPct(pMeasure, pItemModel) <= coalesce(pB, 0.9), then: 'B',
        else: 'C'
      )
    ;;
  }
}

Step 2: Import it and add the metrics

Import the library into your dataset, then define the measure to classify by and the ABC metrics.

Dataset ecommerce {
  models: [orders, order_items, products]

  function_module Abc: Abc

  metric sales {
    label: 'Sales'
    type: 'number'
    definition: @aql order_items | sum(order_items.quantity * products.price) ;;
  }

  metric abc_pct {
    label: 'ABC Cumulative %'
    type: 'number'
    definition: @aql Abc.CumulatedPct(sales, products) ;;
  }

  metric abc_class {
    label: 'ABC Class'
    type: 'text'
    definition: @aql Abc.Class(sales, products, 0.7, 0.9) ;;
  }
}

Parameters

Abc.Class

ParameterTypeWhat it is
pMeasuremetricThe measure to classify by, such as sales. It must add up across items, like a sum or a count.
pItemModelmodelThe model of the items you classify, such as products.
pAnumberUpper bound of class A, from 0 to 1. null uses 0.7.
pBnumberUpper bound of class B, from 0 to 1. null uses 0.9.

Abc.CumulatedPct

Takes pMeasure and pItemModel as above. Returns the item's cumulative share of the total, from 0 to 1.

Use it in a table

Put the item in the table and sort by the measure, biggest first.

explore {
  dimensions {
    product_name: products.name
  }
  measures {
    sales: sales,
    abc_pct: abc_pct,
    abc_class: abc_class
  }
  sorts {
    sales desc
  }
}

Limitations

  • The item must be in the table, for example products.name. The class is computed from the rows of the result.
  • Classes are fixed to A, B and C. Change the bounds with pA and pB.
  • The measure must add up across items. An average or a distinct count doesn't give a meaningful cumulative share.

On this page