File size: 6,471 Bytes
3c5a6a0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""๋ฆฌ๋”๋ณด๋“œ ์ปฌ๋Ÿผ ๊ทœ์น™.

๋ฒค์น˜๋งˆํฌ ์ปฌ๋Ÿผ์€ `<๋ฐ์ดํ„ฐ์…‹>_<์นดํ…Œ๊ณ ๋ฆฌ>` ๋ช…๋ช… ๊ทœ์น™(์˜ˆ: Coupang_Fire, Soil_v2_Smoke,
AVG_Falldown)์„ ๋”ฐ๋ฅธ๋‹ค. ์ด ๊ทœ์น™์œผ๋กœ df.columns ๋งŒ ๋ณด๊ณ  ์นดํ…Œ๊ณ ๋ฆฌ/๋ฐ์ดํ„ฐ์…‹ ์ถ•์„
์ž๋™ ๋ถ„๋ฅ˜ํ•˜๊ณ  ํ‘œ์‹œ/์ˆจ๊น€ ์ปฌ๋Ÿผ์„ ๊ณ„์‚ฐํ•œ๋‹ค โ€” ์‹ ๊ทœ ๋ฒค์น˜๋งˆํฌ๊ฐ€ ๋“ฑ๋ก๋˜์–ด๋„
config ๋ชฉ๋ก์„ ์†๋ณผ ํ•„์š”๊ฐ€ ์—†๋‹ค (๊ธฐ์กด ON_LOAD_COLUMNS/HIDE_COLUMNS ๋Œ€์ฒด).
"""
from typing import List, Optional, Sequence, Tuple

import pandas as pd

import enviroments.config as config

# AVG_* ์ปฌ๋Ÿผ์˜ prefix. ๋ฐ์ดํ„ฐ์…‹ ์ถ•์—์„œ๋Š” ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค.
AVG_DATASET = "AVG"


def split_benchmark_column(column: str) -> Optional[Tuple[str, str]]:
    """`<๋ฐ์ดํ„ฐ์…‹>_<์นดํ…Œ๊ณ ๋ฆฌ>` ์ปฌ๋Ÿผ์„ (๋ฐ์ดํ„ฐ์…‹, ์นดํ…Œ๊ณ ๋ฆฌ)๋กœ ๋ถ„ํ•ด.

    ๊ทœ์น™ ๋ฐ– ์ปฌ๋Ÿผ(TASK, Model, "Model name" ๋“ฑ)์€ None.
    """
    if "_" not in column or " " in column:
        return None
    dataset, _, category = column.rpartition("_")
    if not dataset or not category:
        return None
    return dataset, category


def build_hide_columns(columns: Sequence[str]) -> List[str]:
    """ํ…Œ์ด๋ธ”์—์„œ ์•„์˜ˆ ์ œ์™ธํ•  ์ปฌ๋Ÿผ (์ˆจ๊น€ ์นดํ…Œ๊ณ ๋ฆฌ ํŒจํ„ด + ๊ฐœ๋ณ„ ์ง€์ •)."""
    hidden = []
    for col in columns:
        if col in config.HIDE_COLUMN_EXACT:
            hidden.append(col)
            continue
        parsed = split_benchmark_column(col)
        if parsed and parsed[1] in config.HIDE_CATEGORIES:
            hidden.append(col)
    return hidden


def parse_axes(columns: Sequence[str]) -> Tuple[List[str], List[str]]:
    """df.columns โ†’ (์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก, ๋ฐ์ดํ„ฐ์…‹ ๋ชฉ๋ก). ์ˆจ๊น€ ์ปฌ๋Ÿผ/AVG ๋ฐ์ดํ„ฐ์…‹ ์ œ์™ธ."""
    hidden = set(build_hide_columns(columns))
    categories, datasets = set(), set()
    for col in columns:
        if col in hidden:
            continue
        parsed = split_benchmark_column(col)
        if parsed is None:
            continue
        dataset, category = parsed
        categories.add(category)
        if dataset != AVG_DATASET:
            datasets.add(dataset)
    return sorted(categories), sorted(datasets)


def extra_columns(columns: Sequence[str]) -> List[str]:
    """๋ฒค์น˜๋งˆํฌ ๋ช…๋ช… ๊ทœ์น™ ๋ฐ–์ด๋ฉด์„œ ๊ธฐ๋ณธ/ํ•„์ˆ˜ ์ปฌ๋Ÿผ๋„ ์•„๋‹Œ ๋ถ€๊ฐ€ ์ปฌ๋Ÿผ.

    ์˜ˆ: "Datasets Used", "Infer Speed". ์…€๋ ‰ํ„ฐ์˜ '์ถ”๊ฐ€ ์ •๋ณด' ์ถ•์ด ๋œ๋‹ค.
    """
    always_on = set(config.LEADERBOARD_BASE_COLUMNS) | set(config.OFF_LOAD_COLUMNS)
    hidden = set(build_hide_columns(columns))
    return [
        col
        for col in columns
        if col not in always_on
        and col not in hidden
        and split_benchmark_column(col) is None
    ]


def avg_categories(columns: Sequence[str]) -> List[str]:
    """AVG_<์นดํ…Œ๊ณ ๋ฆฌ> ์ปฌ๋Ÿผ์ด ์กด์žฌํ•˜๋Š” ์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก (์ˆจ๊น€ ์ œ์™ธ, df ์ˆœ์„œ)."""
    hidden = set(build_hide_columns(columns))
    cats = []
    for col in columns:
        if col in hidden:
            continue
        parsed = split_benchmark_column(col)
        if parsed and parsed[0] == AVG_DATASET:
            cats.append(parsed[1])
    return cats


def datasets_for_category(columns: Sequence[str], category: str) -> List[str]:
    """ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ ๋ฒค์น˜๋งˆํฌ(<๋ฐ์ดํ„ฐ์…‹>_<์นดํ…Œ๊ณ ๋ฆฌ>)๋ฅผ ๊ฐ€์ง„ ์ „์ฒด ๋ฐ์ดํ„ฐ์…‹."""
    out = set()
    for col in columns:
        parsed = split_benchmark_column(col)
        if parsed and parsed[1] == category and parsed[0] != AVG_DATASET:
            out.add(parsed[0])
    return sorted(out)


def default_avg_map(columns: Sequence[str]) -> dict:
    """์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ AVG ๊ณ„์‚ฐ ๊ธฐ์ค€์˜ ๊ธฐ๋ณธ๊ฐ’: ์ •์‹ ์„ธํŠธ โˆฉ ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ ๋ณด์œ  ๋ฐ์ดํ„ฐ์…‹."""
    return {
        cat: [
            d
            for d in config.DEFAULT_AVG_DATASETS
            if d in datasets_for_category(columns, cat)
        ]
        for cat in avg_categories(columns)
    }


def apply_avg_override(df: pd.DataFrame, avg_map: Optional[dict] = None) -> pd.DataFrame:
    """AVG_<์นดํ…Œ๊ณ ๋ฆฌ> ์ปฌ๋Ÿผ์„ ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๊ธฐ์ค€ ๋ฐ์ดํ„ฐ์…‹์˜ ํ–‰๋ณ„ ํ‰๊ท ์œผ๋กœ ๋Œ€์ฒด.

    ๊ตฌ๊ธ€ ์‹œํŠธ์˜ AVG ์›๋ณธ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ํ™”๋ฉด ํ‘œ์‹œ๊ฐ’๋งŒ ๊ต์ฒดํ•œ๋‹ค.
    - avg_map: {์นดํ…Œ๊ณ ๋ฆฌ: [๋ฐ์ดํ„ฐ์…‹, ...]}. ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฉด
      config.DEFAULT_AVG_DATASETS (์ •์‹ ๋ฒค์น˜๋งˆํฌ ์„ธํŠธ) ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐ.
    - ๊ทธ๋ฃน ์ปฌ๋Ÿผ ๊ฐ’์ด ํ•˜๋‚˜๋ผ๋„ ๋น„์–ด ์žˆ๋Š” ๋ชจ๋ธ์€ ๋นˆ์นธ (์™„์ฃผํ•œ ๋ชจ๋ธ๋งŒ AVG ํ‘œ์‹œ).
    - ๊ธฐ์ค€ ๋ฐ์ดํ„ฐ์…‹์— ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ ์ปฌ๋Ÿผ์ด ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด ์ „์ฒด ๋นˆ์นธ.
    """
    avg_map = avg_map or {}
    df = df.copy()
    for col in df.columns:
        parsed = split_benchmark_column(col)
        if parsed is None or parsed[0] != AVG_DATASET:
            continue
        category = parsed[1]
        selected = avg_map.get(category) or config.DEFAULT_AVG_DATASETS
        members = [
            f"{d}_{category}" for d in selected if f"{d}_{category}" in df.columns
        ]
        if not members:
            df[col] = ""
            continue
        vals = df[members].apply(pd.to_numeric, errors="coerce")
        avg = vals.mean(axis=1)
        avg[vals.isna().any(axis=1)] = float("nan")
        df[col] = avg.map(lambda v: "" if pd.isna(v) else f"{v:.4f}")
    return df


def compute_display_columns(
    columns: Sequence[str],
    categories: Sequence[str],
    datasets: Sequence[str],
    extras: Sequence[str] = (),
) -> List[str]:
    """์„ ํƒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ ร— ๋ฐ์ดํ„ฐ์…‹ ๊ต์ง‘ํ•ฉ + ๋ถ€๊ฐ€ ์ปฌ๋Ÿผ์˜ ํ‘œ์‹œ ๋ชฉ๋ก (df ์ปฌ๋Ÿผ ์ˆœ์„œ ์œ ์ง€).

    datasets ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ์ „์ฒด ๋ฐ์ดํ„ฐ์…‹(AVG ํฌํ•จ)์œผ๋กœ ์ทจ๊ธ‰.
    ๊ธฐ๋ณธ/ํ•„์ˆ˜ ์ปฌ๋Ÿผ(TASK, Model, Model name ๋“ฑ)์€ ํ•ญ์ƒ ํ‘œ์‹œ.
    """
    selected_cats = set(categories or [])
    selected_ds = set(datasets or [])
    selected_extras = set(extras or [])
    always_on = set(config.LEADERBOARD_BASE_COLUMNS) | set(config.OFF_LOAD_COLUMNS)
    hidden = set(build_hide_columns(columns))

    display = []
    for col in columns:
        if col in hidden:
            continue
        parsed = split_benchmark_column(col)
        if parsed is None:
            if col in always_on or col in selected_extras:
                display.append(col)
            continue
        dataset, category = parsed
        if category not in selected_cats:
            continue
        if selected_ds and dataset not in selected_ds:
            continue
        display.append(col)
    return display