Java自動生成趨勢比對數(shù)據(jù)的方法分享
背景
數(shù)據(jù)之間兩兩趨勢比較在數(shù)據(jù)分析應(yīng)用中是非常常見的應(yīng)用場景,如下所示:
模擬考批次 | 班級 | 學(xué)生 | 語文 | 數(shù)學(xué) | 英語 |
---|---|---|---|---|---|
202302 | 三年一班 | 張小明 | 130 | 145 | 133 |
202302 | 三年一班 | 王二小 | 128 | 138 | 140 |
202302 | 三年一班 | 謝春花 | 136 | 142 | 139 |
202301 | 三年一班 | 張小明 | 132 | 140 | 128 |
202301 | 三年一班 | 王二小 | 125 | 146 | 142 |
202301 | 三年一班 | 謝春花 | 138 | 143 | 140 |
202212 | 三年一班 | 張小明 | 135 | 138 | 120 |
202212 | 三年一班 | 王二小 | 123 | 145 | 138 |
202212 | 三年一班 | 謝春花 | 136 | 140 | 142 |
現(xiàn)在有一個需求:各班級的每個學(xué)生在不同考試批次的各學(xué)科成績的進退步情況,得出數(shù)據(jù)如下
模擬考批次 | 班級 | 學(xué)生 | 語文 | 數(shù)學(xué) | 英語 |
---|---|---|---|---|---|
202302與202301對比 | 三年一班 | 張小明 | -2 | 5 | 5 |
202302與202301對比 | 三年一班 | 王二小 | 3 | -8 | -2 |
202302與202301對比 | 三年一班 | 謝春花 | -2 | -1 | -1 |
202301與202212對比 | 三年一班 | 張小明 | -3 | 2 | 8 |
202301與202212對比 | 三年一班 | 王二小 | 2 | 1 | 4 |
202301與202212對比 | 三年一班 | 謝春花 | 2 | 3 | -2 |
詳細(xì)設(shè)計及實現(xiàn)
趨勢比對定義類 TrendCompare
public class TrendCompare { /** * 主體的字段列表(如三年一班的張小明,那么主體字段列表為 班級 + 學(xué)生姓名) */ private String[] subjectFields; /** * 在某個字段的(介詞) 如三年一班張曉明在不同考試批次的成績對比結(jié)果 */ private String atField; /** * 參與趨勢比較的字段集合 */ private String[] compareFields; /** * 賦值映射集合:給結(jié)果數(shù)據(jù)中指定的key設(shè)置指定的值 */ private Map<String, String> assignValMap; public String[] subjectFields() { return this.subjectFields; } public TrendCompare subjectFields(String... fields) { this.subjectFields = fields; return this; } public String atField() { return this.atField; } public TrendCompare atField(String field) { this.atField = field; return this; } public String[] compareFields() { return this.compareFields; } public TrendCompare compareFields(String... fields) { this.compareFields = fields; return this; } /** * 賦值操作 * * @param field * @param valueEL 值表達式 * @return */ public TrendCompare assignVal(String field, String valueEL) { if (assignValMap == null) { assignValMap = new HashMap<>(); } assignValMap.put(field, valueEL); return this; } public Map<String, String> assignValMap() { return this.assignValMap; } }
該類定義了如下屬性:
- 主體的字段列表
- 介詞字段
- 比對的字段列表
如:各班級的每個學(xué)生在不同考試批次的各學(xué)科成績的進退步情況
上面的需求映射到定義類的結(jié)果如下:
- 主體的字段列表(班級、學(xué)生)
- 介詞字段(考試批次)
- 比對的字段列表(各學(xué)科:語文、數(shù)學(xué)、英語)
趨勢比對執(zhí)行類
該類提供了一個供外部調(diào)用的方法如下
public static <T> List<T> compare(List<T> dataList, TrendCompare trendCompare) { Map<String, List<T>> groupMap = group(dataList, null, trendCompare.subjectFields()); List<T> resultList = new ArrayList<>(); for (List<T> groupDataList : groupMap.values()) { List<T> diffValueList = new ArrayList<>(); int size = groupDataList.size(); if (size > 1) { for (int i = 0; i < size - 1; i++) { //數(shù)據(jù)之間兩兩比較 diffValue = minuend - subtrahend T minuend = groupDataList.get(i); T subtrahend = groupDataList.get(i + 1); T diffValue = minus(trendCompare.compareFields(), minuend, subtrahend); //設(shè)置主體信息 if (trendCompare.subjectFields() != null) { for (String subjectField : trendCompare.subjectFields()) { setFieldValue(diffValue, subjectField, getFieldValue(minuend, subjectField)); } } //設(shè)置介詞字段信息 String atField = trendCompare.atField(); if (StringUtils.isNotEmpty(atField)) { setFieldValue(diffValue, atField, getFieldValue(minuend, atField) + "與" + getFieldValue(subtrahend, atField) + "對比增減"); } diffValueList.add(diffValue); } } if (diffValueList.size() > 0) { T firstData = groupDataList.get(0); Map<String, Object> valMap = new HashMap<>(); //指定的賦值集合進行賦值 if (trendCompare.assignValMap() != null) { for (Map.Entry<String, String> stringStringEntry : trendCompare.assignValMap().entrySet()) { String field = stringStringEntry.getKey(); if (!StringUtils.equalsAny(field, trendCompare.compareFields())) { String valueEL = stringStringEntry.getValue(); valMap.put(field, executeSpEL(valueEL, firstData)); } } } for (Map.Entry<String, Object> entry : valMap.entrySet()) { for (T diffValue : diffValueList) { setFieldValue(diffValue, entry.getKey(), entry.getValue()); } } } resultList.addAll(diffValueList); } return resultList; }
可以看到,該方法要求傳入
- 數(shù)據(jù)集合
- 趨勢比對定義
兩個參數(shù),并最終返回趨勢比對后的結(jié)果集合。
該方法的內(nèi)部邏輯可分為如下2個步驟:
- 按主體分組
- 分組后組內(nèi)數(shù)據(jù)兩兩比對,并最終返回比對結(jié)果。
使用案例
假設(shè)有如下這樣一組數(shù)據(jù)
定義一個學(xué)生類:
public class Student { private String batch; private String banji; private String studentNo; private String name; private String sex; private Double yuwen; private Double math; private Double english; private Double physics; //extra private String maxScoreName1; public Student(String batch, String banji, String studentNo, String name, String sex, Double yuwen, Double math, Double english, Double physics) { this.batch = batch; this.banji = banji; this.studentNo = studentNo; this.name = name; this.sex = sex; this.yuwen = yuwen; this.math = math; this.english = english; this.physics = physics; } }
我們寫一個方法,返回如上數(shù)據(jù):
public List<Student> getDataList() { List<Student> dataList = new ArrayList<>(); dataList.add(new Student("202302", "三年一班", "20001001", "張小明", "男", 130.0, 145.0, 133.0, 92.0)); dataList.add(new Student("202302", "三年一班", "20001002", "王二小", "男", 128.0, 138.0, 140.0, 98.0)); dataList.add(new Student("202302", "三年一班", "20001003", "謝春花", "女", 136.0, 142.0, 139.0, 95.0)); dataList.add(new Student("202302", "三年二班", "20002001", "馮世杰", "男", 129.0, 144.0, 138.0, 96.0)); dataList.add(new Student("202302", "三年二班", "20002002", "馬功成", "男", 130.0, 132.0, 133.0, 98.0)); dataList.add(new Student("202302", "三年二班", "20002003", "魏翩翩", "女", 136.0, 142.0, 137.0, 92.0)); dataList.add(new Student("202301", "三年一班", "20001001", "張小明", "男", 132.0, 142.0, 134.0, 92.0)); dataList.add(new Student("202301", "三年一班", "20001002", "王二小", "男", 126.0, 136.0, 135.0, 94.0)); dataList.add(new Student("202301", "三年一班", "20001003", "謝春花", "女", 136.0, 145.0, 139.0, 95.0)); dataList.add(new Student("202301", "三年二班", "20002001", "馮世杰", "男", 124.0, 143.0, 148.0, 90.0)); dataList.add(new Student("202301", "三年二班", "20002002", "馬功成", "男", 140.0, 133.0, 138.0, 90.0)); dataList.add(new Student("202301", "三年二班", "20002003", "魏翩翩", "女", 126.0, 136.0, 135.0, 92.0)); return dataList; }
趨勢比對定義并執(zhí)行比對:
List<Student> dataList = getDataList(); TrendCompare trendCompare = new TrendCompare() .subjectFields("banji", "name") .atField("batch") .compareFields("yuwen", "math", "english") //.assignVal("batch", "'環(huán)比增減'") ; List<Student> resultList = DataProcessUtil.compare(dataList, trendCompare); for (Student result : resultList) { System.out.println(JSON.toJSONString(result)); }
結(jié)果如下:
{"banji":"三年一班","batch":"202302與202301對比增減","english":-1.0,"math":3.0,"name":"張小明","yuwen":-2.0} {"banji":"三年一班","batch":"202302與202301對比增減","english":5.0,"math":2.0,"name":"王二小","yuwen":2.0} {"banji":"三年一班","batch":"202302與202301對比增減","english":0.0,"math":-3.0,"name":"謝春花","yuwen":0.0} {"banji":"三年二班","batch":"202302與202301對比增減","english":-10.0,"math":1.0,"name":"馮世杰","yuwen":5.0} {"banji":"三年二班","batch":"202302與202301對比增減","english":-5.0,"math":-1.0,"name":"馬功成","yuwen":-10.0} {"banji":"三年二班","batch":"202302與202301對比增減","english":2.0,"math":6.0,"name":"魏翩翩","yuwen":10.0}
以上就是Java自動生成趨勢比對數(shù)據(jù)的方法分享的詳細(xì)內(nèi)容,更多關(guān)于Java生成趨勢比對數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中@EnableAutoConfiguration和@Configuration的區(qū)別
這篇文章主要介紹了SpringBoot中@EnableAutoConfiguration和@Configuration的區(qū)別,@SpringBootApplication相當(dāng)于@EnableAutoConfiguration,@ComponentScan,@Configuration三者的集合,需要的朋友可以參考下2023-08-08Java HashSet(散列集),HashMap(散列映射)的簡單介紹
這篇文章主要介紹了Java HashSet(散列集),HashMap(散列映射)的簡單介紹,幫助大家更好的理解和學(xué)習(xí)Java集合框架的相關(guān)知識,感興趣的朋友可以了解下2021-01-01java序列化與ObjectOutputStream和ObjectInputStream的實例詳解
這篇文章主要介紹了java序列化與ObjectOutputStream和ObjectInputStream的實例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09Java 實現(xiàn)二叉搜索樹的查找、插入、刪除、遍歷
本文主要介紹了Java實現(xiàn)二叉搜索樹的查找、插入、刪除、遍歷等內(nèi)容。具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02java多線程join()方法的作用和實現(xiàn)原理解析(應(yīng)用場景)
join方法主要是用于將當(dāng)前線程掛起,等待其他線程結(jié)束后在執(zhí)行當(dāng)前線程,本文通過應(yīng)用場景分析代碼示例講解java多線程join()方法的作用和實現(xiàn)原理,感興趣的朋友一起看看吧2021-07-07java中isEmpty和isBlank的區(qū)別小結(jié)
Java中的isEmpty和isBlank都是用來判斷字符串是否為空的方法,但在不同的情況下有所區(qū)別,具有一定的參考價值,感興趣的可以了解一下2023-09-09詳解Spring Boot整合Mybatis實現(xiàn) Druid多數(shù)據(jù)源配置
本篇文章主要介紹了詳解Spring Boot整合Mybatis實現(xiàn) Druid多數(shù)據(jù)源配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03Java中break、continue、return語句的使用區(qū)別對比
這篇文章主要介紹了Java中break、continue、return語句的使用區(qū)別對比,本文用非常清爽簡明的語言總結(jié)了這三個關(guān)鍵字的使用技巧,并用一個實例對比使用結(jié)果,需要的朋友可以參考下2015-06-06