sem / modules /utils.py
cyj-26's picture
Upload 25 files
26c3195 verified
import pandas as pd
import numpy as np
import io
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
def detect_scale(series: pd.Series) -> str:
try:
s = series.dropna()
if len(s) == 0:
return "unknown"
if not pd.api.types.is_numeric_dtype(series):
return "categorical"
u = s.unique()
if len(u) <= 2:
return "binary"
if s.min() >= 1 and s.max() <= 7 and len(u) <= 7:
return "likert"
return "continuous"
except Exception:
return "unknown"
def cronbach_alpha(data: pd.DataFrame) -> float:
try:
d = data.select_dtypes(include=[np.number]).dropna()
k = d.shape[1]
if k < 2:
return np.nan
iv = d.var(ddof=1, axis=0).sum()
tv = d.sum(axis=1).var(ddof=1)
if tv == 0:
return np.nan
return round(float((k / (k - 1)) * (1 - iv / tv)), 3)
except Exception:
return np.nan
def calc_ave_cr(loadings):
try:
lam = np.array(loadings, dtype=float)
lam2 = lam ** 2
ave = float(np.mean(lam2))
sl = float(np.sum(lam))
cr = sl**2 / (sl**2 + float(np.sum(1 - lam2)))
return round(ave, 3), round(cr, 3)
except Exception:
return np.nan, np.nan
def sig_stars(p) -> str:
try:
p = float(p)
if p < 0.001: return "***"
if p < 0.01: return "**"
if p < 0.05: return "*"
return "n.s."
except Exception:
return "-"
def fmt_p(p) -> str:
try:
p = float(p)
return "< .001" if p < 0.001 else f"{p:.3f}"
except Exception:
return "-"
# ── Excel μŠ€νƒ€μΌ ──────────────────────────────────────────────────────────────
def _hdr():
return Font(name="Arial", bold=True, color="FFFFFF", size=11)
def _fill():
return PatternFill("solid", fgColor="2F5496")
def _bd():
return Border(left=Side(style="thin"), right=Side(style="thin"),
top=Side(style="thin"), bottom=Side(style="thin"))
def _ctr():
return Alignment(horizontal="center", vertical="center", wrap_text=True)
def _lft():
return Alignment(horizontal="left", vertical="center")
def df_to_sheet(wb: Workbook, sheet_name: str, title: str,
df_data: pd.DataFrame, note: str = "", adopt_col: str = None):
try:
ws = wb.create_sheet(sheet_name[:31])
cols = list(df_data.columns)
nc = max(len(cols), 1)
# 제λͺ©
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=nc)
ws["A1"] = title
ws["A1"].font = Font(name="Arial", bold=True, size=12)
# 헀더
for ci, col in enumerate(cols, 1):
c = ws.cell(row=2, column=ci, value=str(col))
c.font = _hdr(); c.fill = _fill()
c.alignment = _ctr(); c.border = _bd()
# adopt_col 인덱슀
adopt_idx = (cols.index(adopt_col) + 1) if adopt_col and adopt_col in cols else None
# 데이터
adopt_fill_y = PatternFill("solid", fgColor="E2EFDA")
adopt_fill_n = PatternFill("solid", fgColor="FFC7CE")
adopt_font_y = Font(name="Arial", size=10, color="375623")
adopt_font_n = Font(name="Arial", size=10, color="9C0006")
for ri, row in enumerate(df_data.itertuples(index=False), 3):
for ci, val in enumerate(row, 1):
safe_val = "" if (isinstance(val, float) and np.isnan(val)) else val
cell = ws.cell(row=ri, column=ci, value=safe_val)
cell.alignment = _lft() if ci <= 2 else _ctr()
cell.border = _bd()
if adopt_idx and ci == adopt_idx:
is_y = str(val) == "채택"
cell.fill = adopt_fill_y if is_y else adopt_fill_n
cell.font = adopt_font_y if is_y else adopt_font_n
# 주석
if note:
nr = len(df_data) + 4
note_cell = ws.cell(row=nr, column=1, value=note)
note_cell.font = Font(name="Arial", size=9, color="595959", italic=True)
# μ—΄ λ„ˆλΉ„ μžλ™μ‘°μ •
for col_cells in ws.columns:
try:
w = max((len(str(c.value)) if c.value else 0) for c in col_cells)
ws.column_dimensions[col_cells[0].column_letter].width = min(w + 4, 45)
except Exception:
pass
ws.row_dimensions[2].height = 28
except Exception as e:
pass # μ‹œνŠΈ 생성 μ‹€νŒ¨ μ‹œ 쑰용히 λ„˜μ–΄κ°
def build_excel(sheets: dict) -> bytes:
wb = Workbook()
wb.remove(wb.active)
for name, payload in sheets.items():
try:
title = payload[0]
df = payload[1]
note = payload[2] if len(payload) > 2 else ""
adopt_col = payload[3] if len(payload) > 3 else None
if df is not None and len(df) > 0:
df_to_sheet(wb, name, title, df, note, adopt_col)
except Exception:
pass
buf = io.BytesIO()
wb.save(buf)
return buf.getvalue()