我正在使用新的WPF工具包的图表来绘制大型数据集。我还有一个十字准线跟踪器,当它在图表区域上方时跟随鼠标,准确地告诉最近数据点的值是什么(参见Yahoo! Finance图表)。
我使用以下代码查找最接近鼠标当前悬停位置的数据点(关于图表的令人讨厌的细节是它实际上插入数据以告诉你什么是EXACT值在哪里即使鼠标位于数据点之间,也将鼠标悬停在上面:
TimeDataPoint point = mainSeries.Find(
new Predicate<TimeDataPoint>(
delegate(TimeDataPoint p) {
return xValue > p.Date && !mainSeries.Exists(new Predicate<TimeDataPoint>(
delegate(TimeDataPoint middlePoint) {
return middlePoint.Date > p.Date && xValue > middlePoint.Date;
}));
}));
[此处,mainSeries
只是List<TimeDataPoint>
]
这对于相对较小的数据集非常有效,但是一旦我达到12000+点(这将迅速增加),上面的代码就会减慢到停顿状态(它会运行数据12000 + ^ 2次)
我不擅长构建查询,所以我想知道是否可以使用更好的LINQ查询来执行此操作。
编辑:受@Randolpho评论启发的另一个想法是这样的:我将搜索低于给定的所有点(这将是最多n(此处:12,000+))然后选择Max&lt;&gt; (最多也应为O(n))。这应该产生相同的结果,但只有n次操作的顺序,因此应该至少快一点......我的另一种选择是实际过滤数据集并根据用户想要查看的详细程度维持点数的上限。如果有可能提高查询效率,我宁愿不去那条路。
答案 0 :(得分:0)
根据显示/图表的已知分辨率预先计算最近的数据点。然后,当您将鼠标悬停在一个点上时,它是对已知预先计算的值的x / y坐标的简单查找。
出于性能原因,请在单独的线程中进行预先计算,并且在计算完成之前不允许显示这些值。每次更改图表大小时重新计算。
结论:没有LINQ查询可以帮助您在每次对大型数据集执行鼠标悬停时执行。它无法完成。无论如何,你都在看N ^ 2的订单。因此,预先计算并缓存它,因此您只需执行一次计算。
这是一个有趣的想法,但是我还不需要在12000多对中查找x / y吗?你能详细说明我应该如何存储预先计算的x / y对以便快速查找吗?例如,我的数据点位于(200,300)和(250,300),用户的鼠标位于(225,300)。 - Alexandra
嗯,我想这取决于图表。根据您的代码和您提到的Yahoo Finance Charts,我假设您的数据仅因水平位置而异,即对于给定的X值,您正在计算显示数据。
在这种情况下,您可以使用简单的Dictionary<int, TimeDataPoint>
作为缓存。 Key
是变换的X坐标(即在显示图的坐标空间中),Value
是预先计算的TimeDataPoint。字典将显示显示图中每个X坐标的记录,因此400像素宽的图形具有400个预先计算的数据点。
如果您的数据相对于两个轴都有所不同,您可以使用Dictionary<System.Windows.Point, TimeDataPoint>
,几乎以相同的方式,但这会使您的词典中的项目数量增加一个数量级。一个400乘300的图表在字典中有120000个条目,因此权衡是更高的内存占用。
预先计算您的数据是棘手的部分;它必须与你目前的方式不同。我将在此假设您的示例中的xValue
是基于X值的Date插值,因为它与p.Date
进行了比较。
这可能有效:
private Dictionary<int, TimeDataPoint> BuildCache(List<TimeDataPoint> mainSeries)
{
int xPrevious = 0;
int xCurrent = 0;
Dictionary<int, TimeDataPoint> cache = new Dictionary<int, TimeDataPoint>();
foreach(var p in mainSeries)
{
xCurrent = XFromDate(p.Date);
for(int val = xPrevious; val < xCurrent; val++)
{
cache.Add(val, p);
}
xPrevious = xCurrent;
}
return cache;
}
XFromDate
将提取特定日期的X坐标。我会把这件事告诉你。 :)