4. 線性回歸

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Xml.Serialization.Configuration;

namespace Q4
{
    public partial class Form1 : Form
    {
        string t;
        bool ignore = false;
        int len = -1;
        List<PointF> dots = new List<PointF>();

        public Form1()
        {
            InitializeComponent();
            ignore = true;
            textBox1.Text = "線性回歸(Linear Regression)\r\n";
            textBox1.Text += "利用最小平方方法讓一條直線逼近一些點\r\n";
            textBox1.Text += "請輸入資料總數: ";
            ignore = false;

            t = textBox1.Text;
            draw();
        }

        double? M, B;

        void draw()
        {
            var series = new Series("輸入資料");
            series.ChartType = SeriesChartType.Point;
            foreach (var item in dots)
            {
                var p = series.Points[series.Points.AddXY(item.X, item.Y)];
                p.MarkerSize = 10;
                p.MarkerStyle = MarkerStyle.Circle;
            }
            chart1.Series.Clear();
            chart1.Series.Add(series);

            if (M != null)
            {
                var line = new Series("逼近的直線");
                line.ChartType = SeriesChartType.Line;
                line.Points.AddXY(0, B);
                double x = Math.Ceiling(dots.Max(e => e.X));
                line.Points.AddXY(x, M * x + B);
                line.BorderWidth= 3;
                chart1.Series.Add(line);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            chart1.ChartAreas.First()
                .AxisX.MajorGrid.LineDashStyle = ChartDashStyle.DashDot;
            chart1.ChartAreas.First()
                .AxisX.LabelStyle.Format = "0";
            chart1.ChartAreas.First()
                .AxisX.Title = "x";
            chart1.ChartAreas.First()
                .AxisY.MajorGrid.LineDashStyle = ChartDashStyle.DashDot;
            chart1.ChartAreas.First()
                .AxisY.LabelStyle.Format = "0";
            chart1.ChartAreas.First()
                .AxisY.Title = "y";
            chart1.Titles.Add("線性回歸 - 最小平方逼近");
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            if (ignore) return;
            if (textBox1.Text.Length < t.Length)
            {
                textBox1.Text = t;
                textBox1.SelectionStart = textBox1.Text.Length;
                return;
            }
            if (!textBox1.Text.EndsWith("\r\n"))
            {
                return;
            }

            var input = (textBox1.Text.Substring(t.Length, textBox1.Text.Length - t.Length - 2));
            if (len == -1)
            {
                len = int.Parse(input);
            }
            else
            {
                var l = input.Replace("[", "").Replace("]", "").Split(' ').ToList();
                var x = double.Parse(l[0]);
                var y = double.Parse(l[1]);
                dots.Add(new PointF(((float)x), ((float)y)));
                len--;
            }
            if (len != -1 && len != 0)
            {
                ignore = true;
                textBox1.Text += "請輸入每一點資料的x,y座標[x y]: ";
                ignore = false;
                t = textBox1.Text;
                textBox1.SelectionStart = textBox1.Text.Length;
            }
            else if (len == 0)
            {

                var sumx = 0.0;
                var sumy = 0.0;
                var sumxy = 0.0;
                var sumxx = 0.0;
                var avgX = dots.Average(d => d.X);
                var avgY = dots.Average(d => d.Y);
                foreach (var item in dots)
                {
                    sumxx += item.X * item.X;
                    sumxy += item.X * item.Y;
                    sumx += item.X;
                    sumy += item.Y;
                }
                var m = M = (sumxy - sumx * avgY) / (sumxx - sumx * avgX);
                var b = B = avgY - m * avgX;
                ignore = true;
                textBox1.Text += "最小平方直線的回歸係數: \r\n";
                textBox1.Text += $"  斜率 (m) \t= \t{m:f3}\r\n";
                textBox1.Text += $"  截距 (b) \t= \t{b:f3}\r\n";
                textBox1.Text += "總資料點數 \t= \t" + dots.Count;
                draw();
            }
        }
    }
}

Last updated