欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用Java计算一组数据的同比增长和环比

最编程 2024-01-12 21:21:10
...

做数据统计的时候经常会有这样的需求,给你一组历史数据,包含两列,一列是日期,一列是数值,让你计算出同比环比。

其实这样的需求很多,而且不复杂,但是用SQL并不好实现。

我尝试用java实现一下,下面是一个日期生成器,方便后面造案例数据

public class DateUtils {
    /**
     * 最近30天生成器,不包含当天
     *
     * @param today
     * @param interval=30
     * @return
     */
    public static List<LocalDate> generateLast30Days(LocalDate today, int interval) {
        List<LocalDate> localDates = new ArrayList<>();
        for (int i = interval; i > 0; i--) {
            LocalDate localDate = today.minusDays(i);
            localDates.add(localDate);
        }
        return localDates;
    }

    /**
     * 最近12个月生成器,不包括本月
     *
     * @param today
     * @param interval=12
     * @return
     */
    public static List<LocalDate> generateLast12Months(LocalDate today, int interval) {
        List<LocalDate> localDates = new ArrayList<>();
        for (int i = interval; i > 0; i--) {
            LocalDate localDate = today.minusMonths(i);
            localDates.add(localDate.withDayOfMonth(1));
        }
        return localDates;
    }

    /**
     * 指定时间单元前一个时间点的值
     * 比如:today=2022-10-01,
     * timeUnit=Days=>2022-09-30
     * timeUnit=MONTHS=>2022-09-01
     * timeUnit=YEARS=>2021-10-01
     * @param today
     * @param timeUnit
     * @return
     */
    public static LocalDate previousDate(LocalDate today, ChronoUnit timeUnit) {
        switch (timeUnit) {
            case DAYS:
                return today.minusDays(1);
            case MONTHS:
                return today.minusMonths(1);
            case YEARS:
                return today.minusYears(1);
            default:
                throw new IllegalArgumentException("不支持的时间区间类型!");
        }
    }

    public static LocalDate nextDate(LocalDate today, ChronoUnit timeUnit) {
        switch (timeUnit) {
            case DAYS:
                return today.plusDays(1);
            case MONTHS:
                return today.plusMonths(1);
            case YEARS:
                return today.plusYears(1);
            default:
                throw new IllegalArgumentException("不支持的时间区间类型!");
        }
    }
}

接下来,我们定义一个模型,只有两个属性

public class Model {
    private String dt;
    private Double num;

    public Model(String dt, Double num) {
        this.dt = dt;
        this.num = num;
    }

    public String getDt() {
        return dt;
    }

    public Double getNum() {
        return num;
    }

    @Override
    public String toString() {
        return "Model{" +
                "dt='" + dt + '\'' +
                ", num=" + num +
                '}';
    }
}

接下来是要输出的模型,在上面的基础上,增加了环比、同比

import java.text.DecimalFormat;
/**
 * @Author : wangbin
 * @Date : 2023/3/13 13:30
 * @Description:
 */
public class ModelPlus extends Model{
    private static final DecimalFormat decimalFormat = new DecimalFormat("#.##");

    private Double basisRate;
    private Double loopRate;
    public ModelPlus(String dt, Double num) {
        super(dt, num);
    }

    public static ModelPlus build(Model model){
        return new ModelPlus(model.getDt(), model.getNum());
    }

    public Double getBasisRate() {
        return basisRate;
    }

    public void setBasisRate(Double basisRate) {
        this.basisRate = basisRate;
    }

    public Double getLoopRate() {
       return loopRate;
    }

    public void setLoopRate(Double loopRate) {
        this.loopRate = loopRate;
    }

    @Override
    public String toString() {


        return "ModelPlus{" +
                "dt="+this.getDt()+
                ",num="+decimalFormat.format(this.getNum())+
                ",basisRate=" + decimalFormat.format(basisRate) +
                ", loopRate=" + decimalFormat.format(loopRate) +
                '}';
    }


}

最后是我们的核心逻辑,先随机生成近18个月的数据,然后计算出环比、同比

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

public class Main {
private final static DateTimeFormatter formats=DateTimeFormatter.ofPattern("yyyy-MM");
private final static Random rand=new Random();
    public static void main(String[] args) {
        List<Model> models = build();

        Map<String, Double> map = models.stream().collect(Collectors.toMap(Model::getDt, Model::getNum));
        List<ModelPlus> list = models.stream().map(model -> {
            ModelPlus plus = ModelPlus.build(model);
            String dt = plus.getDt();
            Double num = plus.getNum();
            String lastM = getLastMonth(dt);
            String lastYM = getLastYearMonth(dt);
            Double lastMV = map.get(lastM);
            Double lastYMV = map.get(lastYM);

            Double basisRate = basisRate(lastYMV, num);
            plus.setBasisRate(basisRate);
            Double loopRate = basisRate(lastMV, num);
            plus.setLoopRate(loopRate);
            return plus;

        }).collect(Collectors.toList());
        for (ModelPlus modelPlus : list) {
            System.out.println(modelPlus);
        }


    }
    //同比或环比
    public static Double basisRate(Double lastVal,Double thisVal){
        if(lastVal==null||lastVal==0d){
            return 0d;
        }
        return 100*(thisVal-lastVal)/lastVal;
    }

    //上月
    public static String getLastMonth(String month){
        DateTimeFormatter format = new DateTimeFormatterBuilder()
                .appendPattern("yyyy-MM")
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .toFormatter();
        LocalDate parse = LocalDate.from(format.parse(month));
        LocalDate lastMonth = parse.minusMonths(1);
        return format.format(lastMonth);
    }

    //去年同月
    public static String getLastYearMonth(String month){
        DateTimeFormatter format = new DateTimeFormatterBuilder()
                .appendPattern("yyyy-MM")
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .toFormatter();
        LocalDate parse = LocalDate.from(format.parse(month));
        LocalDate lastMonth = parse.minusYears(1);
        return format.format(lastMonth);
    }

    public static List<Model> build() {
        List<LocalDate> localDates = DateUtils.generateLast12Months(LocalDate.now(), 18);


        return localDates.stream().map(localDate -> {
            String dt = formats.format(localDate);
            double v = 100 * rand.nextDouble();
            return new Model(dt, v);
        }).collect(Collectors.toList());

    }


}

输出:

ModelPlus{dt=2021-09,num=47.67,basisRate=0, loopRate=0}
ModelPlus{dt=2021-10,num=22.03,basisRate=0, loopRate=-53.79}
ModelPlus{dt=2021-11,num=57.69,basisRate=0, loopRate=161.89}
ModelPlus{dt=2021-12,num=85.7,basisRate=0, loopRate=48.56}
ModelPlus{dt=2022-01,num=29.54,basisRate=0, loopRate=-65.53}
ModelPlus{dt=2022-02,num=74.51,basisRate=0, loopRate=152.27}
ModelPlus{dt=2022-03,num=57.86,basisRate=0, loopRate=-22.35}
ModelPlus{dt=2022-04,num=7.75,basisRate=0, loopRate=-86.6}
ModelPlus{dt=2022-05,num=87.68,basisRate=0, loopRate=1030.71}
ModelPlus{dt=2022-06,num=25.71,basisRate=0, loopRate=-70.68}
ModelPlus{dt=2022-07,num=84.05,basisRate=0, loopRate=226.9}
ModelPlus{dt=2022-08,num=31.43,basisRate=0, loopRate=-62.61}
ModelPlus{dt=2022-09,num=85.92,basisRate=80.24, loopRate=173.38}
ModelPlus{dt=2022-10,num=69.99,basisRate=217.74, loopRate=-18.55}
ModelPlus{dt=2022-11,num=85.9,basisRate=48.9, loopRate=22.73}
ModelPlus{dt=2022-12,num=31.91,basisRate=-62.77, loopRate=-62.86}
ModelPlus{dt=2023-01,num=74.53,basisRate=152.34, loopRate=133.59}
ModelPlus{dt=2023-02,num=21.13,basisRate=-71.65, loopRate=-71.65}

功能是基本实现了,但是还是有很多的代码,下一步需要把它进行优化。

原文地址:https://www.cnblogs.com/wangbin2188/p/17211256.html