2013-03-26 8 views
5

Używam Weka w Scala (chociaż składnia jest praktycznie identyczna z Java). Próbuję ocenić moje dane za pomocą klastrowania SimpleKMeans, ale klaster nie zaakceptuje danych ciągu. Nie chcę klastra na danych ciągu; Po prostu chcę go użyć do oznaczenia punktów.Weka, SimpleKMeans nie może obsłużyć atrybutów ciągów znaków

Oto dane używam:

@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 

Jak widać, jest to w istocie zbiór punktów na współrzędnych X i Y samolotu. Wartość dowolnych wzorów jest znikoma; jest to po prostu ćwiczenie w pracy z Weka.

Oto kod, który daje mi problemy:

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) 

otrzymuję następujący błąd na simpleKMeans.buildClusterer(instance):

[UnsupportedAttributeTypeException: weka.clusterers.SimpleKMeans: Nie można obsługiwać atrybuty ciąg]

Jak mogę przekonać firmę Weka do przechowywania identyfikatorów podczas łączenia w klastry?


Oto kilka innych kroków Brałem rozwiązywać to:

użyłem WEKA Explorer i załadować te dane jako 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 

To co robi ja chcesz to zrobić w Weka Explorer. Weka zbiera punkty i przechowuje kolumnę ID, aby zidentyfikować każdy punkt. Zrobiłbym to w moim kodzie, ale próbuję to zrobić bez generowania dodatkowych plików. Jak widać z Weka Java API, Instances interpretuje się tylko jako ARFF.

Próbowałem również następujący kod:

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) 

to działa w moim kodu i wyświetla wyniki. Dowodzi to, że Weka działa w ogóle, ale odkąd usuwam atrybut ID, nie mogę naprawdę odwzorować klastrów na oryginalne wartości.

+0

myślę, że identyfikator jest przekształcany nominalnej atrybutu po załadowaniu z CSV. Inną rzeczą, którą możesz spróbować, jest ustawienie atrybutu ID jako atrybutu klasy. Lub ręcznie przekonwertuj go na typ nominalny. – Sentry

+0

Dzięki @Sentry, mogę potwierdzić, że to prawda. Wysłałem odpowiedź z tym, czego się dowiedziałem. –

Odpowiedz

5

mam odpowiedzi na moje własne pytanie, a czyniąc tak, są dwie kwestie, które chciałbym zająć:

  • Dlaczego CSV współpracuje z łańcucha wartości
  • Jak uzyskać informacje klastra z klastra ocena

Jak Sentry zwraca uwagę w komentarzach, identyfikator ma w rzeczywistości zamieniony na nominalny atrybut po załadowaniu z pliku CSV.

Jeżeli dane muszą być w formacie z ARFF (jak w moim przykładzie, gdzie obiekt Instances jest tworzony z StringReader), a następnie filtr StringToNominal mogą być stosowane:

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

Pozwala to na „string " wartości, które mają być używane w klastrach, chociaż tak naprawdę są traktowane jako wartość nominalna. Nie ma to wpływu na tworzenie klastrów (jeśli identyfikator jest unikalny), ale nie przyczynia się do oceny, tak jak się spodziewałem, co prowadzi mnie do następnego wydania.


Miałem nadzieję, aby być w stanie uzyskać ładny mapę klastra i danych, jak cluster: Int -> Array[(ID, latitude, longitude)] lub ID -> cluster: Int. Jednak wyniki klastra nie są tak wygodne. Z mojego doświadczenia wynika, że ​​w ciągu ostatnich kilku dni istnieją dwa podejścia, które można wykorzystać do znalezienia klastra każdego punktu danych.

Aby uzyskać przypisania klastra, simpleKMeans.getAssignments zwraca tablicę liczb całkowitych, czyli przypisania klastra dla każdego elementu danych. Tablica liczb całkowitych jest w tej samej kolejności, co oryginalne elementy danych i musi być ręcznie powiązana z oryginalnymi elementami danych. Można to łatwo osiągnąć w Scali, używając metody zip na oryginalnej liście elementów danych, a następnie za pomocą innych metod, takich jak groupBy lub map, uzyskać kolekcję w ulubionym formacie. Należy pamiętać, że ta metoda nie używa atrybutu identyfikatora pod numerem, a atrybut ID można pominąć w punktach danych w całości.

Można jednak uzyskać centra klastra z simpleKMeans.getClusterCentroids lub eval.clusterResultsToString(). Nie używałem tego zbyt wiele, ale wydaje mi się, że atrybut ID można odzyskać z centrów klastra. O ile mogę powiedzieć, jest to jedyna sytuacja, w której dane ID mogą być wykorzystane lub odzyskane z oceny klastra.

0

Otrzymałem ten sam błąd, mając wartość String w jednym z wierszy pliku CSV z kilkoma milionami wierszy. Oto, w jaki sposób zorientowałem się, która linia ma wartość ciągu.

Wyjątek "Nie można obsłużyć atrybutów ciągów!" nie daje żadnego pojęcia o numerze linii. Dlatego:

  • Zaimportowałem plik CSV do GUI Weka Explorer i utworzyłem plik * .arff.
  • Następnie ręcznie zmieniono typ z ciągu na numeryczny w pliku * .arrf na początku, jak pokazano poniżej.
  • Później próbowałem zbudować klaster za pomocą pliku * .arff.
  • Mam dokładny numer linii jako część wyjątku
  • Usunąłem wiersz z pliku * .arff i załadowałem ponownie. Działało bez żadnego problemu.

Wypisany ciąg -> numeryczny w *.arff plik

@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 

Błąd podano dokładną liczbę linii

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)