5

我在 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将 a 解释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 属性,我无法真正将聚集点映射回原始值。

4

2 回答 2

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)
  ...

这允许在聚类中使用“字符串”值,尽管它实际上只是被视为名义值。它不会影响聚类(如果 ID 是唯一的),但它不会像我希望的那样有助于评估,这将我带到下一个问题。


我希望能够得到一张漂亮的集群和数据地图,比如cluster: Int -> Array[(ID, latitude, longitude)]or ID -> cluster: Int。但是,聚类结果并不那么方便。根据我这几天的经验,有两种方法可用于查找每个数据点的集群。

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

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

于 2013-03-28T01:03:10.270 回答
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)
于 2017-09-18T17:28:18.150 回答