Fixed formatting issues.

This commit is contained in:
Ali Dehghani
2019-08-07 20:45:52 +04:30
parent f615f9aeae
commit 5a10f94946
10 changed files with 489 additions and 430 deletions
+22 -22
View File
@@ -1,5 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>algorithms-miscellaneous-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
@@ -18,29 +18,29 @@
<version>${org.assertj.core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit.version}</version>
</dependency>
</dependencies>
<build>
@@ -59,6 +59,6 @@
<org.assertj.core.version>3.9.0</org.assertj.core.version>
<commons-collections4.version>4.3</commons-collections4.version>
<guava.version>28.0-jre</guava.version>
<retrofit.version>2.6.0</retrofit.version>
<retrofit.version>2.6.0</retrofit.version>
</properties>
</project>
@@ -8,34 +8,34 @@ import java.util.Objects;
*/
public class Centroid {
/**
* The centroid coordinates.
*/
private final Map<String, Double> coordinates;
/**
* The centroid coordinates.
*/
private final Map<String, Double> coordinates;
public Centroid(Map<String, Double> coordinates) {
this.coordinates = coordinates;
}
public Centroid(Map<String, Double> coordinates) {
this.coordinates = coordinates;
}
public Map<String, Double> getCoordinates() {
return coordinates;
}
public Map<String, Double> getCoordinates() {
return coordinates;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Centroid centroid = (Centroid) o;
return Objects.equals(getCoordinates(), centroid.getCoordinates());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Centroid centroid = (Centroid) o;
return Objects.equals(getCoordinates(), centroid.getCoordinates());
}
@Override
public int hashCode() {
return Objects.hash(getCoordinates());
}
@Override
public int hashCode() {
return Objects.hash(getCoordinates());
}
@Override
public String toString() {
return "Centroid " + coordinates;
}
@Override
public String toString() {
return "Centroid " + coordinates;
}
}
@@ -8,13 +8,13 @@ import java.util.Map;
*/
public interface Distance {
/**
* Calculates the distance between two feature vectors.
*
* @param f1 The first set of features.
* @param f2 The second set of features.
* @return Calculated distance.
* @throws IllegalArgumentException If the given feature vectors are invalid.
*/
double calculate(Map<String, Double> f1, Map<String, Double> f2);
/**
* Calculates the distance between two feature vectors.
*
* @param f1 The first set of features.
* @param f2 The second set of features.
* @return Calculated distance.
* @throws IllegalArgumentException If the given feature vectors are invalid.
*/
double calculate(Map<String, Double> f1, Map<String, Double> f2);
}
@@ -8,16 +8,16 @@ import java.util.Map;
*/
public class Errors {
public static double sse(Map<Centroid, List<Record>> clustered, Distance distance) {
double sum = 0;
for (Map.Entry<Centroid, List<Record>> entry : clustered.entrySet()) {
Centroid centroid = entry.getKey();
for (Record record : entry.getValue()) {
double d = distance.calculate(centroid.getCoordinates(), record.getFeatures());
sum += Math.pow(d, 2);
}
}
public static double sse(Map<Centroid, List<Record>> clustered, Distance distance) {
double sum = 0;
for (Map.Entry<Centroid, List<Record>> entry : clustered.entrySet()) {
Centroid centroid = entry.getKey();
for (Record record : entry.getValue()) {
double d = distance.calculate(centroid.getCoordinates(), record.getFeatures());
sum += Math.pow(d, 2);
}
}
return sum;
}
return sum;
}
}
@@ -7,19 +7,18 @@ import java.util.Map;
*/
public class EuclideanDistance implements Distance {
@Override
public double calculate(Map<String, Double> f1, Map<String, Double> f2) {
if (f1 == null || f2 == null)
throw new IllegalArgumentException("Feature vectors can't be null");
@Override
public double calculate(Map<String, Double> f1, Map<String, Double> f2) {
if (f1 == null || f2 == null) throw new IllegalArgumentException("Feature vectors can't be null");
double sum = 0;
for (String key : f1.keySet()) {
Double v1 = f1.get(key);
Double v2 = f2.get(key);
double sum = 0;
for (String key : f1.keySet()) {
Double v1 = f1.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);
}
}
File diff suppressed because it is too large Load Diff
@@ -19,101 +19,126 @@ import static java.util.stream.Collectors.toSet;
public class LastFm {
private static OkHttpClient okHttp = new OkHttpClient.Builder()
.addInterceptor(new LastFmService.Authenticator("put your API key here"))
.build();
private static OkHttpClient okHttp = new OkHttpClient.Builder()
.addInterceptor(new LastFmService.Authenticator("put your API key here"))
.build();
private static Retrofit retrofit = new Retrofit.Builder().client(okHttp)
.addConverterFactory(JacksonConverterFactory.create())
.baseUrl("http://ws.audioscrobbler.com/")
.build();
private static Retrofit retrofit = new Retrofit.Builder()
.client(okHttp)
.addConverterFactory(JacksonConverterFactory.create())
.baseUrl("http://ws.audioscrobbler.com/")
.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 {
List<String> artists = getTop100Artists();
Set<String> tags = getTop100Tags();
List<Record> records = datasetWithTaggedArtists(artists, tags);
public static void main(String[] args) throws IOException {
List<String> artists = getTop100Artists();
Set<String> tags = getTop100Tags();
List<Record> records = datasetWithTaggedArtists(artists, tags);
Map<Centroid, List<Record>> clusters = KMeans.fit(records, 7, new EuclideanDistance(), 1000);
// Print the cluster configuration
clusters.forEach((key, value) -> {
System.out.println("------------------------------ CLUSTER -----------------------------------");
Map<Centroid, List<Record>> clusters = KMeans.fit(records, 7, new EuclideanDistance(), 1000);
// Print the cluster configuration
clusters.forEach((key, value) -> {
System.out.println("------------------------------ CLUSTER -----------------------------------");
System.out.println(sortedCentroid(key));
String members = String.join(", ", value.stream().map(Record::getDescription).collect(toSet()));
System.out.print(members);
System.out.println(sortedCentroid(key));
String members = String.join(", ", value
.stream()
.map(Record::getDescription)
.collect(toSet()));
System.out.print(members);
System.out.println();
System.out.println();
});
System.out.println();
System.out.println();
});
Map<String, Object> json = convertToD3CompatibleMap(clusters);
System.out.println(mapper.writeValueAsString(json));
}
Map<String, Object> json = convertToD3CompatibleMap(clusters);
System.out.println(mapper.writeValueAsString(json));
}
private static Map<String, Object> convertToD3CompatibleMap(Map<Centroid, List<Record>> clusters) {
Map<String, Object> json = new HashMap<>();
json.put("name", "Musicians");
List<Map<String, Object>> children = new ArrayList<>();
clusters.forEach((key, value) -> {
Map<String, Object> child = new HashMap<>();
child.put("name", dominantGenre(sortedCentroid(key)));
List<Map<String, String>> nested = new ArrayList<>();
for (Record record : value) {
nested.add(Collections.singletonMap("name", record.getDescription()));
}
child.put("children", nested);
private static Map<String, Object> convertToD3CompatibleMap(Map<Centroid, List<Record>> clusters) {
Map<String, Object> json = new HashMap<>();
json.put("name", "Musicians");
List<Map<String, Object>> children = new ArrayList<>();
clusters.forEach((key, value) -> {
Map<String, Object> child = new HashMap<>();
child.put("name", dominantGenre(sortedCentroid(key)));
List<Map<String, String>> nested = new ArrayList<>();
for (Record record : value) {
nested.add(Collections.singletonMap("name", record.getDescription()));
}
child.put("children", nested);
children.add(child);
});
json.put("children", children);
return json;
}
children.add(child);
});
json.put("children", children);
return json;
}
private static String dominantGenre(Centroid centroid) {
return centroid
.getCoordinates()
.keySet()
.stream()
.limit(2)
.collect(Collectors.joining(", "));
}
private static String dominantGenre(Centroid centroid) {
return centroid.getCoordinates().keySet().stream().limit(2).collect(Collectors.joining(", "));
}
private static Centroid sortedCentroid(Centroid key) {
List<Map.Entry<String, Double>> entries = new ArrayList<>(key
.getCoordinates()
.entrySet());
entries.sort((e1, e2) -> e2
.getValue()
.compareTo(e1.getValue()));
private static Centroid sortedCentroid(Centroid key) {
List<Map.Entry<String, Double>> entries = new ArrayList<>(key.getCoordinates().entrySet());
entries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
Map<String, Double> sorted = new LinkedHashMap<>();
for (Map.Entry<String, Double> entry : entries) {
sorted.put(entry.getKey(), entry.getValue());
}
Map<String, Double> sorted = new LinkedHashMap<>();
for (Map.Entry<String, Double> entry : entries) {
sorted.put(entry.getKey(), entry.getValue());
}
return new Centroid(sorted);
}
return new Centroid(sorted);
}
private static List<Record> datasetWithTaggedArtists(List<String> artists, Set<String> topTags) throws IOException {
List<Record> records = new ArrayList<>();
for (String artist : artists) {
Map<String, Double> tags = lastFm
.topTagsFor(artist)
.execute()
.body()
.all();
private static List<Record> datasetWithTaggedArtists(List<String> artists,
Set<String> topTags) throws IOException {
List<Record> records = new ArrayList<>();
for (String artist : artists) {
Map<String, Double> tags = lastFm.topTagsFor(artist).execute().body().all();
// Only keep popular tags.
tags
.entrySet()
.removeIf(e -> !topTags.contains(e.getKey()));
// Only keep popular tags.
tags.entrySet().removeIf(e -> !topTags.contains(e.getKey()));
records.add(new Record(artist, tags));
}
return records;
}
records.add(new Record(artist, tags));
}
return records;
}
private static Set<String> getTop100Tags() throws IOException {
return lastFm
.topTags()
.execute()
.body()
.all();
}
private static Set<String> getTop100Tags() throws IOException {
return lastFm.topTags().execute().body().all();
}
private static List<String> getTop100Artists() throws IOException {
List<String> artists = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
artists.addAll(lastFm
.topArtists(i)
.execute()
.body()
.all());
}
private static List<String> getTop100Artists() throws IOException {
List<String> artists = new ArrayList<>();
for (int i = 1; i <= 2; i++) {
artists.addAll(lastFm.topArtists(i).execute().body().all());
}
return artists;
}
return artists;
}
}
@@ -23,84 +23,96 @@ import static java.util.stream.Collectors.toList;
public interface LastFmService {
@GET("/2.0/?method=chart.gettopartists&format=json&limit=50")
Call<Artists> topArtists(@Query("page") int page);
@GET("/2.0/?method=chart.gettopartists&format=json&limit=50")
Call<Artists> topArtists(@Query("page") int page);
@GET("/2.0/?method=artist.gettoptags&format=json&limit=20&autocorrect=1")
Call<Tags> topTagsFor(@Query("artist") String artist);
@GET("/2.0/?method=artist.gettoptags&format=json&limit=20&autocorrect=1")
Call<Tags> topTagsFor(@Query("artist") String artist);
@GET("/2.0/?method=chart.gettoptags&format=json&limit=100")
Call<TopTags> topTags();
@GET("/2.0/?method=chart.gettoptags&format=json&limit=100")
Call<TopTags> topTags();
/**
* HTTP interceptor to intercept all HTTP requests and add the API key to them.
*/
class Authenticator implements Interceptor {
/**
* HTTP interceptor to intercept all HTTP requests and add the API key to them.
*/
class Authenticator implements Interceptor {
private final String apiKey;
private final String apiKey;
Authenticator(String apiKey) {
this.apiKey = apiKey;
}
Authenticator(String apiKey) {
this.apiKey = apiKey;
}
@Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain.request().url().newBuilder().addQueryParameter("api_key", apiKey).build();
Request request = chain.request().newBuilder().url(url).build();
@Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain
.request()
.url()
.newBuilder()
.addQueryParameter("api_key", apiKey)
.build();
Request request = chain
.request()
.newBuilder()
.url(url)
.build();
return chain.proceed(request);
}
}
return chain.proceed(request);
}
}
@JsonAutoDetect(fieldVisibility = ANY)
class TopTags {
@JsonAutoDetect(fieldVisibility = ANY)
class TopTags {
private Map<String, Object> tags;
private Map<String, Object> tags;
@SuppressWarnings("unchecked")
public Set<String> all() {
List<Map<String, Object>> topTags = (List<Map<String, Object>>) tags.get("tag");
return topTags.stream().map(e -> ((String) e.get("name"))).collect(Collectors.toSet());
}
}
@SuppressWarnings("unchecked")
public Set<String> all() {
List<Map<String, Object>> topTags = (List<Map<String, Object>>) tags.get("tag");
return topTags
.stream()
.map(e -> ((String) e.get("name")))
.collect(Collectors.toSet());
}
}
@JsonAutoDetect(fieldVisibility = ANY)
class Tags {
@JsonAutoDetect(fieldVisibility = ANY)
class Tags {
@JsonProperty("toptags")
private Map<String, Object> topTags;
@JsonProperty("toptags") private Map<String, Object> topTags;
@SuppressWarnings("unchecked")
public Map<String, Double> all() {
try {
Map<String, Double> all = new HashMap<>();
List<Map<String, Object>> tags = (List<Map<String, Object>>) topTags.get("tag");
for (Map<String, Object> tag : tags) {
all.put(((String) tag.get("name")), ((Integer) tag.get("count")).doubleValue());
}
@SuppressWarnings("unchecked")
public Map<String, Double> all() {
try {
Map<String, Double> all = new HashMap<>();
List<Map<String, Object>> tags = (List<Map<String, Object>>) topTags.get("tag");
for (Map<String, Object> tag : tags) {
all.put(((String) tag.get("name")), ((Integer) tag.get("count")).doubleValue());
}
return all;
}
catch (Exception e) {
return Collections.emptyMap();
}
}
}
return all;
} catch (Exception e) {
return Collections.emptyMap();
}
}
}
@JsonAutoDetect(fieldVisibility = ANY)
class Artists {
@JsonAutoDetect(fieldVisibility = ANY)
class Artists {
private Map<String, Object> artists;
private Map<String, Object> artists;
@SuppressWarnings("unchecked")
public List<String> all() {
try {
List<Map<String, Object>> artists = (List<Map<String, Object>>) this.artists.get("artist");
return artists.stream().map(e -> ((String) e.get("name"))).collect(toList());
}
catch (Exception e) {
return Collections.emptyList();
}
}
}
@SuppressWarnings("unchecked")
public List<String> all() {
try {
List<Map<String, Object>> artists = (List<Map<String, Object>>) this.artists.get("artist");
return artists
.stream()
.map(e -> ((String) e.get("name")))
.collect(toList());
} catch (Exception e) {
return Collections.emptyList();
}
}
}
}
@@ -9,52 +9,53 @@ import java.util.Objects;
*/
public class Record {
/**
* The record description. For example, this can be the artist name for the famous musician
* example.
*/
private final String description;
/**
* The record description. For example, this can be the artist name for the famous musician
* example.
*/
private final String description;
/**
* Encapsulates all attributes and their corresponding values, i.e. features.
*/
private final Map<String, Double> features;
/**
* Encapsulates all attributes and their corresponding values, i.e. features.
*/
private final Map<String, Double> features;
public Record(String description, Map<String, Double> features) {
this.description = description;
this.features = features;
}
public Record(String description, Map<String, Double> features) {
this.description = description;
this.features = features;
}
public Record(Map<String, Double> features) {
this("", features);
}
public Record(Map<String, Double> features) {
this("", features);
}
public String getDescription() {
return description;
}
public String getDescription() {
return description;
}
public Map<String, Double> getFeatures() {
return features;
}
public Map<String, Double> getFeatures() {
return features;
}
@Override
public String toString() {
String prefix = description == null || description.trim().isEmpty() ? "Record" : description;
@Override
public String toString() {
String prefix = description == null || description
.trim()
.isEmpty() ? "Record" : description;
return prefix + ": " + features;
}
return prefix + ": " + features;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Record record = (Record) o;
return Objects.equals(getDescription(), record.getDescription()) &&
Objects.equals(getFeatures(), record.getFeatures());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Record record = (Record) o;
return Objects.equals(getDescription(), record.getDescription()) && Objects.equals(getFeatures(), record.getFeatures());
}
@Override
public int hashCode() {
return Objects.hash(getDescription(), getFeatures());
}
@Override
public int hashCode() {
return Objects.hash(getDescription(), getFeatures());
}
}
@@ -6,9 +6,11 @@
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
@@ -21,15 +23,19 @@
var diameter = 1100;
var tree = d3.layout.tree()
.size([360, diameter / 2 - 300])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
.separation(function (a, b) {
return (a.parent == b.parent ? 1 : 2) / a.depth;
});
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("lastfm.json", function(error, root) {
d3.json("lastfm.json", function (error, root) {
var nodes = tree.nodes(root),
links = tree.links(nodes);
var link = svg.selectAll(".link")
@@ -41,14 +47,22 @@
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
.attr("transform", function (d) {
return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
})
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dy", ".31em")
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
.text(function(d) { return d.name; });
.attr("text-anchor", function (d) {
return d.x < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)";
})
.text(function (d) {
return d.name;
});
});
d3.select(self.frameElement).style("height", diameter - 150 + "px");
</script>