featrixsphere 0.1.583__py3-none-any.whl → 0.2.1314__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- featrixsphere/__init__.py +3 -3
- featrixsphere/cli.py +148 -23
- featrixsphere/client.py +3432 -4284
- featrixsphere/test_client.py +311 -0
- {featrixsphere-0.1.583.dist-info → featrixsphere-0.2.1314.dist-info}/METADATA +46 -7
- featrixsphere-0.2.1314.dist-info/RECORD +9 -0
- featrixsphere/client_movie_v2.py +0 -489
- featrixsphere/prediction_grid.py +0 -629
- featrixsphere-0.1.583.dist-info/RECORD +0 -10
- {featrixsphere-0.1.583.dist-info → featrixsphere-0.2.1314.dist-info}/WHEEL +0 -0
- {featrixsphere-0.1.583.dist-info → featrixsphere-0.2.1314.dist-info}/entry_points.txt +0 -0
- {featrixsphere-0.1.583.dist-info → featrixsphere-0.2.1314.dist-info}/top_level.txt +0 -0
featrixsphere/prediction_grid.py
DELETED
|
@@ -1,629 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
class PredictionGrid:
|
|
3
|
-
"""
|
|
4
|
-
Grid-based prediction batch with automatic matrix building and visualization.
|
|
5
|
-
|
|
6
|
-
Perfect for exploring prediction surfaces across 1-3 dimensions with automatic plotting.
|
|
7
|
-
Collects all predictions and batches them for efficiency.
|
|
8
|
-
|
|
9
|
-
Usage:
|
|
10
|
-
# 2D parameter sweep with automatic plotting
|
|
11
|
-
grid = client.predict_grid(session_id, degrees_of_freedom=2)
|
|
12
|
-
|
|
13
|
-
# Fill grid (records are collected, not predicted yet)
|
|
14
|
-
for i, spend in enumerate([100, 250, 500]):
|
|
15
|
-
for j, campaign in enumerate(["search", "display"]):
|
|
16
|
-
record = {"spend": spend, "campaign_type": campaign}
|
|
17
|
-
grid.predict(record, grid_position=(i, j))
|
|
18
|
-
|
|
19
|
-
# Process all predictions in one batch
|
|
20
|
-
grid.process_batch()
|
|
21
|
-
|
|
22
|
-
# Now plot results
|
|
23
|
-
grid.plot_heatmap() # Automatic heatmap
|
|
24
|
-
grid.plot_3d() # 3D surface plot
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, session_id: str, client: 'FeatrixSphereClient', degrees_of_freedom: int,
|
|
28
|
-
grid_shape: tuple = None, target_column: str = None):
|
|
29
|
-
self.session_id = session_id
|
|
30
|
-
self.client = client
|
|
31
|
-
self.degrees_of_freedom = degrees_of_freedom
|
|
32
|
-
self.target_column = target_column
|
|
33
|
-
|
|
34
|
-
# Initialize grid matrix based on degrees of freedom
|
|
35
|
-
if grid_shape:
|
|
36
|
-
self.grid_shape = grid_shape
|
|
37
|
-
else:
|
|
38
|
-
# Default grid sizes
|
|
39
|
-
default_sizes = {1: (20,), 2: (10, 10), 3: (8, 8, 8)}
|
|
40
|
-
self.grid_shape = default_sizes.get(degrees_of_freedom, (10,) * degrees_of_freedom)
|
|
41
|
-
|
|
42
|
-
# Initialize matrices for different data types
|
|
43
|
-
self._prediction_matrix = {} # class_name -> matrix
|
|
44
|
-
self._confidence_matrix = None
|
|
45
|
-
self._filled_positions = set()
|
|
46
|
-
|
|
47
|
-
# Batch collection system
|
|
48
|
-
self._pending_records = {} # grid_position -> record
|
|
49
|
-
self._position_to_index = {} # grid_position -> batch_index
|
|
50
|
-
self._batch_processed = False
|
|
51
|
-
|
|
52
|
-
# Metadata for plotting
|
|
53
|
-
self._axis_labels = [f"Param {i+1}" for i in range(degrees_of_freedom)]
|
|
54
|
-
self._axis_values = [[] for _ in range(degrees_of_freedom)]
|
|
55
|
-
self._colormap = 'viridis'
|
|
56
|
-
|
|
57
|
-
# Statistics
|
|
58
|
-
self._stats = {'predictions': 0, 'batched': 0, 'errors': 0}
|
|
59
|
-
|
|
60
|
-
def predict(self, record: Dict[str, Any], grid_position: tuple) -> Dict[str, str]:
|
|
61
|
-
"""
|
|
62
|
-
Add record to grid for batch processing.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
record: Record to predict
|
|
66
|
-
grid_position: Tuple of grid coordinates (i,) for 1D, (i,j) for 2D, (i,j,k) for 3D
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
Status message about queuing for batch processing
|
|
70
|
-
"""
|
|
71
|
-
if len(grid_position) != self.degrees_of_freedom:
|
|
72
|
-
error_msg = f"Grid position must have {self.degrees_of_freedom} dimensions, got {len(grid_position)}"
|
|
73
|
-
raise ValueError(self.client._format_error_with_version(error_msg, f"predict_grid_session_{self.session_id}"))
|
|
74
|
-
|
|
75
|
-
# Check bounds
|
|
76
|
-
for i, pos in enumerate(grid_position):
|
|
77
|
-
if pos >= self.grid_shape[i]:
|
|
78
|
-
error_msg = f"Grid position {pos} exceeds dimension {i} size {self.grid_shape[i]}"
|
|
79
|
-
raise ValueError(self.client._format_error_with_version(error_msg, f"predict_grid_session_{self.session_id}"))
|
|
80
|
-
|
|
81
|
-
# Store record for batch processing
|
|
82
|
-
self._pending_records[grid_position] = record
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
"status": "queued_for_batch",
|
|
86
|
-
"grid_position": grid_position,
|
|
87
|
-
"total_queued": len(self._pending_records),
|
|
88
|
-
"message": f"Record queued at position {grid_position}. Call process_batch() to run predictions."
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
def process_batch(self, show_progress: bool = True) -> Dict[str, Any]:
|
|
92
|
-
"""
|
|
93
|
-
Process all queued records in a single batch prediction.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
show_progress: Whether to show progress during batch processing
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
Batch processing results
|
|
100
|
-
"""
|
|
101
|
-
if not self._pending_records:
|
|
102
|
-
return {"message": "No records to process", "processed": 0}
|
|
103
|
-
|
|
104
|
-
if self._batch_processed:
|
|
105
|
-
return {"message": "Batch already processed", "processed": len(self._filled_positions)}
|
|
106
|
-
|
|
107
|
-
# Convert grid records to list for batch processing
|
|
108
|
-
records_list = []
|
|
109
|
-
position_mapping = {}
|
|
110
|
-
|
|
111
|
-
for grid_pos, record in self._pending_records.items():
|
|
112
|
-
batch_index = len(records_list)
|
|
113
|
-
records_list.append(record)
|
|
114
|
-
position_mapping[batch_index] = grid_pos
|
|
115
|
-
self._position_to_index[grid_pos] = batch_index
|
|
116
|
-
|
|
117
|
-
if show_progress:
|
|
118
|
-
print(f"🚀 Processing {len(records_list)} grid positions in batch...")
|
|
119
|
-
|
|
120
|
-
# Use existing batch prediction system
|
|
121
|
-
try:
|
|
122
|
-
batch_results = self.client.predict_records(
|
|
123
|
-
session_id=self.session_id,
|
|
124
|
-
records=records_list,
|
|
125
|
-
target_column=self.target_column,
|
|
126
|
-
show_progress_bar=show_progress
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
# Process results and populate matrices
|
|
130
|
-
predictions = batch_results.get('predictions', [])
|
|
131
|
-
successful = 0
|
|
132
|
-
failed = 0
|
|
133
|
-
|
|
134
|
-
for prediction in predictions:
|
|
135
|
-
row_index = prediction.get('row_index', 0)
|
|
136
|
-
if row_index in position_mapping:
|
|
137
|
-
grid_pos = position_mapping[row_index]
|
|
138
|
-
|
|
139
|
-
if 'prediction' in prediction and prediction['prediction']:
|
|
140
|
-
prediction_probs = prediction['prediction']
|
|
141
|
-
|
|
142
|
-
# Initialize matrices if first successful prediction
|
|
143
|
-
if not self._prediction_matrix:
|
|
144
|
-
self._initialize_matrices(prediction_probs.keys())
|
|
145
|
-
|
|
146
|
-
# Store prediction results in matrices
|
|
147
|
-
for class_name, probability in prediction_probs.items():
|
|
148
|
-
self._prediction_matrix[class_name][grid_pos] = probability
|
|
149
|
-
|
|
150
|
-
# Store confidence (highest probability)
|
|
151
|
-
max_class = max(prediction_probs, key=prediction_probs.get)
|
|
152
|
-
confidence = prediction_probs[max_class]
|
|
153
|
-
self._confidence_matrix[grid_pos] = confidence
|
|
154
|
-
|
|
155
|
-
# Mark position as filled
|
|
156
|
-
self._filled_positions.add(grid_pos)
|
|
157
|
-
successful += 1
|
|
158
|
-
else:
|
|
159
|
-
failed += 1
|
|
160
|
-
self._stats['errors'] += 1
|
|
161
|
-
|
|
162
|
-
self._stats['predictions'] = successful
|
|
163
|
-
self._stats['batched'] = len(records_list)
|
|
164
|
-
self._batch_processed = True
|
|
165
|
-
|
|
166
|
-
# Clear pending records
|
|
167
|
-
self._pending_records.clear()
|
|
168
|
-
|
|
169
|
-
if show_progress:
|
|
170
|
-
print(f"✅ Batch processing complete: {successful} successful, {failed} failed")
|
|
171
|
-
print(f"📊 Grid filled: {len(self._filled_positions)} positions")
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
"processed": len(records_list),
|
|
175
|
-
"successful": successful,
|
|
176
|
-
"failed": failed,
|
|
177
|
-
"batch_results": batch_results
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
except Exception as e:
|
|
181
|
-
self._stats['errors'] += len(records_list)
|
|
182
|
-
raise Exception(f"Error processing grid batch: {str(e)}")
|
|
183
|
-
|
|
184
|
-
def _initialize_matrices(self, class_names: list):
|
|
185
|
-
"""Initialize prediction matrices for each class."""
|
|
186
|
-
import numpy as np
|
|
187
|
-
|
|
188
|
-
for class_name in class_names:
|
|
189
|
-
self._prediction_matrix[class_name] = np.full(self.grid_shape, np.nan)
|
|
190
|
-
|
|
191
|
-
self._confidence_matrix = np.full(self.grid_shape, np.nan)
|
|
192
|
-
|
|
193
|
-
def set_axis_labels(self, labels: list):
|
|
194
|
-
"""Set custom labels for axes."""
|
|
195
|
-
if len(labels) != self.degrees_of_freedom:
|
|
196
|
-
raise ValueError(f"Must provide {self.degrees_of_freedom} labels")
|
|
197
|
-
self._axis_labels = labels
|
|
198
|
-
|
|
199
|
-
def set_axis_values(self, axis_index: int, values: list):
|
|
200
|
-
"""Set actual values for an axis (for proper tick labels)."""
|
|
201
|
-
if axis_index >= self.degrees_of_freedom:
|
|
202
|
-
raise ValueError(f"Axis index {axis_index} exceeds degrees of freedom {self.degrees_of_freedom}")
|
|
203
|
-
self._axis_values[axis_index] = values
|
|
204
|
-
|
|
205
|
-
def plot_heatmap(self, class_name: str = None, figsize: tuple = (10, 8), title: str = None):
|
|
206
|
-
"""
|
|
207
|
-
Plot 2D heatmap of prediction probabilities.
|
|
208
|
-
|
|
209
|
-
Args:
|
|
210
|
-
class_name: Specific class to plot (default: highest probability class)
|
|
211
|
-
figsize: Figure size
|
|
212
|
-
title: Custom title
|
|
213
|
-
"""
|
|
214
|
-
if self.degrees_of_freedom != 2:
|
|
215
|
-
raise ValueError("Heatmap plotting only supports 2D grids")
|
|
216
|
-
|
|
217
|
-
if not self._batch_processed:
|
|
218
|
-
raise ValueError("Must call process_batch() first")
|
|
219
|
-
|
|
220
|
-
try:
|
|
221
|
-
import matplotlib.pyplot as plt
|
|
222
|
-
import numpy as np
|
|
223
|
-
except ImportError:
|
|
224
|
-
raise ImportError("matplotlib required for plotting. Install with: pip install matplotlib")
|
|
225
|
-
|
|
226
|
-
if not self._prediction_matrix:
|
|
227
|
-
raise ValueError("No predictions processed yet. Call process_batch() first.")
|
|
228
|
-
|
|
229
|
-
# Choose class to plot
|
|
230
|
-
if class_name is None:
|
|
231
|
-
# Use the class with highest average probability
|
|
232
|
-
avg_probs = {}
|
|
233
|
-
for cls, matrix in self._prediction_matrix.items():
|
|
234
|
-
avg_probs[cls] = np.nanmean(matrix)
|
|
235
|
-
class_name = max(avg_probs, key=avg_probs.get)
|
|
236
|
-
|
|
237
|
-
matrix = self._prediction_matrix[class_name]
|
|
238
|
-
|
|
239
|
-
# Create plot
|
|
240
|
-
fig, ax = plt.subplots(figsize=figsize)
|
|
241
|
-
|
|
242
|
-
# Transpose matrix for correct matplotlib display orientation
|
|
243
|
-
# matplotlib imshow: first dimension = Y-axis (vertical), second = X-axis (horizontal)
|
|
244
|
-
# So we need to transpose to get axis 0 on X-axis and axis 1 on Y-axis
|
|
245
|
-
display_matrix = matrix.T
|
|
246
|
-
|
|
247
|
-
# Plot heatmap with transposed matrix
|
|
248
|
-
im = ax.imshow(display_matrix, cmap=self._colormap, aspect='auto', origin='lower')
|
|
249
|
-
|
|
250
|
-
# Set labels (axis 0 = X-axis, axis 1 = Y-axis after transpose)
|
|
251
|
-
ax.set_xlabel(self._axis_labels[0])
|
|
252
|
-
ax.set_ylabel(self._axis_labels[1])
|
|
253
|
-
|
|
254
|
-
# Set tick labels if axis values provided (adjusted for transpose)
|
|
255
|
-
if self._axis_values[0]:
|
|
256
|
-
ax.set_xticks(range(len(self._axis_values[0])))
|
|
257
|
-
ax.set_xticklabels(self._axis_values[0])
|
|
258
|
-
if self._axis_values[1]:
|
|
259
|
-
ax.set_yticks(range(len(self._axis_values[1])))
|
|
260
|
-
ax.set_yticklabels(self._axis_values[1])
|
|
261
|
-
|
|
262
|
-
# Add colorbar
|
|
263
|
-
cbar = plt.colorbar(im, ax=ax)
|
|
264
|
-
cbar.set_label(f'Probability of {class_name}')
|
|
265
|
-
|
|
266
|
-
# Set title
|
|
267
|
-
if title is None:
|
|
268
|
-
title = f'Prediction Heatmap: {class_name}'
|
|
269
|
-
ax.set_title(title)
|
|
270
|
-
|
|
271
|
-
plt.tight_layout()
|
|
272
|
-
return fig, ax
|
|
273
|
-
|
|
274
|
-
def plot_3d(self, class_name: str = None, figsize: tuple = (12, 9), title: str = None,
|
|
275
|
-
value_filter: tuple = None, opacity: float = 0.8, show_wireframe: bool = False):
|
|
276
|
-
"""
|
|
277
|
-
Plot 3D surface of prediction probabilities with filtering and opacity controls.
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
class_name: Specific class to plot (default: highest probability class)
|
|
281
|
-
figsize: Figure size
|
|
282
|
-
title: Custom title
|
|
283
|
-
value_filter: Tuple (min_value, max_value) to filter displayed predictions
|
|
284
|
-
opacity: Surface opacity (0.0 = transparent, 1.0 = opaque)
|
|
285
|
-
show_wireframe: Whether to show wireframe overlay for better shape visibility
|
|
286
|
-
"""
|
|
287
|
-
if self.degrees_of_freedom != 2:
|
|
288
|
-
raise ValueError("3D surface plotting only supports 2D grids")
|
|
289
|
-
|
|
290
|
-
if not self._batch_processed:
|
|
291
|
-
raise ValueError("Must call process_batch() first")
|
|
292
|
-
|
|
293
|
-
try:
|
|
294
|
-
import matplotlib.pyplot as plt
|
|
295
|
-
import numpy as np
|
|
296
|
-
from mpl_toolkits.mplot3d import Axes3D
|
|
297
|
-
except ImportError:
|
|
298
|
-
raise ImportError("matplotlib required for plotting. Install with: pip install matplotlib")
|
|
299
|
-
|
|
300
|
-
if not self._prediction_matrix:
|
|
301
|
-
raise ValueError("No predictions processed yet. Call process_batch() first.")
|
|
302
|
-
|
|
303
|
-
# Choose class to plot
|
|
304
|
-
if class_name is None:
|
|
305
|
-
avg_probs = {}
|
|
306
|
-
for cls, matrix in self._prediction_matrix.items():
|
|
307
|
-
avg_probs[cls] = np.nanmean(matrix)
|
|
308
|
-
class_name = max(avg_probs, key=avg_probs.get)
|
|
309
|
-
|
|
310
|
-
matrix = self._prediction_matrix[class_name].copy()
|
|
311
|
-
|
|
312
|
-
# Apply value filter if specified
|
|
313
|
-
if value_filter is not None:
|
|
314
|
-
min_val, max_val = value_filter
|
|
315
|
-
# Mask values outside the filter range
|
|
316
|
-
mask = (matrix < min_val) | (matrix > max_val)
|
|
317
|
-
matrix[mask] = np.nan
|
|
318
|
-
|
|
319
|
-
# Create meshgrid with proper axis orientation
|
|
320
|
-
x = np.arange(matrix.shape[0]) # axis 0
|
|
321
|
-
y = np.arange(matrix.shape[1]) # axis 1
|
|
322
|
-
X, Y = np.meshgrid(x, y, indexing='ij')
|
|
323
|
-
|
|
324
|
-
# Create 3D plot
|
|
325
|
-
fig = plt.figure(figsize=figsize)
|
|
326
|
-
ax = fig.add_subplot(111, projection='3d')
|
|
327
|
-
|
|
328
|
-
# Plot surface with specified opacity
|
|
329
|
-
surf = ax.plot_surface(X, Y, matrix, cmap=self._colormap, alpha=opacity)
|
|
330
|
-
|
|
331
|
-
# Add wireframe if requested (helps see shape)
|
|
332
|
-
if show_wireframe:
|
|
333
|
-
ax.plot_wireframe(X, Y, matrix, alpha=0.3, color='black', linewidth=0.5)
|
|
334
|
-
|
|
335
|
-
# Set labels (axis 0 = X-axis, axis 1 = Y-axis)
|
|
336
|
-
ax.set_xlabel(self._axis_labels[0])
|
|
337
|
-
ax.set_ylabel(self._axis_labels[1])
|
|
338
|
-
ax.set_zlabel(f'Probability of {class_name}')
|
|
339
|
-
|
|
340
|
-
# Set tick labels if axis values provided
|
|
341
|
-
if self._axis_values[0]:
|
|
342
|
-
ax.set_xticks(range(len(self._axis_values[0])))
|
|
343
|
-
ax.set_xticklabels(self._axis_values[0])
|
|
344
|
-
if self._axis_values[1]:
|
|
345
|
-
ax.set_yticks(range(len(self._axis_values[1])))
|
|
346
|
-
ax.set_yticklabels(self._axis_values[1])
|
|
347
|
-
|
|
348
|
-
# Add colorbar
|
|
349
|
-
cbar = fig.colorbar(surf, ax=ax, shrink=0.5)
|
|
350
|
-
cbar.set_label(f'Probability of {class_name}')
|
|
351
|
-
|
|
352
|
-
# Set title with filter info
|
|
353
|
-
if title is None:
|
|
354
|
-
title = f'3D Prediction Surface: {class_name}'
|
|
355
|
-
if value_filter:
|
|
356
|
-
title += f' (filtered: {value_filter[0]:.3f}-{value_filter[1]:.3f})'
|
|
357
|
-
ax.set_title(title)
|
|
358
|
-
|
|
359
|
-
return fig, ax
|
|
360
|
-
|
|
361
|
-
def plot_3d_interactive(self, class_name: str = None, figsize: tuple = (12, 9)):
|
|
362
|
-
"""
|
|
363
|
-
Create interactive 3D plot with sliders for filtering and opacity control.
|
|
364
|
-
|
|
365
|
-
Perfect for Jupyter notebooks - provides sliders to explore the prediction surface.
|
|
366
|
-
|
|
367
|
-
Args:
|
|
368
|
-
class_name: Specific class to plot (default: highest probability class)
|
|
369
|
-
figsize: Figure size
|
|
370
|
-
|
|
371
|
-
Returns:
|
|
372
|
-
Interactive widget (in Jupyter) or regular plot (elsewhere)
|
|
373
|
-
"""
|
|
374
|
-
if self.degrees_of_freedom != 2:
|
|
375
|
-
raise ValueError("Interactive 3D plotting only supports 2D grids")
|
|
376
|
-
|
|
377
|
-
if not self._batch_processed:
|
|
378
|
-
raise ValueError("Must call process_batch() first")
|
|
379
|
-
|
|
380
|
-
# Check if we're in a Jupyter environment
|
|
381
|
-
try:
|
|
382
|
-
from IPython.display import display
|
|
383
|
-
from ipywidgets import interact, FloatSlider, FloatRangeSlider, Checkbox
|
|
384
|
-
import numpy as np
|
|
385
|
-
jupyter_available = True
|
|
386
|
-
except ImportError:
|
|
387
|
-
print("⚠️ Interactive widgets require Jupyter and ipywidgets")
|
|
388
|
-
print(" Install with: pip install ipywidgets")
|
|
389
|
-
print(" Falling back to static 3D plot...")
|
|
390
|
-
return self.plot_3d(class_name=class_name, figsize=figsize)
|
|
391
|
-
|
|
392
|
-
if not self._prediction_matrix:
|
|
393
|
-
raise ValueError("No predictions processed yet. Call process_batch() first.")
|
|
394
|
-
|
|
395
|
-
# Choose class to plot
|
|
396
|
-
if class_name is None:
|
|
397
|
-
avg_probs = {}
|
|
398
|
-
for cls, matrix in self._prediction_matrix.items():
|
|
399
|
-
avg_probs[cls] = np.nanmean(matrix)
|
|
400
|
-
class_name = max(avg_probs, key=avg_probs.get)
|
|
401
|
-
|
|
402
|
-
matrix = self._prediction_matrix[class_name]
|
|
403
|
-
|
|
404
|
-
# Get value range for sliders
|
|
405
|
-
min_val = float(np.nanmin(matrix))
|
|
406
|
-
max_val = float(np.nanmax(matrix))
|
|
407
|
-
value_range = max_val - min_val
|
|
408
|
-
|
|
409
|
-
print(f"🎛️ Interactive 3D Surface Explorer: {class_name}")
|
|
410
|
-
print(f" Value range: {min_val:.4f} to {max_val:.4f}")
|
|
411
|
-
print(" Use sliders below to filter and adjust opacity")
|
|
412
|
-
|
|
413
|
-
# Create interactive plot function
|
|
414
|
-
def update_plot(value_range=(min_val, max_val), opacity=0.8, wireframe=False):
|
|
415
|
-
"""Update the 3D plot based on slider values."""
|
|
416
|
-
import matplotlib.pyplot as plt
|
|
417
|
-
plt.close('all') # Close previous plots
|
|
418
|
-
|
|
419
|
-
fig, ax = self.plot_3d(
|
|
420
|
-
class_name=class_name,
|
|
421
|
-
figsize=figsize,
|
|
422
|
-
value_filter=value_range,
|
|
423
|
-
opacity=opacity,
|
|
424
|
-
show_wireframe=wireframe
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
# Show current filter stats
|
|
428
|
-
filtered_matrix = matrix.copy()
|
|
429
|
-
mask = (filtered_matrix < value_range[0]) | (filtered_matrix > value_range[1])
|
|
430
|
-
filtered_matrix[mask] = np.nan
|
|
431
|
-
|
|
432
|
-
visible_count = np.sum(~np.isnan(filtered_matrix))
|
|
433
|
-
total_count = np.sum(~np.isnan(matrix))
|
|
434
|
-
visible_percent = (visible_count / total_count) * 100 if total_count > 0 else 0
|
|
435
|
-
|
|
436
|
-
print(f"📊 Showing {visible_count}/{total_count} points ({visible_percent:.1f}%)")
|
|
437
|
-
plt.show()
|
|
438
|
-
|
|
439
|
-
# Create interactive widgets
|
|
440
|
-
value_slider = FloatRangeSlider(
|
|
441
|
-
value=(min_val, max_val),
|
|
442
|
-
min=min_val,
|
|
443
|
-
max=max_val,
|
|
444
|
-
step=value_range / 100,
|
|
445
|
-
description='Value Filter:',
|
|
446
|
-
continuous_update=False,
|
|
447
|
-
style={'description_width': 'initial'}
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
opacity_slider = FloatSlider(
|
|
451
|
-
value=0.8,
|
|
452
|
-
min=0.1,
|
|
453
|
-
max=1.0,
|
|
454
|
-
step=0.1,
|
|
455
|
-
description='Opacity:',
|
|
456
|
-
continuous_update=False,
|
|
457
|
-
style={'description_width': 'initial'}
|
|
458
|
-
)
|
|
459
|
-
|
|
460
|
-
wireframe_checkbox = Checkbox(
|
|
461
|
-
value=False,
|
|
462
|
-
description='Show Wireframe',
|
|
463
|
-
style={'description_width': 'initial'}
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
# Create interactive widget
|
|
467
|
-
return interact(
|
|
468
|
-
update_plot,
|
|
469
|
-
value_range=value_slider,
|
|
470
|
-
opacity=opacity_slider,
|
|
471
|
-
wireframe=wireframe_checkbox
|
|
472
|
-
)
|
|
473
|
-
|
|
474
|
-
def plot_1d(self, class_name: str = None, figsize: tuple = (10, 6), title: str = None):
|
|
475
|
-
"""
|
|
476
|
-
Plot 1D line plot of prediction probabilities.
|
|
477
|
-
|
|
478
|
-
Args:
|
|
479
|
-
class_name: Specific class to plot (default: highest probability class)
|
|
480
|
-
figsize: Figure size
|
|
481
|
-
title: Custom title
|
|
482
|
-
"""
|
|
483
|
-
if self.degrees_of_freedom != 1:
|
|
484
|
-
raise ValueError("1D plotting only supports 1D grids")
|
|
485
|
-
|
|
486
|
-
if not self._batch_processed:
|
|
487
|
-
raise ValueError("Must call process_batch() first")
|
|
488
|
-
|
|
489
|
-
try:
|
|
490
|
-
import matplotlib.pyplot as plt
|
|
491
|
-
import numpy as np
|
|
492
|
-
except ImportError:
|
|
493
|
-
raise ImportError("matplotlib required for plotting. Install with: pip install matplotlib")
|
|
494
|
-
|
|
495
|
-
if not self._prediction_matrix:
|
|
496
|
-
raise ValueError("No predictions processed yet. Call process_batch() first.")
|
|
497
|
-
|
|
498
|
-
# Choose class to plot
|
|
499
|
-
if class_name is None:
|
|
500
|
-
avg_probs = {}
|
|
501
|
-
for cls, matrix in self._prediction_matrix.items():
|
|
502
|
-
avg_probs[cls] = np.nanmean(matrix)
|
|
503
|
-
class_name = max(avg_probs, key=avg_probs.get)
|
|
504
|
-
|
|
505
|
-
matrix = self._prediction_matrix[class_name]
|
|
506
|
-
|
|
507
|
-
# Create plot
|
|
508
|
-
fig, ax = plt.subplots(figsize=figsize)
|
|
509
|
-
|
|
510
|
-
# X values
|
|
511
|
-
x = self._axis_values[0] if self._axis_values[0] else range(len(matrix))
|
|
512
|
-
|
|
513
|
-
# Plot line
|
|
514
|
-
ax.plot(x, matrix, marker='o', linewidth=2, markersize=6)
|
|
515
|
-
|
|
516
|
-
# Set labels
|
|
517
|
-
ax.set_xlabel(self._axis_labels[0])
|
|
518
|
-
ax.set_ylabel(f'Probability of {class_name}')
|
|
519
|
-
|
|
520
|
-
# Set title
|
|
521
|
-
if title is None:
|
|
522
|
-
title = f'Prediction Curve: {class_name}'
|
|
523
|
-
ax.set_title(title)
|
|
524
|
-
|
|
525
|
-
ax.grid(True, alpha=0.3)
|
|
526
|
-
plt.tight_layout()
|
|
527
|
-
|
|
528
|
-
return fig, ax
|
|
529
|
-
|
|
530
|
-
def get_optimal_position(self, class_name: str = None) -> tuple:
|
|
531
|
-
"""
|
|
532
|
-
Find grid position with highest probability for a class.
|
|
533
|
-
|
|
534
|
-
Args:
|
|
535
|
-
class_name: Class to optimize for (default: highest average probability)
|
|
536
|
-
|
|
537
|
-
Returns:
|
|
538
|
-
Grid position tuple with highest probability
|
|
539
|
-
"""
|
|
540
|
-
import numpy as np
|
|
541
|
-
|
|
542
|
-
if not self._batch_processed:
|
|
543
|
-
raise ValueError("Must call process_batch() first")
|
|
544
|
-
|
|
545
|
-
if not self._prediction_matrix:
|
|
546
|
-
raise ValueError("No predictions processed yet. Call process_batch() first.")
|
|
547
|
-
|
|
548
|
-
if class_name is None:
|
|
549
|
-
avg_probs = {}
|
|
550
|
-
for cls, matrix in self._prediction_matrix.items():
|
|
551
|
-
avg_probs[cls] = np.nanmean(matrix)
|
|
552
|
-
class_name = max(avg_probs, key=avg_probs.get)
|
|
553
|
-
|
|
554
|
-
matrix = self._prediction_matrix[class_name]
|
|
555
|
-
optimal_idx = np.unravel_index(np.nanargmax(matrix), matrix.shape)
|
|
556
|
-
|
|
557
|
-
return optimal_idx
|
|
558
|
-
|
|
559
|
-
def get_stats(self) -> Dict[str, Any]:
|
|
560
|
-
"""Get grid statistics."""
|
|
561
|
-
import numpy as np
|
|
562
|
-
|
|
563
|
-
total_positions = int(np.prod(self.grid_shape))
|
|
564
|
-
filled_ratio = len(self._filled_positions) / total_positions if total_positions > 0 else 0
|
|
565
|
-
|
|
566
|
-
return {
|
|
567
|
-
'grid_shape': self.grid_shape,
|
|
568
|
-
'degrees_of_freedom': self.degrees_of_freedom,
|
|
569
|
-
'total_positions': total_positions,
|
|
570
|
-
'filled_positions': len(self._filled_positions),
|
|
571
|
-
'fill_ratio': filled_ratio,
|
|
572
|
-
'pending_records': len(self._pending_records),
|
|
573
|
-
'batch_processed': self._batch_processed,
|
|
574
|
-
'predictions_made': self._stats['predictions'],
|
|
575
|
-
'errors': self._stats['errors'],
|
|
576
|
-
'available_classes': list(self._prediction_matrix.keys()) if self._prediction_matrix else []
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
def export_data(self) -> Dict[str, Any]:
|
|
580
|
-
"""Export grid data for external analysis."""
|
|
581
|
-
import numpy as np
|
|
582
|
-
|
|
583
|
-
if not self._batch_processed:
|
|
584
|
-
raise ValueError("Must call process_batch() first")
|
|
585
|
-
|
|
586
|
-
return {
|
|
587
|
-
'prediction_matrices': {cls: matrix.tolist() for cls, matrix in self._prediction_matrix.items()},
|
|
588
|
-
'confidence_matrix': self._confidence_matrix.tolist() if self._confidence_matrix is not None else None,
|
|
589
|
-
'grid_shape': self.grid_shape,
|
|
590
|
-
'axis_labels': self._axis_labels,
|
|
591
|
-
'axis_values': self._axis_values,
|
|
592
|
-
'filled_positions': list(self._filled_positions),
|
|
593
|
-
'stats': self.get_stats()
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
def _is_resource_blocking_error(self, job: Dict[str, Any]) -> bool:
|
|
597
|
-
"""
|
|
598
|
-
Check if a job failed due to resource blocking/conflicts.
|
|
599
|
-
|
|
600
|
-
Args:
|
|
601
|
-
job: Job information dictionary
|
|
602
|
-
|
|
603
|
-
Returns:
|
|
604
|
-
True if the job failed due to resource blocking, False otherwise
|
|
605
|
-
"""
|
|
606
|
-
# Check for common resource blocking patterns
|
|
607
|
-
error_indicators = [
|
|
608
|
-
job.get('error', '').lower(),
|
|
609
|
-
job.get('message', '').lower(),
|
|
610
|
-
job.get('failure_reason', '').lower()
|
|
611
|
-
]
|
|
612
|
-
|
|
613
|
-
blocking_patterns = [
|
|
614
|
-
'already running',
|
|
615
|
-
'resource conflict',
|
|
616
|
-
'another job is running',
|
|
617
|
-
'blocked by',
|
|
618
|
-
'queue conflict',
|
|
619
|
-
'job conflict',
|
|
620
|
-
'resource busy',
|
|
621
|
-
'concurrent execution not allowed'
|
|
622
|
-
]
|
|
623
|
-
|
|
624
|
-
for indicator in error_indicators:
|
|
625
|
-
if indicator and any(pattern in indicator for pattern in blocking_patterns):
|
|
626
|
-
return True
|
|
627
|
-
|
|
628
|
-
return False
|
|
629
|
-
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
featrixsphere/__init__.py,sha256=ccpEQrXwHB8dqyQiiJtygcl4WuO9R8nwxbY9DaYIVYo,1897
|
|
2
|
-
featrixsphere/cli.py,sha256=4Mvp8xaDHDYfZwNaic5zZOzNBhY7VEIBrAJA7XPx84A,7386
|
|
3
|
-
featrixsphere/client.py,sha256=uGaVlIBEV7MGURoeJ1gdjGeCveBYDj1YpkquSpl-Ze0,426280
|
|
4
|
-
featrixsphere/client_movie_v2.py,sha256=8wm9DTClH_MUBRLKbx4N7UHO5Iy7EOs-zqpCECxY0mU,21401
|
|
5
|
-
featrixsphere/prediction_grid.py,sha256=Thr3FZLZgIAkfOXq9Z8e4wcQ09tCN4VfCvY4PO_P8NM,25111
|
|
6
|
-
featrixsphere-0.1.583.dist-info/METADATA,sha256=ZPAKG54N9GS7kD8SFbsanjETlNC3cLeyWm30nxAGCHc,14870
|
|
7
|
-
featrixsphere-0.1.583.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
featrixsphere-0.1.583.dist-info/entry_points.txt,sha256=QreJeYfD_VWvbEqPmMXZ3pqqlFlJ1qZb-NtqnyhEldc,51
|
|
9
|
-
featrixsphere-0.1.583.dist-info/top_level.txt,sha256=AyN4wjfzlD0hWnDieuEHX0KckphIk_aC73XCG4df5uU,14
|
|
10
|
-
featrixsphere-0.1.583.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|