From 244967bad52d66fea8e2dc22dd2cd28aaa6d4c57 Mon Sep 17 00:00:00 2001 From: PM-pinou <2504420230@qq.com> Date: Fri, 24 Jul 2026 11:58:31 +0800 Subject: [PATCH] fix(cluster_detail): show raw data rows instead of entity profiles --- analysis/views/clustering.py | 51 ++++++++++++++++------- templates/analysis/cluster_detail.html | 56 ++++++++++++++------------ 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/analysis/views/clustering.py b/analysis/views/clustering.py index 2978de3..ec7ffd0 100644 --- a/analysis/views/clustering.py +++ b/analysis/views/clustering.py @@ -161,7 +161,9 @@ def _get_globe_flows(run, max_rows=500): def cluster_detail(request, display_id, cluster_label): - """Detailed view of a single cluster with entity list, SVD features, and NL summary.""" + """Detailed view of a single cluster with raw data rows, SVD features, and NL summary.""" + import json + import polars as pl run = get_object_or_404(AnalysisRun, display_id=display_id) try: cluster_label = int(cluster_label) @@ -169,29 +171,50 @@ def cluster_detail(request, display_id, cluster_label): raise Http404("Invalid cluster label") cluster = get_object_or_404(ClusterResult, run=run, cluster_label=cluster_label) features = cluster.features.all().order_by('-distinguishing_score')[:50] - entities = cluster.entities.all()[:100] + + # Load raw data rows for this cluster from EP row indices + row_data = [] + try: + from analysis.data_loader import load_csv_directory, load_from_db + lf = None + if run.sqlite_table: + lf = load_from_db(run.sqlite_table) + if lf is None and run.csv_glob: + lf, _, _, _, _ = load_csv_directory(run.csv_glob) + if lf is not None: + # Get row indices assigned to this cluster from EntityProfile + eps = cluster.entities.all().order_by('entity_value')[:100] + indices = [] + for ep in eps: + try: + idx = int(ep.entity_value.replace('row_', '')) + indices.append(idx) + except (ValueError, AttributeError): + continue + if indices: + df = lf.collect(streaming=True) + df_filtered = df[indices] + # Convert to dict list for template (limit columns for display) + cols = df_filtered.columns[:20] # show first 20 columns + for row_idx, row in enumerate(df_filtered.select(cols).iter_rows(named=True)): + entry = {'index': indices[row_idx] if row_idx < len(indices) else row_idx} + for k, v in row.items(): + entry[k] = v + row_data.append(entry) + except Exception as exc: + logger.warning(f'Failed to load raw data for cluster detail: {exc}') + nl_summary = '' feats_list = [{'feature_name': f.feature_name, 'score': f.distinguishing_score, 'mean': f.mean, 'std': f.std} for f in features[:10]] if feats_list: nl_summary = nl_describe.describe_cluster(feats_list) - # Build entity data with feature_json values - entity_data = [] - for e in entities: - entry = {'id': e.id, 'value': e.entity_value, - 'embedding_x': e.embedding_x, 'embedding_y': e.embedding_y} - if e.feature_json: - entry['features'] = {k: round(float(v), 4) if isinstance(v, (int, float)) else str(v) - for k, v in e.feature_json.items()} - entity_data.append(entry) - return render(request, 'analysis/cluster_detail.html', { 'run': run, 'cluster': cluster, 'features': features, - 'entities': entities, - 'entity_data_json': json.dumps(entity_data), + 'row_data': row_data, 'nl_summary': nl_summary, }) diff --git a/templates/analysis/cluster_detail.html b/templates/analysis/cluster_detail.html index 643295c..28572cd 100644 --- a/templates/analysis/cluster_detail.html +++ b/templates/analysis/cluster_detail.html @@ -3,14 +3,12 @@ {% block title %}簇 #{{ cluster.cluster_label }} — 运行 #{{ run.display_id }}{% endblock %} {% block content %}
| # | + {% for k, v in row_data.0.items %} +{{ k }} | {% endfor %} - {% if e.feature_json.items|length > 8 %}+{{ e.feature_json.items|length|add:"-8" }} more{% endif %} - - {% endif %} - - {% empty %} -
|---|---|
| {{ forloop.counter }} | + {% for k, v in row.items %} +{{ v|default:"-" }} |
+ {% endfor %}
+
该簇无数据行