Spaces:
Runtime error
Runtime error
feat: synchronize text-to-sql-bot codebase with Hugging Face Space repository, including Docker build configurations
6086e71 | """ | |
| Auto Insights Generator β Statistical analysis and pattern detection on query results. | |
| Generates human-readable insights without LLM (pure statistical analysis). | |
| """ | |
| import structlog | |
| from collections import Counter | |
| logger = structlog.get_logger() | |
| class InsightsGenerator: | |
| """Generates statistical insights from query result data.""" | |
| def generate(self, results: list[dict], query: str = "") -> list[str]: | |
| """ | |
| Analyze query results and generate human-readable insights. | |
| Uses statistical methods β no LLM needed. | |
| """ | |
| if not results: | |
| return ["No data available for analysis."] | |
| insights = [] | |
| # ββ Basic stats ββββββββββββββββββββββββββββββββββ | |
| row_count = len(results) | |
| col_count = len(results[0]) if results else 0 | |
| insights.append(f"π Dataset: **{row_count}** records across **{col_count}** columns") | |
| # ββ Classify columns βββββββββββββββββββββββββββββ | |
| numeric_cols = [] | |
| text_cols = [] | |
| date_cols = [] | |
| columns = list(results[0].keys()) if results else [] | |
| for col in columns: | |
| sample = results[0].get(col) | |
| col_lower = col.lower() | |
| if any(d in col_lower for d in ["date", "time", "created", "updated"]): | |
| date_cols.append(col) | |
| elif isinstance(sample, (int, float)): | |
| numeric_cols.append(col) | |
| else: | |
| try: | |
| if sample is not None: | |
| float(sample) | |
| numeric_cols.append(col) | |
| else: | |
| text_cols.append(col) | |
| except (ValueError, TypeError): | |
| text_cols.append(col) | |
| # ββ Numeric column analysis ββββββββββββββββββββββ | |
| for col in numeric_cols[:4]: | |
| values = self._extract_numeric_values(results, col) | |
| if not values: | |
| continue | |
| avg = sum(values) / len(values) | |
| min_val = min(values) | |
| max_val = max(values) | |
| total = sum(values) | |
| label = col.replace("_", " ").title() | |
| insights.append( | |
| f"**{label}**: Total {total:,.2f} | " | |
| f"Avg {avg:,.2f} | Range [{min_val:,.2f} β {max_val:,.2f}]" | |
| ) | |
| # Outlier detection (IQR method) | |
| outliers = self._detect_outliers(values) | |
| if outliers: | |
| insights.append( | |
| f"β οΈ **{len(outliers)} outliers** detected in {label} " | |
| f"(values: {', '.join(f'{v:,.2f}' for v in outliers[:3])})" | |
| ) | |
| # Concentration analysis | |
| if len(values) > 1: | |
| top_val = max(values) | |
| top_pct = (top_val / total * 100) if total > 0 else 0 | |
| if top_pct > 30: | |
| insights.append(f"π― Top value in **{label}** accounts for {top_pct:.1f}% of total") | |
| # ββ Text column analysis βββββββββββββββββββββββββ | |
| for col in text_cols[:2]: | |
| str_values = [str(row.get(col, "")) for row in results if row.get(col)] | |
| if not str_values: | |
| continue | |
| counter = Counter(str_values) | |
| unique_count = len(counter) | |
| label = col.replace("_", " ").title() | |
| if unique_count == row_count: | |
| insights.append(f"π **{label}** has all unique values") | |
| elif unique_count <= 10: | |
| top_items = counter.most_common(3) | |
| distribution = ", ".join(f"'{k}' ({v})" for k, v in top_items) | |
| insights.append(f"π·οΈ **{label}** distribution: {distribution}") | |
| # ββ Trend detection ββββββββββββββββββββββββββββββ | |
| if date_cols and numeric_cols and len(results) >= 3: | |
| insights.append("π Time-series data detected β trend analysis available") | |
| return insights | |
| def _extract_numeric_values(self, results: list[dict], col: str) -> list[float]: | |
| """Extract numeric values from a column, skipping nulls.""" | |
| values = [] | |
| for row in results: | |
| v = row.get(col) | |
| if v is not None: | |
| try: | |
| values.append(float(v)) | |
| except (ValueError, TypeError): | |
| continue | |
| return values | |
| def _detect_outliers(self, values: list[float]) -> list[float]: | |
| """Detect outliers using the IQR method.""" | |
| if len(values) < 4: | |
| return [] | |
| sorted_vals = sorted(values) | |
| n = len(sorted_vals) | |
| q1 = sorted_vals[int(n * 0.25)] | |
| q3 = sorted_vals[int(n * 0.75)] | |
| iqr = q3 - q1 | |
| lower = q1 - 1.5 * iqr | |
| upper = q3 + 1.5 * iqr | |
| return [v for v in values if v < lower or v > upper] | |