【问题标题】:Weka, SimpleKMeans cannot handle string attributesWeka、SimpleKMeans 无法处理字符串属性
【发布时间】:2013-03-16 06:38:48
【问题描述】:

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

【问题讨论】:

  • 我认为您的 ID 在从 CSV 加载时会转换为名义属性。您可以尝试的另一件事是将 ID 属性设置为类属性。或者手动将其转换为名义类型。
  • 谢谢@Sentry,我可以确认这是正确的。我发布了我所了解的答案。

标签: scala cluster-analysis weka


【解决方案1】:

在具有几百万行的 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)

【讨论】:

    【解决方案2】:

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

    • 为什么 CSV 可以处理字符串值
    • 如何从集群评估中获取集群信息

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

    如果数据必须采用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 -&gt; Array[(ID, latitude, longitude)]ID -&gt; cluster: Int。但是,聚类结果并不那么方便。根据我这几天的经验,有两种方法可以用来找到每个数据点的聚类。

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

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

    【讨论】:

      猜你喜欢
      • 2014-05-07
      • 2016-06-24
      • 2016-10-29
      • 2012-08-03
      • 2017-01-16
      • 2012-07-21
      • 2011-08-26
      • 2011-10-02
      • 1970-01-01
      相关资源
      最近更新 更多