合并数据集中的大致相等的点

时间:2012-02-10 02:19:25

标签: c# algorithm

我正在寻找能够快速运行短(< 30元素)数组并合并大致相等的点的算法。它可能最终会成为某种分段算法。

上下文如下:我正在寻找数据集中最高的峰值。我已经使用J-SEG的一维实现将最高的最大值与渣滓分开了,但是在数据集“平坦”的任何地方,我都会找到高原上每个元素的一个点。我需要能够自适应地将这些点合并到高原中心的单个点。 (这也意味着我不知道会有多少个集群。)

样本数据集1(样本/人工输入) 输入:

97  54686024814922.8
118 406406320535.935
148 24095826539423.7
152 1625624905272.95
160 1625625128029.81
166 1625625152145.47
176 1625625104745.48
179 1625625127365.09
183 1625625152208.44
190 1625624974205.81
194 21068100428092.9
247 54686024895222.1

理想输出:

97  54686024814922.8
118 406406320535.935
148 24095826539423.7
159 1625625061816.08


182 1625625089631.21



194 21068100428092.9
247 54686024895222.1

样本数据集2(实际输入): 输入:

2   196412376940671
123 206108518197124
135 194488685387149
148 178463949513298
154 192912098976702
156 195042451997727
161 195221254214493
168 204760073508681
172 189240741651297
182 191554457423846
187 215014126955355
201 202294866774063

理想输出:

2   196412376940671
123 206108518197124
135 194488685387149
148 178463949513298
157 194391935062974


168 204760073508681
172 189240741651297
182 191554457423846
187 215014126955355
201 202294866774063

示例数据集3(实际输入) 输入:

2   299777367852602
26  263467434856928
35  293412234811901
83  242768805551742
104 226333969841383
107 227548774800053
178 229173574175201
181 229224441416751
204 244334414017228
206 245258151638118
239 198782930497571

理想输出:

2       299777367852602
26      263467434856928 (May be merged
35      293412234811901 depending on parameters)
83      242768805551742
105.5   226941372320718

179.5   229199007795976

205     244796282827673

239     198782930497571

(将根据需要编辑更多信息。)

3 个答案:

答案 0 :(得分:1)

我不确定这是不是你想要的,但是还没有发布任何其他答案,所以我们走了。

我从图表的角度看了它。如果我正在查看图表,并且我想确定哪些点在水平方向上相似,最终会相对于图表比例。所以我创建了一个函数,它接受你想要被认为是相同的比例的百分比。然后它采用该百分比并将其乘以数据集之间的最大差异。

此外,始终将类似值与当前位于平台的平均值进行比较。一旦检测到平台结束,它将x加在一起并除以2得到中间值,然后取平均y值并将其作为最终数据点添加。

无法访问好的样本数据,我所要做的就是我制作的非常糟糕的数据生成器。但在我的测试值中,1%以内的数据通常会消除大约一半的数据点。

现在重要的是要注意这是一维的,x距离完全被忽略。你可以很容易地将它扩展为二维。我还考虑做的其他事情不是输出单个数据点来代表平稳,而是可以输出平均值的起点和终点。

namespace PointCondenser
{
    public static class Extensions
    {
        static public bool AlmostEqual<T>(this T value, T value2, T epsilon)
        {
            return (Math.Abs((dynamic)value - value2) < epsilon);
        }
    }

    public struct Point
    {
        public Point(double x, double y)
        {
            X = x;
            Y = y;
        }

        public override string ToString()
        {
            return string.Format("{0}\t{1}", X, Y);
        }

        public double X;
        public double Y;
    }
    class Program
    {
        static public Point RandomYPoint(int i)
        {
            var r = new Random();

            var r2 = new Random(i);

            var variance = r2.NextDouble() / 100;

            return new Point(i, Math.Abs(r.NextDouble() - variance) * 100);
        }

        static public IEnumerable<Point> SmoothPoints(IEnumerable<Point> points, double percent)
        {
            if (percent <= 0 || percent >= 1)
                throw new ArgumentOutOfRangeException("percent", "Percentage outside of logical bounds");

            var final = new List<Point>();

            var apoints = points.ToArray();

            var largestDifference = apoints.Max(x => x.Y) - apoints.Min(x => x.Y);
            var epsilon = largestDifference * percent;

            var currentPlateau = new List<Point> { apoints[0] };

            for (var i = 1; i < apoints.Length; ++i)
            {
                var point = apoints[i];
                if (point.Y.AlmostEqual(currentPlateau.Average(x => x.Y), epsilon))
                    currentPlateau.Add(point);
                else
                {
                    var x = (currentPlateau[0].X + currentPlateau[currentPlateau.Count - 1].X) / 2.0;
                    var y = currentPlateau.Average(z => z.Y);

                    currentPlateau.Clear();
                    currentPlateau.Add(point);

                    final.Add(new Point(x, y));
                }
            }

            return final;
        }

        static void Main(string[] args)
        {
            var r = new Random();
            var points = new List<Point>();


            for (var i = 0; i < 100; ++i)
            {
                for (var n = 0; n < r.Next(1, 5); ++n)
                {
                    var p = RandomYPoint(points.Count);
                    points.Add(p);
                    Console.WriteLine(p);
                }
                Thread.Sleep(r.Next(10, 250));
            }

            Console.Write("\n\n Condensed \n\n");


            var newPoints = SmoothPoints(points, .01);

            foreach (var p in newPoints)
                Console.WriteLine(p);
        }
    }
}

答案 1 :(得分:0)

无参数聚类的另一种方法是合并最近的数据点。 也就是说,在每次传递中,您将找到两个数据点之间的最小间隙,然后将这些对与该间隙合并。

因此,每次传递粒度都会降低。但是,除非根据您比较的属性对数据点进行排序,否则找到最小的间隙可能很昂贵。

答案 2 :(得分:0)

回想起来,我也可以用线性回归做到这一点:如果斜率接近零,并且到下一个点的斜率类似于高原上先前点的平均斜率,则记录下一个点用于合并并继续。