|
|
|
|
@@ -0,0 +1,300 @@
|
|
|
|
|
{
|
|
|
|
|
"cells": [
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": 2,
|
|
|
|
|
"id": "5d58304b-e94b-428b-91c8-35d649d97dd7",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"name": "stderr",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"usage: ipykernel_launcher.py [-h] --input INPUT [--outdir OUTDIR]\n",
|
|
|
|
|
" [--response RESPONSE] [--area_col AREA_COL]\n",
|
|
|
|
|
" [--fm_col FM_COL] [--recompute_sigma]\n",
|
|
|
|
|
" [--sn_type {LB}]\n",
|
|
|
|
|
"ipykernel_launcher.py: error: the following arguments are required: --input\n"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ename": "SystemExit",
|
|
|
|
|
"evalue": "2",
|
|
|
|
|
"output_type": "error",
|
|
|
|
|
"traceback": [
|
|
|
|
|
"An exception has occurred, use %tb to see the full traceback.\n",
|
|
|
|
|
"\u001b[0;31mSystemExit\u001b[0m\u001b[0;31m:\u001b[0m 2\n"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "stderr",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"/usr/lib/python3.13/site-packages/IPython/core/interactiveshell.py:3585: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n",
|
|
|
|
|
" warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"source": [
|
|
|
|
|
"\n",
|
|
|
|
|
"#!/usr/bin/env python3\n",
|
|
|
|
|
"# -*- coding: utf-8 -*-\n",
|
|
|
|
|
"\"\"\"\n",
|
|
|
|
|
"Taguchi analysis pipeline for FDM experiment (per user's thesis)\n",
|
|
|
|
|
"- Reads a CSV with columns similar to:\n",
|
|
|
|
|
" 'Eksperiment','Orijentacija','Visina sloja','Širina ekstruzije','Postotak ispune',\n",
|
|
|
|
|
" 'Broj slojeva stijenke','A_ekv [mm^2]','Fm kN]','Sigma [Mpa]','SNR [dB]'\n",
|
|
|
|
|
"- Cleans units to numeric, recomputes Sigma (optional) and SNR (LB, n=1),\n",
|
|
|
|
|
"- Builds response tables (means, Δ), ranks factors, selects optimal levels by SNR,\n",
|
|
|
|
|
"- Predicts response at optimal combination (additive model),\n",
|
|
|
|
|
"- Runs Taguchi-style ANOVA on Sigma,\n",
|
|
|
|
|
"- Saves CSV outputs + main-effects plots + LaTeX snippet.\n",
|
|
|
|
|
"Usage:\n",
|
|
|
|
|
" python taguchi_from_csv.py --input ispitni_rezultati.csv --outdir out_tlak\n",
|
|
|
|
|
"\"\"\"\n",
|
|
|
|
|
"import argparse, os, re, json\n",
|
|
|
|
|
"import pandas as pd\n",
|
|
|
|
|
"import numpy as np\n",
|
|
|
|
|
"import matplotlib.pyplot as plt\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"def norm_num(x):\n",
|
|
|
|
|
" if pd.isna(x):\n",
|
|
|
|
|
" return np.nan\n",
|
|
|
|
|
" if isinstance(x, (int, float, np.number)):\n",
|
|
|
|
|
" return float(x)\n",
|
|
|
|
|
" s = str(x).strip()\n",
|
|
|
|
|
" s = s.replace(',', '.')\n",
|
|
|
|
|
" s = s.replace('%','')\n",
|
|
|
|
|
" s = s.replace(' mm','')\n",
|
|
|
|
|
" s = s.replace('MPa','').replace('Mpa','')\n",
|
|
|
|
|
" s = s.replace('kN','').replace('kN]','').replace('[','').replace(']','')\n",
|
|
|
|
|
" try:\n",
|
|
|
|
|
" return float(s)\n",
|
|
|
|
|
" except:\n",
|
|
|
|
|
" return np.nan\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"def compute_snr_lb(y):\n",
|
|
|
|
|
" # larger-the-better; handles n=1 case\n",
|
|
|
|
|
" y = pd.to_numeric(y, errors='coerce')\n",
|
|
|
|
|
" return 20.0*np.log10(y.clip(lower=1e-12))\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"def response_table(df, factor, col):\n",
|
|
|
|
|
" t = df.groupby(factor, as_index=False)[col].mean()\n",
|
|
|
|
|
" t[\"Delta (max-min)\"] = t[col].max() - t[col].min()\n",
|
|
|
|
|
" t[\"Faktor\"] = factor\n",
|
|
|
|
|
" return t\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"def taguchi_anova(df, response, factors):\n",
|
|
|
|
|
" y = df[response].astype(float)\n",
|
|
|
|
|
" mu = y.mean()\n",
|
|
|
|
|
" total_ss = ((y - mu)**2).sum()\n",
|
|
|
|
|
" rows = []\n",
|
|
|
|
|
" dof_used = 0\n",
|
|
|
|
|
" ss_used = 0.0\n",
|
|
|
|
|
" for f in factors:\n",
|
|
|
|
|
" grp = df.groupby(f)[response].agg(['mean','count'])\n",
|
|
|
|
|
" ss_f = (grp['count']*(grp['mean']-mu)**2).sum()\n",
|
|
|
|
|
" dof_f = grp.shape[0]-1\n",
|
|
|
|
|
" rows.append([f, ss_f, dof_f])\n",
|
|
|
|
|
" dof_used += dof_f\n",
|
|
|
|
|
" ss_used += ss_f\n",
|
|
|
|
|
" err_ss = max(total_ss - ss_used, 0.0)\n",
|
|
|
|
|
" err_dof = max(len(df)-1 - dof_used, 0)\n",
|
|
|
|
|
" an = pd.DataFrame(rows, columns=[\"Factor\",\"SS\",\"DOF\"])\n",
|
|
|
|
|
" an[\"MS\"] = an[\"SS\"]/an[\"DOF\"]\n",
|
|
|
|
|
" an[\"Pct_contrib_%\"] = (an[\"SS\"]/total_ss*100.0) if total_ss>0 else np.nan\n",
|
|
|
|
|
" err_row = pd.DataFrame([[\"Error\", err_ss, err_dof, (err_ss/err_dof) if err_dof>0 else np.nan, (err_ss/total_ss*100.0) if total_ss>0 else np.nan]],\n",
|
|
|
|
|
" columns=[\"Factor\",\"SS\",\"DOF\",\"MS\",\"Pct_contrib_%\"])\n",
|
|
|
|
|
" an = pd.concat([an, err_row], ignore_index=True)\n",
|
|
|
|
|
" return an, mu, total_ss\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"def main():\n",
|
|
|
|
|
" ap = argparse.ArgumentParser()\n",
|
|
|
|
|
" ap.add_argument(\"--input\", required=True, help=\"Path to CSV with results\")\n",
|
|
|
|
|
" ap.add_argument(\"--outdir\", default=None, help=\"Output directory\")\n",
|
|
|
|
|
" ap.add_argument(\"--response\", default=\"Sigma [Mpa]\", help=\"Response column to analyze (default Sigma [Mpa])\")\n",
|
|
|
|
|
" ap.add_argument(\"--area_col\", default=\"A_ekv [mm^2]\", help=\"Area column if Sigma should be recomputed from Fm/Area\")\n",
|
|
|
|
|
" ap.add_argument(\"--fm_col\", default=\"Fm kN]\", help=\"Force column (kN)\")\n",
|
|
|
|
|
" ap.add_argument(\"--recompute_sigma\", action=\"store_true\", help=\"If set, recompute Sigma = Fm*1000/Area\")\n",
|
|
|
|
|
" ap.add_argument(\"--sn_type\", default=\"LB\", choices=[\"LB\"], help=\"S/N type (only LB supported here)\")\n",
|
|
|
|
|
" args = ap.parse_args()\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" in_path = args.input\n",
|
|
|
|
|
" outdir = args.outdir or (os.path.splitext(os.path.basename(in_path))[0] + \"_taguchi_out\")\n",
|
|
|
|
|
" os.makedirs(outdir, exist_ok=True)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" df = pd.read_csv(in_path)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Standard column mapping / cleanup for known names\n",
|
|
|
|
|
" rename_map = {\n",
|
|
|
|
|
" \"Visina sloja\":\"Visina sloja [mm]\",\n",
|
|
|
|
|
" \"Širina ekstruzije\":\"Širina ekstruzije [mm]\",\n",
|
|
|
|
|
" \"Postotak ispune\":\"Postotak ispune [%]\",\n",
|
|
|
|
|
" \"Broj slojeva stijenke\":\"Broj stijenki\",\n",
|
|
|
|
|
" \"Sigma [MPa]\":\"Sigma [Mpa]\",\n",
|
|
|
|
|
" \"Fm [kN]\":\"Fm kN]\",\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
" df = df.rename(columns={k:v for k,v in rename_map.items() if k in df.columns})\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Ensure numeric for relevant columns\n",
|
|
|
|
|
" if \"Visina sloja [mm]\" in df.columns:\n",
|
|
|
|
|
" df[\"Visina sloja [mm]\"] = df[\"Visina sloja [mm]\"].apply(norm_num)\n",
|
|
|
|
|
" if \"Širina ekstruzije [mm]\" in df.columns:\n",
|
|
|
|
|
" df[\"Širina ekstruzije [mm]\"] = df[\"Širina ekstruzije [mm]\"].apply(norm_num)\n",
|
|
|
|
|
" if \"Postotak ispune [%]\" in df.columns:\n",
|
|
|
|
|
" df[\"Postotak ispune [%]\"] = df[\"Postotak ispune [%]\"].apply(norm_num)\n",
|
|
|
|
|
" if \"Broj stijenki\" in df.columns:\n",
|
|
|
|
|
" df[\"Broj stijenki\"] = df[\"Broj stijenki\"].apply(norm_num)\n",
|
|
|
|
|
" if args.area_col in df.columns:\n",
|
|
|
|
|
" df[args.area_col] = df[args.area_col].apply(norm_num)\n",
|
|
|
|
|
" if args.fm_col in df.columns:\n",
|
|
|
|
|
" df[args.fm_col] = df[args.fm_col].apply(norm_num)\n",
|
|
|
|
|
" if args.response in df.columns:\n",
|
|
|
|
|
" df[args.response] = df[args.response].apply(norm_num)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Compute Sigma if asked or missing\n",
|
|
|
|
|
" if args.recompute_sigma or args.response not in df.columns or df[args.response].isna().all():\n",
|
|
|
|
|
" if args.fm_col in df.columns and args.area_col in df.columns:\n",
|
|
|
|
|
" df[args.response] = (df[args.fm_col] * 1000.0) / df[args.area_col]\n",
|
|
|
|
|
" else:\n",
|
|
|
|
|
" raise SystemExit(\"Cannot recompute Sigma: missing Fm or Area columns\")\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Compute SNR (LB)\n",
|
|
|
|
|
" df[\"SNR_LB [dB]\"] = compute_snr_lb(df[args.response])\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Save cleaned raw\n",
|
|
|
|
|
" raw_out = os.path.join(outdir, \"0_raw_with_SNR.csv\")\n",
|
|
|
|
|
" df.to_csv(raw_out, index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Factors to analyze (auto detect from known list)\n",
|
|
|
|
|
" candidate_factors = [\"Orijentacija\",\"Visina sloja [mm]\",\"Širina ekstruzije [mm]\",\"Postotak ispune [%]\",\"Broj stijenki\"]\n",
|
|
|
|
|
" factors = [f for f in candidate_factors if f in df.columns]\n",
|
|
|
|
|
" if len(factors) == 0:\n",
|
|
|
|
|
" raise SystemExit(\"No known factor columns found. Expected some of: \" + \", \".join(candidate_factors))\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Response tables and deltas\n",
|
|
|
|
|
" resp_mu = pd.concat([response_table(df, f, args.response) for f in factors], ignore_index=True)\n",
|
|
|
|
|
" resp_sn = pd.concat([response_table(df, f, \"SNR_LB [dB]\") for f in factors], ignore_index=True)\n",
|
|
|
|
|
" resp_mu.to_csv(os.path.join(outdir, \"1_response_means_Sigma.csv\"), index=False)\n",
|
|
|
|
|
" resp_sn.to_csv(os.path.join(outdir, \"2_response_means_SNR.csv\"), index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Ranking (by Delta)\n",
|
|
|
|
|
" rank_mu = resp_mu.groupby(\"Faktor\")[\"Delta (max-min)\"].max().sort_values(ascending=False).reset_index().rename(columns={\"Delta (max-min)\":\"Rang delta (Sigma)\"})\n",
|
|
|
|
|
" rank_sn = resp_sn.groupby(\"Faktor\")[\"Delta (max-min)\"].max().sort_values(ascending=False).reset_index().rename(columns={\"Delta (max-min)\":\"Rang delta (SNR)\"})\n",
|
|
|
|
|
" ranking = pd.merge(rank_mu, rank_sn, on=\"Faktor\")\n",
|
|
|
|
|
" ranking.to_csv(os.path.join(outdir, \"3_factor_ranking.csv\"), index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Optimal levels by SNR\n",
|
|
|
|
|
" opt_levels = {f: df.groupby(f)[\"SNR_LB [dB]\"].mean().sort_values(ascending=False).index[0] for f in factors}\n",
|
|
|
|
|
" opt_table = pd.DataFrame({\"Faktor\": list(opt_levels.keys()), \"Optimalna razina (po S/N)\": list(opt_levels.values())})\n",
|
|
|
|
|
" opt_table.to_csv(os.path.join(outdir, \"4_optimal_levels.csv\"), index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Prediction at optimal combo (additive model) on response\n",
|
|
|
|
|
" grand_mean = df[args.response].mean()\n",
|
|
|
|
|
" k = len(factors)\n",
|
|
|
|
|
" pred_sigma = sum(df.groupby(f)[args.response].mean().loc[opt_levels[f]] for f in factors) - (k-1)*grand_mean\n",
|
|
|
|
|
" grand_mean_snr = df[\"SNR_LB [dB]\"].mean()\n",
|
|
|
|
|
" pred_snr = sum(df.groupby(f)[\"SNR_LB [dB]\"].mean().loc[opt_levels[f]] for f in factors) - (k-1)*grand_mean_snr\n",
|
|
|
|
|
" pred_df = pd.DataFrame({\n",
|
|
|
|
|
" \"Predikcija\": [\"Sigma_opt [MPa]\",\"SNR_opt [dB]\",\"Grand mean Sigma [MPa]\",\"Grand mean SNR [dB]\"],\n",
|
|
|
|
|
" \"Vrijednost\": [pred_sigma, pred_snr, grand_mean, grand_mean_snr]\n",
|
|
|
|
|
" })\n",
|
|
|
|
|
" pred_df.to_csv(os.path.join(outdir, \"5_prediction.csv\"), index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # ANOVA (Taguchi-style) on response\n",
|
|
|
|
|
" anova_df, mu_sigma, totss = taguchi_anova(df, args.response, factors)\n",
|
|
|
|
|
" anova_df.to_csv(os.path.join(outdir, \"6_anova_sigma.csv\"), index=False)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Plots: main effects for SNR\n",
|
|
|
|
|
" for f in factors:\n",
|
|
|
|
|
" means = df.groupby(f)[\"SNR_LB [dB]\"].mean().reset_index()\n",
|
|
|
|
|
" # numeric sort if possible\n",
|
|
|
|
|
" try:\n",
|
|
|
|
|
" means[f] = pd.to_numeric(means[f], errors=\"ignore\")\n",
|
|
|
|
|
" means = means.sort_values(by=f)\n",
|
|
|
|
|
" except:\n",
|
|
|
|
|
" pass\n",
|
|
|
|
|
" plt.figure()\n",
|
|
|
|
|
" plt.plot(means[f], means[\"SNR_LB [dB]\"], marker=\"o\")\n",
|
|
|
|
|
" plt.xlabel(f)\n",
|
|
|
|
|
" plt.ylabel(\"S/N (LB) [dB]\")\n",
|
|
|
|
|
" plt.title(f\"Main effect (S/N): {f}\")\n",
|
|
|
|
|
" plt.tight_layout()\n",
|
|
|
|
|
" plt.savefig(os.path.join(outdir, f\"main_effect_SNR_{f}.png\"), dpi=150)\n",
|
|
|
|
|
" plt.close()\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # LaTeX snippet\n",
|
|
|
|
|
" latex_lines = []\n",
|
|
|
|
|
" latex_lines.append(r\"% --- Taguchi rezultati (S = Sigma [MPa], S/N larger-the-better) ---\")\n",
|
|
|
|
|
" latex_lines.append(r\"\\subsection{Rezultati Taguchijeve metode}\")\n",
|
|
|
|
|
" latex_lines.append(r\"U skladu s ortogonalnom matricom provedena je analiza s kriterijem \\textbf{što-veće-to-bolje}. Za svaku kombinaciju izračunat je S/N omjer \\((\\mathrm{S/N}=20\\log_{10}(\\sigma))\\) te su određeni glavni učinci po razinama i optimalna kombinacija.\")\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Optimal levels\n",
|
|
|
|
|
" latex_lines.append(r\"\\paragraph{Optimalne razine (po S/N).}\")\n",
|
|
|
|
|
" latex_lines.append(opt_table.to_latex(index=False, escape=False))\n",
|
|
|
|
|
" # Prediction\n",
|
|
|
|
|
" latex_lines.append(r\"\\paragraph{Predikcija odziva na optimalnoj kombinaciji.}\")\n",
|
|
|
|
|
" latex_lines.append(pred_df.to_latex(index=False, escape=False, float_format='%.2f'))\n",
|
|
|
|
|
" # Ranking\n",
|
|
|
|
|
" latex_lines.append(r\"\\paragraph{Rang utjecaja faktora.}\")\n",
|
|
|
|
|
" latex_lines.append(ranking.to_latex(index=False, escape=False, float_format='%.3f'))\n",
|
|
|
|
|
" # ANOVA\n",
|
|
|
|
|
" an_fmt = anova_df.copy()\n",
|
|
|
|
|
" for c in [\"SS\",\"MS\",\"Pct_contrib_%\"]:\n",
|
|
|
|
|
" if c in an_fmt.columns:\n",
|
|
|
|
|
" an_fmt[c] = an_fmt[c].astype(float).round(3)\n",
|
|
|
|
|
" latex_lines.append(r\"\\paragraph{ANOVA (Taguchi).}\")\n",
|
|
|
|
|
" latex_lines.append(an_fmt.to_latex(index=False, escape=False))\n",
|
|
|
|
|
" latex_lines.append(r\"Napomena: budući da je \\(n{=}1\\), pogreška (Error) procijenjena je iz preostalih stupnjeva slobode (Taguchi pooling).\")\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" with open(os.path.join(outdir, \"taguchi_results.tex\"), \"w\", encoding=\"utf-8\") as f:\n",
|
|
|
|
|
" f.write(\"\\n\\n\".join(latex_lines))\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" # Small JSON summary\n",
|
|
|
|
|
" summary = {\n",
|
|
|
|
|
" \"outdir\": outdir,\n",
|
|
|
|
|
" \"factors\": factors,\n",
|
|
|
|
|
" \"opt_levels\": opt_levels,\n",
|
|
|
|
|
" \"pred_sigma\": pred_sigma,\n",
|
|
|
|
|
" \"grand_mean_sigma\": grand_mean,\n",
|
|
|
|
|
" }\n",
|
|
|
|
|
" with open(os.path.join(outdir, \"summary.json\"), \"w\", encoding=\"utf-8\") as f:\n",
|
|
|
|
|
" json.dump(summary, f, ensure_ascii=False, indent=2)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
" print(\"Done. Outputs in:\", outdir)\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"if __name__ == \"__main__\":\n",
|
|
|
|
|
" main()"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"id": "56399c17-5135-4fa3-8809-358fef72570b",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": []
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"metadata": {
|
|
|
|
|
"kernelspec": {
|
|
|
|
|
"display_name": "Python 3 (ipykernel)",
|
|
|
|
|
"language": "python",
|
|
|
|
|
"name": "python3"
|
|
|
|
|
},
|
|
|
|
|
"language_info": {
|
|
|
|
|
"codemirror_mode": {
|
|
|
|
|
"name": "ipython",
|
|
|
|
|
"version": 3
|
|
|
|
|
},
|
|
|
|
|
"file_extension": ".py",
|
|
|
|
|
"mimetype": "text/x-python",
|
|
|
|
|
"name": "python",
|
|
|
|
|
"nbconvert_exporter": "python",
|
|
|
|
|
"pygments_lexer": "ipython3",
|
|
|
|
|
"version": "3.13.3"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"nbformat": 4,
|
|
|
|
|
"nbformat_minor": 5
|
|
|
|
|
}
|