/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.addthis.hydra.data.util; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; import java.text.SimpleDateFormat; import com.addthis.codec.annotations.FieldConfig; import com.addthis.codec.codables.Codable; /** * A class to store time values in buckets of variable size. */ public class TimeBuckets implements Codable { /** * @param args */ @FieldConfig(codable = true, required = true) private TreeMap<Long, Long> map; @FieldConfig(codable = true) private long blockSize; public TimeBuckets() { } public TimeBuckets init(long size) { blockSize = size; map = new TreeMap<>(); return this; } public void initBuckets(String date) { if (date != null) { SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmm"); try { Date jobDate = sdf.parse(date + "2359"); sdf.setTimeZone(TimeZone.getTimeZone("US/Eastern")); Long l = jobDate.getTime(); this.acceptEmpty(l); } catch (Exception e) { } } } public void accept(Long time) { Long keyval = toKey(time); Long count = map.get(keyval); if (count == null) { if (map.size() > 0) { // Add empty buckets as necessary if (map.firstKey() - keyval > blockSize) { Long firstKey = map.firstKey(); for (Long l = keyval + blockSize; l < firstKey; l += blockSize) { map.put(l, 0L); } } if (keyval - map.lastKey() > blockSize) { for (Long l = map.lastKey() + blockSize; l < keyval; l += blockSize) { map.put(l, 0L); } } } map.put(keyval, 1L); } else { map.put(keyval, count + 1); } } public void acceptEmpty(Long time) { Long keyval = toKey(time); map.put(keyval, 0L); } public TreeMap<Long, Long> getMap() { return map; } public Long[] getCounts() { Long[] rv = new Long[1]; rv = map.values().toArray(rv); return rv; } public int size() { return map.size(); } @SuppressWarnings("unchecked") public Map.Entry<String, Long>[] getEntries() { Map.Entry[] e = new Map.Entry[map.size()]; e = map.entrySet().toArray(e); return e; } public TreeMap<String, Long> getChangeTimes(double minRatio, int minSize, double minZScore, int inactiveThreshold, int windowSize) { TreeMap<String, Long> rv = new TreeMap<>(); Long[] counts = this.getCounts(); double mean = FindChangePoints.mean(counts); if (mean > 10) { List<ChangePoint> cps = FindChangePoints.findSignificantPoints(counts, minSize, minRatio, minZScore, inactiveThreshold, windowSize); for (ChangePoint cp : cps) { Integer index = cp.getIndex(); String key = cp.getType().name() + "," + map.keySet().toArray()[index]; rv.put(key, cp.getSize()); } } return rv; } public Map<Long, Long> getPeaks(int maxWidth, int minHt) { Long[] counts = this.getCounts(); List<ChangePoint> peaks = FindChangePoints.findHighPoints(counts, maxWidth, minHt); Map<Long, Long> rv = new HashMap<>(); for (ChangePoint peak : peaks) { int index = peak.getIndex(); Long peakTime = (Long) map.keySet().toArray()[index]; rv.put(peakTime, peak.getSize()); } return rv; } private Long toKey(Long time) { return (time / blockSize) * blockSize; } }