我已经在MySQL本身中做了这个,因为这似乎是正确的方式,但是我必须做一些业务逻辑计算,然后需要在结果列表上应用组,任何建议在Java中这样做而不影响性能(已经看过lambdaj,似乎由于大量使用代理而减慢了速度,还没有尝试过)。
List<Item>
包含名称、值、unixtimestamp作为属性,由数据库返回。每张唱片间隔5分钟。
我应该能够通过一个动态的样本时间,说1小时,这意味着必须组每12个记录到一个记录,然后应用min, max, avg,最后对每个组。
欢迎指教。
[Update]使下面的工作,但对索引映射值上的每个列表元素进行聚合。如您所见,我创建了一个列表映射,其中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;
}
我有个建议:
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;
沿着这些路线将工作,即按您描述的方式分组您的数据集。然后你可以应用最小、最大、平均等结果列表。但是,由于这些函数当然要迭代个人组列表再次它可能是更好的将这些计算纳入到这个解决方案,并有函数返回的东西像地图汇总是一个新的类型包含字段为平均,最小,最大,然后在组中的项目列表?至于性能,我认为这是可以接受的。这是一个简单的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;
}
这只是一个粗略的解决方案。我们可能想要添加一些更多的属性到聚合等
不考虑min/max等的计算。,我注意到你的performConsolidation
方法看起来可以使用Multimaps.index
。只需将项目和计算所需值的Function<Item, Integer>
传递给它:
return (int) floor(((Double.valueOf(item.getItem()[2])) / 1000) / (60 * 30));
这不会节省大量的代码,但它可以更容易地看到发生了什么一目了然:index(items, timeBucketer)
.
如果您可以使用我的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)