我正在寻找能够快速运行短(< 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
(将根据需要编辑更多信息。)
答案 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)
回想起来,我也可以用线性回归做到这一点:如果斜率接近零,并且到下一个点的斜率类似于高原上先前点的平均斜率,则记录下一个点用于合并并继续。