diff --git a/backend/routers/statistics.py b/backend/routers/statistics.py index f260dca..e3376bc 100644 --- a/backend/routers/statistics.py +++ b/backend/routers/statistics.py @@ -116,7 +116,6 @@ class KeyValueCount(BaseModel): class InpatientKpis(BaseModel): total_admissions: int median_los_days: float - mean_cost: float cure_rate: float emergency_admit_ratio: float @@ -129,17 +128,6 @@ class LosByDisease(BaseModel): n: int -class CostByDisease(BaseModel): - diagnosis: str - mean_cost: float - n: int - - -class CostVsLos(BaseModel): - los: int - cost: float - - class LabelCount(BaseModel): outcome: Optional[str] = None route: Optional[str] = None @@ -168,9 +156,6 @@ class InpatientClinicalResponse(BaseModel): kpis: InpatientKpis los_histogram: list[KeyValueCount] los_by_disease: list[LosByDisease] - cost_histogram: list[KeyValueCount] - cost_by_disease: list[CostByDisease] - cost_vs_los: list[CostVsLos] outcome_counts: list[OutcomeCount] admission_route_counts: list[RouteCount] bmi_by_age_band: list[BmiByAge] @@ -252,11 +237,10 @@ class TemporalResponse(BaseModel): def _empty_inpatient_clinical() -> InpatientClinicalResponse: return InpatientClinicalResponse( kpis=InpatientKpis( - total_admissions=0, median_los_days=0.0, mean_cost=0.0, + total_admissions=0, median_los_days=0.0, cure_rate=0.0, emergency_admit_ratio=0.0, ), - los_histogram=[], los_by_disease=[], cost_histogram=[], - cost_by_disease=[], cost_vs_los=[], outcome_counts=[], + los_histogram=[], los_by_disease=[], outcome_counts=[], admission_route_counts=[], bmi_by_age_band=[], ) @@ -287,8 +271,6 @@ def _compute_inpatient_clinical() -> InpatientClinicalResponse: total = len(df) median_los = float(df_los["los"].median()) if len(df_los) else 0.0 - cost = pd.to_numeric(df["住院总费用"], errors="coerce") - mean_cost = float(cost.mean()) if cost.notna().any() else 0.0 outcome = df["出院情况"].fillna("未知") cure_n = int(outcome.isin(["治愈", "好转"]).sum()) @@ -301,7 +283,6 @@ def _compute_inpatient_clinical() -> InpatientClinicalResponse: kpis = InpatientKpis( total_admissions=total, median_los_days=round(median_los, 2), - mean_cost=round(mean_cost, 2), cure_rate=round(cure_rate, 4), emergency_admit_ratio=round(emerg_ratio, 4), ) @@ -328,39 +309,6 @@ def _compute_inpatient_clinical() -> InpatientClinicalResponse: n=int(len(grp)), )) - # Cost histogram: 0-2k,2-4k,4-6k,6-8k,8-10k,10k+ - cost_valid = cost.dropna() - cost_bins = [(0, 2000, "0-2k"), (2000, 4000, "2-4k"), (4000, 6000, "4-6k"), - (6000, 8000, "6-8k"), (8000, 10000, "8-10k")] - cost_histogram: list[KeyValueCount] = [] - for lo, hi, label in cost_bins: - cost_histogram.append(KeyValueCount( - bin_label=label, count=int(((cost_valid >= lo) & (cost_valid < hi)).sum()))) - cost_histogram.append(KeyValueCount(bin_label="10k+", count=int((cost_valid >= 10000).sum()))) - - # Cost by disease (top 8 by n) - cost_by_disease: list[CostByDisease] = [] - df_cost = df[cost.notna()].copy() - df_cost["_cost"] = cost[cost.notna()] - if len(df_cost): - top_cd = df_cost["诊断名称"].value_counts().head(8).index.tolist() - for d in top_cd: - grp = df_cost[df_cost["诊断名称"] == d]["_cost"] - cost_by_disease.append(CostByDisease( - diagnosis=str(d), - mean_cost=round(float(grp.mean()), 2), - n=int(len(grp)), - )) - - # cost vs los scatter (up to 500 points) - cost_vs_los: list[CostVsLos] = [] - scatter_df = df_los[cost.reindex(df_los.index).notna()].copy() - scatter_df["_cost"] = cost.reindex(scatter_df.index) - if len(scatter_df) > 500: - scatter_df = scatter_df.sample(n=500, random_state=42) - for _, r in scatter_df.iterrows(): - cost_vs_los.append(CostVsLos(los=int(r["los"]), cost=round(float(r["_cost"]), 2))) - # outcome counts outcome_counts = [ OutcomeCount(outcome=str(k), count=int(v)) @@ -399,9 +347,6 @@ def _compute_inpatient_clinical() -> InpatientClinicalResponse: kpis=kpis, los_histogram=los_histogram, los_by_disease=los_by_disease, - cost_histogram=cost_histogram, - cost_by_disease=cost_by_disease, - cost_vs_los=cost_vs_los, outcome_counts=outcome_counts, admission_route_counts=admission_route_counts, bmi_by_age_band=bmi_by_age_band, diff --git a/frontend/e2e/clinical.spec.ts b/frontend/e2e/clinical.spec.ts index 958833f..a04c1de 100644 --- a/frontend/e2e/clinical.spec.ts +++ b/frontend/e2e/clinical.spec.ts @@ -10,7 +10,6 @@ const CLINICAL_FIXTURE = { kpis: { total_admissions: 5822, median_los_days: 4, - mean_cost: 6294, cure_rate: 0.991, emergency_admit_ratio: 0.47, }, @@ -23,19 +22,6 @@ const CLINICAL_FIXTURE = { { diagnosis: '肺炎', p25: 3, median: 5, p75: 7, n: 800 }, { diagnosis: '支气管炎', p25: 2, median: 4, p75: 6, n: 600 }, ], - cost_histogram: [ - { bin_label: '0-3k', count: 1800 }, - { bin_label: '3k-6k', count: 2200 }, - ], - cost_by_disease: [ - { diagnosis: '肺炎', mean_cost: 7200, n: 800 }, - { diagnosis: '支气管炎', mean_cost: 5100, n: 600 }, - ], - cost_vs_los: [ - { los: 3, cost: 5000 }, - { los: 5, cost: 7200 }, - { los: 7, cost: 9100 }, - ], outcome_counts: [ { outcome: '治愈', count: 3474 }, { outcome: '好转', count: 2298 }, diff --git a/frontend/src/components/clinical/ClinicalKpiRow.tsx b/frontend/src/components/clinical/ClinicalKpiRow.tsx index b49c442..cc86811 100644 --- a/frontend/src/components/clinical/ClinicalKpiRow.tsx +++ b/frontend/src/components/clinical/ClinicalKpiRow.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import { Users, CalendarDays, Wallet, HeartPulse, Siren } from 'lucide-react'; +import { Users, CalendarDays, HeartPulse, Siren } from 'lucide-react'; import { StatCard } from '@/components/StatCard'; import { TESTIDS } from '@/utils/testids'; import type { InpatientClinicalResponse } from '@/services/api'; @@ -8,12 +8,12 @@ interface ClinicalKpiRowProps { kpis: InpatientClinicalResponse['kpis']; } -/** 住院临床 5 项核心指标。375px 下 2 列,sm 起 5 列。 */ +/** 住院临床 4 项核心指标。375px 下 2 列,sm 起 4 列。 */ export const ClinicalKpiRow = memo(function ClinicalKpiRow({ kpis }: ClinicalKpiRowProps) { return (
- 住院天数、费用、出院结局与入院途径等临床特征分析 + 住院天数、出院结局、入院途径与年龄别 BMI 等临床特征分析