相关介绍
轴
- 高级轴功能 – 如何进一步自定义轴
- 自定义轴网 – 轴网线的高级自定义
- 自定义刻度 – 刻度线和刻度标签的高级自定义
- DateTime Axes – 在 DataTime 轴上绘制数据值
- 多轴 – 刻度线自定义和多轴图
布局
- 布局选项 – 如何自定义绘图的布局
绘图类型
- 注释 – 注释是位于数据区域上方的始终可见的文本标签。
- 箭头 – 箭头指向坐标空间中的某个位置。
- 轴线 – 轴线表示轴上的位置。
- 轴跨度 – 轴跨度表示轴的范围。
- 条形图 – 条形图将值表示为水平或垂直矩形
- 箱形图 – 箱形图一目了然地显示分布
- 标注 – 标注显示标签,并通过标记图上点的箭头连接。
- Coxcomb 图 – Coxcomb 图是一个饼图,其中切片的角度是恒定的,但半径不是。
- 十字准线 – 十字准线结合了水平轴线和垂直轴线,以在坐标空间中标记位置。
- 椭圆 – 椭圆是具有定义中心和不同 X 和 Y 半径的曲线。圆是 X 半径等于其 Y 半径的椭圆。
- 误差线 – 误差线表示测量值的可能范围
- FillY 图 – FillY 图在定义的 X 位置显示两个 Y 值之间的垂直范围
- 财务图 – 财务图显示按时间范围分箱的价格数据
- 函数 – 函数图是一种线图,其中 Y 位置由依赖于 X 的函数定义,而不是离散数据点的集合。
- 热图 – 热图将 2D 数据中的值显示为具有不同强度的像元的图像
- 图像 – 可以通过多种方式将图像放置在绘图上
- Line Plot – 可以使用 Start、End 和可选的 LineStyle 将线图放置在坐标空间中的绘图上。
- 棒棒糖图 – 棒棒糖图是条形图的一种变体,它使用从基线延伸到标记(头部)的线条(茎)来表示数据点。与传统条形图相比,Lollipop 突出显示单个数据点的视觉混乱程度较低。
- 标记 – 标记可以放置在坐标空间的绘图上。
- 相量图 – 相量图在以原点为中心的径向轴上显示矢量
- 饼图 – 饼图将数字比例说明为圆的切片。
- 极轴 – 创建极轴并将其添加到绘图中,以在圆形坐标系上显示数据。
- 总体图 – 总体图显示单个值的集合。
- 雷达图 – 雷达图(也称为蜘蛛图或星形图)将多轴数据表示为围绕中心点圆周排列的轴上的 2D 形状。
- 径向仪表 – 径向仪表图将标量数据显示为圆形仪表。
- 散点图 – 散点图在坐标空间中的 X/Y 位置显示点。
- 形状 – 可以添加到绘图的基本形状
- 信号图 – 信号图显示均匀分布的数据
- SignalConst – SignalConst 是一种信号图,它包含不可变的数据点并占用更多内存,但为超大型数据集提供更高的性能。它很少需要,但最适合用于绘制包含数百万个点的数据。
- SignalXY 绘图 – SignalXY 是一种高性能绘图类型,针对 X 值始终升序的 X/Y 对进行了优化。对于大型数据集,SignalXY 图的性能比散点图(允许无序的 X 点)高得多,但不如信号图(需要 X 点之间的固定间距)的性能。
- 文本 – 文本标签可以放置在坐标空间的绘图上
- Vector Field – 向量场显示以坐标空间中的点为根的向量集合
统计学
- 回归 – 使线条适合数据的统计操作
杂项
- 高级样式 – 适用于寻求广泛自定义选项的用户的功能。
- 国际化 – 跨具有不同文本和数字要求的区域性使用 ScottPlot。
- 调色板 – 可用于表示分类数据的颜色集合
- Colormaps – 可用于表示连续数据的颜色渐变
xaml
<Window x:Class="RealTimePlotDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RealTimePlotDemo"
xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF"
mc:Ignorable="d"
Title="Real-Time Plot Demo" Height="650" Width="1100">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<!-- 折线图区域 -->
<RowDefinition Height="3*"/>
<!-- 直方图区域 -->
</Grid.RowDefinitions>
<ScottPlot:WpfPlot Name="linePlot" Grid.Row="0" />
<!-- 折线图 -->
<ScottPlot:WpfPlot Name="histogramPlot" Grid.Row="1" />
<!-- 直方图 -->
</Grid>
</Window>
xaml.cs
using ScottPlot;
using System;
using System.IO;
using System.Linq;
using System.Timers;
using System.Windows;
namespace RealTimePlotDemo {
public partial class MainWindow : Window {
// 常量定义
private const int DataSize = 50000; // 数据点总数
private const double XInterval = 2.0; // X 值之间的间隔
private const double YAmplitude = 0.5; // 正弦波的幅度
private const double NoiseRange = 0.02; // 随机噪声的范围
private const double UpdateInterval = 100; // 图形更新的时间间隔(毫秒)
private const double VisibleRange = 50; // 可视范围的长度(X 轴)
private const int BatchSize = 100; // 每次绘制的数据点数量,设置为 1 确保每个点对应一个矩形
private Timer _timer; // 定时器
private double[] _lineDataX; // X 轴数据
private double[] _lineDataY; // Y 轴数据
private double[] _histogramData; // 直方图数据
private double[] _histogramX; // 直方图的 X 轴数据
private Random _random; // 随机数生成器
private string _dataFilePath; // 保存数据的文件路径
private int _currentIndex = 0; // 当前读取的索引
public MainWindow() {
InitializeComponent();
_random = new Random();
_lineDataX = new double[DataSize];
_lineDataY = new double[DataSize];
_histogramData = new double[DataSize]; // 直方图数据长度,修改为与数据点数相同
_histogramX = new double[DataSize]; // 直方图的 X 轴数据
// 保存数据的文件路径
_dataFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "data.csv");
// 生成所有数据并保存到本地
GenerateAndSaveData();
// 初始化折线图和直方图
InitializePlots();
// 设置定时器,每隔指定的时间间隔更新图形
_timer = new Timer(UpdateInterval);
_timer.Elapsed += UpdatePlots;
_timer.Start();
}
// 生成数据并保存到文件
private void GenerateAndSaveData() {
using (var writer = new StreamWriter(_dataFilePath)) {
for (int i = 0; i < DataSize; i++) {
_lineDataX[i] = i * XInterval; // 增加 X 值之间的间隔
// 生成 Y 值,使用正弦函数,并添加随机噪声
double noise = _random.NextDouble() * NoiseRange - (NoiseRange / 2); // 随机噪声范围
_lineDataY[i] = YAmplitude * Math.Sin(_lineDataX[i]) + noise; // Y 值变化幅度减小
// 保存数据到文件
writer.WriteLine($"{_lineDataX[i]},{_lineDataY[i]}");
}
}
}
// 初始化折线图和直方图
private void InitializePlots() {
linePlot.Plot.Axes.SetLimitsX(0, VisibleRange); // X 轴范围
linePlot.Plot.Axes.SetLimitsY(0, 1.2); // Y 轴范围
histogramPlot.Plot.Axes.SetLimitsX(0, VisibleRange); // 直方图 X 轴范围
histogramPlot.Plot.Axes.SetLimitsY(0, 1.2); // 直方图 Y 轴范围
linePlot.Refresh();
histogramPlot.Refresh();
}
// 更新图形
private void UpdatePlots(object sender, ElapsedEventArgs e) {
// 检查是否读取完所有数据
if (_currentIndex >= DataSize) {
_timer.Stop();
return;
}
// 逐渐读取并更新折线图
ReadData();
Dispatcher.Invoke(() => {
RefreshLinePlot();
RefreshHistogramPlot();
UpdateAxisLimits(); // 更新坐标轴限制
});
}
// 逐步读取数据
private void ReadData() {
// 逐步读取数据
int endIndex = Math.Min(_currentIndex + BatchSize, DataSize);
for (int i = _currentIndex; i < endIndex; i++) {
_lineDataX[i] = i * XInterval; // 假设每个点间隔为 XInterval
_lineDataY[i] = _random.NextDouble(); // 随机生成 Y 值
}
_currentIndex = endIndex; // 更新当前索引
}
// 刷新折线图
private void RefreshLinePlot() {
linePlot.Plot.Clear();
linePlot.Plot.Add.Scatter(_lineDataX.Take(_currentIndex).ToArray(), _lineDataY.Take(_currentIndex).ToArray());
linePlot.Refresh();
}
// 刷新直方图
private void RefreshHistogramPlot() {
histogramPlot.Plot.Clear();
// 使用已读取的所有数据来绘制直方图,而不是只绘制最近的 BatchSize 个点
int barCount = _currentIndex; // 直方图的条数为当前读取的所有点
double[] sampledHistogramX = new double[barCount]; // 每个点对应一个矩形
double[] sampledHistogramData = new double[barCount];
// 填充直方图数据
for (int i = 0; i < barCount; i++) {
sampledHistogramX[i] = _lineDataX[i]; // 对应的 X 值
sampledHistogramData[i] = _lineDataY[i]; // 对应的 Y 值
}
// 添加矩形到图表中
histogramPlot.Plot.Add.Bars(sampledHistogramX, sampledHistogramData); // 矩形宽度可以根据需要调整
// 更新坐标轴范围
UpdateHistogramAxisLimits(sampledHistogramData);
histogramPlot.Refresh();
}
// 更新坐标轴限制
private void UpdateAxisLimits() {
if (_currentIndex > 0) {
// 计算当前X轴的最小值和最大值
double minX = _lineDataX[Math.Max(0, _currentIndex - (int)VisibleRange)];
double maxX = _lineDataX[_currentIndex - 1];
// 更新 X 轴范围
linePlot.Plot.Axes.SetLimitsX(minX, maxX);
histogramPlot.Plot.Axes.SetLimitsX(minX, maxX); // 更新直方图 X 轴范围
}
}
// 更新直方图的Y轴限制
private void UpdateHistogramAxisLimits(double[] sampledHistogramData) {
if (sampledHistogramData.Length > 0) {
// 更新Y轴范围
double maxY = sampledHistogramData.Max();
histogramPlot.Plot.Axes.SetLimitsY(0, maxY * 1.1); // 给 Y 轴留一点空间
}
}
}
// 随机数扩展方法
public static class RandomExtensions {
public static double[] NextDoubles(this Random rand, int count) {
return Enumerable.Range(0, count).Select(_ => rand.NextDouble()).ToArray();
}
}
}