- La regresión cuadrática en C# se construye sobre la misma base que la regresión lineal: matriz de diseño, mínimos cuadrados y operaciones con matrices.
- El método Solve separa predictores y variable dependiente, aplica transposición, producto e inversión de matrices para obtener los coeficientes del modelo.
- La métrica R cuadrado permite evaluar el ajuste del modelo, comparando la variabilidad explicada frente a la total, y es clave para valorar la calidad predictiva.
- Con una estructura de consola y métodos estáticos para datos sintéticos y álgebra matricial, se obtiene un flujo claro desde los datos hasta la predicción numérica.
La regresión con C# suele explicarse casi siempre con ejemplos lineales, pero en muchos problemas reales la relación entre las variables no es una simple recta, sino una curva que puede modelarse muy bien con una función cuadrática. Aunque el contenido de referencia que se suele encontrar en la red gira en torno a la regresión lineal, todo ese enfoque se puede llevar sin demasiadas complicaciones a modelos polinómicos de segundo grado, manteniendo la misma base matemática: trabajar con matrices, coeficientes y mínimos cuadrados.
En este artículo vamos a aprovechar todo el conocimiento disponible sobre regresión lineal en C# (matrices de diseño, inversión de matrices, cálculo de R cuadrado, estructura del código, etc.) y lo vamos a reescribir y adaptar para entender con detalle cómo se implementa un esquema de regresión cuadrática en C#. Verás cómo a partir de los mismos cimientos (transposición, producto e inversión de matrices) puedes pasar de una recta a una parábola, y cómo integrar todo ello en un programa de consola en .NET, con un código fácil de seguir aunque haya algo de “miga” matemática.
Qué es la regresión (lineal y cuadrática) en el contexto de C#
En términos muy simples, un problema de regresión consiste en predecir el valor numérico de una variable a partir de una o varias variables explicativas. En la literatura clásica se habla de variable dependiente (lo que queremos estimar) y variables independientes o de predicción (los datos que utilizamos como entrada). Cuando solo hay una variable independiente, se suele hablar de regresión simple; si hay varias, de regresión múltiple o multivariable.
La regresión lineal clásica asume que la relación entre la salida y las entradas puede representarse como una combinación lineal de los predictores. En un ejemplo típico, podríamos querer anticipar los ingresos anuales de una persona teniendo en cuenta años de estudios, experiencia profesional y una variable indicadora de sexo. Cada predictor se multiplica por un coeficiente y, además, se incluye un término constante que actúa como “desfase” o intercepto.
En una regresión cuadrática, la idea es parecida pero se da un paso más: se añaden términos al cuadrado de las variables (y, si se desea, términos cruzados) para recoger curvaturas en los datos. De esta forma, en lugar de buscar la mejor recta que pasa por el conjunto de puntos, buscamos la mejor parábola (u otro polinomio de segundo grado) que minimiza la suma de los errores cuadráticos entre valores observados y valores estimados.
Desde el punto de vista del código en C#, tanto la versión lineal como la cuadrática se resuelven con el mismo tipo de enfoque matricial: se construye una matriz de diseño a partir de los datos, se plantea una ecuación de mínimos cuadrados y se obtienen los coeficientes calculando una expresión cerrada basada en productos e inversas de matrices.
De datos crudos a matriz de diseño en C#
Un programa típico de regresión en C# arranca con un conjunto de datos, que pueden ser reales o generados de forma sintética. Un enfoque muy habitual es preparar unas cuantas filas donde cada una contiene los valores de los predictores y, al final, la salida numérica que queremos predecir. En el ejemplo clásico de ingresos, habría columnas como años de educación, experiencia laboral, sexo codificado como 0 o 1, y por último el ingreso en miles de unidades monetarias.
Para aplicar el método de mínimos cuadrados usando álgebra matricial, se transforma esa tabla de datos en una matriz de diseño. Esta matriz se construye añadiendo delante de todas las columnas originales una columna inicial rellena de unos. Esa columna representa el término independiente (el intercepto) del modelo. En el caso lineal, cada columna posterior de la matriz de diseño refleja un predictor; en el caso cuadrático, añadiremos también las columnas correspondientes a los cuadrados de los predictores, y si queremos complicarlo un poco más, columnas con productos cruzados entre ellos.
En C#, una forma sencilla de representar matrices es como un array de arrays de tipo double, sin necesidad de crear una clase Matrix ad hoc. Un método típico llamado, por ejemplo, Design, recibe la matriz de datos original y devuelve una nueva matriz que ya incluye tanto la columna de unos como todos los predictores que se vayan a utilizar en el modelo (lineales y cuadráticos).
En el caso cuadrático con una sola variable x, la matriz de diseño para cada fila contendría algo de este estilo: 1, x, x², y; donde 1 es el término constante, x es el predictor lineal, x² el término cuadrático y y el valor real de la variable dependiente. El salto desde la regresión lineal a la cuadrática es simplemente aumentar el número de columnas con estas potencias adicionales.
Resolver los coeficientes mediante mínimos cuadrados y matrices
Bajo la superficie, tanto en regresión lineal como en la cuadrática, el problema se reduce a encontrar los coeficientes que hacen mínima la suma de los errores al cuadrado. Esa solución puede escribirse de forma compacta con notación matricial y se conoce muchas veces como solución en forma cerrada o ecuación normal de mínimos cuadrados.
En notación matemática, si llamamos X a la matriz de diseño (sin la última columna de la variable dependiente), Y al vector columna con los valores reales de salida y β al vector de coeficientes que queremos estimar, se usa una fórmula basada en transposición, producto e inversa de matrices. Aunque sobre el papel pueda parecer intimidante, en código C# se traduce en encadenar unas pocas operaciones de matrices bien definidas: primero transponer X, luego multiplicar esa transpuesta por X, invertir el resultado, multiplicar de nuevo y, por último, aplicar ese resultado a Y para obtener el vector β.
Para que esto funcione, son indispensables tres operaciones: transposición de una matriz (intercambiar filas y columnas), producto matricial (combinar dos matrices compatibles fila a columna) e inversión de matrices cuadradas. La transposición es muy sencilla de implementar; el producto, una vez que se comprenden las dimensiones (n x m multiplicado por m x p da una matriz n x p), también resulta manejable. El hueso duro es la inversión de matrices, que suele requerir un algoritmo numérico más elaborado, por ejemplo una descomposición LU, como la famosa descomposición de Doolittle.
En regresión cuadrática, el tamaño de la matriz que hay que invertir depende del número de predictores que incluimos (lineales y al cuadrado). En general, si tenemos n variables de entrada y añadimos sus términos cuadráticos, el número de columnas en X crece, y la matriz a invertir será de tamaño (m x m), siendo m el número total de parámetros (término constante incluido). Esto hace que la técnica de inversión directa sea poco adecuada si el número de variables es enorme, pero funciona muy bien para problemas de dimensión moderada.
Estructura típica de un programa de regresión en C#
Un proyecto clásico para experimentar con regresión en C# se suele crear como aplicación de consola en Visual Studio. Basta con abrir un nuevo proyecto, elegir consola, ponerle un nombre descriptivo (por ejemplo, QuadraticRegression) y trabajar sobre el archivo Program.cs (o renombrarlo a algo más claro según gustos). No se requieren bibliotecas externas especiales, porque todo se puede implementar con las clases básicas de System y unos cuantos métodos estáticos.
La estructura del programa suele ser bastante lineal: en el método Main se imprimen algunos mensajes de inicio y fin, se generan o cargan los datos, se construye la matriz de diseño, se resuelven los coeficientes por mínimos cuadrados, se calcula alguna métrica de ajuste (como el R cuadrado) y se realiza una predicción de prueba para comprobar que todo está engranado correctamente.
Lo habitual es agrupar la lógica en varios métodos estáticos auxiliares, por ejemplo uno para generar datos sintéticos para hacer pruebas sin depender de ficheros externos, otro para construir la matriz de diseño, uno central llamado Solve que reciba esa matriz y devuelva los coeficientes, y otros métodos para calcular métricas como R² y para mostrar por consola matrices y vectores con formato amigable.
Además, se define un pequeño bloque de funciones dedicadas exclusivamente a operaciones de álgebra matricial: MatrixTranspose para la transposición, MatrixProduct para el producto de dos matrices, MatrixInverse para calcular el inverso y algún método de apoyo para crear matrices de un tamaño concreto, duplicarlas o resolver sistemas lineales internos. La mayor parte de la “chicha” técnica se concentra en MatrixInverse y en la descomposición de matrices, que son los puntos más delicados desde el punto de vista numérico.
Para que el código sea más legible, suele emplearse la representación más simple de matriz, es decir, un double[][], en lugar de diseñar una clase compleja con operadores sobrecargados. Esta elección simplifica las cosas y hace que el ejemplo sea más fácil de trasladar o adaptar a otros proyectos sin arrastrar demasiada infraestructura de código.
El método Solve: corazón del cálculo de coeficientes
El núcleo del algoritmo de regresión, tanto lineal como cuadrática, se concentra en un método que podemos llamar Solve. Este método recibe la matriz de diseño completa, en la que cada fila contiene los predictores (incluido el 1 inicial para el término constante) y, habitualmente, la última columna con la variable dependiente. A partir de ahí, se separa la información en dos matrices internas: una X con los predictores y una Y con los valores de salida.
Para crear esas dos matrices, se recorre la matriz de diseño fila a fila. En cada iteración, se copian todas las columnas menos la última en X y se deja la columna final como valor de Y. Es importante tener cuidado con los índices para no equivocarse al separar los datos, ya que un simple desajuste puede dar lugar a coeficientes completamente erróneos sin que el compilador avise de nada raro.
Una vez que se tienen X e Y, el método Solve encadena las operaciones matriciales: primero se obtiene la matriz transpuesta Xt, luego se calcula XtX como el producto de Xt por X, posteriormente se calcula la inversa de XtX y, con ella, se multiplica de nuevo por Xt. El resultado es una matriz intermedia que, al multiplicarse por Y, da lugar a otra matriz columna cuyos valores son precisamente los coeficientes del modelo.
Esos coeficientes se suelen pasar a un vector unidimensional mediante un pequeño método auxiliar, para manejarlos con comodidad en el resto del programa. Así se puede definir una función Income o similar que, dados los valores de los predictores y el vector de coeficientes, devuelva la predicción del modelo simplemente multiplicando y sumando los términos.
En regresión cuadrática, el procedimiento es exactamente el mismo: solo cambia que X tiene más columnas, porque se incluyen también x² y, si se quiere, los términos de interacción. La transpuesta, los productos y la inversa se calculan con las mismas rutinas, por lo que el salto de un modelo lineal a uno cuadrático no requiere reescribir la lógica central, sino únicamente ampliar la matriz de diseño.
Cálculo del R cuadrado como medida de ajuste
Aunque se puede entrenar un modelo de regresión sin más, siempre es recomendable cuantificar qué tal se ajusta a los datos. Para ello se usa muy a menudo la métrica R cuadrado, también llamada coeficiente de determinación. Esta métrica toma valores entre 0 y 1 e indica qué porcentaje de la variabilidad de la variable dependiente explican los predictores del modelo.
En términos matemáticos, R² se suele definir como 1 menos el cociente entre la suma residual de cuadrados y la suma total de cuadrados. La suma residual de cuadrados (SSres) es la suma de todas las diferencias al cuadrado entre valores observados y valores estimados por el modelo; la suma total de cuadrados (SStot) se calcula como la suma de las diferencias al cuadrado entre cada valor observado y la media global de todos los valores observados.
En un método C# dedicado a calcular R cuadrado, se suele recorrer la colección de datos, obtener para cada fila la predicción usando los coeficientes y acumular las dos cantidades: por un lado, la suma de los residuos al cuadrado, y por otro la suma de la desviación al cuadrado respecto a la media. Con esas dos magnitudes, se aplica directamente la fórmula de R² para obtener un número comprensible y fácil de interpretar.
Un valor de R cuadrado cercano a 1 se suele considerar una señal de buen ajuste, pero la interpretación exacta depende mucho del tipo de datos y del contexto. En disciplinas con datos muy ruidosos, valores en torno al 0,6 o 0,7 pueden ser bastante aceptables. Para los modelos cuadráticos, R² también ayuda a comparar si merece la pena incorporar términos al cuadrado frente a un modelo puramente lineal, aunque hay que tener cuidado con el exceso de ajuste.
Existe una variante denominada R cuadrado ajustado que tiene en cuenta el número de predictores y el número de observaciones, penalizando los modelos con demasiadas variables. Sin embargo, en muchos ejemplos de introducción a la regresión en C#, el R² simple resulta más que suficiente para hacerse una idea clara de la capacidad explicativa del modelo.
Generación de datos de prueba y predicción con el modelo
Cuando se quiere ilustrar un algoritmo de regresión en C#, es habitual trabajar con datos sintéticos generados por código. Esto permite controlar perfectamente la escala de las variables y evitar dependencias con ficheros externos. Un método típico, por ejemplo DummyData, acepta parámetros como el número de filas a generar y una semilla para el generador aleatorio, y devuelve una matriz con todos los elementos listos para alimentar a la matriz de diseño.
En estos datos ficticios puede fijarse un rango de valores razonable para cada predictor, por ejemplo, años de educación dentro de un intervalo pequeño, experiencia laboral en un tramo concreto o una variable indicadora binaria para sexo. A partir de esos predictores, se puede calcular la variable dependiente con una fórmula controlada, añadiendo un poco de ruido aleatorio para que los datos no queden demasiado perfectos.
Una vez entrenado el modelo y obtenidos los coeficientes, se puede escribir un método que, dados unos valores concretos de entrada, calcule la predicción. Ese método, que podría llamarse Income, toma los parámetros numéricos y el vector de coeficientes, y devuelve el resultado de la combinación lineal (o cuadrática) adecuada. En un programa de demostración, suele usarse una instancia concreta, por ejemplo una persona con cierto nivel de estudios y experiencia, para mostrar la predicción generada por el modelo.
En regresión cuadrática, la función de predicción debe tener en cuenta los términos al cuadrado. De este modo, en vez de utilizar solo coef0 + coef1 * x, se evalúa algo del estilo coef0 + coef1 * x + coef2 * x * x. Si se tienen varias variables, la estructura se complica un poco, pero el esquema es exactamente el mismo: cada columna de la matriz de diseño tiene asociado un coeficiente y la predicción se obtiene multiplicando valor a valor y sumando todos los resultados.
Con este enfoque, el programa en C# funciona como una pequeña “caja negra” de regresión: se introducen filas de datos, se calcula la matriz de diseño, se resuelven los coeficientes con Solve, se mide el ajuste con R cuadrado y, finalmente, se aplica el modelo a nuevos casos para obtener estimaciones numéricas que se puedan utilizar en otras partes del sistema.
Al final, toda esta estructura de código y métodos de apoyo demuestra que, aunque el concepto teórico de regresión pueda parecer abstracto, su implementación práctica en C# se basa en una serie de pasos bien acotados, reutilizables y relativamente mecánicos, especialmente una vez que se tienen controladas las operaciones con matrices y la generación de datos de ejemplo.
El conjunto de ideas que rodea a la regresión lineal y cuadrática en C# convierte a este tipo de problemas en una especie de “Hello World” de la estadística clásica aplicada a la programación: sirve tanto para entender mejor el puente entre teoría matemática y código como para sentar las bases de técnicas de aprendizaje automático más avanzadas que comparten el mismo espíritu: ajustar modelos a datos reales con la ayuda de operaciones numéricas y estructuras bien organizadas en código.