Fixed the compilation failure.
This commit is contained in:
parent
d72ed9ad11
commit
f8e37b3162
@ -8,13 +8,13 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public interface Distance {
|
public interface Distance {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the distance between two feature vectors.
|
* Calculates the distance between two feature vectors.
|
||||||
*
|
*
|
||||||
* @param f1 The first set of features.
|
* @param f1 The first set of features.
|
||||||
* @param f2 The second set of features.
|
* @param f2 The second set of features.
|
||||||
* @return Calculated distance.
|
* @return Calculated distance.
|
||||||
* @throws IllegalArgumentException If the given feature vectors are invalid.
|
* @throws IllegalArgumentException If the given feature vectors are invalid.
|
||||||
*/
|
*/
|
||||||
double calculate(Map<String, Double> f1, Map<String, Double> f2);
|
double calculate(Map<String, Double> f1, Map<String, Double> f2);
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,16 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class Errors {
|
public class Errors {
|
||||||
|
|
||||||
public static double sse(Map<Centroid, List<Record>> clustered, Distance distance) {
|
public static double sse(Map<Centroid, List<Record>> clustered, Distance distance) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (Map.Entry<Centroid, List<Record>> entry : clustered.entrySet()) {
|
for (Map.Entry<Centroid, List<Record>> entry : clustered.entrySet()) {
|
||||||
Centroid centroid = entry.getKey();
|
Centroid centroid = entry.getKey();
|
||||||
for (Record record : entry.getValue()) {
|
for (Record record : entry.getValue()) {
|
||||||
double d = distance.calculate(centroid.getCoordinates(), record.getFeatures());
|
double d = distance.calculate(centroid.getCoordinates(), record.getFeatures());
|
||||||
sum += Math.pow(d, 2);
|
sum += Math.pow(d, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,19 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class EuclideanDistance implements Distance {
|
public class EuclideanDistance implements Distance {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double calculate(Map<String, Double> f1, Map<String, Double> f2) {
|
public double calculate(Map<String, Double> f1, Map<String, Double> f2) {
|
||||||
if (f1 == null || f2 == null)
|
if (f1 == null || f2 == null)
|
||||||
throw new IllegalArgumentException("Feature vectors can't be null");
|
throw new IllegalArgumentException("Feature vectors can't be null");
|
||||||
|
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (String key : f1.keySet()) {
|
for (String key : f1.keySet()) {
|
||||||
Double v1 = f1.get(key);
|
Double v1 = f1.get(key);
|
||||||
Double v2 = f2.get(key);
|
Double v2 = f2.get(key);
|
||||||
|
|
||||||
if (v1 != null && v2 != null) sum += Math.pow(v1 - v2, 2);
|
if (v1 != null && v2 != null) sum += Math.pow(v1 - v2, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.sqrt(sum);
|
return Math.sqrt(sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,40 +13,40 @@ import static java.util.stream.Collectors.toSet;
|
|||||||
|
|
||||||
public class LastFm {
|
public class LastFm {
|
||||||
|
|
||||||
private static OkHttpClient okHttp = new OkHttpClient.Builder()
|
private static OkHttpClient okHttp = new OkHttpClient.Builder()
|
||||||
.addInterceptor(new LastFmService.Authenticator("put your API key here"))
|
.addInterceptor(new LastFmService.Authenticator("put your API key here"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static Retrofit retrofit = new Retrofit.Builder().client(okHttp)
|
private static Retrofit retrofit = new Retrofit.Builder().client(okHttp)
|
||||||
.addConverterFactory(JacksonConverterFactory.create())
|
.addConverterFactory(JacksonConverterFactory.create())
|
||||||
.baseUrl("http://ws.audioscrobbler.com/")
|
.baseUrl("http://ws.audioscrobbler.com/")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static LastFmService lastFm = retrofit.create(LastFmService.class);
|
private static LastFmService lastFm = retrofit.create(LastFmService.class);
|
||||||
|
|
||||||
private static ObjectMapper mapper = new ObjectMapper();
|
private static ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
List<String> artists = getTop100Artists();
|
List<String> artists = getTop100Artists();
|
||||||
Set<String> tags = getTop100Tags();
|
Set<String> tags = getTop100Tags();
|
||||||
List<Record> records = datasetWithTaggedArtists(artists, tags);
|
List<Record> records = datasetWithTaggedArtists(artists, tags);
|
||||||
|
|
||||||
Map<Centroid, List<Record>> clusters = KMeans.fit(records, 7, new EuclideanDistance(), 1000);
|
Map<Centroid, List<Record>> clusters = KMeans.fit(records, 7, new EuclideanDistance(), 1000);
|
||||||
// Print the cluster configuration
|
// Print the cluster configuration
|
||||||
clusters.forEach((key, value) -> {
|
clusters.forEach((key, value) -> {
|
||||||
System.out.println("------------------------------ CLUSTER -----------------------------------");
|
System.out.println("------------------------------ CLUSTER -----------------------------------");
|
||||||
|
|
||||||
System.out.println(sortedCentroid(key));
|
System.out.println(sortedCentroid(key));
|
||||||
String members = String.join(", ", value.stream().map(Record::getDescription).collect(toSet()));
|
String members = String.join(", ", value.stream().map(Record::getDescription).collect(toSet()));
|
||||||
System.out.print(members);
|
System.out.print(members);
|
||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println();
|
System.out.println();
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, Object> json = convertToD3CompatibleMap(clusters);
|
Map<String, Object> json = convertToD3CompatibleMap(clusters);
|
||||||
System.out.println(mapper.writeValueAsString(json));
|
System.out.println(mapper.writeValueAsString(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> convertToD3CompatibleMap(Map<Centroid, List<Record>> clusters) {
|
private static Map<String, Object> convertToD3CompatibleMap(Map<Centroid, List<Record>> clusters) {
|
||||||
Map<String, Object> json = new HashMap<>();
|
Map<String, Object> json = new HashMap<>();
|
||||||
@ -69,45 +69,45 @@ public class LastFm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String dominantGenre(Centroid centroid) {
|
private static String dominantGenre(Centroid centroid) {
|
||||||
return centroid.getCoordinates().keySet().stream().limit(2).collect(Collectors.joining(", "));
|
return centroid.getCoordinates().keySet().stream().limit(2).collect(Collectors.joining(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Centroid sortedCentroid(Centroid key) {
|
private static Centroid sortedCentroid(Centroid key) {
|
||||||
List<Map.Entry<String, Double>> entries = new ArrayList<>(key.getCoordinates().entrySet());
|
List<Map.Entry<String, Double>> entries = new ArrayList<>(key.getCoordinates().entrySet());
|
||||||
entries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
|
entries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
|
||||||
|
|
||||||
Map<String, Double> sorted = new LinkedHashMap<>();
|
Map<String, Double> sorted = new LinkedHashMap<>();
|
||||||
for (Map.Entry<String, Double> entry : entries) {
|
for (Map.Entry<String, Double> entry : entries) {
|
||||||
sorted.put(entry.getKey(), entry.getValue());
|
sorted.put(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Centroid(sorted);
|
return new Centroid(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Record> datasetWithTaggedArtists(List<String> artists,
|
private static List<Record> datasetWithTaggedArtists(List<String> artists,
|
||||||
Set<String> topTags) throws IOException {
|
Set<String> topTags) throws IOException {
|
||||||
List<Record> records = new ArrayList<>();
|
List<Record> records = new ArrayList<>();
|
||||||
for (String artist : artists) {
|
for (String artist : artists) {
|
||||||
Map<String, Double> tags = lastFm.topTagsFor(artist).execute().body().all();
|
Map<String, Double> tags = lastFm.topTagsFor(artist).execute().body().all();
|
||||||
|
|
||||||
// Only keep popular tags.
|
// Only keep popular tags.
|
||||||
tags.entrySet().removeIf(e -> !topTags.contains(e.getKey()));
|
tags.entrySet().removeIf(e -> !topTags.contains(e.getKey()));
|
||||||
|
|
||||||
records.add(new Record(artist, tags));
|
records.add(new Record(artist, tags));
|
||||||
}
|
}
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> getTop100Tags() throws IOException {
|
private static Set<String> getTop100Tags() throws IOException {
|
||||||
return lastFm.topTags().execute().body().all();
|
return lastFm.topTags().execute().body().all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> getTop100Artists() throws IOException {
|
private static List<String> getTop100Artists() throws IOException {
|
||||||
List<String> artists = new ArrayList<>();
|
List<String> artists = new ArrayList<>();
|
||||||
for (int i = 1; i <= 2; i++) {
|
for (int i = 1; i <= 2; i++) {
|
||||||
artists.addAll(lastFm.topArtists(i).execute().body().all());
|
artists.addAll(lastFm.topArtists(i).execute().body().all());
|
||||||
}
|
}
|
||||||
|
|
||||||
return artists;
|
return artists;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
@ -16,86 +18,89 @@ import retrofit2.Call;
|
|||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
public interface LastFmService {
|
public interface LastFmService {
|
||||||
|
|
||||||
@GET("/2.0/?method=chart.gettopartists&format=json&limit=50")
|
@GET("/2.0/?method=chart.gettopartists&format=json&limit=50")
|
||||||
Call<Artists> topArtists(@Query("page") int page);
|
Call<Artists> topArtists(@Query("page") int page);
|
||||||
|
|
||||||
@GET("/2.0/?method=artist.gettoptags&format=json&limit=20&autocorrect=1")
|
@GET("/2.0/?method=artist.gettoptags&format=json&limit=20&autocorrect=1")
|
||||||
Call<Tags> topTagsFor(@Query("artist") String artist);
|
Call<Tags> topTagsFor(@Query("artist") String artist);
|
||||||
|
|
||||||
@GET("/2.0/?method=chart.gettoptags&format=json&limit=100")
|
@GET("/2.0/?method=chart.gettoptags&format=json&limit=100")
|
||||||
Call<TopTags> topTags();
|
Call<TopTags> topTags();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP interceptor to intercept all HTTP requests and add the API key to them.
|
* HTTP interceptor to intercept all HTTP requests and add the API key to them.
|
||||||
*/
|
*/
|
||||||
class Authenticator implements Interceptor {
|
class Authenticator implements Interceptor {
|
||||||
|
|
||||||
private final String apiKey;
|
private final String apiKey;
|
||||||
|
|
||||||
Authenticator(String apiKey) {
|
Authenticator(String apiKey) {
|
||||||
this.apiKey = apiKey;
|
this.apiKey = apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response intercept(Chain chain) throws IOException {
|
public Response intercept(Chain chain) throws IOException {
|
||||||
HttpUrl url = chain.request().url().newBuilder().addQueryParameter("api_key", apiKey).build();
|
HttpUrl url = chain.request().url().newBuilder().addQueryParameter("api_key", apiKey).build();
|
||||||
Request request = chain.request().newBuilder().url(url).build();
|
Request request = chain.request().newBuilder().url(url).build();
|
||||||
|
|
||||||
return chain.proceed(request);
|
return chain.proceed(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonAutoDetect(fieldVisibility = ANY)
|
@JsonAutoDetect(fieldVisibility = ANY)
|
||||||
class TopTags {
|
class TopTags {
|
||||||
|
|
||||||
private Map<String, Object> tags;
|
private Map<String, Object> tags;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Set<String> all() {
|
public Set<String> all() {
|
||||||
List<Map<String, Object>> topTags = (List<Map<String, Object>>) tags.get("tag");
|
List<Map<String, Object>> topTags = (List<Map<String, Object>>) tags.get("tag");
|
||||||
return topTags.stream().map(e -> ((String) e.get("name"))).collect(Collectors.toSet());
|
return topTags.stream().map(e -> ((String) e.get("name"))).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonAutoDetect(fieldVisibility = ANY)
|
@JsonAutoDetect(fieldVisibility = ANY)
|
||||||
class Tags {
|
class Tags {
|
||||||
|
|
||||||
@JsonProperty("toptags")
|
@JsonProperty("toptags")
|
||||||
private Map<String, Object> topTags;
|
private Map<String, Object> topTags;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, Double> all() {
|
public Map<String, Double> all() {
|
||||||
try {
|
try {
|
||||||
Map<String, Double> all = new HashMap<>();
|
Map<String, Double> all = new HashMap<>();
|
||||||
List<Map<String, Object>> tags = (List<Map<String, Object>>) topTags.get("tag");
|
List<Map<String, Object>> tags = (List<Map<String, Object>>) topTags.get("tag");
|
||||||
for (Map<String, Object> tag : tags) {
|
for (Map<String, Object> tag : tags) {
|
||||||
all.put(((String) tag.get("name")), ((Integer) tag.get("count")).doubleValue());
|
all.put(((String) tag.get("name")), ((Integer) tag.get("count")).doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return all;
|
return all;
|
||||||
} catch (Exception e) {
|
}
|
||||||
return Collections.emptyMap();
|
catch (Exception e) {
|
||||||
}
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonAutoDetect(fieldVisibility = ANY)
|
@JsonAutoDetect(fieldVisibility = ANY)
|
||||||
class Artists {
|
class Artists {
|
||||||
|
|
||||||
private Map<String, Object> artists;
|
private Map<String, Object> artists;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<String> all() {
|
public List<String> all() {
|
||||||
try {
|
try {
|
||||||
List<Map<String, Object>> artists = (List<Map<String, Object>>) this.artists.get("artist");
|
List<Map<String, Object>> artists = (List<Map<String, Object>>) this.artists.get("artist");
|
||||||
return artists.stream().map(e -> ((String) e.get("name"))).collect(toList());
|
return artists.stream().map(e -> ((String) e.get("name"))).collect(toList());
|
||||||
} catch (Exception e) {
|
}
|
||||||
return Collections.emptyList();
|
catch (Exception e) {
|
||||||
}
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,52 +9,52 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class Record {
|
public class Record {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The record description. For example, this can be the artist name for the famous musician
|
* The record description. For example, this can be the artist name for the famous musician
|
||||||
* example.
|
* example.
|
||||||
*/
|
*/
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates all attributes and their corresponding values, i.e. features.
|
* Encapsulates all attributes and their corresponding values, i.e. features.
|
||||||
*/
|
*/
|
||||||
private final Map<String, Double> features;
|
private final Map<String, Double> features;
|
||||||
|
|
||||||
public Record(String description, Map<String, Double> features) {
|
public Record(String description, Map<String, Double> features) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.features = features;
|
this.features = features;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Record(Map<String, Double> features) {
|
public Record(Map<String, Double> features) {
|
||||||
this("", features);
|
this("", features);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Double> getFeatures() {
|
public Map<String, Double> getFeatures() {
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String prefix = description == null || description.trim().isEmpty() ? "Record" : description;
|
String prefix = description == null || description.trim().isEmpty() ? "Record" : description;
|
||||||
|
|
||||||
return prefix + ": " + features;
|
return prefix + ": " + features;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
Record record = (Record) o;
|
Record record = (Record) o;
|
||||||
return Objects.equals(getDescription(), record.getDescription()) &&
|
return Objects.equals(getDescription(), record.getDescription()) &&
|
||||||
Objects.equals(getFeatures(), record.getFeatures());
|
Objects.equals(getFeatures(), record.getFeatures());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(getDescription(), getFeatures());
|
return Objects.hash(getDescription(), getFeatures());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user