Java - BigDecimal 计算分位(百分位)

news2024/9/17 7:20:15

        日常开发中,如果使用数据库来直接查询一组数据的分位数,就比较简单,直接使用对应的函数就可以了,例如:

        PERCENT_RANK() OVER(PARTITION BY 分组列名 ORDER BY 目标列名) AS 目标列名_分位数

        如果是需要在代码逻辑部分进行分位数的计算,就需要我们自己写一个工具类来支持计算了

import static java.lang.Float.NaN;

public static Double getPercentile(List<Double> dataList, double target) {
    double[] doubles = Doubles.toArray(dataList); // 将List转换为数组
    Arrays.sort(doubles); // 对数组进行升序排序

    double result; // 结果
    int lt = 0; // 小于target的数量
    int lt2 = 1; // 重复的小于target的数量
    int pos = -1; // target在数组中的位置
    double a = NaN; // 小于target的前一个值
    double c = NaN; // 大于等于target的第一个值
    for (int i = 0; i < doubles.length; i++) {
        if (doubles[i] == target) {
            pos = i; // 找到target的位置
            break;
        } else if (doubles[i] < target) {
            lt++; // 小于target的数量加一
            if (doubles[i] == a) {
                lt2++; // 重复的小于target的数量加一
            } else {
                lt2 = 1; // 重置重复的小于target的数量
                a = doubles[i]; // 更新小于target的前一个值
            }
        } else {
            c = doubles[i]; // 找到大于等于target的第一个值
            break;
        }
    }
    result = (double) lt / (doubles.length - 1); // 计算百分位数
    if (pos < 0) {
        double pa = (double) (lt - lt2) / (doubles.length - 1); // 计算百分位数
        result = pa + ((target - a) / (c - a)) * (result - pa); // 插值计算百分位数
    }
    return result; // 返回百分位数
}
    public static void main(String[] args) {
        ArrayList<Double> dataList3 = com.google.common.collect.Lists.newArrayList(new Double("0.200010009"), new Double("0.300010009"), new Double("0.400010009"), new Double("0.500010009"), new Double("0.600010009"));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.200010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.300010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.400010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.500010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.600010009")));
    }

Console:
dataList3_percentile: 0.0
dataList3_percentile: 0.25
dataList3_percentile: 0.5
dataList3_percentile: 0.75
dataList3_percentile: 1.0

         这样其实已经达到我们想要的结果了,但是,如果我们是计算金融相关的数据,或者明确要求使用BigDecimal来处理数据,我们就得稍微改下上面的算法了。


public static BigDecimal getPercentile(List<BigDecimal> dataList, BigDecimal target) {
    if (target == null) { // 如果目标值为空,则返回空
        return null;
    }
    //升序排序
    List<BigDecimal> bigDecimals = dataList.stream().sorted().collect(Collectors.toList()); // 对数据进行升序排序

    if (bigDecimals.size() == 1) { // 如果数据只有一个元素,返回0
        return BigDecimal.ZERO;
    }

    BigDecimal result; // 结果
    int lt = 0; // 小于目标值的计数
    int lt2 = 1; // 重复元素的计数
    int pos = -1; // 目标值的位置
    BigDecimal a = BigDecimal.ZERO; // a的值
    BigDecimal c = BigDecimal.ZERO; // c的值

    for (int i = 0; i < bigDecimals.size(); i++) { // 遍历数据
        if (Objects.equals(bigDecimals.get(i), target)) { // 如果当前元素等于目标值
            pos = i; // 记录目标值的位置
            break;
        } else if (bigDecimals.get(i).compareTo(target) < 0) { // 如果当前元素小于目标值
            lt++; // 小于目标值的计数加一
            if (Objects.equals(bigDecimals.get(i), a)) { // 如果当前元素等于a
                lt2++; // 重复元素计数加一
            } else {
                lt2 = 1; // 重复元素计数重置为1
                a = bigDecimals.get(i); // 更新a的值
            }
        } else { // 如果当前元素大于目标值
            c = bigDecimals.get(i); // 更新c的值
            break;
        }
    }

    result = BigDecimal.valueOf((double) lt / (bigDecimals.size() - 1)); // 计算结果

    if (pos < 0) { // 如果目标值不在数据中
        BigDecimal pa = BigDecimal.valueOf((lt - lt2) / (bigDecimals.size() - 1)); // 计算pa
        result = pa.add(target.subtract(a).multiply(c.subtract(a))).multiply(result.subtract(pa)); // 更新结果
    }

    return result; // 返回结果
}

        为了验证结果是否一致,也方便对比,我把Double的结果和BigDecimal的计算结果,放到了一起输出; 

public static void main(String[] args) {
        ArrayList<Double> dataList3 = com.google.common.collect.Lists.newArrayList(new Double("0.200010009"), new Double("0.300010009"), new Double("0.400010009"), new Double("0.500010009"), new Double("0.600010009"));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.200010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.300010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.400010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.500010009")));
        System.out.println("dataList3_percentile: "+getPercentile(dataList3,new Double("0.600010009")));

        ArrayList<BigDecimal> dataList4 = com.google.common.collect.Lists.newArrayList(new BigDecimal("0.200010009"), new BigDecimal("0.300010009"), new BigDecimal("0.400010009"), new BigDecimal("0.500010009"), new BigDecimal("0.600010009"));
        System.out.println("dataList4_percentile: "+getPercentile(dataList4,new BigDecimal("0.200010009")));
        System.out.println("dataList4_percentile: "+getPercentile(dataList4,new BigDecimal("0.300010009")));
        System.out.println("dataList4_percentile: "+getPercentile(dataList4,new BigDecimal("0.400010009")));
        System.out.println("dataList4_percentile: "+getPercentile(dataList4,new BigDecimal("0.500010009")));
        System.out.println("dataList4_percentile: "+getPercentile(dataList4,new BigDecimal("0.600010009")));
    }

Console:
dataList3_percentile: 0.0
dataList3_percentile: 0.25
dataList3_percentile: 0.5
dataList3_percentile: 0.75
dataList3_percentile: 1.0

dataList4_percentile: 0.0
dataList4_percentile: 0.25
dataList4_percentile: 0.5
dataList4_percentile: 0.75
dataList4_percentile: 1.0

        一些大的工具类库应该是有支持这种计算的,但我还是想自己在本地写一个工具方法,毕竟到时候万一有问题改起来不是灵活一点么

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBMS-2.2 数据库设计(2)——数据库规范化设计理论

本文章的素材与知识来自李国良老师和冠宇老师。 依赖理论 对于关系数据库中的依赖&#xff0c;分为函数依赖、多值依赖和连接依赖。 一.函数依赖 1.函数依赖 &#xff08;1&#xff09;定义&#xff1a; &#xff08;2&#xff09;理解&#xff1a; 通俗地讲&#xff0c;…

【Linux】借命令行参数的引导,探索环境变量的奥秘

目录 1.命令行参数 1.1.概念&#xff1a; 1.2.利用命令行参数打造计算器&#xff1a; 2.环境变量 2.1.环境变量是什么&#xff1f; 2.2.有什么方法可以不用带路径&#xff0c;直接就可以运行自己的程序呢&#xff1f; 法一&#xff1a; 法二&#xff1a; 2.3.通过代码…

PostgreSQL技术内幕9:PostgreSQL事务原理解析

文章目录 0.简介1.PG事务整体介绍1.1 事务类型介绍 1.2 事务模块介绍2. 代码分析 0.简介 有了上一篇数据库事务并发控制协议的介绍&#xff0c;对于数据库事务和并发控制有了基本的认识&#xff0c;本文将介绍PG事务模块&#xff0c;主要介绍PG支持的事务类型&#xff08;普通…

git为不同的项目设置不同的提交作者

方法1&#xff1a;找到项目的.git文件夹打开 打开config在下面添加自己作者信息 [user]name 作者名email 邮箱方法2&#xff1a;直接在.git文件夹设置作者名&#xff08;不使用–global参数&#xff09; git config user.name "xxxxx"如果想要修改之前提交的…

银行结算业务

1.1 银行本票 银行本票是由银行签发的,承诺自己在见票时无条件支付票款给收款人或持票人的业务。银行本票按票面划分为定额本票和不定额本票,按币种划分为人民币银行本票和外币银行本票。人民币银行本票仅在同一交换区域内使用,资金清算利用当地人民银行组织的资金清算形式…

vllm源码解析(五):LLM模型推理

八 模型推理细节探索 8.1 回顾下step的流程 def step(self) -> List[Union[RequestOutput, EmbeddingRequestOutput]]:# 多GPU并行推理时走AsyncLLMEngine分支。如果进入当前LLMEngine,性能会下降&#xff0c;这里会抛出异常。if self.parallel_config.pipeline_parallel_s…

基于机器学习的电商优惠券核销预测

1. 项目简介 随着移动互联网的快速发展&#xff0c;O2O&#xff08;Online to Offline&#xff09;模式已成为电商领域的一大亮点。优惠券作为一种有效的营销工具&#xff0c;被广泛应用于吸引新客户和激活老用户。然而&#xff0c;传统的随机投放方式往往效率低下&#xff0c;…

JavaWeb【day11】--(SpringBootWeb案例)

SpringBootWeb案例 前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能&#xff0c;还有两个需要实现&#xff1a; 新增员工 修改员工 首先我们先完成"新增员工"的功能开发&#xff0c;再完成"修改员工"的功能开发。而在&quo…

万能视频下载器-下载所有网站上的任何视频

万能视频下载器-下载所有网站上的任何视频 在Edge浏览器中发现了一款令人惊叹的视频下载扩展插件&#xff0c;简直就是视觉盛宴的利器&#xff01;只需轻点几下&#xff0c;在拓展商店中轻松查找并安装&#xff0c;你便能随时随地随心所欲地把心仪的视频收入囊中。无论是教学资…

matlab仿真 OFDM系统仿真

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第九章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; clear all N8;%子载波数 f1:N;%各个子载波频率 xrandi([0 3],1,N);%子载波上的数据 x1qammod(x,4);%4-QAM调制 t0:0.001:1-0.…

学习周报-2024.9.3

目录 摘要 Abstract 文献阅读&#xff1a;一种改善河流水质预测的耦合模型以解决非平稳性和数据限制 一、现有问题 二、提出方法 三、相关知识 1、基于小波分析的LSTM&#xff08;WA-LSTM&#xff09; 2、迁移学习TL改进WA-LSTM 四、WA-LSTM-TL模型 五、研究实验 1、…

手写NACOS的服务的注册与发现|心跳机制|轮询调用服务功能

背景 手写NACOS的服务的部分核心功能&#xff0c;提高自身的编码能力 本篇文章设计的是单体NACOS后端服务&#xff0c;提供SDK给多个NACOS客户端使用 本文编写了注册与发现|心跳机制|轮询调用服务功能&#xff0c;可当做入门级阅读 nacos-service 项目结构 代码内容 pom配置…

Detect It Easy

Detect It Easy&#xff08;简称 DIE&#xff09;项目的网址为 https://github.com/horsicq/Detect-It-Easy 下载完安装包后&#xff0c;直接双击die.exe即可进入到操作界面 工具介绍&#xff1a; 它可以用来检测程序架构和文件类型。如图所示。其中&#xff0c;「模式」说明程…

UE5 贝塞尔曲线导弹

首先创建导弹Actor蓝图 代码逻辑&#xff0c;这其中创建的所有变量都不用添加值&#xff0c;这些逻辑要画图来解释&#xff0c;比较麻烦&#xff0c;大家自行理解一下 接下来进入人物蓝图编写代码逻辑&#xff0c;我这里是在两个不同的位置发射两枚导弹 宏中的代码&#xff0c;…

时序预测|基于粒子群优化支持向量机的时间序列预测Matlab程序PSO-SVM 单变量和多变量 含基础模型

时序预测|基于粒子群优化支持向量机的时间序列预测Matlab程序PSO-SVM 单变量和多变量 含基础模型 文章目录 一、基本原理1. 问题定义2. 数据准备3. SVM 模型构建4. 粒子群优化&#xff08;PSO&#xff09;5. 优化与模型训练6. 模型评估与预测7. 流程总结8. MATLAB 实现概述 二、…

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点&#xff1a; 鼠标左键双击&#xff0c;设定红色的起点。左键双击设定起点&#xff0c;用红色标记。 设定终点&#xff1a; 鼠标右键双击&#xf…

轻松上手,高效产出:音频剪辑工具年度精选

不知道你有没有拍vlog记录生活的习惯&#xff0c;有时候视频里穿插进自己的声音能让视频更加丰富贴上自己的标签。这次我们一起探讨当下有哪些好用的在线音频剪辑工具。 1.FOXIT音频剪辑 链接直达>>https://www.foxitsoftware.cn/audio-clip/ 这个工具是一款专业的音…

GNU的伪操作 (25)

这里主要是 对 GNU的 各个伪操作进行 详细的解释。 先来看着几个 伪操作。 .byte, .short, .long, .quad , .float , 这个是关于 字节的。 .string .ascii 是关于字符串的。 这个字符串编译器是可以自动在末尾补0 的。 举例&#xff1a; val: .word 0x11223344 mov r…

计算机组成原理(SRAM电路图示)

1.该电路由6个MOS管&#xff08;T1-T6&#xff09;组成 2.T1-T4是一个由MOS管组成的触发器基本电路&#xff1b; T5&#xff0c;T6像开关&#xff0c;受行地址选择信号控制&#xff1b; T7,T8受列地址选择控制&#xff0c;分别与位线A&#xff0c;和相连 3.假设触发器…

FinOps原则:云计算成本管理的关键

导语&#xff1a; FinOps 原则为我们提供了北极星&#xff08;North Star&#xff09;&#xff0c;在我们实践云财务管理时指导我们的活动。这些原则由 FinOps 基金会成员制定&#xff0c;并通过经验磨练出来。 北极星&#xff08;North Star&#xff09;的含义&#xff1a; …