データビニングの説明

Feb 20 2023
背景と免責事項: 私が働いている会社では、顧客を支払い行動に基づいて 4 つのクラスに分類しています。通常、ビジネス環境で顧客をセグメント化する場合、Recency (顧客が最近購入した頻度)、Frequency (顧客が購入する頻度)、Monetary (顧客が各購入に費やす金額) の 3 つのパラメータが使用されます。
顧客の購入履歴のビニング例

背景と免責事項: 私が働いている会社では、顧客を支払い行動に基づいて 4 つのクラスに分類しています。通常、ビジネス環境で顧客をセグメント化する場合、Recency (顧客が最近購入した頻度)、Frequency (顧客が購入する頻度)、Monetary (顧客が各購入に費やす金額) の 3 つのパラメータが使用されます。ただし、この場合は、ビニングの概念を説明するために周波数のみを使用します。今後、顧客セグメンテーションについてさらに詳しく説明していきます。

しかし、ビニングとは何でしょうか?

データ ビニングは、データを有限範囲の間隔に整理するプロセスです。それらの間隔の範囲は「ビン」と呼ばれます。

「データ バケット化」とも呼ばれるデータ ビニングは、連続データ値を離散間隔または「ビン」にグループ化するために機械学習とデータ マイニングで使用されるデータ前処理手法です。ビニングは、データの変動を平滑化することによるデータ エラーの削減、データ分布に関する洞察の取得、実数値の特徴を離散間隔に変換する (ワンホット エンコーディングとも呼ばれます) など、複数の目的に役立ちます。

ビニング方法には、周波数ビニングと等幅ビニングという 2 つの主なタイプがあります。これらの方法にはバリエーションが存在しますが、それらはすべて基本的にこれら 2 つのアプローチのいずれかに基づいています。このチュートリアルでは、基本的な等長ビニング アルゴリズムの実装に焦点を当てます。等幅ビニングでは、データセット内の値の範囲を、最小値と最大値の間の指定された数の等間隔の間隔に分割します。

const width = (max - min) / number_of_bins
const bins_values = [min, min + width, min + 2 * width, ..., min + number_of_bins * width]

データ ビニングの概念を説明するために、Typescript を使用した実際の例を考えてみましょう。特定の期間内に顧客が行った購入数を記録するデータセットがあるとします。データセットは次の配列で表されます。

const trxFreq = [229, 293, 394, 39, 39, 49, 93, 100, 29, 48, 188]

顧客の購入頻度のデータセットを分析するには、データを「D 顧客」から「A 顧客」までのラベルが付いた 4 つの異なるビンに分割する必要があります。これを達成するために、まずビンを保存するための単純なデータ構造を作成します。

/**
 * Structure of each bin
 */
export interface Bin {
  /**
   * Bucket.
   */
  item: {
    value: number
    label?: string
  }

  /**
   * Values in a bin
   */
  values: number[]
}

/**
 * Array type of bins
 */
export type Bins = Bin[]

関数のコードはtoBin次のようになります。

/**
 * Convert input dataset array of numbers into bins.
 *
 * @param data dataset array
 * @param binSize Number of bins
 * @param labels Optional labels for the bins
 */
export function toBins(data: number[], binSize: number, labels?: string[]) {
   // Implementation
}

  • ビン 1: 購入頻度が最も低い / 幸運な顧客
  • ビン 2: 購入頻度が低い / ほとんど関心のない顧客
  • ビン 3: 中程度の頻度で購入 / 大丈夫な顧客
  • ビン 4: 購入頻度が高い / 優良顧客

const sampleCount = data.length

// Sort data in descending order
const sortedData = data.sort((lhs, rhs) => lhs - rhs)
// Find min and max samples
const minSample = sortedData[0]
const maxSample = sortedData[sampleCount - 1]

// Create equal spaced bins
const binWidth = (maxSample - minSample) / binSize
const bins: Bins = Array(binSize)
  .fill({ values: [] })
  .map((bin, i) => ({
    item: {
      value: minSample + binWidth * i,
      label: labels?.[i]
    },
    ...bin
   }))

ビンの幅は、最大値と最小値の差をとり、それを目的のビンの数で割ることによって計算されます。この幅は、値を適切なビンにグループ化するために使用されます。

最後に、ビンを保持する空の配列を作成します。アルゴリズムの次のステップでデータを入力します。これらの空のビンを作成することで、その後のデータのビンへのグループ化のためのフレームワークを確立します。

// Search for the next chunk from the sorted array to add into the bin values
let minIndex = 0
let maxIndex = sampleCount - 1

for (let i = 0; i < binSize; i++) {
  while (
    minIndex < sampleCount &&
    sortedData[minIndex] < bins[i].item.value
  ) {
    minIndex++
  }
  while (
    maxIndex >= 0 &&
    sortedData[maxIndex] > bins[i].item.value + binWidth
  ) {
    maxIndex--
  }
  for (let j = minIndex; j <= maxIndex; j++) {
    bins[i].values.push(sortedData[j])
  }

  // Slice item from the sorted array into the bin
  bins[i].values = sortedData.slice(minIndex, maxIndex + 1)

  // Get the maxIndex ready for the next iteration.
  maxIndex = sampleCount - 1
}

このステップでは、適切なインデックスを見つけるためにデータセットを昇順で並べ替える必要があることに注意することが重要です。事前にデータセットを並べ替えないと、ビニングが正しく行われなくなります。

アルゴリズムの動作を確認するために、完全に機能する例を以下に示します。


export interface Bin {
  
  item: {
    value: number
    label?: string
  }

  values: number[]
}

export type Bins = Bin[]

export function toBins(data: number[], binSize: number, labels?: string[]) {
  const sampleCount = data.length

  if (binSize >= sampleCount) {
    throw new RangeError(
      `Bin size ${binSize} should be less than sample size: ${sampleCount}`
    )
  }

  // Sort data in descending order
  const sortedData = data.sort((lhs, rhs) => lhs - rhs)
  // Find min and max samples
  const minSample = sortedData[0]
  const maxSample = sortedData[sampleCount - 1]

  // Create equal spaced bins
  const binWidth = (maxSample - minSample) / binSize
  const bins: Bins = Array(binSize)
    .fill({ values: [] })
    .map((bin, i) => ({
      item: {
        value: minSample + binWidth * i,
        label: labels?.[i]
      },
      ...bin
    }))

  let minIndex = 0
  let maxIndex = sampleCount - 1

  for (let i = 0; i < binSize; i++) {
    while (
      minIndex < sampleCount &&
      sortedData[minIndex] < bins[i].item.value
    ) {
      minIndex++
    }
    while (
      maxIndex >= 0 &&
      sortedData[maxIndex] > bins[i].item.value + binWidth
    ) {
      maxIndex--
    }

    // Slice a bucket
    bins[i].values = sortedData.slice(minIndex, maxIndex + 1)

    // Get the maxIndex ready for the next iteration.
    maxIndex = sampleCount - 1
  }

  return bins
}

関数が実装されると、クライアント コードによって関数を呼び出して、グループ化されたデータを表すオブジェクトtoBinの配列を生成できます。Binこれを行うには、クライアント コードは入力データセット、必要な数のビン、およびオプションでラベルの配列を関数に渡す必要がありますtoBin。

例として、サンプル トランザクションを提供し、このデータセットに対して関数を呼び出して、グループ化されたデータを表すオブジェクトtoBinの配列を生成できます。Bin

const trxFreq = [229, 293, 394, 39, 39, 49, 93, 100, 29, 48, 188]
const labels = ['D-Customers', 'C-Customers', 'B-Customers', 'A-Customers']
const bins = toBins(trxFreq, labels.length, labels)

console.log(bins)
// Prints
// [
//    {
//      "item": { "value": 29, "label": "D-Customers" },
//      "item": [29, 39, 39, 48, 49, 93, 100 ]
//    },
//    {
//      "iem": { "value": 120.25, "label": "C-Customers" },
//      "values": [ 188 ]
//    },
//    {
//      "item": { "value": 211.5, "label": "B-Customers" },
//      "values": [ 229, 293 ]
//    },
//    {
//      "item": { "value": 302.75, "label": "A-Customers" },
//      "values": [ 394 ]
//    }
// ]

入力データセットを並べ替える必要があるため、実装したアルゴリズムの時間計算量はO(n log n)であることに注意してください。さらに、アルゴリズムの空間複雑度はO(k)です。ここで、k はビンの数です。

締めくくりのメモと要点:

要約すると、このチュートリアルでは、単純な等幅ビニング アルゴリズムについて説明し、それを使用して購入頻度に基づいて顧客をグループ化する方法の例を示しました。ビニングは、エラーを削減したり、データセットに関する洞察を得たり、実際の値を離散間隔に変換したりするために使用できる一般的なデータ前処理手法です。ただし、この手法には、特定のデータセットに適切なビン サイズを決定するなど、いくつかの課題があります。

特定の使用例に応じて、k 最近傍 (kNN) アルゴリズムなどの代替手法を使用して、データセットを定義されたグループに編成することができます。使用する最も適切な前処理手法を決定するには、プロジェクトの特定の要件を慎重に検討することが重要です。

ビニングやその他のデータ前処理手法について詳しく知りたい場合は、私の記事を参照して他の記事、チュートリアル、リソースを参照してください。新しい記事を公開したときにメールを受け取るようにオプトインします。機械学習とデータ サイエンスの最新トレンドに関するさらなる洞察と実践的なヒントをお楽しみに!

最後に、このチュートリアルの github リポジトリは次のとおりです。ありがとうございます。