Weka,SimpleKMeans无法处理字符串属性

Dav*_*ski 5 scala cluster-analysis weka

我在Scala中使用Weka(虽然语法几乎与Java相同).我试图用SimpleKMeans群集器来评估我的数据,但是群集器不会接受字符串数据.我不想对字符串数据进行聚类; 我只是想用它来标记点.

这是我正在使用的数据:

@relation Locations
@attribute ID string
@attribute Latitude numeric
@attribute Longitude numeric
@data
'Carnegie Mellon University', 40.443064, -79.944163
'Stanford University', 37.427539, -122.170169
'Massachusetts Institute of Technology', 42.358866, -71.093823
'University of California Berkeley', 37.872166, -122.259444
'University of Washington', 47.65601, -122.30934
'University of Illinois Urbana Champaign', 40.091022, -88.229992
'University of Southern California', 34.019372, -118.28611
'University of California San Diego', 32.881494, -117.243079
Run Code Online (Sandbox Code Playgroud)

如您所见,它基本上是x和y坐标平面上的点集合.任何模式的价值都可以忽略不计; 这只是与Weka合作的一项练习.

这是给我带来麻烦的代码:

val instance = new Instances(new StringReader(wekaHeader + wekaData))

val simpleKMeans = new SimpleKMeans()
simpleKMeans.buildClusterer(instance)

val eval = new ClusterEvaluation()
eval.setClusterer(simpleKMeans)
eval.evaluateClusterer(new Instances(instance))

Logger.info(eval.clusterResultsToString)
Run Code Online (Sandbox Code Playgroud)

我收到以下错误simpleKMeans.buildClusterer(instance):

[UnsupportedAttributeTypeException:weka.clusterers.SimpleKMeans:无法处理字符串属性!]

如何在进行群集时让Weka保留ID?


以下是我为解决此问题而采取的其他几个步骤:

我使用了Weka Explorer并将这些数据加载为CSV:

ID, Latitude, Longitude
'Carnegie Mellon University', 40.443064, -79.944163
'Stanford University', 37.427539, -122.170169
'Massachusetts Institute of Technology', 42.358866, -71.093823
'University of California Berkeley', 37.872166, -122.259444
'University of Washington', 47.65601, -122.30934
'University of Illinois Urbana Champaign', 40.091022, -88.229992
'University of Southern California', 34.019372, -118.28611
'University of California San Diego', 32.881494, -117.243079
Run Code Online (Sandbox Code Playgroud)

这就是我想在Weka Explorer中做的事情.Weka对点进行聚类并保留ID列以标识每个点.我会在我的代码中执行此操作,但我尝试在不生成其他文件的情况下执行此操作.正如您在Weka Java API中看到的那样,仅将其Instances解释java.io.Reader为ARFF.

我也尝试了以下代码:

val instance = new Instances(new StringReader(wekaHeader + wekaData))
instance.deleteAttributeAt(0)

val simpleKMeans = new SimpleKMeans()
simpleKMeans.buildClusterer(instance)

val eval = new ClusterEvaluation()
eval.setClusterer(simpleKMeans)
eval.evaluateClusterer(new Instances(instance))

Logger.info(eval.clusterResultsToString)
Run Code Online (Sandbox Code Playgroud)

这适用于我的代码,并显示结果.这证明Weka一般都在工作,但由于我正在删除ID属性,我无法真正将聚集点映射回原始值.

Dav*_*ski 5

我正在回答我自己的问题,在这样做的过程中,我想解决两个问题:

  • 为什么CSV适用于字符串值
  • 如何从群集评估中获取群集信息

正如Sentry在评论中指出的那样,当从CSV加载时,ID实际上会转换为名义属性.

如果数据必须采用某种ARFF格式(例如在我的示例中,Instances从a创建对象StringReader),则StringToNominal可以应用过滤器:

  val instances = new Instances(new StringReader(wekaHeader + wekaData))

  val filter = new StringToNominal()
  filter.setAttributeRange("first")
  filter.setInputFormat(instances)

  val filteredInstance = Filter.useFilter(instances, filter)

  val simpleKMeans = new SimpleKMeans()
  simpleKMeans.buildClusterer(instance)
  ...
Run Code Online (Sandbox Code Playgroud)

这允许在聚类中使用"字符串"值,尽管它实际上只是作为标称值处理.它不会影响聚类(如果ID是唯一的),但它没有像我希望的那样对评估做出贡献,这让我想到了下一个问题.


我希望能够得到一个很好的集群和数据地图,比如cluster: Int -> Array[(ID, latitude, longitude)]ID -> cluster: Int.但是,群集结果并不方便.根据我过去几天的经验,有两种方法可用于查找每个数据点的集群.

要获取集群分配,请simpleKMeans.getAssignments返回一个整数数组,该数组是每个数据元素的集群分配.整数数组的顺序与原始数据项的顺序相同,必须手动关联回原始数据项.这可以通过在zip原始数据项列表上使用该方法然后使用其他方法(如groupBymap以您喜欢的格式获取集合)在Scala中轻松完成.请记住,这种方法不能单独使用id属性和ID属性可以从数据点被忽略完全.

但是,您也可以使用simpleKMeans.getClusterCentroids或获取群集中心eval.clusterResultsToString().我没有用过这么多,但在我看来,ID属性可以在这里从集群中心恢复.据我所知,这是唯一可以从群集评估中使用或恢复ID数据的情况.