UNPKG

bowling-analysis-system

Version:

A comprehensive system for analyzing bowling techniques using video processing and metrics calculation

220 lines (182 loc) 6.99 kB
/** * @module bowling_analysis/metrics/BiasProcessorUtils * @description Utility functions for bias processing and analysis */ /** * Analyzes bias data structure and returns key patterns * @param {Object} biasData - The bias data to analyze * @returns {Object} Analysis of the bias patterns */ function analyzeBiasStructure(biasData) { const patterns = biasData.correlationPatterns || {}; const eventTypes = Object.keys(patterns); const metricsByEvent = {}; for (const event of eventTypes) { const eventPatterns = patterns[event] || []; metricsByEvent[event] = { count: eventPatterns.length, topMetrics: eventPatterns .sort((a, b) => b.weight - a.weight) .slice(0, 3) .map(p => ({ path: p.metric, weight: p.weight })) }; } return { totalPatterns: Object.values(patterns).reduce((sum, arr) => sum + arr.length, 0), events: metricsByEvent }; } /** * Calculate variability from metrics * @param {Object} metricsSection - The metrics section to calculate variability from * @returns {number} The calculated variability */ function calculateVariabilityFromMetrics(metricsSection) { // Default variability if no calculation is possible const defaultVariability = 0.2; if (!metricsSection || typeof metricsSection !== 'object') { return defaultVariability; } // Find time series data if available const timeSeriesKeys = Object.keys(metricsSection).filter(key => metricsSection[key] && metricsSection[key].series && Array.isArray(metricsSection[key].series) ); if (timeSeriesKeys.length === 0) { return defaultVariability; } // Calculate average variability across available time series let totalVariability = 0; let seriesCount = 0; timeSeriesKeys.forEach(key => { const series = metricsSection[key].series; if (series.length < 2) return; // Calculate standard deviation of the series const values = series.map(point => point.value).filter(val => typeof val === 'number'); if (values.length < 2) return; const mean = values.reduce((sum, val) => sum + val, 0) / values.length; const squaredDiffs = values.map(val => Math.pow(val - mean, 2)); const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length; const stdDev = Math.sqrt(variance); // Use raw standard deviation directly as variability measure totalVariability += stdDev; seriesCount++; }); return seriesCount > 0 ? (totalVariability / seriesCount) : defaultVariability; } /** * Calculate consistency from metrics * @param {Object} metricsSection - The metrics section to calculate consistency from * @returns {number} The calculated consistency */ function calculateConsistencyFromMetrics(metricsSection) { // Default consistency if no calculation is possible const defaultConsistency = 0.7; if (!metricsSection || typeof metricsSection !== 'object') { return defaultConsistency; } // Consistency is inversely related to variability const variability = calculateVariabilityFromMetrics(metricsSection); // Return raw inverse of variability without scaling return 1 / (variability + 1); } /** * Calculate event detection score from events * @param {Object} events - The events object * @returns {number} The calculated event detection score */ function calculateEventDetectionScore(events) { // Default detection score if no calculation is possible const defaultScore = 0.7; if (!events || typeof events !== 'object') { return defaultScore; } // Get primary event types we expect to find const primaryEvents = ['releasePoint', 'frontFootLanding', 'backFootLanding']; // Count how many of the primary events we detected const detectedCount = primaryEvents.filter(eventType => events[eventType] && typeof events[eventType] === 'object' && events[eventType].frame !== undefined ).length; // Calculate a score based on the proportion of detected events // Scale it to be between 0.5 (no events) and 1.0 (all events) return 0.5 + (detectedCount / primaryEvents.length) * 0.5; } /** * Calculate event confidence from events * @param {Object} events - The events object * @returns {number} The calculated event confidence */ function calculateEventConfidence(events) { // Default confidence if no calculation is possible const defaultConfidence = 0.7; if (!events || typeof events !== 'object') { return defaultConfidence; } // Get primary event types we expect to find const primaryEvents = ['releasePoint', 'frontFootLanding', 'backFootLanding']; // Calculate average confidence across detected events let totalConfidence = 0; let eventCount = 0; primaryEvents.forEach(eventType => { if ( events[eventType] && typeof events[eventType] === 'object' && typeof events[eventType].confidence === 'number' ) { totalConfidence += events[eventType].confidence; eventCount++; } }); return eventCount > 0 ? (totalConfidence / eventCount) : defaultConfidence; } /** * Calculate event consistency from events * @param {Object} events - The events object * @returns {number} The calculated event consistency */ function calculateEventConsistency(events) { // Default consistency if no calculation is possible const defaultConsistency = 0.7; if (!events || typeof events !== 'object') { return defaultConsistency; } // For consistency, we need multiple samples, but we usually only have // one set of events per analysis. So we estimate consistency based on // the confidence of the events and their relative timing // Get confidence as a base const confidence = calculateEventConfidence(events); // Adjust consistency based on the confidence // Higher confidence typically correlates with higher consistency return 0.4 + (confidence * 0.6); } /** * Calculate event variability from events * @param {Object} events - The events object * @returns {number} The calculated event variability */ function calculateEventVariability(events) { // Default variability if no calculation is possible const defaultVariability = 0.2; if (!events || typeof events !== 'object') { return defaultVariability; } // Variability is inversely related to consistency const consistency = calculateEventConsistency(events); // Return raw inverse value without scaling return 1 - consistency; } module.exports = { analyzeBiasStructure, calculateVariabilityFromMetrics, calculateConsistencyFromMetrics, calculateEventDetectionScore, calculateEventConfidence, calculateEventConsistency, calculateEventVariability };