簡體   English   中英

Java groupby列並找到平均值

[英]Java groupby column and find average

我有我從 CSV 中讀取的以下格式的數據。

accountId, recordType, amount
1, past, 40
1, past, 40
1, present, 60
2, past, 20
2, present, 10
2, present, 60

什么是我可以處理它以按帳戶 ID 和記錄類型分組並找到平均金額的最簡單方法。 我知道它可以用結構、多個哈希映射等來完成,這會使代碼看起來很難看

預期輸出

accountId, recordType, amount
1, past, 40
1, present, 60
2, past, 20
2, present, 35

這是我嘗試過的,它不完整,但這就是我不滿意的方法

//Map to store count of accountId to events
Map<String, Float> countHistory = new HashMap<String, Float>();
Map<String, Float> countPresent = new HashMap<String, Float>();

//Map to store count of accountId to sum of instance launched
Map<String, Float> amountPresent = new HashMap<String, Float>();
Map<String, Float> amountHistory = new HashMap<String, Float>();

for(LaunchEvent e : inputList) {
    if(e.getDataset().equalsIgnoreCase("history")) {
        countHistory.put(e.getAccountId(), amountHistory.getOrDefault(e.getAccountId(), 0.0f) + 1.0f);
        amountHistory.put(e.getAccountId(), amountHistory.getOrDefault(e.getAccountId(), 0.0f) + Float.valueOf(e.getAmount()));
    } else {
        amountPresent.put(e.getAccountId(), amountPresent.getOrDefault(e.getAccountId(), 0.0f) + 1.0f);
        amountPresent.put(e.getAccountId(), amountPresent.getOrDefault(e.getAccountId(), 0.0f) + Float.valueOf(e.getAmount()));
    }
}

關鍵是使用 Java 作為 OO 語言,您可以在其中定義類和對象。

每行是一個包含三個字段的帳戶。 因此,讓我們定義一個具有這三個字段的類Account

您希望通過一個鍵對帳戶進行分組,該鍵由帳戶的兩個字段組成。 如果兩個字段相等,則鍵應該相等。 因此,讓我們定義一個AccountGroupingKey類,它代表該鍵並正確覆蓋equals()hashCode()

對於每個鍵,您需要具有該鍵的帳戶金額的平均值。 所以你想要一個Map<AccountGroupingKey, Double>

如何制作這張地圖? 通過使用groupingBy收集器,因為您想...按鍵對帳戶進行分組。 我們將使用averagingInt收集器將每組帳戶轉換為整數的平均值。

所以最后,您只需要以下內容。

它可能看起來很冗長,但是如果您省略自動生成的 getter、equals 和 hashCode 並專注於邏輯,它實際上非常簡潔易讀。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class AccountGroupingAndAveraging {

    static class Account {
        private final int id;
        private final String type;
        private final int amount;

        public Account(int id, String type, int amount) {
            this.id = id;
            this.type = type;
            this.amount = amount;
        }

        public int getId() {
            return id;
        }

        public String getType() {
            return type;
        }

        public int getAmount() {
            return amount;
        }
    }

    static class AccountGroupingKey {
        private final int id;
        private final String type;

        public AccountGroupingKey(Account account) {
            this.id = account.getId();
            this.type = account.getType();
        }

        public int getId() {
            return id;
        }

        public String getType() {
            return type;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            AccountGroupingKey that = (AccountGroupingKey) o;
            return id == that.id &&
                Objects.equals(type, that.type);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id, type);
        }
    }

    public static void main(String[] args) {
        List<Account> accounts = Arrays.asList(
            new Account(1, "past", 40),
            new Account(1, "past", 40),
            new Account(1, "present", 60),
            new Account(2, "past", 20),
            new Account(2, "present", 10),
            new Account(2, "present", 60)
        );

        Map<AccountGroupingKey, Double> result =
            accounts.stream().collect(
                Collectors.groupingBy(AccountGroupingKey::new,
                                      Collectors.averagingInt(Account::getAmount)));
        result.forEach((key, average) -> System.out.println(key.id + ", " + key.type + ", " + average));
    }
}

感謝 JB Nizet。 我采納了他的想法並簡化了解決方案,而無需額外上課。

Map<String, Map<String, Double>> res = events.stream()
       .collect(Collectors.groupingBy(LaunchEvent::getAccountId,
                Collectors.groupingBy(LaunchEvent::getRecordType,
                Collectors.averagingDouble(LaunchEvent::getAmount))));

這將產生輸出

Result {1={past=7.5, present=15.0}, 2={past=10.0, present=35.0}}

暫無
暫無

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

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