我正在C#库中编写代码以在(二维)数据集上执行clustering - 基本上将数据分成组或集群。为了有用,库需要接收“通用”或“自定义”数据,对其进行聚类并返回聚簇数据。
要做到这一点,我需要假设传入的数据集中的每个数据都有一个与之关联的2D向量(在我的情况下为Q_ASSERT(QStringLiteral("foo%1baz").arg("bar") == QStringLiteral("foobarbaz"));
,Lat
- 我正在使用纵坐标)。
我的第一个想法是使用泛型类型,并传入两个列表,一个通用数据列表(即Lng
)和另一个指定2D向量的相同长度(即List<T>
,其中List<Coordinate>
是我的用于指定lat,lng对的类,其中列表通过索引彼此对应。但这是相当繁琐的,因为这意味着在算法中我必须以某种方式跟踪这些指数。
我的下一个想法是使用界面,我在其中定义界面
Coordinate
并确保我传入的数据实现此接口(即我可以假设传入的每个数据都有public interface IPoint
{
double Lat { get; set; }
double Lng { get; set; }
}
和Lat
)。
但这也不适合我。我正在使用我的C#库来集中传输网络中的停靠点(在不同的项目中)。该类称为Lng
,此类也来自外部库,因此我无法实现该类的接口。
我所做的是继承自Stop
,创建一个名为Stop
的类,如下所示:
ClusterableStop
如您所见,实现了public class ClusterableStop : GTFS.Entities.Stop, IPoint
{
public ClusterableStop(Stop stop)
{
Id = stop.Id;
Code = stop.Code;
Name = stop.Name;
Description = stop.Description;
Latitude = stop.Latitude;
Longitude = stop.Longitude;
Zone = stop.Zone;
Url = stop.Url;
LocationType = stop.LocationType;
ParentStation = stop.ParentStation;
Timezone = stop.Timezone;
WheelchairBoarding = stop.WheelchairBoarding;
}
public double Lat
{
get
{
return this.Latitude;
}
}
public double Lng
{
get
{
return this.Longitude;
}
}
}
接口。现在我使用IPoint
的构造函数首先将数据集中的所有ClusterableStop
转换为Stop
,然后运行算法并将结果作为ClusterableStop
s。
这不是我想要的,因为我想根据它们所属的集群对ClusterableStop
做些什么。我不能这样做,因为我实际上已经实例化 new 停止,即Stop
s !!
我仍然可以实现我想要的,因为例如我可以通过Id检索原始对象。但肯定有更优雅的方式来完成所有这些?这是使用接口的正确方法吗?这似乎是一个简单的想法 - 传入并获取自定义数据 - 但结果却变得如此复杂。
答案 0 :(得分:4)
由于您只需要将(纬度,经度)对与2D数组的每个元素相关联,您可以创建一个带有委托的方法,该委托为每个数据生成相关位置,如下所示:
ClusterList Cluster<T>(IList<T> data, Func<int,Coordinate> getCoordinate) {
for (int i = 0 ; i != data.Count ; i++) {
T item = data[i];
Coordinate coord = getCoord(i);
...
}
}
现在由调用者决定Coordinate
与每个数据元素的配对方式。
请注意,按列表位置关联并非您可用的唯一选项。另一种选择是传递一个接受该项的委托,并返回其坐标:
ClusterList Cluster<T>(IEnumerable<T> data, Func<T,Coordinate> getCoordinate) {
foreach (var item in data) {
Coordinate coord = getCoord(item);
...
}
}
虽然这种方法比基于索引的方法更好,但是当坐标在对象本身上不可用时,它需要调用者在T
上保留某种关联容器,这必须要么使用基于散列的容器,或者是IComparable<T>
。第一种方法对T
没有限制。
在您的情况下,第二种方法更可取:
var clustered = Cluster(
myListOfStops
, stop => new Coordinate(stop.Latitude, stop.Longitude)
);
答案 1 :(得分:2)
您是否考虑过使用Tuples来完成工作 - 有时这是一种在不创建全新类的情况下关联两个类的有用方法。您可以创建元组列表:
List<Tuple<Point, Stop>>
其中Point是你聚集的东西。