Spaces:
Sleeping
Sleeping
Download src/plot2eq/data_prep/normalize.py from YauheniShe/Plot2Eq: direct link, hf CLI and curl.
- Browser
- Download file 4.59 kB
-
https://huggingface.co/spaces/YauheniShe/Plot2Eq/resolve/main/src/plot2eq/data_prep/normalize.py
- Command line
-
hf download hf://spaces/YauheniShe/Plot2Eq/src/plot2eq/data_prep/normalize.py
-
curl -L -o normalize.py https://huggingface.co/spaces/YauheniShe/Plot2Eq/resolve/main/src/plot2eq/data_prep/normalize.py
4.59 kB
| import gzip | |
| import pickle | |
| from pathlib import Path | |
| import numpy as np | |
| from scipy.interpolate import interp1d | |
| from scipy.ndimage import binary_opening | |
| from tqdm import tqdm | |
| def raw_to_normalized_features( | |
| points, num_points=256, jump_threshold=0.3, structure_size=3 | |
| ): | |
| # 1. Первичная нормализация и маска (Bounding Box) | |
| x_raw, y_raw = points[:, 0], points[:, 1] | |
| x_min, x_max = np.min(x_raw), np.max(x_raw) | |
| y_min, y_max = np.min(y_raw), np.max(y_raw) | |
| x_range = x_max - x_min if x_max != x_min else 1.0 | |
| y_range = y_max - y_min if y_max != y_min else 1.0 | |
| x_norm = (x_raw - x_min) / x_range | |
| y_norm = np.full_like(y_raw, 0.5) if y_max == y_min else (y_raw - y_min) / y_range | |
| dx = np.diff(x_norm) | |
| gap_indices = np.where(dx > 3 * np.median(dx))[0] if len(dx) > 0 else [] | |
| valid_intervals = [] | |
| start_idx = 0 | |
| for gap_idx in gap_indices: | |
| valid_intervals.append((x_norm[start_idx], x_norm[gap_idx])) | |
| start_idx = gap_idx + 1 | |
| valid_intervals.append((x_norm[start_idx], x_norm[-1])) | |
| x_fixed = np.linspace(0, 1, num_points) | |
| sort_idx = np.argsort(x_norm) | |
| y_fixed = np.interp(x_fixed, x_norm[sort_idx], y_norm[sort_idx]) | |
| mask = np.zeros_like(x_fixed, dtype=np.float32) | |
| for start, end in valid_intervals: | |
| mask[(x_fixed >= start) & (x_fixed <= end)] = 1.0 | |
| # 2. Удаление ложных асимптот по оси Y | |
| valid_y_orig = y_fixed[mask == 1] | |
| if len(valid_y_orig) < 2: | |
| return None | |
| y_range_mask = np.nanmax(valid_y_orig) - np.nanmin(valid_y_orig) | |
| if y_range_mask > 1e-5: | |
| y_diff = np.abs(np.diff(y_fixed)) | |
| asymptote_indices = np.where( | |
| (y_diff > y_range_mask * jump_threshold) | |
| & (mask[:-1] == 1) | |
| & (mask[1:] == 1) | |
| )[0] | |
| for idx in asymptote_indices: | |
| mask[idx] = 0 | |
| # 3. Удаление ошметков (< 3 пикселей) | |
| structure = np.ones(structure_size) | |
| cleaned_mask = binary_opening(mask, structure=structure).astype(int) # type: ignore | |
| if np.sum(cleaned_mask) < 5: | |
| return None | |
| # 4. Обрезка BBox по X и финальная интерполяция | |
| valid_indices = np.where(cleaned_mask == 1)[0] | |
| i_min, i_max = valid_indices[0], valid_indices[-1] | |
| if i_max == i_min: | |
| return None | |
| x_orig = np.linspace(0, 1, len(y_fixed)) | |
| x_min_bbox, x_max_bbox = x_orig[i_min], x_orig[i_max] | |
| x_new = np.linspace(x_min_bbox, x_max_bbox, num_points) | |
| y_interp_func = interp1d( | |
| x_orig, y_fixed, kind="linear", bounds_error=False, fill_value=np.nan | |
| ) | |
| y_resampled = y_interp_func(x_new) | |
| mask_interp_func = interp1d( | |
| x_orig, cleaned_mask, kind="nearest", bounds_error=False, fill_value=0 | |
| ) | |
| mask_resampled = mask_interp_func(x_new).astype(int) | |
| # 5. Финальный Min-Max Scaling по Y | |
| valid_y_resampled = y_resampled[mask_resampled == 1] | |
| if len(valid_y_resampled) == 0 or np.isnan(valid_y_resampled).all(): | |
| return None | |
| y_min_val, y_max_val = np.nanmin(valid_y_resampled), np.nanmax(valid_y_resampled) | |
| diff = y_max_val - y_min_val | |
| y_scaled = ( | |
| np.full_like(y_resampled, 0.5) | |
| if diff < 1e-6 | |
| else (y_resampled - y_min_val) / diff | |
| ) | |
| y_scaled[mask_resampled == 0] = 0.0 | |
| return np.vstack((y_scaled.astype(np.float32), mask_resampled.astype(np.float32))) | |
| def run_normalization(input_dir: Path, output_dir: Path): | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| input_files = sorted(input_dir.glob("chunk_*.pkl.gz")) | |
| total_processed = 0 | |
| for filepath in tqdm(input_files, desc="Нормализация и чистка графиков"): | |
| with gzip.open(filepath, "rb") as f: | |
| data = pickle.load(f) | |
| new_data = [] | |
| for item in data: | |
| features = raw_to_normalized_features(item["points"]) | |
| if features is not None: | |
| new_data.append( | |
| { | |
| "expr_str": item["expr_str"], | |
| "expr_instantiated_str": item["expr_instantiated_str"], | |
| "tokens": item["tokens"], | |
| "features": features, | |
| } | |
| ) | |
| if new_data: | |
| with gzip.open(output_dir / filepath.name, "wb") as f_out: | |
| pickle.dump(new_data, f_out) | |
| total_processed += len(new_data) | |
| print(f"Идеальных графиков сохранено: {total_processed}") | |