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()
实现的效果如下所示:


C#代码进行可视化
这是本文重点介绍的内容,本文的C#代码通过Scottplot进行可视化。
Scottplot简介
ScottPlot 是一个免费的开源绘图库,用于 .NET,可以轻松以交互方式显示大型数据集。
控制台程序可视化
首先我先介绍一下在控制台程序中进行可视化。
首先添加Scottplot包:

将上篇文章中的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;
}
}
}
然后得到的图片如下所示:

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

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

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

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

有两种方法实现,第一种方法用了定时器:
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最后一个元素。
实现效果如下所示:


另一种方法可以通过异步实现:
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程序为例向大家介绍了C#如何基于ScottPlot进行数据可视化,并介绍了实现动态绘图的两种方式,一种是使用定时器,另一种是使用异步操作,希望对你有所帮助。
C#基于ScottPlot进行可视化的更多相关文章
- 基于Kibana的可视化监控报警插件sentinl入门
sentinl是什么 Kibi/Kibana Alert & Reporting App Watching your data, 24/7/365 sentinl是一个免费的kibana预警与 ...
- 基于flask的可视化动漫分析网站【python入门必学】
课程设计项目名称:基于flask的可视化动漫分析网站,如果你在学习Python的过程中,往往因为没有好的教程或者没人指导从而导致自己容易放弃,为此我建了个Python交流.裙 :一久武其而而流一思(数 ...
- JVM调优(二)——基于JVisualVM的可视化监控
JVM调优(二)--基于JVisualVM的可视化监控 工具路径://java/jdk1.8xxx/bin/JVisuaVM.exe 监控本地的Tomcat 监控远程Tomcat 监控普通的JAVA进 ...
- Visual-platform,基于Vue的可视化大屏开发GUI框架
visual-platform 基于Vue的可视化大屏开发GUI框架 ------ CreatedBy 漆黑小T 构建用于开发可视化大屏项目的自适应布局的GUI框架. github仓库: https: ...
- 性能工具---JConsole基于JMX的可视化监视、管理工具
与visualvm类似: JConsole: (Java Monitoring and Management Console),一种基于JMX的可视化监视.管理工具 VisualVM:(All-in- ...
- [z]Windows 下基于 Eclipse 的可视化远程 Linux C/C++ 开发环境搭建
http://blog.csdn.net/lostaway/article/details/8086056 1.简介 Windows 下远程 Linux 开发工具,比较著名的就是 WinGDB 和 M ...
- 图数据库HugeGraph:HugeGraph-Hubble基于Web的可视化图管理初体验
原创/朱季谦 一.HugeGraph-Hubble简介 关于HugeGraph,官方资料是这样介绍的,它是一款易用.高效.通用的开源图数据库系统(Graph Database), 实现了 Apache ...
- 基于leaflet地图可视化(一)
最近,在学习地图可视化是基于公司的项目.但公司在项目上居然用图片来代替.无语~~~项目效果图(第一版)如下: 突发奇想,2016年自己就接触过地图可视化.但那是没有深入研究.只会用R语言来实现点基础. ...
- 搭建一个基于CentOS的可视化zookeeper管理工具zkUI实现对zk的可视化管理
一. zookeeper 可视化工具 JMX => CLRProfile ZKUI => java写的一个可视化的web网站 github中下载 https://github.com/ ...
- B/S 端构建的基于 WebGL 3D 可视化档案馆管理系统
前言 档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能.为企事业单位的档案现代化管理,提供完整的解决方案,档 ...
随机推荐
- PostgreSQL学习笔记-2.基础知识:INSERT、SELECT、运算符、表达式、约束
PostgreSQL INSERT INTO 语句用于向表中插入新记录,兼容SQL通用语法. 语法 INSERT INTO 语句语法格式如下: INSERT INTO TABLE_NAME (colu ...
- Java虚拟机(JVM):第二幕:自动内存管理 - Java内存区域与内存溢出异常
前言:Java与C++之间有一堵高墙,主要是有内存动态分配和垃圾收集技术组成的.墙外的人想要进来,墙内的人想要出去. 一.运行时数据区域 JVM在执行Java程序时,会将其管理的内存划分为若干个不同的 ...
- Java 集合的排序(正序倒序)、查找元素的下边、最大值、最小值
Java 集合的排序(正序倒序).查找元素的下边.最大值.最小值 集合的排序 集合查找对应元素的下标 集合的最大最小值 集合的排序 使用Collections.sort()排序,默认是递增.加上比较器 ...
- 手撕Vue-查找指令和模板
接着上一篇文章,我们已经实现了提取元素到内存的过程,接下来我们要实现的是查找指令和模板. 大致的思路是这样的: 遍历所有的节点 需要判断当前遍历到的节点是一个元素还是一个文本 如果是一个元素, 我们需 ...
- Util应用框架Web Api开发环境搭建
要使用Util应用框架开发项目,首先需要搭建合适的开发环境. 迈出第一步,对于很多.Net新人可能并不简单. 如果你对.Net环境并不熟悉,请尽量按照本文档进行操作. 操作系统 请安装 Windows ...
- 如何在 Ubuntu上使用snap安装Docker
1 检查系统版本 具有sudo或root用户权限 2 安装 SNAP ctrl+alt+T 打开终端 运行以下命令以安装 SNAP sudo apt update sudo apt install s ...
- 一文读懂计算机底层网络原理,包括TCP、UDP、header,什么是包、帧、段等关键问题
说到计算机网络原理,大家可能马上联想到,七层协议,传输层,链路层,三次握手四次挥手:前端的同学,还会想到我们用Crome F12的network里面的headers,状态码等.后端同学可能会联想到,抓 ...
- 【scipy 基础】--线性代数
SciPy的linalg模块是SciPy库中的一个子模块,它提供了许多用于线性代数运算的函数和工具,如矩阵求逆.特征值.行列式.线性方程组求解等. 相比于NumPy的linalg模块,SciPy的li ...
- (Good topic)字符串的最大公因子 (3.21leetcode每日打卡)
对于字符串 S 和 T,只有在 S = T + ... + T(T 与自身连接 1 次或多次)时,我们才认定 "T 能除尽 S". 返回最长字符串 X,要求满足 X 能除尽 s ...
- VUE同级组件之前方法调用
实现:Index.vue页面调用nav.vue页面里的getLeftMenu()方法 一.首先先建一个公共文件,命名eventBus.js,内空为: import Vue from 'vue'expo ...