WorldArena2.0 / src /data_loader.py
WorldArena's picture
Update src/data_loader.py
5ed3eee verified
Raw
History Blame Contribute Delete
10.6 kB
import glob
import json
import os
from typing import List, Optional
import numpy as np
import pandas as pd
BASIC_METRICS = [
"Image Quality",
"Aesthetic Quality",
"JEPA Similarity",
"Dynamic Degree",
"Flow Score",
"Motion Smoothness",
"Subject Consistency",
"Background Consistency",
"Photometric Consistency",
"Interaction Quality",
"Trajectory Accuracy",
"Depth Accuracy",
"Perspectivity",
"Instruction Following",
"Semantic Alignment",
]
DIMENSION_MAP = {
"Visual Quality": ["Image Quality", "Aesthetic Quality", "JEPA Similarity"],
"Motion Quality": ["Dynamic Degree", "Flow Score", "Motion Smoothness"],
"Content Consistency": [
"Subject Consistency",
"Background Consistency",
"Photometric Consistency",
],
"Physics Adherence": ["Interaction Quality", "Trajectory Accuracy"],
"3D Accuracy": ["Depth Accuracy", "Perspectivity"],
"Controllability": [
"Instruction Following",
"Semantic Alignment",
],
}
ALL_METRICS = BASIC_METRICS + list(DIMENSION_MAP) + ["EWMScore"]
DIMENSION_METRICS = list(DIMENSION_MAP)
METRIC_CHOICES = sorted(ALL_METRICS)
TASK_AGGREGATES = {
"Data Engine": "Data Engine(",
"Action Planner": "Action Planner(",
"RL Environment": "RL Environment(",
"Visuo-Tactile Success Rate": "Visuo-Tactile(",
"Real Action Planner": "Real Action Planner(",
}
class DataLoader:
def __init__(self, results_dir: str = "./worldarena-results"):
self.results_dir = results_dir
self.df_all: Optional[pd.DataFrame] = None
self.BASIC_METRICS = BASIC_METRICS
self.DIMENSION_MAP = DIMENSION_MAP
self.DIMENSION_METRICS = DIMENSION_METRICS
self.ALL_METRICS = ALL_METRICS
self.METRIC_CHOICES = METRIC_CHOICES
def load_results(self) -> pd.DataFrame:
rows = []
file_patterns = ("*.json", "*.xlsx", "*.csv")
all_files = [
path
for pattern in file_patterns
for path in sorted(glob.glob(os.path.join(self.results_dir, pattern)))
]
for file_path in all_files:
model_name = os.path.splitext(os.path.basename(file_path))[0]
if file_path.endswith(".json"):
row = self._load_json_file(file_path, model_name)
else:
row = self._load_table_file(file_path, model_name)
if row:
rows.append(row)
df = pd.DataFrame(rows)
return self._process_data(df) if not df.empty else df
def _load_json_file(self, file_path: str, model_name: str) -> dict:
try:
with open(file_path, "r", encoding="utf-8") as file:
data = json.load(file)
row = {
"Model": data.get("Model", model_name),
"open_source": data.get(
"open source", data.get("open_source", "unknown")
),
"year": data.get("year", data.get("date", "unknown")),
}
def extract_values(data_dict, prefix=""):
for key, value in data_dict.items():
metric_name = f"{prefix}{key}" if prefix else key
if isinstance(value, dict):
extract_values(value, f"{metric_name}_")
elif isinstance(value, (int, float)):
row[metric_name] = value
extract_values(data.get("Metrics", {}))
return row
except Exception as exc:
print(f"Error loading JSON file {file_path}: {exc}")
return {}
def _load_table_file(self, file_path: str, model_name: str) -> dict:
try:
if file_path.endswith(".xlsx"):
table = pd.read_excel(file_path)
else:
table = pd.read_csv(file_path)
row = {"Model": model_name, "open_source": "unknown", "year": "unknown"}
for metadata_key in ("open source", "open_source"):
if metadata_key in table.columns:
row["open_source"] = table[metadata_key].iloc[0]
break
for metadata_key in ("year", "date"):
if metadata_key in table.columns:
row["year"] = table[metadata_key].iloc[0]
break
aliases = {
metric: [
metric,
metric.lower().replace(" ", "_"),
metric.replace(" ", "_"),
]
for metric in BASIC_METRICS
}
aliases["Image Quality"].append("imaging_quality")
aliases["JEPA Similarity"].append("JEPA_normalized")
for metric, candidates in aliases.items():
matching = next(
(candidate for candidate in candidates if candidate in table.columns),
None,
)
row[metric] = (
round(table[matching].mean(), 4) if matching else np.nan
)
return row
except Exception as exc:
print(f"Error loading table file {file_path}: {exc}")
return {}
@staticmethod
def _normalize_metadata(df: pd.DataFrame) -> pd.DataFrame:
if "open_source" in df.columns:
source_map = {
"yes": "Open-source",
"open-source": "Open-source",
"opensource": "Open-source",
"true": "Open-source",
"1": "Open-source",
"no": "Closed-source",
"closed-source": "Closed-source",
"closedsource": "Closed-source",
"false": "Closed-source",
"0": "Closed-source",
}
df["open_source"] = (
df["open_source"].astype(str).str.lower().map(source_map).fillna(
df["open_source"].astype(str).str.lower()
)
)
if "year" in df.columns:
df["year"] = df["year"].astype(str).str.extract(r"(\d{4})")[0]
return df
@staticmethod
def _to_percentage(series: pd.Series) -> pd.Series:
values = pd.to_numeric(series, errors="coerce")
valid = values.dropna()
if not valid.empty and valid.min() >= 0 and valid.max() <= 1:
values = values * 100
values = values.mask(values < 0, 0)
return values.round(2)
def _process_task_success_data(self, df: pd.DataFrame) -> pd.DataFrame:
aggregate_columns = {}
task_cols = []
for aggregate_name, prefix in TASK_AGGREGATES.items():
matching_cols = [
column for column in df.columns if column.startswith(prefix)
]
if matching_cols:
aggregate_columns[aggregate_name] = matching_cols
task_cols.extend(matching_cols)
for column in task_cols:
df[column] = self._to_percentage(df[column])
for aggregate_name, columns in aggregate_columns.items():
df[aggregate_name] = df[columns].mean(axis=1, skipna=True).round(2)
aggregate_cols = list(aggregate_columns)
df["EWMScore"] = df[aggregate_cols].mean(axis=1, skipna=True).round(2)
df = self._normalize_metadata(df).dropna(subset=["EWMScore"])
meta_cols = ["Model", "open_source", "year"]
return df[meta_cols + aggregate_cols + ["EWMScore"] + task_cols]
def _process_policy_evaluator_data(self, df: pd.DataFrame) -> pd.DataFrame:
df["Policy Evaluator"] = self._to_percentage(df["Policy Evaluator"])
df = self._normalize_metadata(df).dropna(subset=["Policy Evaluator"])
return df[["Model", "open_source", "year", "Policy Evaluator"]]
def _process_video_quality_data(self, df: pd.DataFrame) -> pd.DataFrame:
for metric in BASIC_METRICS:
if metric not in df.columns:
df[metric] = np.nan
else:
df[metric] = self._to_percentage(df[metric])
for dimension, sub_metrics in DIMENSION_MAP.items():
df[dimension] = df[sub_metrics].mean(axis=1, skipna=True).round(2)
df["EWMScore"] = df[BASIC_METRICS].mean(axis=1, skipna=True).round(2)
df = self._normalize_metadata(df)
df = df.dropna(subset=["EWMScore"])
meta_cols = ["Model", "open_source", "year"]
metric_cols = BASIC_METRICS + list(DIMENSION_MAP) + ["EWMScore"]
return df[meta_cols + metric_cols]
def _process_data(self, df: pd.DataFrame) -> pd.DataFrame:
metric_cols = [
column
for column in df.columns
if column not in ["Model", "open_source", "year"]
]
if any(
column.startswith(prefix)
for column in metric_cols
for prefix in TASK_AGGREGATES.values()
):
return self._process_task_success_data(df)
if "Policy Evaluator" in metric_cols and not any(
metric in metric_cols for metric in BASIC_METRICS
):
return self._process_policy_evaluator_data(df)
return self._process_video_quality_data(df)
def reload_data(self) -> str:
self.df_all = self.load_results()
if self.df_all is None or self.df_all.empty:
return (
f"No JSON or table files found in {self.results_dir}. "
"Please upload some results."
)
return f"Loaded {len(self.df_all)} models from {self.results_dir}"
def get_open_source_choices(self) -> List[str]:
if self.df_all is None or "open_source" not in self.df_all.columns:
return ["All"]
choices = sorted(
str(value)
for value in self.df_all["open_source"].dropna().unique()
if value != ""
)
return ["All"] + choices
def get_year_choices(self) -> List[str]:
if self.df_all is None or "year" not in self.df_all.columns:
return ["All"]
years = sorted(
(
str(value)
for value in self.df_all["year"].dropna().unique()
if value != ""
),
reverse=True,
)
return ["All"] + years