File size: 6,799 Bytes
dc610d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
"""
Battery Data Format Detector
Auto-detects NASA vs Stanford format with validation.

MIT/Stanford Quality: Robust format detection with confidence scoring.
"""

import pandas as pd
import numpy as np
from typing import Literal, Dict


def detect_battery_format(
    data: pd.DataFrame,
    confidence_threshold: float = 0.8
) -> Dict[str, any]:
    """
    Automatically detect battery data format (NASA vs Stanford).
    
    NASA format: cycle_index, voltage, current, temperature, capacity
    Stanford format: 99 columns of voltage discharge curve values
    
    Parameters:
    -----------
    data : DataFrame
        Battery data to detect format
    confidence_threshold : float
        Minimum confidence required (default 0.8)
    
    Returns:
    --------
    dict: {
        'format': 'nasa' | 'stanford' | 'unknown',
        'confidence': float,
        'reason': str,
        'details': dict
    }
    """
    detection_scores = {
        'nasa': 0.0,
        'stanford': 0.0
    }
    
    details = {
        'columns': list(data.columns),
        'shape': data.shape,
        'column_count': len(data.columns)
    }
    
    # Check 1: Column names
    nasa_required_cols = {'cycle_index', 'voltage', 'current', 'temperature', 'capacity'}
    nasa_cols_present = sum(1 for col in nasa_required_cols if col in data.columns)
    
    if nasa_cols_present == 5:
        detection_scores['nasa'] += 0.5  # Strong indicator
    elif nasa_cols_present >= 3:
        detection_scores['nasa'] += 0.3  # Moderate indicator
    
    # Check 2: Column count
    # Stanford has exactly 99 columns (voltage measurements)
    if len(data.columns) == 99:
        detection_scores['stanford'] += 0.4
    elif len(data.columns) > 50:  # Likely voltage array
        detection_scores['stanford'] += 0.2
    
    if len(data.columns) == 5:
        detection_scores['nasa'] += 0.3
    
    # Check 3: Data patterns
    # Stanford values are small (voltage deltas, typically < 0.01)
    # NASA values are absolute (voltage 3-4V, current 0-2A, etc.)
    
    if len(data.columns) > 0:
        first_col_values = data.iloc[:, 0].dropna()
        if len(first_col_values) > 0:
            first_col_mean = abs(first_col_values.mean())
            first_col_max = abs(first_col_values.max())
            
            # Stanford: small values (voltage deltas)
            if first_col_max < 0.01:
                detection_scores['stanford'] += 0.3
            
            # NASA: larger values (cycle indices or voltages)
            if first_col_max > 1.0:
                detection_scores['nasa'] += 0.2
    
    # Check 4: Row count
    # Stanford typically has ~1000 cycles per cell
    # NASA typically has ~168 cycles
    if len(data) > 500:
        detection_scores['stanford'] += 0.1
    elif len(data) < 300:
        detection_scores['nasa'] += 0.1
    
    # Determine format
    max_score = max(detection_scores['nasa'], detection_scores['stanford'])
    
    if detection_scores['nasa'] > detection_scores['stanford'] and detection_scores['nasa'] >= confidence_threshold:
        format_type = 'nasa'
        confidence = detection_scores['nasa']
        reason = f"NASA format detected: {nasa_cols_present}/5 required columns present"
    elif detection_scores['stanford'] > detection_scores['nasa'] and detection_scores['stanford'] >= confidence_threshold:
        format_type = 'stanford'
        confidence = detection_scores['stanford']
        reason = f"Stanford format detected: {len(data.columns)} columns (matrix format)"
    else:
        format_type = 'unknown'
        confidence = max_score
        reason = f"Format uncertain: NASA score={detection_scores['nasa']:.2f}, Stanford score={detection_scores['stanford']:.2f}"
    
    details['detection_scores'] = detection_scores
    
    return {
        'format': format_type,
        'confidence': confidence,
        'reason': reason,
        'details': details
    }


def validate_format(data: pd.DataFrame, expected_format: str) -> Dict:
    """
    Validate that data matches expected format.
    
    Parameters:
    -----------
    data : DataFrame
        Battery data
    expected_format : str
        'nasa' or 'stanford'
    
    Returns:
    --------
    dict: Validation results
    """
    errors = []
    warnings = []
    
    if expected_format == 'nasa':
        # Check required columns
        required = ['cycle_index', 'voltage', 'current', 'temperature', 'capacity']
        missing = [col for col in required if col not in data.columns]
        
        if missing:
            errors.append(f"Missing required columns: {missing}")
        
        # Check value ranges
        if 'voltage' in data.columns:
            v_min, v_max = data['voltage'].min(), data['voltage'].max()
            if v_min < 0 or v_max > 5:
                warnings.append(f"Unusual voltage range: {v_min:.2f}-{v_max:.2f}V")
        
        if 'capacity' in data.columns:
            if data['capacity'].min() < 0:
                errors.append("Negative capacity values found")
    
    elif expected_format == 'stanford':
        # Check column count
        if len(data.columns) != 99:
            warnings.append(f"Expected 99 columns, got {len(data.columns)}")
        
        # Check value ranges (should be small voltage deltas)
        max_abs_value = abs(data.values).max()
        if max_abs_value > 0.1:
            warnings.append(f"Values seem large for Stanford format: max={max_abs_value:.4f}")
    
    is_valid = len(errors) == 0
    
    return {
        'is_valid': is_valid,
        'errors': errors,
        'warnings': warnings
    }


if __name__ == "__main__":
    # Test format detection
    print("Testing format detector...")
    
    # Test NASA format
    print("\n1. NASA B0005:")
    nasa_data = pd.read_csv('./data/nasa_converted/B0005.csv')
    result = detect_battery_format(nasa_data)
    print(f"   Format: {result['format']}")
    print(f"   Confidence: {result['confidence']:.2f}")
    print(f"   Reason: {result['reason']}")
    
    # Test Stanford format
    print("\n2. Stanford cell1 (original):")
    stanford_data = pd.read_csv('./data/stanford/cell1.csv', header=None)
    result = detect_battery_format(stanford_data)
    print(f"   Format: {result['format']}")
    print(f"   Confidence: {result['confidence']:.2f}")
    print(f"   Reason: {result['reason']}")
    
    # Test Stanford converted
    print("\n3. Stanford cell1 (converted):")
    stanford_converted = pd.read_csv('./data/stanford_converted/cell1.csv')
    result = detect_battery_format(stanford_converted)
    print(f"   Format: {result['format']}")
    print(f"   Confidence: {result['confidence']:.2f}")
    print(f"   Reason: {result['reason']}")
    
    print("\n✅ Format detector test complete!")