Volume 2: Organizational Intelligence Platforms

Pattern 4: Interaction Outcome Classification

Intent

Establish a consistent taxonomy for classifying interaction outcomes (opened, clicked, completed, ignored, bounced, etc.) that enables pattern recognition, effectiveness measurement, and predictive modeling across all communication types.

Also Known As

  • Outcome Taxonomy
  • Result Classification
  • Interaction Status Model
  • Response Classification
  • Engagement Scoring

Problem

Raw event logs capture what happened, but not how successful it was.

When Sarah sends 23 payment reminders, the event log shows: - email_sent (23 times) - email_opened (12 times) - email_clicked (4 times) - payment_received (18 times)

But the system doesn't understand success: - Which outcomes indicate engagement? (opened? clicked?) - Which outcomes indicate success? (payment received) - Which outcomes indicate failure? (bounced? ignored?) - How do we compare across channels? (email opened vs SMS delivered vs phone answered)

Without outcome classification: - Can't measure effectiveness ("Did this work?") - Can't compare strategies ("Which approach is better?") - Can't predict success ("Will this family respond?") - Can't optimize automatically ("What should we try instead?")

The challenge: Different interaction types have different possible outcomes: - Email: sent → delivered → opened → clicked → responded - SMS: sent → delivered → (replied) - Phone: attempted → voicemail → callback → conversation - Portal: viewed → engaged → action_taken - Payment: invoiced → viewed → paid

We need a unified way to understand "How did this interaction go?"

Context

When this pattern applies:

  • Multiple interaction types exist (not just one)
  • Need to measure and compare effectiveness
  • Building predictive models (outcomes are the training data)
  • Optimizing processes based on what works
  • Reporting on engagement and success rates

When this pattern may not be needed:

  • Only one interaction type with obvious binary outcome (worked/didn't work)
  • No need to optimize or compare approaches
  • Outcomes are always immediately clear (no ambiguity)
  • Very small scale where manual assessment suffices

Forces

Competing concerns:

1. Granularity vs Simplicity - Want detailed outcomes (opened, opened_on_mobile, opened_twice) - But too many categories make analysis complex - Balance: Core set of outcomes, details in metadata

2. Universal vs Domain-Specific - Want outcomes that work across all domains - But each domain has unique success criteria - Balance: Common outcomes + domain extensions

3. Binary vs Graduated - Simple binary (success/failure) is easy to analyze - But graduated scale (no_response → opened → clicked → converted) captures more - Balance: Both - success flag + outcome details

4. Objective vs Subjective - System-measured outcomes are objective (email bounced) - Human-assessed outcomes are subjective (conversation went well) - Balance: Flag which outcomes are system vs human assessed

5. Immediate vs Delayed - Some outcomes are immediate (email bounced) - Others delayed (payment made 2 weeks after reminder) - Balance: Track both immediate outcome and ultimate outcome

Solution

Define a hierarchical outcome taxonomy with:

Level 1: Outcome Category (universal) - success - Interaction achieved its purpose - partial_success - Engagement but not completion - neutral - Delivered but no clear signal - failure - Did not achieve purpose - error - Technical failure

Level 2: Outcome Type (interaction-specific) - Email: opened, clicked, replied, bounced, spam_reported - SMS: delivered, failed, replied, opted_out - Phone: answered, voicemail, no_answer, busy, wrong_number - Portal: action_taken, viewed_only, ignored - Payment: paid_on_time, paid_late, unpaid, disputed

Level 3: Outcome Metadata (context-specific) - Time to outcome (opened email in 2.3 hours) - Multiple occurrences (opened 3 times) - Device/context (opened on mobile) - Quality measures (3-minute phone conversation vs 30-second)

Map outcomes to success for each interaction purpose:

For payment reminders: - paid_on_timesuccess - paid_latepartial_success - email_openedneutral (engaged but didn't pay yet) - email_ignoredfailure - email_bouncederror

Structure

Extended Event Log Schema

-- Add outcome classification to interaction log
ALTER TABLE interaction_log
ADD outcome_category VARCHAR(50) NULL,  -- success, partial_success, neutral, failure, error
ADD outcome_type VARCHAR(100) NULL,     -- opened, clicked, paid, etc.
ADD outcome_confidence DECIMAL(3,2) DEFAULT 1.0,  -- 0.0 to 1.0, for uncertain outcomes
ADD is_ultimate_outcome BIT DEFAULT 0,  -- FALSE = intermediate, TRUE = final
ADD outcome_timestamp DATETIME2 NULL,   -- When outcome determined (may differ from interaction_timestamp)
ADD outcome_assessed_by VARCHAR(50) DEFAULT 'system';  -- 'system' or 'human'

-- Indexes
CREATE INDEX IX_outcome_category ON interaction_log(outcome_category);
CREATE INDEX IX_outcome_type ON interaction_log(outcome_type);
CREATE INDEX IX_ultimate_outcome ON interaction_log(is_ultimate_outcome);

Outcome Configuration Table

-- Define what outcomes mean for each interaction type and purpose
CREATE TABLE outcome_definitions (
  definition_id INT PRIMARY KEY IDENTITY(1,1),

  -- What interaction is this for?
  interaction_type VARCHAR(100) NOT NULL,
  interaction_purpose VARCHAR(100) NULL,  -- 'payment_reminder', 'enrollment_followup'

  -- Outcome mapping
  outcome_type VARCHAR(100) NOT NULL,
  outcome_category VARCHAR(50) NOT NULL,

  -- Weighting for scoring
  success_weight DECIMAL(4,2) DEFAULT 1.0,  -- How much does this outcome count as success?

  -- Learning
  leads_to_ultimate_outcome BIT DEFAULT 0,  -- Does this typically lead to final success?

  -- Metadata
  description NVARCHAR(500),
  created_date DATETIME2 DEFAULT GETDATE(),

  CONSTRAINT UQ_outcome_def UNIQUE (interaction_type, interaction_purpose, outcome_type)
);

-- Example data
INSERT INTO outcome_definitions (interaction_type, interaction_purpose, outcome_type, outcome_category, success_weight, leads_to_ultimate_outcome) VALUES
('email_sent', 'payment_reminder', 'opened', 'partial_success', 0.3, 1),
('email_sent', 'payment_reminder', 'clicked', 'partial_success', 0.5, 1),
('email_sent', 'payment_reminder', 'ignored', 'failure', 0.0, 0),
('email_sent', 'payment_reminder', 'bounced', 'error', 0.0, 0),
('payment_received', 'payment_reminder', 'paid_on_time', 'success', 1.0, 1),
('payment_received', 'payment_reminder', 'paid_late', 'partial_success', 0.7, 1),
('sms_sent', 'payment_reminder', 'delivered', 'neutral', 0.2, 1),
('sms_sent', 'payment_reminder', 'replied', 'partial_success', 0.6, 1),
('phone_call_made', 'payment_reminder', 'answered', 'partial_success', 0.8, 1),
('phone_call_made', 'payment_reminder', 'voicemail', 'neutral', 0.2, 1);

Implementation

Outcome Classification Function

class OutcomeClassifier {
  constructor(db) {
    this.db = db;
    this.definitions = new Map(); // Cache outcome definitions
  }

  async loadDefinitions() {
    const defs = await this.db.query(`
      SELECT * FROM outcome_definitions
    `);

    defs.forEach(def => {
      const key = `${def.interaction_type}:${def.interaction_purpose}:${def.outcome_type}`;
      this.definitions.set(key, def);
    });
  }

  async classifyOutcome(interaction) {
    const {
      interaction_type,
      interaction_purpose,
      outcome_type,
      family_id
    } = interaction;

    // Look up outcome definition
    const key = `${interaction_type}:${interaction_purpose}:${outcome_type}`;
    const definition = this.definitions.get(key);

    if (!definition) {
      console.warn(`No definition for outcome: ${key}`);
      return {
        outcome_category: 'neutral',
        success_weight: 0.5,
        confidence: 0.5
      };
    }

    return {
      outcome_category: definition.outcome_category,
      outcome_type: outcome_type,
      success_weight: definition.success_weight,
      leads_to_ultimate: definition.leads_to_ultimate_outcome,
      confidence: 1.0
    };
  }

  async updateInteractionOutcome(interactionId, outcomeData) {
    const classification = await this.classifyOutcome(outcomeData);

    await this.db.query(`
      UPDATE interaction_log
      SET 
        outcome_category = ?,
        outcome_type = ?,
        outcome_confidence = ?,
        outcome_timestamp = NOW(),
        outcome_assessed_by = 'system'
      WHERE interaction_id = ?
    `, [
      classification.outcome_category,
      outcomeData.outcome_type,
      classification.confidence,
      interactionId
    ]);
  }
}

Ultimate Outcome Tracking

// Link intermediate outcomes to ultimate outcomes
async function trackUltimateOutcome(initialInteractionId, ultimateOutcome) {
  // Mark the ultimate outcome
  await db.query(`
    UPDATE interaction_log
    SET 
      is_ultimate_outcome = 1,
      outcome_category = ?,
      outcome_type = ?
    WHERE interaction_id = ?
  `, [ultimateOutcome.category, ultimateOutcome.type, ultimateOutcome.interactionId]);

  // Find all related intermediate outcomes
  const relatedInteractions = await db.query(`
    SELECT interaction_id, outcome_type
    FROM interaction_log
    WHERE family_id = (
      SELECT family_id FROM interaction_log WHERE interaction_id = ?
    )
    AND interaction_timestamp BETWEEN 
      DATE_SUB((SELECT interaction_timestamp FROM interaction_log WHERE interaction_id = ?), INTERVAL 30 DAY)
      AND (SELECT interaction_timestamp FROM interaction_log WHERE interaction_id = ?)
    AND interaction_id != ?
  `, [initialInteractionId, initialInteractionId, ultimateOutcome.interactionId, ultimateOutcome.interactionId]);

  // Mark which intermediate outcomes led to ultimate outcome
  for (const interaction of relatedInteractions) {
    await db.query(`
      UPDATE interaction_log
      SET metadata = JSON_SET(
        COALESCE(metadata, '{}'),
        '$.led_to_ultimate_outcome_id',
        ?
      )
      WHERE interaction_id = ?
    `, [ultimateOutcome.interactionId, interaction.interaction_id]);
  }
}

Example: Payment Reminder Campaign

async function processPaymentReminderCampaign(familyId, dueDate) {
  const campaign = {
    family_id: familyId,
    purpose: 'payment_reminder',
    interactions: []
  };

  // Step 1: Send email reminder (7 days before)
  const emailResult = await sendEmail(familyId, 'payment_reminder', {
    due_date: dueDate,
    amount: 450
  });

  campaign.interactions.push({
    interaction_id: emailResult.interactionId,
    type: 'email_sent',
    timestamp: new Date()
  });

  // Wait for email outcome (system tracks via webhook)
  // After 24 hours, check if opened
  setTimeout(async () => {
    const emailOutcome = await db.query(`
      SELECT outcome_type FROM interaction_log
      WHERE interaction_id = ?
    `, [emailResult.interactionId]);

    if (emailOutcome[0]?.outcome_type === 'ignored') {
      // Email ignored, escalate to SMS (3 days before)
      const smsResult = await sendSMS(familyId, 
        `Payment of $450 due on ${dueDate}. Please remit via portal.`
      );

      campaign.interactions.push({
        interaction_id: smsResult.interactionId,
        type: 'sms_sent',
        timestamp: new Date()
      });
    }
  }, 24 * 60 * 60 * 1000);

  // When payment received, mark ultimate outcome
  // (This happens in payment processing handler)
}

// Payment received handler
async function onPaymentReceived(payment) {
  const { family_id, payment_date, due_date } = payment;

  // Log payment interaction
  const paymentInteraction = await logger.log({
    family_id: family_id,
    interaction_type: 'payment_received',
    interaction_category: 'financial',
    channel: payment.method,
    outcome_type: payment_date <= due_date ? 'paid_on_time' : 'paid_late',
    outcome_category: payment_date <= due_date ? 'success' : 'partial_success',
    is_ultimate_outcome: 1,
    metadata: {
      amount: payment.amount,
      days_late: Math.max(0, moment(payment_date).diff(moment(due_date), 'days'))
    }
  });

  // Link to previous reminder interactions
  await trackUltimateOutcome(null, {
    interactionId: paymentInteraction.interaction_id,
    category: payment_date <= due_date ? 'success' : 'partial_success',
    type: payment_date <= due_date ? 'paid_on_time' : 'paid_late'
  });
}

Effectiveness Analysis

-- What's the success rate of email payment reminders?
SELECT 
  COUNT(*) as total_reminders,
  SUM(CASE WHEN outcome_category = 'success' THEN 1 ELSE 0 END) as successful,
  SUM(CASE WHEN outcome_category = 'partial_success' THEN 1 ELSE 0 END) as partial,
  SUM(CASE WHEN outcome_category = 'failure' THEN 1 ELSE 0 END) as failed,
  SUM(CASE WHEN outcome_category = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
FROM interaction_log il
WHERE interaction_type = 'email_sent'
  AND JSON_VALUE(metadata, '$.purpose') = 'payment_reminder'
  AND interaction_timestamp >= DATEADD(month, -6, GETDATE());
-- Which intermediate outcomes lead to ultimate success?
-- (Helps understand what behaviors predict payment)
SELECT 
  intermediate.outcome_type,
  COUNT(*) as occurrence_count,
  SUM(CASE WHEN ultimate.outcome_category = 'success' THEN 1 ELSE 0 END) as led_to_success,
  SUM(CASE WHEN ultimate.outcome_category = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_prediction_rate
FROM interaction_log intermediate
JOIN interaction_log ultimate ON 
  JSON_VALUE(intermediate.metadata, '$.led_to_ultimate_outcome_id') = CAST(ultimate.interaction_id AS VARCHAR)
WHERE ultimate.is_ultimate_outcome = 1
  AND ultimate.interaction_type = 'payment_received'
GROUP BY intermediate.outcome_type
ORDER BY success_prediction_rate DESC;

Results might show: - email_clicked → 87% payment rate (strong predictor) - email_opened → 52% payment rate (moderate predictor) - email_ignored → 12% payment rate (weak predictor)

Outcome-Based Optimization

async function optimizeReminderStrategy(familyId) {
  // Analyze historical effectiveness for this family
  const history = await db.query(`
    SELECT 
      interaction_type,
      channel,
      outcome_type,
      outcome_category,
      COUNT(*) as attempt_count,
      SUM(CASE WHEN outcome_category IN ('success', 'partial_success') THEN 1 ELSE 0 END) as success_count
    FROM interaction_log
    WHERE family_id = ?
      AND JSON_VALUE(metadata, '$.purpose') = 'payment_reminder'
      AND interaction_timestamp >= DATE_SUB(NOW(), INTERVAL 1 YEAR)
    GROUP BY interaction_type, channel, outcome_type, outcome_category
  `, [familyId]);

  // Calculate success rates by channel
  const channelEffectiveness = {};
  history.forEach(row => {
    if (!channelEffectiveness[row.channel]) {
      channelEffectiveness[row.channel] = { attempts: 0, successes: 0 };
    }
    channelEffectiveness[row.channel].attempts += row.attempt_count;
    channelEffectiveness[row.channel].successes += row.success_count;
  });

  // Select best channel
  let bestChannel = 'email'; // default
  let bestRate = 0;

  Object.entries(channelEffectiveness).forEach(([channel, stats]) => {
    const rate = stats.successes / stats.attempts;
    if (rate > bestRate && stats.attempts >= 3) {  // Need at least 3 attempts for confidence
      bestChannel = channel;
      bestRate = rate;
    }
  });

  return {
    recommendedChannel: bestChannel,
    successRate: bestRate,
    confidence: Math.min(channelEffectiveness[bestChannel]?.attempts / 10, 1.0)
  };
}

Variations

By Interaction Purpose

Payment Reminders: - Success: paid_on_time - Partial: paid_late, payment_plan_requested - Failure: unpaid_after_30_days

Enrollment Follow-ups: - Success: enrolled - Partial: trial_scheduled, application_started - Failure: declined, no_response_30_days

At-Risk Interventions: - Success: engagement_improved, remained_enrolled - Partial: acknowledged_concern, agreed_to_mentor - Failure: withdrew, no_response

By Assessment Method

System-Assessed (Objective): - Email opened (tracking pixel fired) - Payment received (database record created) - Portal login (authentication logged)

Human-Assessed (Subjective): - Phone conversation quality ("went well", "concerns raised") - In-person interaction ("receptive", "defensive") - Overall engagement assessment ("improving", "declining")

Hybrid: - Email opened (system) + clicked important link (system) + replied thoughtfully (human)

By Time Horizon

Immediate Outcomes (within minutes/hours): - Email opened - SMS delivered - Portal page viewed

Short-term Outcomes (within days): - Email clicked - SMS replied - Phone call returned - Form submitted

Long-term Outcomes (within weeks/months): - Payment made - Enrollment completed - Withdrawal prevented - Behavior changed

Consequences

Benefits

1. Measure what works "Email payment reminders: 67% open rate, 23% click rate, but only 18% payment rate. SMS reminders: 91% delivery, 73% payment rate. Switch to SMS."

2. Learn from history "Email clicks predict 87% payment probability. Email opens only predict 52%. Focus on optimizing for clicks, not just opens."

3. Optimize automatically System learns which channels and approaches work for each family and adapts.

4. A/B testing infrastructure With clear outcome classification, can test: Template A vs Template B, Timing X vs Timing Y, Channel 1 vs Channel 2.

5. Predictive modeling training data Outcomes are the "labels" for supervised learning. "Given these interaction patterns, predict outcome."

6. Attribution analysis "Which touchpoint deserves credit for enrollment? Email opened, then trial attended, then phone call, then enrolled. Phone call gets 0.6 credit, email gets 0.3, trial gets 0.1."

Costs

1. Configuration overhead Must define outcomes for each interaction type × purpose combination. Initial setup takes thought.

2. Complexity in ambiguous cases "They opened the email but didn't pay yet. Is that neutral or partial success?" Requires judgment.

3. Delayed outcomes complicate analysis Email sent Monday, opened Tuesday, payment made Friday. Which interaction "caused" payment?

4. Subjectivity in human-assessed outcomes "Conversation went well" is opinion, not fact. Different assessors may disagree.

5. Outcome definitions evolve What counts as "success" may change over time. Historical data may need reclassification.

Sample Code

Complete outcome tracking system:

class OutcomeTracker {
  constructor(db, classifier) {
    this.db = db;
    this.classifier = classifier;
  }

  // Track immediate outcome (e.g., email opened)
  async trackImmediateOutcome(interactionId, outcomeType) {
    const interaction = await this.db.getInteraction(interactionId);

    const classification = await this.classifier.classifyOutcome({
      interaction_type: interaction.interaction_type,
      interaction_purpose: JSON.parse(interaction.metadata).purpose,
      outcome_type: outcomeType
    });

    await this.db.query(`
      UPDATE interaction_log
      SET 
        outcome_type = ?,
        outcome_category = ?,
        outcome_confidence = ?,
        outcome_timestamp = NOW()
      WHERE interaction_id = ?
    `, [
      outcomeType,
      classification.outcome_category,
      classification.confidence,
      interactionId
    ]);

    // Check if this should trigger next action
    if (classification.outcome_category === 'failure') {
      await this.triggerEscalation(interaction);
    }
  }

  // Track ultimate outcome (e.g., payment received)
  async trackUltimateOutcome(familyId, purpose, outcomeType) {
    // Find all related interactions in lookback window
    const relatedInteractions = await this.db.query(`
      SELECT interaction_id, outcome_type, interaction_timestamp
      FROM interaction_log
      WHERE family_id = ?
        AND JSON_VALUE(metadata, '$.purpose') = ?
        AND interaction_timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
      ORDER BY interaction_timestamp DESC
    `, [familyId, purpose]);

    // Create ultimate outcome interaction
    const ultimateInteraction = await logger.log({
      family_id: familyId,
      interaction_type: 'outcome_achieved',
      interaction_category: purpose,
      channel: 'system',
      outcome_type: outcomeType,
      outcome_category: this.categorizeUltimateOutcome(outcomeType),
      is_ultimate_outcome: 1,
      metadata: {
        purpose: purpose,
        related_interactions: relatedInteractions.map(i => i.interaction_id)
      }
    });

    // Link intermediate interactions
    for (const interaction of relatedInteractions) {
      await this.db.query(`
        UPDATE interaction_log
        SET metadata = JSON_SET(
          COALESCE(metadata, '{}'),
          '$.led_to_ultimate_outcome_id',
          ?
        )
        WHERE interaction_id = ?
      `, [ultimateInteraction.interaction_id, interaction.interaction_id]);
    }

    return ultimateInteraction;
  }

  categorizeUltimateOutcome(outcomeType) {
    const successOutcomes = ['paid_on_time', 'enrolled', 'remained', 'improved'];
    const partialOutcomes = ['paid_late', 'trial_scheduled', 'acknowledged'];

    if (successOutcomes.some(s => outcomeType.includes(s))) return 'success';
    if (partialOutcomes.some(p => outcomeType.includes(p))) return 'partial_success';
    return 'failure';
  }

  async triggerEscalation(interaction) {
    // If outcome was failure, trigger next step in sequence
    console.log(`Outcome failed for interaction ${interaction.interaction_id}, triggering escalation`);
    // Implementation depends on escalation strategy
  }

  // Analytics: What predicts success?
  async analyzePredictors(purpose) {
    const results = await this.db.query(`
      SELECT 
        intermediate.outcome_type as predictor,
        COUNT(*) as total_occurrences,
        SUM(CASE WHEN ultimate.outcome_category = 'success' THEN 1 ELSE 0 END) as success_count,
        SUM(CASE WHEN ultimate.outcome_category = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
      FROM interaction_log intermediate
      JOIN interaction_log ultimate ON 
        JSON_VALUE(intermediate.metadata, '$.led_to_ultimate_outcome_id') = CAST(ultimate.interaction_id AS VARCHAR)
      WHERE ultimate.is_ultimate_outcome = 1
        AND JSON_VALUE(ultimate.metadata, '$.purpose') = ?
      GROUP BY intermediate.outcome_type
      HAVING COUNT(*) >= 10  -- Need sufficient sample
      ORDER BY success_rate DESC
    `, [purpose]);

    return results;
  }
}

module.exports = OutcomeTracker;

Known Uses

Homeschool Co-Op Intelligence Platform - Defined 47 outcome types across 8 interaction purposes - Discovered: email_clicked predicts payment 3.7x better than email_opened - Discovered: phone_answered leads to enrollment 2.1x more than email_responded - Used outcomes to train payment risk model (82% accuracy)

E-commerce Conversion Tracking - Cart abandoned → email sent → email opened → link clicked → purchase completed - Multi-touch attribution across 5-10 touchpoints - Each touchpoint gets fractional credit based on position and effectiveness

Marketing Automation Platforms - Lead score based on engagement outcomes - "Hot lead" = multiple high-value outcomes (demo_attended, pricing_viewed, proposal_requested) - "Cold lead" = only low-value outcomes (email_opened, website_visited)

Requires: - Pattern 1: Universal Event Log - provides raw interactions to classify - Pattern 3: Multi-Channel Tracking - provides outcomes across channels

Enhances: - Pattern 11: Historical Pattern Matching - outcomes are the patterns to match - Pattern 12: Risk Stratification Models - outcomes are training labels - Pattern 21: Automated Workflow Execution - optimize based on outcome effectiveness - Pattern 26: Feedback Loop Implementation - outcomes close the feedback loop

Enabled by this: - Pattern 15: Intervention Recommendation Engine - recommend based on outcome history - Pattern 16: Cohort Discovery & Analysis - discover which outcomes predict what - Pattern 24: Template-Based Communication - A/B test templates based on outcomes

References

Academic Foundations

  • Davenport, Thomas H., and Jeanne G. Harris (2007). Competing on Analytics. Harvard Business Press. ISBN: 978-1422103326
  • Kaushik, Avinash (2009). Web Analytics 2.0. Sybex. ISBN: 978-0470529393 - Attribution models, outcome measurement
  • Kohavi, Ron, Diane Tang, and Ya Xu (2020). Trustworthy Online Controlled Experiments. Cambridge University Press. ISBN: 978-1108724265
  • Provost, Foster, and Tom Fawcett (2013). Data Science for Business. O'Reilly. ISBN: 978-1449361327 - Classification fundamentals

Practical Implementation

Analytics & Attribution

Machine Learning Resources

  • Imbalanced Classification: Chawla, N.V., et al. (2002). "SMOTE: Synthetic Minority Over-sampling Technique." https://arxiv.org/abs/1106.1813
  • Feature Engineering Book: Zheng, A., & Casari, A. (2018). Feature Engineering for Machine Learning. O'Reilly. ISBN: 978-1491953242
  • MLflow: https://mlflow.org/ - ML lifecycle management (track classification experiments)
  • Pattern 1: Universal Event Log - Raw events to classify
  • Pattern 11: Historical Pattern Matching - Use classifications for matching
  • Pattern 12: Risk Stratification Models - Classification as feature
  • Volume 3, Pattern 14: Cross-Field Validation - Validate outcome data quality

Tools & Services