# Created: 2026-03-07
# Purpose: Horizontal bar chart for 7 forensic audio features
# Dependencies: plotly, numpy
"""Horizontal bar chart visualization for 7 forensic audio features."""
import plotly.graph_objects as go
def plot_feature_bars(feature_stats: dict) -> go.Figure:
"""7개 포렌식 피처를 horizontal bar chart로 시각화.
Args:
feature_stats: Dict with feature names as keys and normalized values (0-1)
Returns:
plotly Figure (horizontal bar chart)
"""
# 7개 포렌식 피처 (짧은 레이블)
features = [
"Spectral Flux",
"H/P Ratio",
"Temporal Accel",
"Temporal Delta",
"Percussive",
"Harmonic",
"Residual Energy",
]
# 기본값 (feature_stats가 없으면 중간값)
if not feature_stats:
values = [0.5] * 7
else:
values = [
feature_stats.get("spectral_flux", 0.5),
feature_stats.get("hp_ratio", 0.5),
feature_stats.get("temporal_accel", 0.5),
feature_stats.get("temporal_delta", 0.5),
feature_stats.get("percussive_strength", 0.5),
feature_stats.get("harmonic_strength", 0.5),
feature_stats.get("residual_energy", 0.5),
]
# AI 가능성 기준: 높을수록 AI 시그니처
# Residual Energy, H/P Ratio는 높을수록 AI
# Temporal Delta/Accel는 낮을수록 AI (부드러운 변화)
# Harmonic/Percussive는 특정 비율로 수렴
# Spectral Flux는 낮을수록 AI (일관적 변화)
# AI 시그니처 강도에 따라 색상 결정
colors = []
for i, (feat, val) in enumerate(zip(features, values)):
if "Residual" in feat or "H/P" in feat:
# 높을수록 AI
if val >= 0.7:
colors.append('#ff4757') # AI (red)
elif val >= 0.4:
colors.append('#ffa502') # Uncertain (orange)
else:
colors.append('#2ed573') # Human (green)
elif "Temporal" in feat or "Spectral" in feat:
# 낮을수록 AI
if val <= 0.3:
colors.append('#ff4757') # AI (red)
elif val <= 0.6:
colors.append('#ffa502') # Uncertain (orange)
else:
colors.append('#2ed573') # Human (green)
else:
# Harmonic/Percussive는 중립
colors.append('#5f9ea0') # Neutral (cyan)
fig = go.Figure(go.Bar(
x=values,
y=features,
orientation='h',
marker=dict(
color=colors,
line=dict(color='#fff', width=1)
),
text=[f"{v:.2f}" for v in values],
textposition='inside',
textfont=dict(size=11, color='white', family='monospace'),
hovertemplate="%{y}
Score: %{x:.3f}",
))
fig.update_layout(
xaxis=dict(
title="Feature Strength",
range=[0, 1],
tickfont=dict(size=10, color='#aaa'),
gridcolor='#333',
),
yaxis=dict(
tickfont=dict(size=11, color='white'),
),
plot_bgcolor='#1a1a2e',
paper_bgcolor='#1a1a2e',
font=dict(color='white'),
margin=dict(l=140, r=20, t=40, b=40),
height=300,
showlegend=False,
title=dict(
text="Forensic Feature Strength",
font=dict(size=13),
x=0.5, xanchor='center'
)
)
return fig