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

时间:2013-03-26 12:52:39

标签: 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

如您所见,它基本上是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)

我在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

这就是我想在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)

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

2 个答案:

答案 0 :(得分:5)

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

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

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

如果数据必须采用ARFF格式(例如我的示例中Instances创建了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)
  ...

这允许在群集中使用“string”值,尽管它实际上只被视为名义值。它不会影响聚类(如果ID是唯一的),但它没有像我希望的那样对评估做出贡献,这使我进入下一个问题。


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

要获取群集分配,simpleKMeans.getAssignments将返回一个整数数组,这些整数是每个数据元素的群集分配。整数数组的顺序与原始数据项的顺序相同,必须手动关联回原始数据项。这可以通过在原始数据项列表上使用zip方法在Scala中轻松完成,然后使用其他方法(如groupBymap)以您喜欢的格式获取集合。请注意,仅此方法不会使用ID属性 >,而且可以从完全的数据点中省略ID属性。

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

答案 1 :(得分:0)

我在CSV文件中的一行中有一个字符串值,并且有几百万行时,我遇到了同样的错误。这是我如何计算出哪一行有字符串值。

异常“无法处理字符串属性!”没有给出关于行号的任何线索。因此:

  • 我将CSV文件导入Weka Explorer GUI并创建了一个* .arff文件。
  • 然后在* .arrf文件中手动将类型从字符串更改为数字,如下所示。
  • 之后我尝试使用* .arff文件构建集群。
  • 我得到了确切的行号作为例外的一部分
  • 我从* .arff文件中删除了该行并再次加载。它没有任何问题。

转换字符串 - > * .arff文件中的数字

@attribute total numeric
@attribute avgDailyMB numeric
@attribute mccMncCount numeric
@attribute operatorCount numeric
@attribute authSuccessRate numeric
@attribute totalMonthlyRequets numeric
@attribute tokenCount numeric
@attribute osVersionCount numeric
@attribute totalAuthUserIds numeric
@attribute makeCount numeric
@attribute modelCount numeric
@attribute maxDailyRequests numeric
@attribute avgDailyRequests numeric

错误报告了确切的行号

java.io.IOException: number expected, read Token[value.total], line 1750464
    at weka.core.converters.ArffLoader$ArffReader.errorMessage(ArffLoader.java:354)
    at weka.core.converters.ArffLoader$ArffReader.getInstanceFull(ArffLoader.java:728)
    at weka.core.converters.ArffLoader$ArffReader.getInstance(ArffLoader.java:545)
    at weka.core.converters.ArffLoader$ArffReader.readInstance(ArffLoader.java:514)
    at weka.core.converters.ArffLoader$ArffReader.readInstance(ArffLoader.java:500)
    at weka.core.Instances.<init>(Instances.java:138)
    at com.lokendra.dissertation.ModelingUtils.kMeans(ModelingUtils.java:50)
    at com.lokendra.dissertation.ModelingUtils.main(ModelingUtils.java:28)