C#基于ScottPlot實(shí)現(xiàn)可視化的示例代碼
前言
上一篇文章跟大家分享了用NumSharp實(shí)現(xiàn)簡(jiǎn)單的線性回歸,但是沒有進(jìn)行可視化,可能對(duì)擬合的過程沒有直觀的感受,因此今天跟大家介紹一下使用C#基于Scottplot進(jìn)行可視化,當(dāng)然Python的代碼,我也會(huì)同步進(jìn)行可視化。
Python代碼進(jìn)行可視化
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]
# 創(chuàng)建圖形和軸
fig, ax = plt.subplots()
line1, = ax.plot(x1, y1, 'ro')
line2, = ax.plot(x2,y2)
# 添加標(biāo)簽和標(biāo)題
plt.xlabel('x')
plt.ylabel('y')
plt.title('Graph of y = mx + b')
# 添加網(wǎng)格
plt.grid(True)
# 定義更新函數(shù)
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}')
# 創(chuàng)建動(dòng)畫
animation = FuncAnimation(fig, update, frames=len(data2), interval=500)
# 顯示動(dòng)畫
plt.show()
實(shí)現(xiàn)的效果如下所示:


C#代碼進(jìn)行可視化
這是本文重點(diǎn)介紹的內(nèi)容,本文的C#代碼通過Scottplot進(jìn)行可視化。
Scottplot簡(jiǎn)介
ScottPlot 是一個(gè)免費(fèi)的開源繪圖庫(kù),用于 .NET,可以輕松以交互方式顯示大型數(shù)據(jù)集。
控制臺(tái)程序可視化
首先我先介紹一下在控制臺(tái)程序中進(jìn)行可視化。
首先添加Scottplot包:

將上篇文章中的C#代碼修改如下:
using NumSharp;
namespace LinearRegressionDemo
{
internal class Program
{
static void Main(string[] args)
{
//創(chuàng)建double類型的列表
List<double> Array = new List<double>();
List<double> ArgsList = new List<double>();
// 指定CSV文件的路徑
string filePath = "你的data.csv路徑";
// 調(diào)用ReadCsv方法讀取CSV文件數(shù)據(jù)
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 = , 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);
// 遍歷每一行數(shù)據(jù)
foreach (string line in lines)
{
// 使用逗號(hào)分隔符拆分每一行的數(shù)據(jù)
string[] values = line.Split(',');
// 打印每一行的數(shù)據(jù)
foreach (string value in values)
{
array.Add(Convert.ToDouble(value));
}
}
}
catch (Exception ex)
{
Console.WriteLine("發(fā)生錯(cuò)誤: " + 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表示所有行的第二列。
當(dāng)然我們不滿足于只是保存圖片,在控制臺(tái)應(yīng)用程序中,再添加一個(gè) ScottPlot.WinForms包:

右鍵控制臺(tái)項(xiàng)目選擇屬性,將目標(biāo)OS改為Windows:

將上述代碼中的
myPlot.SaveFig("圖片.png");
修改為:
var viewer = new ScottPlot.FormsPlotViewer(myPlot); viewer.ShowDialog();
再次運(yùn)行結(jié)果如下:

winform進(jìn)行可視化
我也想像Python代碼中那樣畫動(dòng)圖,因此做了個(gè)winform程序進(jìn)行演示。
首先創(chuàng)建一個(gè)winform,添加ScottPlot.WinForms包,然后從工具箱中添加FormsPlot這個(gè)控件:

有兩種方法實(shí)現(xiàn),第一種方法用了定時(shí)器:
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()
{
//創(chuàng)建double類型的列表
List<double> Array = new List<double>();
List<double> ArgsList = new List<double>();
// 指定CSV文件的路徑
string filePath = "你的data.csv路徑";
// 調(diào)用ReadCsv方法讀取CSV文件數(shù)據(jù)
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);
// 遍歷每一行數(shù)據(jù)
foreach (string line in lines)
{
// 使用逗號(hào)分隔符拆分每一行的數(shù)據(jù)
string[] values = line.Split(',');
// 打印每一行的數(shù)據(jù)
foreach (string value in values)
{
array.Add(Convert.ToDouble(value));
}
}
}
catch (Exception ex)
{
Console.WriteLine("發(fā)生錯(cuò)誤: " + 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)
{
// 初始化定時(shí)器
updateTimer.Interval = 1000; // 設(shè)置定時(shí)器觸發(fā)間隔(毫秒)
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)
{
}
}
}
簡(jiǎn)單介紹一下思路,首先創(chuàng)建List<double> argsList用來(lái)保存每次迭代生成的參數(shù)b、m,然后用
var argsArr = np.array(ArgsList).reshape(num_iterations, 2);
將argsList通過np.array()方法轉(zhuǎn)化為NDArray,然后再調(diào)用reshape方法,轉(zhuǎn)化成行數(shù)等于迭代次數(shù),列數(shù)為2,即每一行對(duì)應(yīng)一組參數(shù)值b、m。
b_each = argsArr[$":", 0];
m_each = argsArr[$":", 1];
argsArr[$":", 0]表示每一行中第一列的值,也就是每一個(gè)b,argsArr[$":", 1]表示每一行中第二列的值。
double b = b_each[-1];
double m = m_each[-1];
b_each[-1]用了NumSharp的功能表示b_each最后一個(gè)元素。
實(shí)現(xiàn)效果如下所示:


另一種方法可以通過異步實(shí)現(xiàn):
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()
{
//創(chuàng)建double類型的列表
List<double> Array = new List<double>();
List<double> ArgsList = new List<double>();
// 指定CSV文件的路徑
string filePath = "你的data.csv路徑";
// 調(diào)用ReadCsv方法讀取CSV文件數(shù)據(jù)
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);
// 遍歷每一行數(shù)據(jù)
foreach (string line in lines)
{
// 使用逗號(hào)分隔符拆分每一行的數(shù)據(jù)
string[] values = line.Split(',');
// 打印每一行的數(shù)據(jù)
foreach (string value in values)
{
array.Add(Convert.ToDouble(value));
}
}
}
catch (Exception ex)
{
Console.WriteLine("發(fā)生錯(cuò)誤: " + 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();
}
}
}點(diǎn)擊更新按鈕開始執(zhí)行異步任務(wù):
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);
}
實(shí)現(xiàn)效果如下:


以上就是C#基于ScottPlot實(shí)現(xiàn)可視化的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C# ScottPlot可視化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用多線程句柄設(shè)置鼠標(biāo)忙碌狀態(tài)的實(shí)現(xiàn)方法
怎樣利用多線程句柄設(shè)置鼠標(biāo)忙碌狀態(tài)呢?下面小編就為大家介紹一下具體的實(shí)現(xiàn)方法吧!需要的朋友可以過來(lái)參考下2013-08-08
使用Linq注意事項(xiàng)避免報(bào)錯(cuò)的方法
這篇文章主要介紹了使用Linq注意事項(xiàng)避免報(bào)錯(cuò)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
詳解C#使用AD(Active Directory)驗(yàn)證內(nèi)網(wǎng)用戶名密碼
這篇文章主要介紹了詳解C#使用AD(Active Directory)驗(yàn)證內(nèi)網(wǎng)用戶名密碼的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10

