簡體   English   中英

Java:如何在支持最小,最大,平均,每組最后一種聚合的列表上進行聚合

[英]Java : How to do aggregation over a list supporting min, max, avg, last kind of aggregations in each group

我已經在MySQL本身中進行了較早的操作,因為這似乎是正確的方法,但是我必須進行一些業務邏輯計算,然后需要在結果列表中應用group by,在Java中做到這一點的任何建議都不得影響性能(看着lambdaj,似乎由於大量使用代理而放慢了速度,但尚未嘗試)。

List<Item>包含名稱,值,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;
    }

這是一個建議:

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減去= items.get(0).timestamp / sample_period; //請注意,因為兩個數字均為整數/整數,所以我們有一個整數除法

然后在for循環中:

group_key = items.timestamp / sample_period-減去;

遵循這些原則將起作用,即按照您的描述對數據集進行分組。 然后,您可以將min max 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;
}

那只是一個粗略的解決方案。 我們可能想要向聚合等添加更多屬性。

除了最小/最大/等的計算之外,我注意到您的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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM