当前位置: 代码网 > it编程>编程语言>Asp.net > C#基于ScottPlot进行可视化

C#基于ScottPlot进行可视化

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

c#基于scottplot进行可视化

前言

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

python代码进行可视化

python代码用matplotlib做了可视化,我就不具体介绍了。

修改之后的python代码如下:

#the optimal values of m and b can be actually calculated with way less effort than doing a linear regression. 
#this is just to demonstrate gradient descent

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import funcanimation


# y = mx + b
# m is slope, b is y-intercept
def compute_error_for_line_given_points(b, m, points):
    totalerror = 0
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        totalerror += (y - (m * x + b)) ** 2
    return totalerror / float(len(points))

def step_gradient(b_current, m_current, points, learningrate):
    b_gradient = 0
    m_gradient = 0
    n = float(len(points))
    for i in range(0, len(points)):
        x = points[i, 0]
        y = points[i, 1]
        b_gradient += -(2/n) * (y - ((m_current * x) + b_current))
        m_gradient += -(2/n) * x * (y - ((m_current * x) + b_current))
    new_b = b_current - (learningrate * b_gradient)
    new_m = m_current - (learningrate * m_gradient)
    return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
    b = starting_b
    m = starting_m
    args_data = []
    for i in range(num_iterations):
        b, m = step_gradient(b, m, np.array(points), learning_rate)
        args_data.append((b,m))
    return args_data

if __name__ == '__main__':
     points = np.genfromtxt("data.csv", delimiter=",")
     learning_rate = 0.0001
     initial_b = 0 # initial y-intercept guess
     initial_m = 0 # initial slope guess
     num_iterations = 10
     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)))
     print ("running...")
     args_data = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
     
     b = args_data[-1][0]
     m = args_data[-1][1]

     print ("after {0} iterations b = {1}, m = {2}, error = {3}".format(num_iterations, b, m, compute_error_for_line_given_points(b, m, points)))
    
     data = np.array(points).reshape(100,2)
     x1 = data[:,0]
     y1 = data[:,1]
     
     x2 = np.linspace(20, 80, 100)
     y2 = initial_m * x2 + initial_b

     data2 = np.array(args_data)
     b_every = data2[:,0]
     m_every = data2[:,1]

     # 创建图形和轴
     fig, ax = plt.subplots()
     line1, = ax.plot(x1, y1, 'ro')
     line2, = ax.plot(x2,y2)

     # 添加标签和标题
     plt.xlabel('x')
     plt.ylabel('y')
     plt.title('graph of y = mx + b')

     # 添加网格
     plt.grid(true)

    # 定义更新函数
     def update(frame):
        line2.set_ydata(m_every[frame] * x2 + b_every[frame])
        ax.set_title(f'{frame} graph of y = {m_every[frame]:.2f}x + {b_every[frame]:.2f}')
    
# 创建动画
animation = funcanimation(fig, update, frames=len(data2), interval=500)

# 显示动画
plt.show()

实现的效果如下所示:

python代码的可视化

image-20240113200232614

c#代码进行可视化

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

scottplot简介

scottplot 是一个免费的开源绘图库,用于 .net,可以轻松以交互方式显示大型数据集。

控制台程序可视化

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

首先添加scottplot包:

image-20240113201207374

将上篇文章中的c#代码修改如下:

using numsharp;

namespace linearregressiondemo
{
    internal class program
    {    
        static void main(string[] args)
        {   
            //创建double类型的列表
            list<double> array = new list<double>();
            list<double> argslist = new list<double>();

            // 指定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;
            double num_iterations = 10;

            console.writeline($"starting gradient descent at b = {initial_b}, m = {initial_m}, error = {compute_error_for_line_given_points(initial_b, initial_m, array)}");
            console.writeline("running...");
            argslist = gradient_descent_runner(array, initial_b, initial_m, learning_rate, num_iterations);
            double b = argslist[argslist.count - 2];
            double m = argslist[argslist.count - 1];
            console.writeline($"after {num_iterations} iterations b = {b}, m = {m}, error = {compute_error_for_line_given_points(b, m, array)}");
            console.readline();

            var x1 = array[$":", 0];
            var y1 = array[$":", 1];
            var y2 = m * x1 + b;

            scottplot.plot myplot = new(400, 300);
            myplot.addscatterpoints(x1.toarray<double>(), y1.toarray<double>(), markersize: 5);
            myplot.addscatter(x1.toarray<double>(), y2.toarray<double>(), markersize: 0);
            myplot.title($"y = {m:0.00}x + {b:0.00}");

            myplot.savefig("图片.png");
       
        }

        static list<double> readcsv(string filepath)
        {
            list<double> array = new list<double>();
            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<double> gradient_descent_runner(ndarray array, double starting_b, double starting_m, double learningrate,double num_iterations)
        {
            double[] args = new double[2];
            list<double> argslist = new list<double>();
            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;
        }


    }
}

然后得到的图片如下所示:

image-20240113202345301

在以上代码中需要注意的地方:

  var x1 = array[$":", 0];
  var y1 = array[$":", 1];

是在使用numsharp中的切片,x1表示所有行的第一列,y1表示所有行的第二列。

当然我们不满足于只是保存图片,在控制台应用程序中,再添加一个 scottplot.winforms包:

image-20240113202751162

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

image-20240113212334704

将上述代码中的

  myplot.savefig("图片.png");

修改为:

 var viewer = new scottplot.formsplotviewer(myplot);
 viewer.showdialog();

再次运行结果如下:

image-20240113203022718

winform进行可视化

我也想像python代码中那样画动图,因此做了个winform程序进行演示。

首先创建一个winform,添加scottplot.winforms包,然后从工具箱中添加formsplot这个控件:

image-20240113205227384

有两种方法实现,第一种方法用了定时器:

using numsharp;
namespace winformdemo
{
    public partial class form1 : form
    {
        system.windows.forms.timer updatetimer = new system.windows.forms.timer();
        int num_iterations;
        int count = 0;
        ndarray? x1, y1, b_each, m_each;
        public form1()
        {
            initializecomponent();
        }

        private void button1_click(object sender, eventargs e)
        {
            startlinearregression();
        }

        public void startlinearregression()
        {
            //创建double类型的列表
            list<double> array = new list<double>();
            list<double> argslist = new list<double>();

            // 指定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];
            m_each = argsarr[$":", 1];

            double b = b_each[-1];
            double m = m_each[-1];
            var y2 = m * x1 + b;

            formsplot1.plot.addscatterpoints(x1.toarray<double>(), y1.toarray<double>(), markersize: 5);
            //formsplot1.plot.addscatter(x1.toarray<double>(), y2.toarray<double>(), markersize: 0);
            formsplot1.render();


        }

        static list<double> readcsv(string filepath)
        {
            list<double> array = new list<double>();
            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<double> gradient_descent_runner(ndarray array, double starting_b, double starting_m, double learningrate, double num_iterations)
        {
            double[] args = new double[2];
            list<double> argslist = new list<double>();
            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 button2_click(object sender, eventargs e)
        {
            // 初始化定时器
            updatetimer.interval = 1000; // 设置定时器触发间隔(毫秒)
            updatetimer.tick += updatetimer_tick;
            updatetimer.start();
        }

        private void updatetimer_tick(object? sender, eventargs e)
        {
            if (count >= num_iterations)
            {
                updatetimer.stop();
            }
            else
            {
                updateplot(count);
            }

            count++;
        }

        public void updateplot(int count)
        {

            double b = b_each?[count];
            double m = m_each?[count];

            var y2 = m * x1 + b;

            formsplot1.plot.clear();
            formsplot1.plot.addscatterpoints(x1?.toarray<double>(), y1?.toarray<double>(), markersize: 5);
            formsplot1.plot.addscatter(x1?.toarray<double>(), y2.toarray<double>(), markersize: 0);
            formsplot1.plot.title($"第{count + 1}次迭代:y = {m:0.00}x + {b:0.00}");
            formsplot1.render();
        }

        private void button3_click(object sender, eventargs e)
        {
            updatetimer.stop();
        }

        private void form1_load(object sender, eventargs e)
        {

        }
    }
}

简单介绍一下思路,首先创建list<double> argslist用来保存每次迭代生成的参数b、m,然后用

           var argsarr = np.array(argslist).reshape(num_iterations, 2);  

argslist通过np.array()方法转化为ndarray,然后再调用reshape方法,转化成行数等于迭代次数,列数为2,即每一行对应一组参数值b、m。

            b_each = argsarr[$":", 0];
            m_each = argsarr[$":", 1];

argsarr[$":", 0]表示每一行中第一列的值,也就是每一个b,argsarr[$":", 1]表示每一行中第二列的值。

            double b = b_each[-1];
            double m = m_each[-1];

b_each[-1]用了numsharp的功能表示b_each最后一个元素。

实现效果如下所示:

winform绘图效果1

image-20240113205549690

另一种方法可以通过异步实现:

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<double> array = new list<double>();
            list<double> argslist = new list<double>();

            // 指定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];
            m_each = argsarr[$":", 1];

            double b = b_each[-1];
            double m = m_each[-1];
            var y2 = m * x1 + b;

            formsplot1.plot.addscatterpoints(x1.toarray<double>(), y1.toarray<double>(), markersize: 5);      
            formsplot1.render();
        }

        static list<double> readcsv(string filepath)
        {
            list<double> array = new list<double>();
            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<double> gradient_descent_runner(ndarray array, double starting_b, double starting_m, double learningrate, double num_iterations)
        {
            double[] args = new double[2];
            list<double> argslist = new list<double>();
            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()
        {
            for (int i = 0; i < num_iterations; i++)
            {
                double b = b_each?[i];
                double m = m_each?[i];
                var y2 = m * x1 + b;

                formsplot1.plot.clear();
                formsplot1.plot.addscatterpoints(x1?.toarray<double>(), y1?.toarray<double>(), markersize: 5);
                formsplot1.plot.addscatter(x1?.toarray<double>(), y2.toarray<double>(), markersize: 0);
                formsplot1.plot.title($"第{i + 1}次迭代:y = {m:0.00}x + {b:0.00}");
                formsplot1.render();
           
                await task.delay(1000);
            }


        }

        private async void button2_click(object sender, eventargs e)
        {
            await updategraph();
        }
    }
}

点击更新按钮开始执行异步任务:

 private async void button2_click(object sender, eventargs e)
        {
            await updategraph();
        }
 public async task updategraph()
        {
            for (int i = 0; i < num_iterations; i++)
            {
                double b = b_each?[i];
                double m = m_each?[i];
                var y2 = m * x1 + b;

                formsplot1.plot.clear();
                formsplot1.plot.addscatterpoints(x1?.toarray<double>(), y1?.toarray<double>(), markersize: 5);
                formsplot1.plot.addscatter(x1?.toarray<double>(), y2.toarray<double>(), markersize: 0);
                formsplot1.plot.title($"第{i + 1}次迭代:y = {m:0.00}x + {b:0.00}");
                formsplot1.render();
           
                await task.delay(1000);
            }

实现效果如下:

winform绘图效果2

image-20240113210320131

总结

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com