5

時系列の保存と取得を処理する Teafiles.net というオープン ソースの .Net ライブラリに出会いました。独自の製品である teahouse は、このような時系列をグラフ化できます。茶屋の製品は、オープンソースか有償ライセンスかにかかわらず、ソースコードとしても利用できるのだろうか。現在のチャート ビューに表示されているデータポイントのみをロードできるようにするための技術と、同様のソリューションを実装する方法に興味があります。

私は似たようなものを実装しようとしていて、誰かが似たような技術に出くわしたり、有料の喫茶店のライセンスがソースコードでも利用できるかどうかを知っているかどうか疑問に思っていました.

4

1 に答える 1

4

私は現在、 ZedGraphライブラリに基づくトレンド ソリューションを開発しており、 TeaFilesを使用して、データベースからの大量のデータをキャッシュしています。

TeaHouse ソリューションの背後にある技術の種類は正確にはわかりません。しかし、TeaFile からの膨大な量のデータから 2 つの日付の間にある一連のポイントを表示するアプローチも使用しました。

ZedGraph ライブラリには、データ ポイントFilteredPointListの自動デシメーションを実行するオブジェクトがあります。表示する日付の範囲と、表示するポイントの最大量を選択できる方法が含まれています。通常、これはビューの実際の幅に対応します。SetBounds

(FilteredPointList 元のソース コード)は、XY データを含む double の 2 つの配列を使用します。TeaFilePointListT を DateTime と double プロパティを含む構造体と見なして、配列を TeaFile オブジェクトで置き換えることにより、このクラスを に簡単に適合させることができます。

実装は最適ではありませんが、私はこの方法で始めました。後でこのコードを更新して、TeaFile の MemoryMappedFile 機能を含めることができます。この方法ではるかに高速になります。

public class TeaFilePointList : IPointList
{
    TeaFile<point> tf;

    private int _maxPts = -1;
    private int _minBoundIndex = -1;
    private int _maxBoundIndex = -1;

    struct point
    {
        public TeaTime.Time x;
        public double y;
    }

    public TeaFilePointList(DateTime[] x, double[] y)
    {
        tf = TeaFile<point>.Create(Path.GetRandomFileName() + ".tea");
        for (var i = 0; i < x.Length; i++)
            tf.Write(new point() { x = x[i], y = y[i] });
    }

    public void SetBounds(double min, double max, int maxPts)
    {
        _maxPts = maxPts;

        // find the index of the start and end of the bounded range

        var xmin = (DateTime)new XDate(min);
        var xmax = (DateTime)new XDate(max);

        int first = tf.BinarySearch(xmin, item => (DateTime)item.x);
        int last = tf.BinarySearch(xmax, item => (DateTime)item.x);

        // Make sure the bounded indices are legitimate
        // if BinarySearch() doesn't find the value, it returns the bitwise
        // complement of the index of the 1st element larger than the sought value

        if (first < 0)
        {
            if (first == -1)
                first = 0;
            else
                first = ~(first + 1);
        }

        if (last < 0)
            last = ~last;

        _minBoundIndex = first;
        _maxBoundIndex = last;
    }

    public int Count
    {
        get
        {
            int arraySize = (int)tf.Count;

            // Is the filter active?
            if (_minBoundIndex >= 0 && _maxBoundIndex >= 0 && _maxPts > 0)
            {
                // get the number of points within the filter bounds
                int boundSize = _maxBoundIndex - _minBoundIndex + 1;

                // limit the point count to the filter bounds
                if (boundSize < arraySize)
                    arraySize = boundSize;

                // limit the point count to the declared max points
                if (arraySize > _maxPts)
                    arraySize = _maxPts;
            }

            return arraySize;
        }
    }

    public PointPair this[int index]
    {
        get
        {
            if (_minBoundIndex >= 0 && _maxBoundIndex >= 0 && _maxPts >= 0)
            {
                // get number of points in bounded range
                int nPts = _maxBoundIndex - _minBoundIndex + 1;

                if (nPts > _maxPts)
                {
                    // if we're skipping points, then calculate the new index
                    index = _minBoundIndex + (int)((double)index * (double)nPts / (double)_maxPts);
                }
                else
                {
                    // otherwise, index is just offset by the start of the bounded range
                    index += _minBoundIndex;
                }
            }

            double xVal, yVal;
            if (index >= 0 && index < tf.Count)
                xVal = new XDate(tf.Items[index].x);
            else
                xVal = PointPair.Missing;

            if (index >= 0 && index < tf.Count)
                yVal = tf.Items[index].y;
            else
                yVal = PointPair.Missing;

            return new PointPair(xVal, yVal, PointPair.Missing, null);
        }
    }

    public object Clone()
    {
        throw new NotImplementedException(); // I'm lazy...
    }

    public void Close()
    {
        tf.Close();
        tf.Dispose();
        File.Delete(tf.Name);
    }
}

最も困難な部分は、DateTime を使用してレコードをすばやく検索するために、TeaFile の BinarySearch を実装することでした。逆コンパイラを使用して Array.BinarySearch の実装を調べ、次の拡張機能を記述しました。

public static int BinarySearch<T, U>(this TeaFile<T> tf, U target, Func<T, U> indexer) where T : struct
{
    var lo = 0;
    var hi = (int)tf.Count - 1;
    var comp = Comparer<U>.Default;

    while(lo <= hi)
    {
        var median = lo + (hi - lo >> 1);
        var num = comp.Compare(indexer(tf.Items[median]), target);
        if (num == 0)
            return median;
        if (num < 0)
            lo = median + 1;
        else
            hi = median - 1;
    }

    return ~lo;
}

ZedGraph がニーズに合わない場合でも、少なくともアイデアは得られました。FilteredPointList クラスで使用されるデシメーション アルゴリズムは非常に優れており、別の方法でニーズに合わせて調整できます。

于 2013-12-13T21:14:21.737 に答える