翼度科技»论坛 编程开发 .net 查看内容

C#基于ScottPlot进行可视化

13

主题

13

帖子

39

积分

新手上路

Rank: 1

积分
39
C#基于ScottPlot进行可视化

前言

上一篇文章跟大家分享了用NumSharp实现简单的线性回归,但是没有进行可视化,可能对拟合的过程没有直观的感受,因此今天跟大家介绍一下使用C#基于Scottplot进行可视化,当然Python的代码,我也会同步进行可视化。
Python代码进行可视化

Python代码用matplotlib做了可视化,我就不具体介绍了。
修改之后的python代码如下:
  1. #The optimal values of m and b can be actually calculated with way less effort than doing a linear regression.
  2. #this is just to demonstrate gradient descent
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. from matplotlib.animation import FuncAnimation
  6. # y = mx + b
  7. # m is slope, b is y-intercept
  8. def compute_error_for_line_given_points(b, m, points):
  9.     totalError = 0
  10.     for i in range(0, len(points)):
  11.         x = points[i, 0]
  12.         y = points[i, 1]
  13.         totalError += (y - (m * x + b)) ** 2
  14.     return totalError / float(len(points))
  15. def step_gradient(b_current, m_current, points, learningRate):
  16.     b_gradient = 0
  17.     m_gradient = 0
  18.     N = float(len(points))
  19.     for i in range(0, len(points)):
  20.         x = points[i, 0]
  21.         y = points[i, 1]
  22.         b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
  23.         m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
  24.     new_b = b_current - (learningRate * b_gradient)
  25.     new_m = m_current - (learningRate * m_gradient)
  26.     return [new_b, new_m]
  27. def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
  28.     b = starting_b
  29.     m = starting_m
  30.     args_data = []
  31.     for i in range(num_iterations):
  32.         b, m = step_gradient(b, m, np.array(points), learning_rate)
  33.         args_data.append((b,m))
  34.     return args_data
  35. if __name__ == '__main__':
  36.      points = np.genfromtxt("data.csv", delimiter=",")
  37.      learning_rate = 0.0001
  38.      initial_b = 0 # initial y-intercept guess
  39.      initial_m = 0 # initial slope guess
  40.      num_iterations = 10
  41.      print ("Starting gradient descent at b = {0}, m = {1}, error = {2}".format(initial_b, initial_m, compute_error_for_line_given_points(initial_b, initial_m, points)))
  42.      print ("Running...")
  43.      args_data = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
  44.      
  45.      b = args_data[-1][0]
  46.      m = args_data[-1][1]
  47.      print ("After {0} iterations b = {1}, m = {2}, error = {3}".format(num_iterations, b, m, compute_error_for_line_given_points(b, m, points)))
  48.    
  49.      data = np.array(points).reshape(100,2)
  50.      x1 = data[:,0]
  51.      y1 = data[:,1]
  52.      
  53.      x2 = np.linspace(20, 80, 100)
  54.      y2 = initial_m * x2 + initial_b
  55.      data2 = np.array(args_data)
  56.      b_every = data2[:,0]
  57.      m_every = data2[:,1]
  58.      # 创建图形和轴
  59.      fig, ax = plt.subplots()
  60.      line1, = ax.plot(x1, y1, 'ro')
  61.      line2, = ax.plot(x2,y2)
  62.      # 添加标签和标题
  63.      plt.xlabel('x')
  64.      plt.ylabel('y')
  65.      plt.title('Graph of y = mx + b')
  66.      # 添加网格
  67.      plt.grid(True)
  68.     # 定义更新函数
  69.      def update(frame):
  70.         line2.set_ydata(m_every[frame] * x2 + b_every[frame])
  71.         ax.set_title(f'{frame} Graph of y = {m_every[frame]:.2f}x + {b_every[frame]:.2f}')
  72.    
  73. # 创建动画
  74. animation = FuncAnimation(fig, update, frames=len(data2), interval=500)
  75. # 显示动画
  76. plt.show()
复制代码
实现的效果如下所示:


C#代码进行可视化

这是本文重点介绍的内容,本文的C#代码通过Scottplot进行可视化。
Scottplot简介

ScottPlot 是一个免费的开源绘图库,用于 .NET,可以轻松以交互方式显示大型数据集。
控制台程序可视化

首先我先介绍一下在控制台程序中进行可视化。
首先添加Scottplot包:

将上篇文章中的C#代码修改如下:
  1. using NumSharp;
  2. namespace LinearRegressionDemo
  3. {
  4.     internal class Program
  5.     {   
  6.         static void Main(string[] args)
  7.         {   
  8.             //创建double类型的列表
  9.             List<double> Array = new List<double>();
  10.             List<double> ArgsList = new List<double>();
  11.             // 指定CSV文件的路径
  12.             string filePath = "你的data.csv路径";
  13.             // 调用ReadCsv方法读取CSV文件数据
  14.             Array = ReadCsv(filePath);
  15.             var array = np.array(Array).reshape(100,2);
  16.             double learning_rate = 0.0001;
  17.             double initial_b = 0;
  18.             double initial_m = 0;
  19.             double num_iterations = 10;
  20.             Console.WriteLine($"Starting gradient descent at b = {initial_b}, m = {initial_m}, error = {compute_error_for_line_given_points(initial_b, initial_m, array)}");
  21.             Console.WriteLine("Running...");
  22.             ArgsList = gradient_descent_runner(array, initial_b, initial_m, learning_rate, num_iterations);
  23.             double b = ArgsList[ArgsList.Count - 2];
  24.             double m = ArgsList[ArgsList.Count - 1];
  25.             Console.WriteLine($"After {num_iterations} iterations b = {b}, m = {m}, error = {compute_error_for_line_given_points(b, m, array)}");
  26.             Console.ReadLine();
  27.             var x1 = array[$":", 0];
  28.             var y1 = array[$":", 1];
  29.             var y2 = m * x1 + b;
  30.             ScottPlot.Plot myPlot = new(400, 300);
  31.             myPlot.AddScatterPoints(x1.ToArray<double>(), y1.ToArray<double>(), markerSize: 5);
  32.             myPlot.AddScatter(x1.ToArray<double>(), y2.ToArray<double>(), markerSize: 0);
  33.             myPlot.Title($"y = {m:0.00}x + {b:0.00}");
  34.             myPlot.SaveFig("图片.png");
  35.       
  36.         }
  37.         static List<double> ReadCsv(string filePath)
  38.         {
  39.             List<double> array = new List<double>();
  40.             try
  41.             {
  42.                 // 使用File.ReadAllLines读取CSV文件的所有行
  43.                 string[] lines = File.ReadAllLines(filePath);            
  44.                 // 遍历每一行数据
  45.                 foreach (string line in lines)
  46.                 {
  47.                     // 使用逗号分隔符拆分每一行的数据
  48.                     string[] values = line.Split(',');
  49.                     // 打印每一行的数据
  50.                     foreach (string value in values)
  51.                     {
  52.                         array.Add(Convert.ToDouble(value));
  53.                     }                  
  54.                 }
  55.             }
  56.             catch (Exception ex)
  57.             {
  58.                 Console.WriteLine("发生错误: " + ex.Message);
  59.             }
  60.             return array;
  61.         }
  62.         public static double compute_error_for_line_given_points(double b,double m,NDArray array)
  63.         {
  64.             double totalError = 0;
  65.             for(int i = 0;i < array.shape[0];i++)
  66.             {
  67.                 double x = array[i, 0];
  68.                 double y = array[i, 1];
  69.                 totalError += Math.Pow((y - (m*x+b)),2);
  70.             }
  71.             return totalError / array.shape[0];
  72.         }
  73.         public static double[] step_gradient(double b_current,double m_current,NDArray array,double learningRate)
  74.         {
  75.             double[] args = new double[2];
  76.             double b_gradient = 0;
  77.             double m_gradient = 0;
  78.             double N = array.shape[0];
  79.             for (int i = 0; i < array.shape[0]; i++)
  80.             {
  81.                 double x = array[i, 0];
  82.                 double y = array[i, 1];
  83.                 b_gradient += -(2 / N) * (y - ((m_current * x) + b_current));
  84.                 m_gradient += -(2 / N) * x * (y - ((m_current * x) + b_current));
  85.             }
  86.             double new_b = b_current - (learningRate * b_gradient);
  87.             double new_m = m_current - (learningRate * m_gradient);
  88.             args[0] = new_b;
  89.             args[1] = new_m;
  90.             return args;
  91.         }
  92.         public static List<double> gradient_descent_runner(NDArray array, double starting_b, double starting_m, double learningRate,double num_iterations)
  93.         {
  94.             double[] args = new double[2];
  95.             List<double> argsList = new List<double>();
  96.             args[0] = starting_b;
  97.             args[1] = starting_m;
  98.             for(int i = 0 ; i < num_iterations; i++)
  99.             {
  100.                 args = step_gradient(args[0], args[1], array, learningRate);
  101.                 argsList.AddRange(args);
  102.             }
  103.             return argsList;
  104.         }
  105.     }
  106. }
复制代码
然后得到的图片如下所示:

在以上代码中需要注意的地方:
  1.   var x1 = array[$":", 0];
  2.   var y1 = array[$":", 1];
复制代码
是在使用NumSharp中的切片,x1表示所有行的第一列,y1表示所有行的第二列。
当然我们不满足于只是保存图片,在控制台应用程序中,再添加一个 ScottPlot.WinForms包:

右键控制台项目选择属性,将目标OS改为Windows:

将上述代码中的
  1.   myPlot.SaveFig("图片.png");
复制代码
修改为:
  1. var viewer = new ScottPlot.FormsPlotViewer(myPlot);
  2. viewer.ShowDialog();
复制代码
再次运行结果如下:

winform进行可视化

我也想像Python代码中那样画动图,因此做了个winform程序进行演示。
首先创建一个winform,添加ScottPlot.WinForms包,然后从工具箱中添加FormsPlot这个控件:

有两种方法实现,第一种方法用了定时器:
  1. using NumSharp;
  2. namespace WinFormDemo
  3. {
  4.     public partial class Form1 : Form
  5.     {
  6.         System.Windows.Forms.Timer updateTimer = new System.Windows.Forms.Timer();
  7.         int num_iterations;
  8.         int count = 0;
  9.         NDArray? x1, y1, b_each, m_each;
  10.         public Form1()
  11.         {
  12.             InitializeComponent();
  13.         }
  14.         private void button1_Click(object sender, EventArgs e)
  15.         {
  16.             StartLinearRegression();
  17.         }
  18.         public void StartLinearRegression()
  19.         {
  20.             //创建double类型的列表
  21.             List<double> Array = new List<double>();
  22.             List<double> ArgsList = new List<double>();
  23.             // 指定CSV文件的路径
  24.             string filePath = "你的data.csv路径";
  25.             // 调用ReadCsv方法读取CSV文件数据
  26.             Array = ReadCsv(filePath);
  27.             var array = np.array(Array).reshape(100, 2);
  28.             double learning_rate = 0.0001;
  29.             double initial_b = 0;
  30.             double initial_m = 0;
  31.             num_iterations = 10;
  32.             ArgsList = gradient_descent_runner(array, initial_b, initial_m, learning_rate, num_iterations);
  33.             x1 = array[$":", 0];
  34.             y1 = array[$":", 1];
  35.             var argsArr = np.array(ArgsList).reshape(num_iterations, 2);
  36.             b_each = argsArr[$":", 0];
  37.             m_each = argsArr[$":", 1];
  38.             double b = b_each[-1];
  39.             double m = m_each[-1];
  40.             var y2 = m * x1 + b;
  41.             formsPlot1.Plot.AddScatterPoints(x1.ToArray<double>(), y1.ToArray<double>(), markerSize: 5);
  42.             //formsPlot1.Plot.AddScatter(x1.ToArray<double>(), y2.ToArray<double>(), markerSize: 0);
  43.             formsPlot1.Render();
  44.         }
  45.         static List<double> ReadCsv(string filePath)
  46.         {
  47.             List<double> array = new List<double>();
  48.             try
  49.             {
  50.                 // 使用File.ReadAllLines读取CSV文件的所有行
  51.                 string[] lines = File.ReadAllLines(filePath);
  52.                 // 遍历每一行数据
  53.                 foreach (string line in lines)
  54.                 {
  55.                     // 使用逗号分隔符拆分每一行的数据
  56.                     string[] values = line.Split(',');
  57.                     // 打印每一行的数据
  58.                     foreach (string value in values)
  59.                     {
  60.                         array.Add(Convert.ToDouble(value));
  61.                     }
  62.                 }
  63.             }
  64.             catch (Exception ex)
  65.             {
  66.                 Console.WriteLine("发生错误: " + ex.Message);
  67.             }
  68.             return array;
  69.         }
  70.         public static double compute_error_for_line_given_points(double b, double m, NDArray array)
  71.         {
  72.             double totalError = 0;
  73.             for (int i = 0; i < array.shape[0]; i++)
  74.             {
  75.                 double x = array[i, 0];
  76.                 double y = array[i, 1];
  77.                 totalError += Math.Pow((y - (m * x + b)), 2);
  78.             }
  79.             return totalError / array.shape[0];
  80.         }
  81.         public static double[] step_gradient(double b_current, double m_current, NDArray array, double learningRate)
  82.         {
  83.             double[] args = new double[2];
  84.             double b_gradient = 0;
  85.             double m_gradient = 0;
  86.             double N = array.shape[0];
  87.             for (int i = 0; i < array.shape[0]; i++)
  88.             {
  89.                 double x = array[i, 0];
  90.                 double y = array[i, 1];
  91.                 b_gradient += -(2 / N) * (y - ((m_current * x) + b_current));
  92.                 m_gradient += -(2 / N) * x * (y - ((m_current * x) + b_current));
  93.             }
  94.             double new_b = b_current - (learningRate * b_gradient);
  95.             double new_m = m_current - (learningRate * m_gradient);
  96.             args[0] = new_b;
  97.             args[1] = new_m;
  98.             return args;
  99.         }
  100.         public static List<double> gradient_descent_runner(NDArray array, double starting_b, double starting_m, double learningRate, double num_iterations)
  101.         {
  102.             double[] args = new double[2];
  103.             List<double> argsList = new List<double>();
  104.             args[0] = starting_b;
  105.             args[1] = starting_m;
  106.             for (int i = 0; i < num_iterations; i++)
  107.             {
  108.                 args = step_gradient(args[0], args[1], array, learningRate);
  109.                 argsList.AddRange(args);
  110.             }
  111.             return argsList;
  112.         }
  113.         private void button2_Click(object sender, EventArgs e)
  114.         {
  115.             // 初始化定时器
  116.             updateTimer.Interval = 1000; // 设置定时器触发间隔(毫秒)
  117.             updateTimer.Tick += UpdateTimer_Tick;
  118.             updateTimer.Start();
  119.         }
  120.         private void UpdateTimer_Tick(object? sender, EventArgs e)
  121.         {
  122.             if (count >= num_iterations)
  123.             {
  124.                 updateTimer.Stop();
  125.             }
  126.             else
  127.             {
  128.                 UpdatePlot(count);
  129.             }
  130.             count++;
  131.         }
  132.         public void UpdatePlot(int count)
  133.         {
  134.             double b = b_each?[count];
  135.             double m = m_each?[count];
  136.             var y2 = m * x1 + b;
  137.             formsPlot1.Plot.Clear();
  138.             formsPlot1.Plot.AddScatterPoints(x1?.ToArray<double>(), y1?.ToArray<double>(), markerSize: 5);
  139.             formsPlot1.Plot.AddScatter(x1?.ToArray<double>(), y2.ToArray<double>(), markerSize: 0);
  140.             formsPlot1.Plot.Title($"第{count + 1}次迭代:y = {m:0.00}x + {b:0.00}");
  141.             formsPlot1.Render();
  142.         }
  143.         private void button3_Click(object sender, EventArgs e)
  144.         {
  145.             updateTimer.Stop();
  146.         }
  147.         private void Form1_Load(object sender, EventArgs e)
  148.         {
  149.         }
  150.     }
  151. }
复制代码
简单介绍一下思路,首先创建List argsList用来保存每次迭代生成的参数b、m,然后用
  1.            var argsArr = np.array(ArgsList).reshape(num_iterations, 2);  
复制代码
将argsList通过np.array()方法转化为NDArray,然后再调用reshape方法,转化成行数等于迭代次数,列数为2,即每一行对应一组参数值b、m。
  1.             b_each = argsArr[$":", 0];
  2.             m_each = argsArr[$":", 1];
复制代码
argsArr[$":", 0]表示每一行中第一列的值,也就是每一个b,argsArr[$":", 1]表示每一行中第二列的值。
  1.             double b = b_each[-1];
  2.             double m = m_each[-1];
复制代码
b_each[-1]用了NumSharp的功能表示b_each最后一个元素。
实现效果如下所示:


另一种方法可以通过异步实现:
  1. using NumSharp;namespace WinFormDemo{    public partial class Form2 : Form    {              int num_iterations;        NDArray? x1, y1, b_each, m_each;        public Form2()        {            InitializeComponent();        }        private void button1_Click(object sender, EventArgs e)        {            StartLinearRegression();        }        public void StartLinearRegression()        {            //创建double类型的列表            List Array = new List();            List ArgsList = new List();            // 指定CSV文件的路径            string filePath = "你的data.csv路径";            // 调用ReadCsv方法读取CSV文件数据            Array = ReadCsv(filePath);            var array = np.array(Array).reshape(100, 2);            double learning_rate = 0.0001;            double initial_b = 0;            double initial_m = 0;            num_iterations = 10;            ArgsList = gradient_descent_runner(array, initial_b, initial_m, learning_rate, num_iterations);            x1 = array[$":", 0];            y1 = array[$":", 1];            var argsArr = np.array(ArgsList).reshape(num_iterations, 2);            b_each = argsArr[$":", 0];
  2.             m_each = argsArr[$":", 1];            double b = b_each[-1];
  3.             double m = m_each[-1];            var y2 = m * x1 + b;            formsPlot1.Plot.AddScatterPoints(x1.ToArray(), y1.ToArray(), markerSize: 5);                  formsPlot1.Render();        }        static List ReadCsv(string filePath)        {            List array = new List();            try            {                // 使用File.ReadAllLines读取CSV文件的所有行                string[] lines = File.ReadAllLines(filePath);                // 遍历每一行数据                foreach (string line in lines)                {                    // 使用逗号分隔符拆分每一行的数据                    string[] values = line.Split(',');                    // 打印每一行的数据                    foreach (string value in values)                    {                        array.Add(Convert.ToDouble(value));                    }                }            }            catch (Exception ex)            {                Console.WriteLine("发生错误: " + ex.Message);            }            return array;        }        public static double compute_error_for_line_given_points(double b, double m, NDArray array)        {            double totalError = 0;            for (int i = 0; i < array.shape[0]; i++)            {                double x = array[i, 0];                double y = array[i, 1];                totalError += Math.Pow((y - (m * x + b)), 2);            }            return totalError / array.shape[0];        }        public static double[] step_gradient(double b_current, double m_current, NDArray array, double learningRate)        {            double[] args = new double[2];            double b_gradient = 0;            double m_gradient = 0;            double N = array.shape[0];            for (int i = 0; i < array.shape[0]; i++)            {                double x = array[i, 0];                double y = array[i, 1];                b_gradient += -(2 / N) * (y - ((m_current * x) + b_current));                m_gradient += -(2 / N) * x * (y - ((m_current * x) + b_current));            }            double new_b = b_current - (learningRate * b_gradient);            double new_m = m_current - (learningRate * m_gradient);            args[0] = new_b;            args[1] = new_m;            return args;        }        public static List gradient_descent_runner(NDArray array, double starting_b, double starting_m, double learningRate, double num_iterations)        {            double[] args = new double[2];            List argsList = new List();            args[0] = starting_b;            args[1] = starting_m;            for (int i = 0; i < num_iterations; i++)            {                args = step_gradient(args[0], args[1], array, learningRate);                argsList.AddRange(args);            }            return argsList;        }        private void Form2_Load(object sender, EventArgs e)        {        }        public async Task UpdateGraph()
  4.         {
  5.             for (int i = 0; i < num_iterations; i++)
  6.             {
  7.                 double b = b_each?[i];
  8.                 double m = m_each?[i];
  9.                 var y2 = m * x1 + b;
  10.                 formsPlot1.Plot.Clear();
  11.                 formsPlot1.Plot.AddScatterPoints(x1?.ToArray<double>(), y1?.ToArray<double>(), markerSize: 5);
  12.                 formsPlot1.Plot.AddScatter(x1?.ToArray<double>(), y2.ToArray<double>(), markerSize: 0);
  13.                 formsPlot1.Plot.Title($"第{i + 1}次迭代:y = {m:0.00}x + {b:0.00}");
  14.                 formsPlot1.Render();
  15.            
  16.                 await Task.Delay(1000);
  17.             }        }        private async void button2_Click(object sender, EventArgs e)
  18.         {
  19.             await UpdateGraph();
  20.         }    }}
复制代码
点击更新按钮开始执行异步任务:
  1. private async void button2_Click(object sender, EventArgs e)
  2.         {
  3.             await UpdateGraph();
  4.         }
复制代码
  1. public async Task UpdateGraph()
  2.         {
  3.             for (int i = 0; i < num_iterations; i++)
  4.             {
  5.                 double b = b_each?[i];
  6.                 double m = m_each?[i];
  7.                 var y2 = m * x1 + b;
  8.                 formsPlot1.Plot.Clear();
  9.                 formsPlot1.Plot.AddScatterPoints(x1?.ToArray<double>(), y1?.ToArray<double>(), markerSize: 5);
  10.                 formsPlot1.Plot.AddScatter(x1?.ToArray<double>(), y2.ToArray<double>(), markerSize: 0);
  11.                 formsPlot1.Plot.Title($"第{i + 1}次迭代:y = {m:0.00}x + {b:0.00}");
  12.                 formsPlot1.Render();
  13.            
  14.                 await Task.Delay(1000);
  15.             }
复制代码
实现效果如下:


总结

本文以一个控制台应用与一个winform程序为例向大家介绍了C#如何基于ScottPlot进行数据可视化,并介绍了实现动态绘图的两种方式,一种是使用定时器,另一种是使用异步操作,希望对你有所帮助。

来源:https://www.cnblogs.com/mingupupu/Undeclared/17963079
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具