java
@Getter
@Setter
public class Data {
private String date;
private String id;
private String report;
private Double sum;
private Double avg;
}
java
@ToString
@Getter
@Setter
@JsonNaming(value = PropertyNamingStrategies.UpperSnakeCaseStrategy.class)
@SuppressWarnings({"squid:S116"})
public class PivotData {
@JsonProperty("date")
private String date;
@JsonProperty("id")
private String id;
private Double POWER;
private Double ENERGY;
private Double TEMP;
private Double HERTZ;
}
java
@UtilityClass
public class PivotDataConverter {
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final Map<String, String> AVG_REPORTS = Set.of("POWER", "TEMP", "HERTZ").stream()
.collect(Collectors.toUnmodifiableMap(s -> s, s -> s));
public static List<PivotData> from(List<Data> list) {
return MAPPER.convertValue(list.stream()
.collect(Collectors.groupingByConcurrent(data -> Pair.of(data.getDate(), data.getId())))
.entrySet()
.parallelStream()
.map(entry -> {
Map<String, Object> row = entry.getValue()
.stream()
.collect(Collectors.toMap(Data::getReport,
data -> AVG_REPORTS.containsKey(data.getReport()) ? data.getAvg() : data.getSum()));
row.put("date", entry.getKey().getLeft());
row.put("id", entry.getKey().getRight());
return row;
}).toList(), new TypeReference<>() {
});
}
}
날짜와 리포트 항목으로 이루어진 시계열 데이터의 통계 정보를 날짜와 리포트 항목별 값 형태로 이루어지는 피봇 테이블 리스트로 바꿔보았습니다. 날짜를 포함한 하나 이상의 필드로 그룹핑 되면서 리포트 항목에 따라 합계 또는 평균값을 사용해야 합니다. Collectors.groupingBy 를 사용해서 데이터 리스트를 날짜 기준으로 그룹핑하고 각 리포트 항목을 하나의 Map 으로 생성하고나서 ObjectMapper를 사용해 피봇된 형태의 클래스를 가진 리스트로 변환했습니다. 위 예시에서 전력량에 대해서만 합계를 사용하지만 실제로는 대부분의 리포트 항목에 대해 합계값을 사용하게 되므로 평균값을 사용해야하는 리포트 항목만을 별도로 관리하도록 코드를 작성했습니다.