65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016年11月29日

CPOL

7分钟阅读

viewsIcon

8952

downloadIcon

362

添加工具以帮助估算正确的延迟和嵌入维度

引言

WinRQA 是一个用 C# 编写的 Windows 应用程序,专注于递归图和递归分析。在关于此应用程序的第一篇文章中,我发布了该应用程序的第一个基本版本。在本文中,我发布了一个包含一些有用工具的新版本。(如果您喜欢阅读西班牙语版本,可以访问我的博客)。

此版本中的更改如下

  • 现在,您可以读取包含多个时间序列的 CSV 文件(列必须用分号分隔)。这样,例如,您可以处理完整的理论系统,并将其与从整个系统中的单个时间序列构建的简化版本进行比较。
  • 如果两个系统具有不同的窗口大小,交叉递归图现在可以是矩形的。这是此类递归图的正确方法。此外,在递归量化测量的计算中,一些小的误差也已得到纠正。
  • 我添加了一个工具来绘制序列线性自相关随延迟变化的情况。这可以帮助我们选择用于递归图的正确延迟值。
  • 我还添加了一个与前者类似的工具,但使用互信息而不是线性相关。这可以帮助从非线性角度选择延迟。
  • 最后,我添加了另一个图形工具,以关联维度为参考,帮助我们选择嵌入维度。

使用新功能

这是带有新工具窗口的应用程序图像

The updated WinRQA application

在主递归图窗口中,现在有第三个工具栏用于访问这些工具。

The new tool bar

AC 按钮启动自相关工具,MI 按钮启动互信息工具,ED 按钮用于嵌入维度工具。

自相关工具窗口如下所示

Autocorrelation tool window

使用刷新按钮按钮,将计算从 1 到最大距离文本框中选定的最大距离的序列自相关。

使用窗口限制选择窗口按钮,您可以选择用于计算自相关的序列值范围。您可以独立选择从整个时间序列的开头开始,或从用于构建递归图的窗口的开头开始。数据的结尾也是如此,它可以是整个序列的结尾,也可以是递归图窗口的结尾。

用鼠标左键单击图表,您可以选择延迟。相应的值将立即在主递归图表单中更新。

互信息工具几乎相同,但由于这是一种概率方法,您必须提供一个值(分辨率)来离散数据,以便为不同序列值的分布构建频率直方图。

一个常用的标准是在图表的第一个局部最小值处选择延迟。

Mutual information window tool

用法与相关窗口中一样,您可以通过用鼠标左键单击图表来选择延迟。

最后一个工具用于选择嵌入维度。在此窗口中,您可以更改的参数是探索的最大嵌入维度数量。每个点在水平轴上代表一个嵌入维度,在垂直轴上代表相应的关联维度。您可以在关联维度开始降低的点处选择嵌入维度。

Embedding dimension selector tool

 

使用代码

三个新工具窗口在以下类中实现

  • 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 类变量包含为每个嵌入维度计算的关联维度。

本次发布就到这里,感谢阅读!!!

© . All rights reserved.