C# 中的递归图,扩展 WinRQA 应用程序





5.00/5 (4投票s)
添加工具以帮助估算正确的延迟和嵌入维度
引言
WinRQA 是一个用 C# 编写的 Windows 应用程序,专注于递归图和递归分析。在关于此应用程序的第一篇文章中,我发布了该应用程序的第一个基本版本。在本文中,我发布了一个包含一些有用工具的新版本。(如果您喜欢阅读西班牙语版本,可以访问我的博客)。
此版本中的更改如下
- 现在,您可以读取包含多个时间序列的 CSV 文件(列必须用分号分隔)。这样,例如,您可以处理完整的理论系统,并将其与从整个系统中的单个时间序列构建的简化版本进行比较。
- 如果两个系统具有不同的窗口大小,交叉递归图现在可以是矩形的。这是此类递归图的正确方法。此外,在递归量化测量的计算中,一些小的误差也已得到纠正。
- 我添加了一个工具来绘制序列线性自相关随延迟变化的情况。这可以帮助我们选择用于递归图的正确延迟值。
- 我还添加了一个与前者类似的工具,但使用互信息而不是线性相关。这可以帮助从非线性角度选择延迟。
- 最后,我添加了另一个图形工具,以关联维度为参考,帮助我们选择嵌入维度。
使用新功能
这是带有新工具窗口的应用程序图像
在主递归图窗口中,现在有第三个工具栏用于访问这些工具。
AC 按钮启动自相关工具,MI 按钮启动互信息工具,ED 按钮用于嵌入维度工具。
自相关工具窗口如下所示
使用按钮,将计算从 1 到最大距离文本框中选定的最大距离的序列自相关。
使用按钮,您可以选择用于计算自相关的序列值范围。您可以独立选择从整个时间序列的开头开始,或从用于构建递归图的窗口的开头开始。数据的结尾也是如此,它可以是整个序列的结尾,也可以是递归图窗口的结尾。
用鼠标左键单击图表,您可以选择延迟。相应的值将立即在主递归图表单中更新。
互信息工具几乎相同,但由于这是一种概率方法,您必须提供一个值(分辨率)来离散数据,以便为不同序列值的分布构建频率直方图。
一个常用的标准是在图表的第一个局部最小值处选择延迟。
用法与相关窗口中一样,您可以通过用鼠标左键单击图表来选择延迟。
最后一个工具用于选择嵌入维度。在此窗口中,您可以更改的参数是探索的最大嵌入维度数量。每个点在水平轴上代表一个嵌入维度,在垂直轴上代表相应的关联维度。您可以在关联维度开始降低的点处选择嵌入维度。
使用代码
三个新工具窗口在以下类中实现
- frmAutoCorrelation,用于自相关工具。
- frmMutualInformation,用于互信息工具。
- frmEmbeddingDimension,用于嵌入维度工具。
- GraphHelper,一个用于绘制图表刻度的帮助程序。
这三个工具的结构基本相同。有一个绘制图表的方法,一个 BackgroundWorker 用于进行计算,以及在刷新 () 按钮的 Click 事件中读取和准备数据的代码。
对于自相关,过程非常简单。首先,代码简单地读取时间序列数据,从用户界面选择的开始到结束。
BackgroundWorker 代码如下所示
// Calculate mean and variance
float mean = 0f;
float variance = 0f;
int pos = 0;
while (pos < _dataBuffer.Length)
{
mean += _dataBuffer[pos];
variance += _dataBuffer[pos] * _dataBuffer[pos];
pos++;
if (bgProcess.CancellationPending)
{
e.Cancel = true;
return;
}
if (pos > _end)
{
break;
}
}
mean /= (float)pos;
variance /= (float)pos;
variance -= mean * mean;
// Calculate linear correlation
for (int off = 1; off <= _correlation.Length; off++)
{
float corr = 0f;
pos = 0;
while (pos < _dataBuffer.Length - off)
{
corr += (_dataBuffer[pos] - mean) * (_dataBuffer[pos + off] - mean);
pos++;
if (bgProcess.CancellationPending)
{
e.Cancel = true;
return;
}
if (pos > _end)
{
break;
}
}
corr /= (float)pos;
corr /= variance;
_correlation[off - 1] = corr;
bgProcess.ReportProgress((int)(((double)off * 100) / (double)_correlation.Length));
}
首先,计算数据的均值和方差,因为它们是计算相关系数所必需的。
接下来,对于每个延迟值,计算数据与相同数据按给定延迟位移的线性相关性,并将其存储在 _correlation 类变量中。
互信息稍微复杂一些。这是线性自相关到非线性情况的推广。它可以使用以下公式计算
P(s(t), s(t + τ))log2(P(s(t), s(t + τ))/P(s(t))P(s(t + τ)))
s(t) 是时刻 t 的序列值,而 s(t + τ) 是时刻 t + τ 的序列值(即,由延迟 τ 分隔)。P(s(t)) 是序列中值 s(t) 的概率,P(s(t + τ)) 是值 s(t + τ) 的相同概率。
P(s(t), s(t + τ)) 是同时具有值 s(t + τ) 和 s(t) 并由距离 τ 分隔的概率。log2 显然是以 2 为底的对数。
用此公式计算的值对序列中的所有值进行求和。这个过程对 1 到最大选定延迟之间的不同延迟值重复进行。
要做的第一件事是构建一个频率直方图来计算所涉及的不同概率。这在 BackgroundWorker 的第一步中完成
// Create histogram
int pos = 0;
for (int ix = 0; ix < _dataBuffer.Length; ix++)
{
_histogram[(int)((_dataBuffer[ix] - _minValue) / _resolution)]++;
if (bgProcess.CancellationPending)
{
e.Cancel = true;
return;
}
}
// Calculate mutual information
Dictionary<Point, int> pairs = new Dictionary<Point, int>();
for (int off = 1; off <= _mInformation.Length; off++)
{
pairs.Clear();
pos = 0;
while (pos < _dataBuffer.Length - off)
{
Point pair = new Point((int)((_dataBuffer[pos] - _minValue) / _resolution), (int)((_dataBuffer[pos + off] - _minValue) / _resolution));
if (pairs.ContainsKey(pair))
{
pairs[pair]++;
}
else
{
pairs[pair] = 1;
}
pos++;
if (bgProcess.CancellationPending)
{
e.Cancel = true;
return;
}
}
float p = 0f;
foreach (KeyValuePair<Point, int> kv in pairs)
{
double pc = (double)kv.Value / (double)pos;
double px = (double)_histogram[kv.Key.X] / (double)pos;
double pxo = (double)_histogram[kv.Key.Y] / (double)pos;
float pp = (float)Math.Round(pc * (double)Math.Log(pc / (px * pxo), 2), 10);
p += pp;
}
_mInformation[off - 1] = p;
bgProcess.ReportProgress((int)(((double)off * 100) / (double)_mInformation.Length));
}
_resolution 类变量用于将序列值的范围划分为 _resolution 个不同的离散值集。
对于联合概率,我使用了一个 Dictionary,其 Key 是一个 Point 结构。这个 Point 包含一对中的两个值,而相应的 Value 是这对值在当前延迟下出现的次数。
一旦计算了所有值对,就计算每个值对的概率,并将当前延迟的互信息值存储在 _mInformation 类变量中。
最后,对于嵌入维度,要处理的数据不是时间序列,而是一组扩展的向量,其最大维度是我们想要测试的。这组向量是在刷新按钮的 click 事件中构建的
_extSeries = new float[1 + ((bToEnd.Checked ? _parent.End : _parent.DataManager.Samples) - (bFromStart.Checked ? _start : 0)), maxd];
_parent.DataManager.Open();
if (bFromStart.Checked)
{
_parent.DataManager.Seek(_start);
}
float [] data = new float[(bToEnd.Checked ? _parent.End + ((maxd - 1) * _parent.Delay) : _parent.DataManager.Samples) - (bFromStart.Checked ? _start : 0)];
_parent.DataManager.ReadBuffer(data);
// Build all the vectors for the max dimension
int pos = 0;
while (pos < data.Length)
{
// Reuse the value in all needed places, to avoid reprocess of the data
for (int d = 0; d < maxd; d++)
{
int pd = pos - (d * _parent.Delay);
if (pd >= 0)
{
if (pd / _extSeries.GetLength(0) == 0)
{
_extSeries[pd % _extSeries.GetLength(0), d] = data[pos];
}
}
else
{
break;
}
}
pos++;
}
data = null;
_extSeries 变量包含向量,数组的第一维与要使用的时间序列块一样长,第二维等于最大维度。
关联维度可以计算如下
ν = lim(r->0) Log(C(r)) / Log(r)
r 是用于确定邻域的半径,此工具使用递归图表单中输入的半径。
C(r) 是关联积分,用递归分析的话来说,这与给定 r 半径的递归率相同。
关联维度可以被认为是系统吸引子分形维度的下限。嵌入维度应选择至少 2d + 1 的值,其中 d 是原始相空间的整数维度。因此,您可以将关联维度的上限作为 d 的一个好值。
关联维度在 BackgroundWorker 中计算
float[] sums = new float[((_extSeries.GetLength(0) - 1) * _extSeries.GetLength(0)) / 2];
for (int d = 0; d < _dimData.Length; d++)
{
int xt = 0;
int rp = 0;
for (int xi = 0; xi < _extSeries.GetLength(1) - 1; xi++)
{
for (int xj = xi + 1; xj < _extSeries.GetLength(1); xj++)
{
sums[xt] += (_extSeries[xi, d] - _extSeries[xj, d]) * (_extSeries[xi, d] - _extSeries[xj, d]);
if (Math.Sqrt(sums[xt]) <= _currRadius)
{
rp++;
}
xt++;
}
}
_dimData[d] = -(float)(Math.Log((double)rp / (double)sums.Length) / Math.Log(_currRadius));
bgProcess.ReportProgress((int)(((double)d * 100) / (double)_dimData.Length));
}
计算是在每个主循环迭代中考虑向量的一个新维度进行的。每次都进行所有以前的求和是不必要的,因此,它们存储在 sums 数组中。
_dimData 类变量包含为每个嵌入维度计算的关联维度。
本次发布就到这里,感谢阅读!!!