• 用C#实现最小二乘法(用OxyPlot绘图)✨


    最小二乘法介绍✨

    最小二乘法(Least Squares Method)是一种常见的数学优化技术,广泛应用于数据拟合、回归分析和参数估计等领域。其目标是通过最小化残差平方和来找到一组参数,使得模型预测值与观测值之间的差异最小化。

    最小二乘法的原理✨

    线性回归模型将因变量 (y) 与至少一个自变量 (x) 之间的关系建立为:

    image-20240118105946580

    在 OLS 方法中,我们必须选择一个b1和b0的值,以便将 y 的实际值和拟合值之间的差值的平方和最小化。

    平方和的公式如下:

    image-20240118110247858

    我们可以把它看成是一个关于b1和b0的函数,分别对b1和b0求偏导,然后让偏导等于0,就可以得到最小平方和对应的b1和b0的值。

    先说结果,斜率最后推导出来如下所示:

    截距推导出来结果如下:

    don't worry about that,慢慢推导总是可以弄明白的(不感兴趣可以直接略过):

    最小二乘法推导1

    最小二乘法推导2

    最小二乘法推导3

    用C#实现最小二乘法✨

    创建数据点✨

    首先创建想要拟合的数据点:

     NDArray? x, y;

    x,y为全局变量。

      //使用NumSharp创建线性回归的数据集
     x = np.arange(0, 10, 0.2);
     y = 2 * x + 3 + np.random.normal(0, 3, x.size);

    使用到了NumSharp,需要为项目添加NumSharp包:

    image-20240120100221733

     x = np.arange(0, 10, 0.2);

    的意思是x从0增加到10(不包含10),步长为0.2:

    image-20240120100455351

    np.random.normal(0, 3, x.size);

    的意思是生成了一个均值为0,标准差为3,数量与x数组长度相同的正态分布随机数数组。这个数组被用作线性回归数据的噪声。

    使用OxyPlot画散点图✨

    OxyPlot是一个用于在.NET应用程序中创建数据可视化图表的开源图表库。它提供了丰富的功能和灵活性,使开发者能够轻松地在其应用程序中集成各种类型的图表,包括折线图、柱状图、饼图等。

    image-20240120101110294

    添加OxyPlot.WindowsForms包:

    image-20240120101228438

    将PlotView控件添加到窗体设计器上:

    image-20240120101340414

    // 初始化散点图数据
    var scatterSeries = new ScatterSeries
    {
       MarkerType = MarkerType.Circle,
       MarkerSize = 5,
       MarkerFill = OxyColors.Blue
    };

    表示标志为圆形,标志用蓝色填充,标志的大小为5。

      for (int i = 0; i < x.size; i++)
    {
         scatterSeries.Points.Add(new ScatterPoint(x[i], y[i]));
    }

    添加数据点。

     PlotModel? plotModel;

    将plotModel设置为全局变量。

     // 创建 PlotModel
    plotModel = new PlotModel()
    {
        Title = "散点图"
    };
    plotModel.Series.Add(scatterSeries);

    // 将 PlotModel 设置到 PlotView
    plotView1.Model = plotModel;

    这样就成功绘制了散点图,效果如下所示:

    image-20240120102920929

    使用最小二乘法拟合数据点✨

    double a = 0;
    double c = 0;

    double x_mean = x?.mean();
    double y_mean = y?.mean();

    //计算a和c
    for(int i = 0; i < x?.size; i++)
    {
       a += (x[i] - x_mean) * (y?[i] - y_mean);
       c += (x[i] - x_mean) * (x[i] - x_mean);
    }

    //计算斜率和截距
    double m = a / c;
    double b = y_mean - m * x_mean;

    //拟合的直线
    var y2 = m * x + b;

    套用公式就可以,a表示上面斜率公式的上面那部分,c表示上面斜率公式的下面那部分。

    double x_mean = x?.mean();
    double y_mean = y?.mean();

    计算x与y的平均值。

    使用OxyPlot画拟合出来的直线✨

     //画这条直线         
    var lineSeries = new LineSeries
    {
        Points = { new DataPoint(x?[0], y2[0]), new DataPoint(x?[-1], y2[-1]) },
        Color = OxyColors.Red
    };

    // 创建 PlotModel        
    plotModel?.Series.Add(lineSeries);

    // 为图表添加标题
    if (plotModel != null)
    {
        plotModel.Title = $"拟合的直线 y = {m:0.00}x + {b:0.00}";
    }

    // 刷新 PlotView
    plotView1.InvalidatePlot(true);
     Points = { new DataPoint(x?[0], y2[0]), new DataPoint(x?[-1], y2[-1]) },

    画直线只要添加两个点就好了x?[0], y2[0]表示x和y的第一个点,x?[-1], y2[-1])表示x和y的最后一个点,使用了NumSharp的切片语法。

    画出来的效果如下所示:

    image-20240120103737259

    C#实现的全部代码:

    using NumSharp;
    using OxyPlot.Series;
    using OxyPlot;
    namespace OlsRegressionDemoUsingWinform
    {
       public partial class Form1 : Form
      {
           NDArray? x, y;
           PlotModel? plotModel;
           public Form1()
          {
               InitializeComponent();
          }

           private void button1_Click(object sender, EventArgs e)
          {
               //使用NumSharp创建线性回归的数据集
               x = np.arange(0, 10, 0.2);
               y = 2 * x + 3 + np.random.normal(0, 3, x.size);

               // 初始化散点图数据
               var scatterSeries = new ScatterSeries
              {
                   MarkerType = MarkerType.Circle,
                   MarkerSize = 5,
                   MarkerFill = OxyColors.Blue
              };

               for (int i = 0; i < x.size; i++)
              {
                   scatterSeries.Points.Add(new ScatterPoint(x[i], y[i]));
              }

               // 创建 PlotModel
               plotModel = new PlotModel()
              {
                   Title = "散点图"
              };
               plotModel.Series.Add(scatterSeries);

               // 将 PlotModel 设置到 PlotView
               plotView1.Model = plotModel;




          }

           private void button2_Click(object sender, EventArgs e)
          {
               double a = 0;
               double c = 0;

               double x_mean = x?.mean();
               double y_mean = y?.mean();

               //计算a和c
               for(int i = 0; i < x?.size; i++)
              {
                   a += (x[i] - x_mean) * (y?[i] - y_mean);
                   c += (x[i] - x_mean) * (x[i] - x_mean);
              }

               //计算斜率和截距
               double m = a / c;
               double b = y_mean - m * x_mean;

               //拟合的直线
               var y2 = m * x + b;

               //画这条直线        
               var lineSeries = new LineSeries
              {
                   Points = { new DataPoint(x?[0], y2[0]), new DataPoint(x?[-1], y2[-1]) },
                   Color = OxyColors.Red
              };

               // 创建 PlotModel        
               plotModel?.Series.Add(lineSeries);

               // 为图表添加标题
               if (plotModel != null)
              {
                   plotModel.Title = $"拟合的直线 y = {m:0.00}x + {b:0.00}";
              }
             
               // 刷新 PlotView
               plotView1.InvalidatePlot(true);

          }
      }
    }

    用Python实现最小二乘法✨

    import numpy as np
    import matplotlib.pyplot as plt

    # 用最小二乘法拟合 y = mx + b

    # 设置随机数种子以保证结果的可复现性
    np.random.seed(0)

    # 生成一个在[0, 10]区间内均匀分布的100个数作为x
    x = np.linspace(0, 10, 100)

    # 生成y,y = 2x + 噪声,其中噪声是[0, 10)之间的随机整数
    y = 2 * x + 5 + np.random.randint(0, 10, size=100)

    # 计算x和y的均值
    x_mean = np.mean(x)
    y_mean = np.mean(y)

    a = 0
    c = 0

    for i in range(x.shape[0]):
      a += (x[i] - x_mean) * (y[i] - y_mean)
      c += (x[i] - x_mean) ** 2

    # 计算斜率和截距
    m = a / c
    b = y_mean - m * x_mean
     
    # 画这条直线
    y2 = m * x + b
    plt.plot(x, y2, color='red')

    # 画数据点
    plt.scatter(x, y)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title(f'y = {m:.2f}x + {b:.2f}')
    plt.show()

    运行效果如下所示:

    image-20240120104300224

    总结✨

    本文向大家介绍了最小二乘法以及公式推导的过程,并使用C#与Python进行实现。重点介绍了C#中是如何实现的,同时介绍了在C#中如何使用OxyPlot绘图。希望对你有所帮助。

    参考✨

    1、Understanding Ordinary Least Squares (OLS) Regression | Built In

    2、Machine Learning Series-Linear Regression Ordinary Least Square Method - YouTube

     
  • 相关阅读:
    自建云服务计费系统
    #案例:切换标签页/切换窗口!
    arm设备上的嵌入式开发编译环境搭建
    LeetCode 241. 为运算表达式设计优先级(分治/记忆化递归/动态规划)
    数据结构(Java):树&二叉树
    HarmonyOS鸿蒙原生应用开发设计- 图标库
    【校招VIP】 java开源框架之mq
    Git LFS提交大文件
    Qt之QOpenGLWidget开始3D显示
    Linux配置java,maven,marshalsec环境
  • 原文地址:https://www.cnblogs.com/mingupupu/p/17976196