Daten-Binning erklärt

Feb 20 2023
Hintergrund und Haftungsausschluss: In dem Unternehmen, in dem ich arbeite, haben wir unsere Kunden basierend auf ihrem Zahlungsverhalten in vier Klassen eingeteilt. Typischerweise werden bei der Segmentierung von Kunden in einem Geschäftsumfeld drei Parameter verwendet: Aktualität (wie kürzlich ein Kunde einen Kauf getätigt hat), Häufigkeit (wie oft ein Kunde Einkäufe tätigt) und Monetär (wie viel ein Kunde für jeden Kauf ausgibt).
Binning-Beispiel zur Kaufhistorie eines Kunden

Hintergrund und Haftungsausschluss: In dem Unternehmen, in dem ich arbeite, haben wir unsere Kunden basierend auf ihrem Zahlungsverhalten in vier Klassen eingeteilt. Typischerweise werden bei der Segmentierung von Kunden in einem Geschäftsumfeld drei Parameter verwendet: Aktualität (wie kürzlich ein Kunde einen Kauf getätigt hat), Häufigkeit (wie oft ein Kunde Einkäufe tätigt) und Monetär (wie viel ein Kunde für jeden Kauf ausgibt). In diesem Fall verwende ich jedoch nur die Frequenz, um das Konzept des Binnings zu erläutern. In Zukunft werde ich die Kundensegmentierung ausführlicher erläutern.

Aber was ist Binning?

Beim Daten-Binning werden Ihre Daten in einem endlichen Bereich von Intervallen organisiert. Diese Intervallbereiche werden „Bins“ genannt.

Data Binning, auch „ Data Bucketing “ genannt , ist eine Datenvorverarbeitungstechnik, die beim maschinellen Lernen und Data Mining verwendet wird, um kontinuierliche Datenwerte in diskrete Intervalle oder „Bins“ zu gruppieren. Binning kann mehreren Zwecken dienen, darunter der Reduzierung von Datenfehlern durch Glättung von Datenschwankungen, der Gewinnung von Erkenntnissen über die Datenverteilung und der Konvertierung reellwertiger Merkmale in diskrete Intervalle, auch bekannt als One-Hot-Codierung.

Es gibt zwei Haupttypen von Binning-Methoden: Frequenz-Binning und Gleichbreiten-Binning. Es gibt Variationen dieser Methoden, die jedoch alle im Wesentlichen auf einem dieser beiden Ansätze basieren. In diesem Tutorial liegt der Schwerpunkt auf der Implementierung eines grundlegenden Binning-Algorithmus gleicher Länge. Beim Binning mit gleicher Breite wird der Wertebereich in einem Datensatz in eine bestimmte Anzahl gleichmäßig beabstandeter Intervalle zwischen den Minimal- und Maximalwerten unterteilt.

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

Um das Konzept des Data Binning zu veranschaulichen, betrachten wir ein praktisches Beispiel mit Typescript. Angenommen, wir verfügen über einen Datensatz, der die Anzahl der Käufe von Kunden innerhalb eines bestimmten Zeitraums aufzeichnet. Der Datensatz wird im folgenden Array dargestellt:

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

Um den Datensatz zur Kaufhäufigkeit von Kunden zu analysieren, ist es notwendig, die Daten in vier verschiedene Klassen mit der Bezeichnung „D-Kunden“ bis „A-Kunden“ zu unterteilen. Um dies zu erreichen, erstellen wir zunächst eine einfache Datenstruktur zum Speichern unserer Bins.

/**
 * 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[]

Der Code für die toBinFunktion könnte etwa so aussehen:

/**
 * 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
}

  • Bin 1: Käufe mit der niedrigsten Häufigkeit / Glückliche Kunden
  • Bin 2: Geringe Kauffrequenz / Wenig interessierte Kunden
  • Bin 3: Moderate Häufigkeitskäufe / Okay-Kunden
  • Bin 4: Häufige Käufe / Gute Kunden

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
   }))

Die Bin-Breite wird berechnet, indem die Differenz zwischen den Maximal- und Minimalwerten durch die Anzahl der gewünschten Bins dividiert wird. Diese Breite wird verwendet, um Werte in die entsprechenden Abschnitte zu gruppieren.

Schließlich erstellen wir ein leeres Array zur Aufnahme unserer Bins, das wir im nächsten Schritt des Algorithmus mit Daten füllen. Durch die Erstellung dieser leeren Bins schaffen wir den Rahmen für die anschließende Gruppierung von Daten in Bins.

// 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
}

Es ist wichtig zu beachten, dass dieser Schritt erfordert, dass der Datensatz in aufsteigender Reihenfolge sortiert wird, um die entsprechenden Indizes zu finden. Wenn der Datensatz nicht vorher sortiert würde, würde dies zu einer falschen Klasseneinteilung führen.

Um den Algorithmus in Aktion zu sehen, finden Sie unten ein voll funktionsfähiges Beispiel.


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
}

Sobald die toBinFunktion implementiert wurde, kann sie vom Clientcode aufgerufen werden, um ein Array von BinObjekten zu erzeugen, die die gruppierten Daten darstellen. Dazu muss der Clientcode den Eingabedatensatz, die gewünschte Anzahl von Bins und optional ein Array von Beschriftungen an die toBinFunktion übergeben.

Als Beispiel können wir Beispieltransaktionen bereitstellen und die toBinFunktion für diesen Datensatz aufrufen, um ein Array von BinObjekten zu erzeugen, die die gruppierten Daten darstellen.

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 ]
//    }
// ]

Es ist erwähnenswert, dass der von uns implementierte Algorithmus aufgrund der Notwendigkeit, den Eingabedatensatz zu sortieren, eine zeitliche Komplexität von O(n log n) aufweist. Darüber hinaus beträgt die räumliche Komplexität des Algorithmus O(k) , wobei k die Anzahl der Bins ist.

Schlussbemerkungen und Erkenntnisse:

Zusammenfassend haben wir in diesem Tutorial einen einfachen Binning-Algorithmus mit gleicher Breite behandelt und ein Beispiel dafür gegeben, wie er verwendet werden kann, um Kunden basierend auf ihrer Kaufhäufigkeit zu gruppieren. Binning ist eine gängige Datenvorverarbeitungstechnik, die eingesetzt werden kann, um Fehler zu reduzieren, Einblicke in einen Datensatz zu gewinnen oder reale Werte in diskrete Intervalle umzuwandeln. Mit dieser Technik sind jedoch einige Herausforderungen verbunden, beispielsweise die Entscheidung über die geeignete Bin-Größe für einen bestimmten Datensatz.

Abhängig vom spezifischen Anwendungsfall können alternative Techniken eingesetzt werden, um einen Datensatz in definierte Gruppen zu organisieren, beispielsweise der k-Nearest Neighbors (kNN)-Algorithmus. Es ist wichtig, die spezifischen Anforderungen eines Projekts sorgfältig abzuwägen, um die am besten geeignete Vorverarbeitungstechnik zu bestimmen.

Wenn Sie mehr über Binning oder andere Datenvorverarbeitungstechniken erfahren möchten, lesen Sie unbedingt meine Artikel, um weitere Artikel, Tutorials und Ressourcen zu erhalten. Melden Sie sich an, um E-Mails zu erhalten, wenn ich neue Artikel veröffentliche. Bleiben Sie dran für weitere Einblicke und praktische Tipps zu den neuesten Trends im maschinellen Lernen und in der Datenwissenschaft!

Zum Schluss ist hier das Github-Repo für dieses Tutorial. Vielen Dank!