Fixed formatting issues.
This commit is contained in:
parent
f615f9aeae
commit
5a10f94946
@ -9,8 +9,7 @@ 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");
|
||||
if (f1 == null || f2 == null) throw new IllegalArgumentException("Feature vectors can't be null");
|
||||
|
||||
double sum = 0;
|
||||
for (String key : f1.keySet()) {
|
||||
|
@ -72,8 +72,11 @@ public class KMeans {
|
||||
* @return Collection of new and relocated centroids.
|
||||
*/
|
||||
private static List<Centroid> relocateCentroids(Map<Centroid, List<Record>> clusters) {
|
||||
return clusters.entrySet().stream()
|
||||
.map(e -> average(e.getKey(), e.getValue())).collect(toList());
|
||||
return clusters
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> average(e.getKey(), e.getValue()))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,13 +100,18 @@ public class KMeans {
|
||||
|
||||
// The average function works correctly if we clear all coordinates corresponding
|
||||
// to present record attributes
|
||||
records.stream().flatMap(e -> e.getFeatures().keySet().stream())
|
||||
records
|
||||
.stream()
|
||||
.flatMap(e -> e
|
||||
.getFeatures()
|
||||
.keySet()
|
||||
.stream())
|
||||
.forEach(k -> average.put(k, 0.0));
|
||||
|
||||
for (Record record : records) {
|
||||
record.getFeatures().forEach(
|
||||
(k, v) -> average.compute(k, (k1, currentValue) -> v + currentValue)
|
||||
);
|
||||
record
|
||||
.getFeatures()
|
||||
.forEach((k, v) -> average.compute(k, (k1, currentValue) -> v + currentValue));
|
||||
}
|
||||
|
||||
average.forEach((k, v) -> average.put(k, v / records.size()));
|
||||
@ -119,8 +127,7 @@ public class KMeans {
|
||||
* @param record The feature vector.
|
||||
* @param centroid The centroid.
|
||||
*/
|
||||
private static void assignToCluster(Map<Centroid, List<Record>> clusters,
|
||||
Record record, Centroid centroid) {
|
||||
private static void assignToCluster(Map<Centroid, List<Record>> clusters, Record record, Centroid centroid) {
|
||||
clusters.compute(centroid, (key, list) -> {
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
@ -140,8 +147,7 @@ public class KMeans {
|
||||
* @param distance To calculate the distance between two items.
|
||||
* @return The nearest centroid to the given feature vector.
|
||||
*/
|
||||
private static Centroid nearestCentroid(Record record, List<Centroid> centroids,
|
||||
Distance distance) {
|
||||
private static Centroid nearestCentroid(Record record, List<Centroid> centroids, Distance distance) {
|
||||
double minimumDistance = Double.MAX_VALUE;
|
||||
Centroid nearest = null;
|
||||
|
||||
@ -174,7 +180,9 @@ public class KMeans {
|
||||
Map<String, Double> mins = new HashMap<>();
|
||||
|
||||
for (Record record : records) {
|
||||
record.getFeatures().forEach((key, value) -> {
|
||||
record
|
||||
.getFeatures()
|
||||
.forEach((key, value) -> {
|
||||
// compares the value with the current max and choose the bigger value between them
|
||||
maxs.compute(key, (k1, max) -> max == null || value > max ? value : max);
|
||||
|
||||
@ -183,8 +191,13 @@ public class KMeans {
|
||||
});
|
||||
}
|
||||
|
||||
Set<String> attributes = records.stream()
|
||||
.flatMap(e -> e.getFeatures().keySet().stream()).collect(toSet());
|
||||
Set<String> attributes = records
|
||||
.stream()
|
||||
.flatMap(e -> e
|
||||
.getFeatures()
|
||||
.keySet()
|
||||
.stream())
|
||||
.collect(toSet());
|
||||
for (int i = 0; i < k; i++) {
|
||||
Map<String, Double> coordinates = new HashMap<>();
|
||||
for (String attribute : attributes) {
|
||||
@ -199,18 +212,13 @@ public class KMeans {
|
||||
return centroids;
|
||||
}
|
||||
|
||||
private static void applyPreconditions(List<Record> records, int k,
|
||||
Distance distance, int maxIterations) {
|
||||
if (records == null || records.isEmpty())
|
||||
throw new IllegalArgumentException("The dataset can't be empty");
|
||||
private static void applyPreconditions(List<Record> records, int k, Distance distance, int maxIterations) {
|
||||
if (records == null || records.isEmpty()) throw new IllegalArgumentException("The dataset can't be empty");
|
||||
|
||||
if (k <= 1)
|
||||
throw new IllegalArgumentException("It doesn't make sense to have less than or equal to 1 cluster");
|
||||
if (k <= 1) throw new IllegalArgumentException("It doesn't make sense to have less than or equal to 1 cluster");
|
||||
|
||||
if (distance == null)
|
||||
throw new IllegalArgumentException("The distance calculator is required");
|
||||
if (distance == null) throw new IllegalArgumentException("The distance calculator is required");
|
||||
|
||||
if (maxIterations <= 0)
|
||||
throw new IllegalArgumentException("Max iterations should be a positive number");
|
||||
if (maxIterations <= 0) throw new IllegalArgumentException("Max iterations should be a positive number");
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ public class LastFm {
|
||||
.addInterceptor(new LastFmService.Authenticator("put your API key here"))
|
||||
.build();
|
||||
|
||||
private static Retrofit retrofit = new Retrofit.Builder().client(okHttp)
|
||||
private static Retrofit retrofit = new Retrofit.Builder()
|
||||
.client(okHttp)
|
||||
.addConverterFactory(JacksonConverterFactory.create())
|
||||
.baseUrl("http://ws.audioscrobbler.com/")
|
||||
.build();
|
||||
@ -43,7 +44,10 @@ public class LastFm {
|
||||
System.out.println("------------------------------ CLUSTER -----------------------------------");
|
||||
|
||||
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.println();
|
||||
@ -67,7 +71,6 @@ public class LastFm {
|
||||
}
|
||||
child.put("children", nested);
|
||||
|
||||
|
||||
children.add(child);
|
||||
});
|
||||
json.put("children", children);
|
||||
@ -75,12 +78,21 @@ public class LastFm {
|
||||
}
|
||||
|
||||
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) {
|
||||
List<Map.Entry<String, Double>> entries = new ArrayList<>(key.getCoordinates().entrySet());
|
||||
entries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue()));
|
||||
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) {
|
||||
@ -90,14 +102,19 @@ public class LastFm {
|
||||
return new Centroid(sorted);
|
||||
}
|
||||
|
||||
private static List<Record> datasetWithTaggedArtists(List<String> artists,
|
||||
Set<String> topTags) throws IOException {
|
||||
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();
|
||||
Map<String, Double> tags = lastFm
|
||||
.topTagsFor(artist)
|
||||
.execute()
|
||||
.body()
|
||||
.all();
|
||||
|
||||
// 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));
|
||||
}
|
||||
@ -105,13 +122,21 @@ public class LastFm {
|
||||
}
|
||||
|
||||
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 {
|
||||
List<String> artists = new ArrayList<>();
|
||||
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;
|
||||
|
@ -45,8 +45,17 @@ public interface LastFmService {
|
||||
|
||||
@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();
|
||||
HttpUrl url = chain
|
||||
.request()
|
||||
.url()
|
||||
.newBuilder()
|
||||
.addQueryParameter("api_key", apiKey)
|
||||
.build();
|
||||
Request request = chain
|
||||
.request()
|
||||
.newBuilder()
|
||||
.url(url)
|
||||
.build();
|
||||
|
||||
return chain.proceed(request);
|
||||
}
|
||||
@ -60,15 +69,17 @@ public interface LastFmService {
|
||||
@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());
|
||||
return topTags
|
||||
.stream()
|
||||
.map(e -> ((String) e.get("name")))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@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() {
|
||||
@ -80,8 +91,7 @@ public interface LastFmService {
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
@ -96,9 +106,11 @@ public interface LastFmService {
|
||||
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 artists
|
||||
.stream()
|
||||
.map(e -> ((String) e.get("name")))
|
||||
.collect(toList());
|
||||
} catch (Exception e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ public class Record {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String prefix = description == null || description.trim().isEmpty() ? "Record" : description;
|
||||
String prefix = description == null || description
|
||||
.trim()
|
||||
.isEmpty() ? "Record" : description;
|
||||
|
||||
return prefix + ": " + features;
|
||||
}
|
||||
@ -49,8 +51,7 @@ public class Record {
|
||||
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());
|
||||
return Objects.equals(getDescription(), record.getDescription()) && Objects.equals(getFeatures(), record.getFeatures());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user