Spaces:
Sleeping
Sleeping
| """ | |
| Export utilities for the Advanced Data Explorer. | |
| Downsampling, multi-window CSV packaging, size estimation. | |
| """ | |
| import io | |
| import zipfile | |
| import pandas as pd | |
| from typing import List, Optional | |
| def downsample_dataframe(df: pd.DataFrame, target_hz: float) -> pd.DataFrame: | |
| """Downsample a time-indexed DataFrame to a target frequency. | |
| Args: | |
| df: DataFrame with a 'timestamp' column | |
| target_hz: Target frequency in Hz | |
| Returns: | |
| Downsampled DataFrame | |
| """ | |
| if "timestamp" not in df.columns or len(df) < 2: | |
| return df | |
| sorted_df = df.sort_values("timestamp") | |
| dt = sorted_df["timestamp"].diff().dt.total_seconds().dropna() | |
| current_hz = 1.0 / dt.median() if dt.median() > 0 else 1.0 | |
| if target_hz >= current_hz: | |
| return sorted_df | |
| step = max(1, int(round(current_hz / target_hz))) | |
| return sorted_df.iloc[::step].reset_index(drop=True) | |
| def package_multi_window_csv( | |
| dfs: List[pd.DataFrame], | |
| labels: Optional[List[str]] = None, | |
| base_filename: str = "export", | |
| ) -> bytes: | |
| """Package multiple DataFrames into a zip of CSVs. | |
| Args: | |
| dfs: List of DataFrames to export | |
| labels: Optional labels for each window (used in filenames) | |
| base_filename: Base filename for the zip | |
| Returns: | |
| Bytes of the zip file | |
| """ | |
| buffer = io.BytesIO() | |
| with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf: | |
| for i, df in enumerate(dfs): | |
| label = labels[i] if labels and i < len(labels) else f"window_{i + 1}" | |
| safe_label = label.replace(" ", "_").replace("/", "-") | |
| csv_name = f"{base_filename}_{safe_label}.csv" | |
| csv_bytes = df.to_csv(index=False).encode("utf-8") | |
| zf.writestr(csv_name, csv_bytes) | |
| buffer.seek(0) | |
| return buffer.getvalue() | |
| def estimate_csv_size(df: pd.DataFrame) -> str: | |
| """Estimate CSV file size as a human-readable string.""" | |
| # Rough estimate: 50 bytes per cell | |
| cells = df.shape[0] * df.shape[1] | |
| bytes_est = cells * 50 | |
| if bytes_est < 1024: | |
| return f"{bytes_est} B" | |
| elif bytes_est < 1024 * 1024: | |
| return f"{bytes_est / 1024:.0f} KB" | |
| else: | |
| return f"{bytes_est / (1024 * 1024):.1f} MB" | |