我之前在MySQL本身做过这个,因为这似乎是正确的方法,但我必须做一些业务逻辑计算,然后需要在结果列表中应用组,任何建议在Java中执行此操作而不妥协性能(看过lambdaj,看起来由于代理的大量使用而放慢了速度,但是尝试过避风港)。
List<Item>
包含name,value,unixtimestamp作为属性,并由数据库返回。
每条记录相隔5分钟。
我应该能够按动态采样时间分组,比如1小时,这意味着必须将每12条记录分组到一条记录中,然后在每组上应用最小值,最大值,平均值,最后一张。
任何建议表示赞赏。
[更新]让以下工作,但尚未对索引的地图值上的每个列表元素进行聚合。如您所见,我创建了一个列表映射,其中key是请求的整数表示示例时间(30是此处请求的示例)。
private List<Item> performConsolidation(List<Item> items) {
ListMultimap<Integer, Item> groupByTimestamp = ArrayListMultimap.create();
List<Item> consolidatedItems = new ArrayList<>();
for (Item item : items) {
groupByTimestamp.put((int)floor(((Double.valueOf(item.getItem()[2])) / 1000) / (60 * 30)), item);
}
return consolidatedItems;
}
答案 0 :(得分:1)
这是一个建议:
public Map<Long,List<Item>> group_items(List<Item> items,long sample_period) {
Map<Long,List<Item>> grouped_result = new HashMap<Long,List<Item>>();
long group_key;
for (Item item: items) {
group_key = item.timestamp / sample_period;
if (grouped_result.containsKey(group_key)) {
grouped_result.get(group_key).add(item);
}
else {
grouped_result.put(group_key, new ArrayList<Item>());
grouped_result.get(group_key).add(item);
}
}
return grouped_result;
}
sample_period是分组的秒数:3600 =小时,900 = 15分钟
地图中的键当然可以是相当大的数字(取决于采样周期),但此分组将保留组的内部时间顺序,即较低的键是按时间顺序排在第一位的键。如果我们假设原始列表中的数据按时间顺序排序,我们当然可以得到第一个键的值,然后从键中减去该值。这样我们就会得到键0,1等。在这种情况下,在for循环开始之前我们需要:
int subtract = items.get(0).timestamp / sample_period; //注意,因为两个数字一个整数/长整数我们有一个整数除法
然后在for循环中:
group_key = items.timestamp / sample_period - subtract;
这些行中的某些内容将起作用,即按照您的描述对数据集进行分组。然后,您可以将最小平均avg等应用于结果列表。但由于这些函数当然必须再次迭代单个组列表,最好将这些计算结合到此解决方案中,并使函数返回类似于Map的位置,其中Aggregates是包含avg,min,max字段的新类型,然后是组中的项目列表?至于表现,我认为这是可以接受的。这是一个简单的O(N)解决方案。 编辑:
确定只想添加更完整的解决方案/建议,同时计算最小值,最大值和平均值:
public class Aggregate {
public double avg;
public double min;
public double max;
public List<Item> items = new ArrayList<Item>();
public Aggregate(Item item) {
min = item.value;
max = item.value;
avg = item.value;
items.add(item);
}
public void addItem(Item item) {
items.add(item);
if (item.value < this.min) {
this.min = item.value;
}
else if (item.value > this.max) {
this.max = item.value;
}
this.avg = (this.avg * (this.items.size() - 1) + item.value) / this.items.size();
}
}
public Map<Long,Aggregate> group_items(List<Item> items,long sample_period) {
Map<Long,Aggregate> grouped_result = new HashMap<Long,Aggregate>();
long group_key;
long subtract = items.get(0).timestamp / sample_period;
for (Item item: items) {
group_key = items.timestamp / sample_period - subtract;
if (grouped_result.containsKey(group_key)) {
grouped_result.get(group_key).addItem(item);
}
else {
grouped_result.put(group_key, new Aggregate(item));
}
}
return grouped_result;
}
这只是一个粗略的解决方案。我们可能想要为聚合添加更多属性等。
答案 1 :(得分:0)
不考虑min / max / etc的计算,我注意到你的performConsolidation
方法看起来可以使用Multimaps.index
。只需传递项目和计算所需值的Function<Item, Integer>
:
return (int) floor(((Double.valueOf(item.getItem()[2])) / 1000) / (60 * 30));
这不会节省大量代码,但可以让您更容易看到一目了然的事情:index(items, timeBucketer)
。
答案 2 :(得分:0)
如果您可以使用我的xpresso项目,您可以执行以下操作:
让您的输入列表为:
list<tuple> items = x.list(x.tuple("name1",1d,100),x.tuple("name2",3d,105),x.tuple("name1",4d,210));
首先解压缩元组列表以获得列表元组:
tuple3<list<String>,list<Double>,list<Integer>> unzipped = x.unzip(items, String.class, Double.class, Integer.class);
然后你可以按照你想要的方式进行聚合:
x.print(x.tuple(x.last(unzipped.value0), x.avg(unzipped.value1), x.max(unzipped.value2)));
前面的内容将产生:
(name1,2.67,210)