new TreeMultiMap<Double,String>();
// Create a second time span than is twice the duration. We will use
// this to check whether two partition's vectors were adjacent in the
// slice by seeing wether the timestamps fall within this duration
TimeSpan twoPartitions = new TimeSpan(partitionDuration.getYears() * 2,
partitionDuration.getMonths() * 2,
partitionDuration.getWeeks() * 2,
partitionDuration.getDays() * 2,
partitionDuration.getHours() * 2);
// Once we have all the vectors for each word in each sspace,
// calculate how much the vector has changed.
for (Map.Entry<String,SortedMap<Long,double[]>> e :
wordToTemporalSemantics.entrySet()) {
String word = e.getKey();
SortedMap<Long,double[]> m = e.getValue();
// Skip computing shifts for words without enough partitions
if (m.size() < 2)
continue;
// Get the timestamps as a navigable map so we can identify the last
// two keys in it more easly.
NavigableMap<Long,double[]> timestampToVector =
(e instanceof NavigableMap)
? (NavigableMap<Long,double[]>)m
: new TreeMap<Long,double[]>(m);
Map.Entry<Long,double[]> mostRecent = timestampToVector.lastEntry();
// Skip calculating the shift for words who most recent partition
// was not the same as the most recent partition for TRI
if (!mostRecent.getKey().equals(startOfMostRecentPartition))
continue;
Map.Entry<Long,double[]> secondMostRecent =
timestampToVector.lowerEntry(mostRecent.getKey());
// Skip calculating the shift for words where the two most recent
// partitoins aren't contiguous. Check for this using the custom
// time span that covers two partitions
if (!twoPartitions.insideRange(secondMostRecent.getKey(),
mostRecent.getKey()))
continue;
// Compute the semantic shift of the two partitions