sacar_frontera_pareto.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. import pandas as pd
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. from sklearn.ensemble import RandomForestRegressor
  5. from sklearn.preprocessing import PolynomialFeatures
  6. from sklearn.linear_model import LinearRegression
  7. from sklearn.pipeline import make_pipeline
  8. # Cargar datos
  9. df = pd.read_excel('Bueno.xlsx', sheet_name='resultados1')
  10. # Verificar columnas
  11. print(df.columns)
  12. # Nos interesan: tf, tw, Flecha_Media
  13. # Exploración rápida
  14. print(df[['tf', 'tw', 'Flecha_Media']].describe())
  15. def pareto_frontier(X, Y, maximize_X=True, minimize_Y=True):
  16. """
  17. Devuelve una máscara booleana con los puntos que pertenecen al frente de Pareto.
  18. X: array de objetivo 1 (tw)
  19. Y: array de objetivo 2 (tf)
  20. maximize_X: si True, se maximiza X (lo convertimos a -X)
  21. minimize_Y: si True, se minimiza Y (se deja igual)
  22. """
  23. if not maximize_X:
  24. X = -X
  25. if minimize_Y:
  26. Y = -Y
  27. costs = np.vstack((X, Y)).T
  28. is_efficient = np.ones(costs.shape[0], dtype=bool)
  29. for i, c in enumerate(costs):
  30. if is_efficient[i]:
  31. # Un punto es dominado si existe otro que es <= en todos los objetivos y < en al menos uno
  32. dominated = np.all(costs[is_efficient] <= c, axis=1) & np.any(costs[is_efficient] < c, axis=1)
  33. is_efficient[is_efficient] = ~dominated
  34. is_efficient[i] = True # el punto actual no se elimina a sí mismo
  35. return is_efficient
  36. # Para una sola variable objetivo (Flecha_Media), usamos todos los puntos
  37. df_pareto = df.copy()
  38. print(f"Total puntos: {len(df)}")
  39. # Visualizar nube de puntos en 3D: tw, tf, Flecha_Media
  40. from mpl_toolkits.mplot3d import Axes3D
  41. fig = plt.figure(figsize=(12, 8))
  42. ax = fig.add_subplot(111, projection='3d')
  43. scatter = ax.scatter(df_pareto['tw'], df_pareto['tf'], df_pareto['Flecha_Media'],
  44. c=df_pareto['Flecha_Media'], cmap='viridis', s=30, alpha=0.6)
  45. ax.set_xlabel('tw')
  46. ax.set_ylabel('tf')
  47. ax.set_zlabel('Flecha Media')
  48. ax.set_title('Nube de puntos: Flecha_Media = f(tw, tf)')
  49. plt.colorbar(scatter, ax=ax, label='Flecha Media')
  50. plt.show()
  51. # Preparar variables
  52. X = df_pareto[['tw', 'tf']].values
  53. y = df_pareto['Flecha_Media'].values
  54. # Crear pipeline con características polinómicas de grado 2 (puedes probar grado 3)
  55. model = make_pipeline(PolynomialFeatures(degree=2, include_bias=False),
  56. LinearRegression())
  57. model.fit(X, y)
  58. # Evaluar ajuste
  59. y_pred = model.predict(X)
  60. r2 = model.score(X, y)
  61. print(f"R² en Flecha_Media (Modelo Polinómico): {r2:.4f}")
  62. # Extraer coeficientes (opcional, para ver la expresión)
  63. linear_step = model.named_steps['linearregression']
  64. poly_step = model.named_steps['polynomialfeatures']
  65. feature_names = poly_step.get_feature_names_out(['tw', 'tf'])
  66. coef_df = pd.DataFrame({'feature': feature_names, 'coef': linear_step.coef_})
  67. print("\nCoeficientes del modelo polinómico:")
  68. print(coef_df)
  69. # Guardar también el intercept
  70. intercept = linear_step.intercept_
  71. print(f"Intercept: {intercept:.6f}")
  72. rf = RandomForestRegressor(n_estimators=100, random_state=42)
  73. rf.fit(X, y)
  74. r2_rf = rf.score(X, y)
  75. print(f"\nR² Random Forest: {r2_rf:.4f}")
  76. from gplearn.genetic import SymbolicRegressor
  77. # Configurar regresión simbólica
  78. est_gp = SymbolicRegressor(population_size=2000,
  79. generations=20,
  80. stopping_criteria=0.01,
  81. p_crossover=0.7,
  82. p_subtree_mutation=0.1,
  83. p_hoist_mutation=0.05,
  84. p_point_mutation=0.1,
  85. max_samples=0.9,
  86. verbose=1,
  87. parsimony_coefficient=0.01,
  88. random_state=0)
  89. est_gp.fit(X, y)
  90. # Mostrar la expresión simbólica de forma legible
  91. print("\n" + "="*80)
  92. print("EXPRESIÓN SIMBÓLICA ENCONTRADA (Regresión Genética):")
  93. print("="*80)
  94. # Función para formatear el árbol de forma legible
  95. def format_expression(expr_str, indent=0):
  96. """Formatea recursivamente la expresión en un árbol legible"""
  97. expr_str = expr_str.strip()
  98. # Identificar operación y argumentos
  99. if expr_str[0] in ['a', 's', 'm', 'd']: # add, sub, mul, div
  100. # Encontrar la operación
  101. paren_count = 0
  102. op_end = 0
  103. for i, c in enumerate(expr_str):
  104. if c == '(':
  105. paren_count += 1
  106. op_end = i
  107. break
  108. operation = expr_str[:op_end]
  109. # Extraer argumentos
  110. inner = expr_str[op_end+1:-1] # Quita ( y )
  111. args = []
  112. current_arg = ""
  113. paren_count = 0
  114. for c in inner:
  115. if c == ',' and paren_count == 0:
  116. args.append(current_arg)
  117. current_arg = ""
  118. else:
  119. if c == '(':
  120. paren_count += 1
  121. elif c == ')':
  122. paren_count -= 1
  123. current_arg += c
  124. args.append(current_arg)
  125. # Mapear operaciones a símbolos
  126. op_map = {'add': '+', 'sub': '−', 'mul': '×', 'div': '÷'}
  127. op_symbol = op_map.get(operation, operation)
  128. result = " " * indent + f"({op_symbol})\n"
  129. for i, arg in enumerate(args):
  130. result += format_expression(arg, indent + 2)
  131. return result
  132. else:
  133. # Es un número o variable
  134. return " " * indent + f"├─ {expr_str}\n"
  135. expr_tree_formatted = format_expression(str(est_gp._program))
  136. print("Árbol de operaciones:")
  137. print(expr_tree_formatted)
  138. # Calcular R² de la regresión simbólica
  139. y_pred_gp = est_gp.predict(X)
  140. r2_gp = 1 - np.sum((y - y_pred_gp)**2) / np.sum((y - np.mean(y))**2)
  141. print(f"R² Regresión Simbólica: {r2_gp:.4f}\n")
  142. # Función para convertir la notación prefija a LaTeX
  143. expr_str = str(est_gp._program)
  144. def prefix_to_latex(expr_str):
  145. """Convierte notación prefija (add(a,b)) a LaTeX"""
  146. expr_str = expr_str.strip()
  147. # Si es un número o variable, retorna directamente
  148. if expr_str[0] not in ['a', 's', 'm', 'd']:
  149. return expr_str
  150. # Encuentra la operación
  151. op_end = 0
  152. for i, c in enumerate(expr_str):
  153. if c == '(':
  154. operation = expr_str[:i]
  155. op_end = i
  156. break
  157. # Extrae los argumentos
  158. inner = expr_str[op_end+1:-1]
  159. args = []
  160. current = ""
  161. paren_depth = 0
  162. for c in inner:
  163. if c == ',' and paren_depth == 0:
  164. args.append(current.strip())
  165. current = ""
  166. else:
  167. if c == '(':
  168. paren_depth += 1
  169. elif c == ')':
  170. paren_depth -= 1
  171. current += c
  172. args.append(current.strip())
  173. # Convierte recursivamente
  174. converted_args = [prefix_to_latex(arg) for arg in args]
  175. if operation == 'add':
  176. return "({} + {})".format(converted_args[0], converted_args[1])
  177. elif operation == 'sub':
  178. return "({} - {})".format(converted_args[0], converted_args[1])
  179. elif operation == 'mul':
  180. return "{{{0}}} \\times {{{1}}}".format(converted_args[0], converted_args[1])
  181. elif operation == 'div':
  182. return "\\frac{{{0}}}{{{1}}}".format(converted_args[0], converted_args[1])
  183. else:
  184. return "({})".format(', '.join(converted_args))
  185. expr_latex = prefix_to_latex(expr_str)
  186. print("\nExpresión en LaTeX:")
  187. print(expr_latex)
  188. # Guardar la ecuación a un archivo de texto para referencia
  189. with open('expresion_simbolica.txt', 'w', encoding='utf-8') as f:
  190. f.write("EXPRESIONES OBTENIDAS - FLECHA_MEDIA = f(tw, tf)\n")
  191. f.write("="*80 + "\n")
  192. f.write("VARIABLES:\n")
  193. f.write("X0 = tw (espesor del alma / web thickness)\n")
  194. f.write("X1 = tf (espesor del ala / flange thickness)\n")
  195. f.write("="*80 + "\n\n")
  196. # Modelo Polinómico
  197. f.write("1. MODELO POLINÓMICO (R² = {:.4f})\n".format(r2))
  198. f.write("-"*80 + "\n")
  199. f.write("Ecuación en LaTeX:\n\n")
  200. f.write("Flecha_{{Media}} = {:.6f}".format(intercept))
  201. for feat, coef in zip(feature_names, linear_step.coef_):
  202. sign = "+" if coef >= 0 else "-"
  203. f.write(" {0} {1:.6f} \\cdot {2}".format(sign, abs(coef), feat))
  204. f.write("\n\n")
  205. f.write("Coeficientes:\n")
  206. f.write(coef_df.to_string())
  207. f.write("\n\n")
  208. # Random Forest
  209. f.write("2. RANDOM FOREST (R² = {:.4f})\n".format(r2_rf))
  210. f.write("-"*80 + "\n")
  211. f.write("El modelo Random Forest es un ensemble de 100 árboles de decisión.\n")
  212. f.write("No tiene una ecuación analítica cerrada.\n")
  213. f.write("Importancias de características (feature importance):\n")
  214. importances = rf.feature_importances_
  215. for name, imp in zip(['tw', 'tf'], importances):
  216. f.write(" - {0}: {1:.4f}\n".format(name, imp))
  217. f.write("\n")
  218. # Regresión Simbólica
  219. f.write("3. REGRESIÓN SIMBÓLICA / GENETIC PROGRAMMING (R² = {:.4f})\n".format(r2_gp))
  220. f.write("-"*80 + "\n")
  221. f.write("Árbol de operaciones:\n")
  222. f.write(expr_tree_formatted)
  223. f.write("\nEcuación en LaTeX:\n\n")
  224. f.write(expr_latex + "\n\n")
  225. f.write("Notación de árbol (formato original):\n")
  226. f.write(str(est_gp._program) + "\n\n")
  227. f.write("="*80 + "\n")
  228. f.write("RESUMEN R²:\n")
  229. f.write(" Polinómico: {:.4f}\n".format(r2))
  230. f.write(" Random Forest: {:.4f}\n".format(r2_rf))
  231. f.write(" Simbólico: {:.4f}\n".format(r2_gp))
  232. f.write("="*80 + "\n")
  233. print("\n✓ Expresión guardada en 'expresion_simbolica.txt'")
  234. # Comparación de todos los modelos
  235. print("="*80)
  236. print("RESUMEN DE MODELOS:")
  237. print("="*80)
  238. print(f"R² Modelo Polinómico: {r2:.4f}")
  239. print(f"R² Random Forest: {r2_rf:.4f}")
  240. print(f"R² Regresión Simbólica: {r2_gp:.4f}")
  241. print("="*80 + "\n")
  242. # Crear malla para visualizar la superficie ajustada
  243. tw_min, tw_max = df_pareto['tw'].min(), df_pareto['tw'].max()
  244. tf_min, tf_max = df_pareto['tf'].min(), df_pareto['tf'].max()
  245. tw_range = np.linspace(tw_min, tw_max, 30)
  246. tf_range = np.linspace(tf_min, tf_max, 30)
  247. tw_mesh, tf_mesh = np.meshgrid(tw_range, tf_range)
  248. # Predicciones con el modelo polinómico
  249. X_plot = np.column_stack([tw_mesh.ravel(), tf_mesh.ravel()])
  250. flecha_pred = model.predict(X_plot)
  251. flecha_mesh = flecha_pred.reshape(tw_mesh.shape)
  252. # Visualizar superficie ajustada
  253. fig = plt.figure(figsize=(16, 10))
  254. # Subplot 1: Datos reales + superficie del modelo polinómico
  255. ax1 = fig.add_subplot(231, projection='3d')
  256. ax1.scatter(df_pareto['tw'], df_pareto['tf'], df_pareto['Flecha_Media'],
  257. c='red', s=30, alpha=0.6, label='Datos reales')
  258. ax1.plot_surface(tw_mesh, tf_mesh, flecha_mesh, alpha=0.5, cmap='coolwarm')
  259. ax1.set_xlabel('tw')
  260. ax1.set_ylabel('tf')
  261. ax1.set_zlabel('Flecha Media')
  262. ax1.set_title(f'Modelo Polinómico (R²={r2:.4f})')
  263. ax1.legend()
  264. # Predicciones con Random Forest
  265. flecha_pred_rf = rf.predict(X_plot)
  266. flecha_mesh_rf = flecha_pred_rf.reshape(tw_mesh.shape)
  267. # Subplot 2: Datos reales + superficie del Random Forest
  268. ax2 = fig.add_subplot(232, projection='3d')
  269. ax2.scatter(df_pareto['tw'], df_pareto['tf'], df_pareto['Flecha_Media'],
  270. c='red', s=30, alpha=0.6, label='Datos reales')
  271. ax2.plot_surface(tw_mesh, tf_mesh, flecha_mesh_rf, alpha=0.5, cmap='coolwarm')
  272. ax2.set_xlabel('tw')
  273. ax2.set_ylabel('tf')
  274. ax2.set_zlabel('Flecha Media')
  275. ax2.set_title(f'Random Forest (R²={r2_rf:.4f})')
  276. ax2.legend()
  277. # Predicciones con Regresión Simbólica
  278. flecha_pred_gp = est_gp.predict(X_plot)
  279. flecha_mesh_gp = flecha_pred_gp.reshape(tw_mesh.shape)
  280. # Subplot 3: Datos reales + superficie de Regresión Simbólica
  281. ax3 = fig.add_subplot(233, projection='3d')
  282. ax3.scatter(df_pareto['tw'], df_pareto['tf'], df_pareto['Flecha_Media'],
  283. c='red', s=30, alpha=0.6, label='Datos reales')
  284. ax3.plot_surface(tw_mesh, tf_mesh, flecha_mesh_gp, alpha=0.5, cmap='coolwarm')
  285. ax3.set_xlabel('tw')
  286. ax3.set_ylabel('tf')
  287. ax3.set_zlabel('Flecha Media')
  288. ax3.set_title(f'Regresión Simbólica (R²={r2_gp:.4f})')
  289. ax3.legend()
  290. # Subplots 4, 5, 6: Residuos
  291. ax4 = fig.add_subplot(234)
  292. residuos_poly = y - y_pred
  293. ax4.scatter(y_pred, residuos_poly, alpha=0.5)
  294. ax4.axhline(y=0, color='r', linestyle='--')
  295. ax4.set_xlabel('Predicciones')
  296. ax4.set_ylabel('Residuos')
  297. ax4.set_title('Residuos - Modelo Polinómico')
  298. ax4.grid(True, alpha=0.3)
  299. ax5 = fig.add_subplot(235)
  300. residuos_rf = y - rf.predict(X)
  301. ax5.scatter(rf.predict(X), residuos_rf, alpha=0.5)
  302. ax5.axhline(y=0, color='r', linestyle='--')
  303. ax5.set_xlabel('Predicciones')
  304. ax5.set_ylabel('Residuos')
  305. ax5.set_title('Residuos - Random Forest')
  306. ax5.grid(True, alpha=0.3)
  307. ax6 = fig.add_subplot(236)
  308. residuos_gp = y - y_pred_gp
  309. ax6.scatter(y_pred_gp, residuos_gp, alpha=0.5)
  310. ax6.axhline(y=0, color='r', linestyle='--')
  311. ax6.set_xlabel('Predicciones')
  312. ax6.set_ylabel('Residuos')
  313. ax6.set_title('Residuos - Regresión Simbólica')
  314. ax6.grid(True, alpha=0.3)
  315. plt.tight_layout()
  316. plt.show()