java 列表通过boxplot箱线图数据清洗 过滤最大值 最小值

图片.png

如图 某天天气的变化曲线 某个时间 突然值很高影响统计 这样的异常数据如何通过Java过滤呢

package org.jeecg.modules.data.analysis;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.stat.descriptive.rank.Median;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 列表通过boxplot箱线图数据清洗
 * https://blog.csdn.net/zhongkaigood/article/details/113887879
 *
 * @author: Haiming Yu
 * @createDate:2022/12/22
 * @description:
 */
@Slf4j
public class BoxPlotFilter {

    /**
     *箱形图(Box-plot)又称为盒须图、盒式图或箱线图 样本大于等于4个
     * @param data
     * @param multiplierMax 最大值过滤区间 可以单独设置更大 方便过滤最高值
     * @param multiplierMin 最小值过滤范围
     * @return
     */
    public static List<Double> boxPlotFilterListDouble(List<Double> data, Double multiplierMax, Double multiplierMin) {
        List<Double> returnList = new ArrayList<>(data);
        try {
            //排序
            log.debug("排序前data:" + data);
            Collections.sort(data);
            log.debug("排序后data:" + data);

            double[] dataA = new double[data.size()];
            for (int i = 0; i < data.size(); i++) {
                dataA[i] = data.get(i);
            }

            //下四分位数
            double q1 = dataA[dataA.length / 4 - 1] / 4 + dataA[dataA.length / 4] * 3 / 4;
            //中位数
            Median median = new Median();
            double q2 = median.evaluate(dataA);
            //上四分位数
            double q3 = dataA[(dataA.length - dataA.length / 4) - 1] * 3 / 4 + dataA[(dataA.length - dataA.length / 4)] / 4;
            //计算四分位距IQR
            double iqr = q3 - q1;
            // 默认乘 1.5 剔除过多正常值后 改成1.7
            if (ObjectUtil.isEmpty(multiplierMax)) {
                multiplierMax = 1.7;
            }
            // 默认乘 1.5
            if (ObjectUtil.isEmpty(multiplierMin)) {
                multiplierMin = 1.5;
            }
            double max = q3 + multiplierMax * iqr;
            double min = q1 - multiplierMin * iqr;
            log.debug("\n下四分位:" + q1 + " 中位数:" + q2 + " 上四分位:" + q3 + " \n最大值:" + max + " 最小值:" + min);
            List<Double> errorData = new ArrayList<>();
            for (Double vo : data) {
                if (vo.compareTo(min) < 0 || vo.compareTo(max) > 0) {
                    double zero = 0.00;
                    //忽略零比较多的情况
                    boolean compTo1 = min.compareTo(max) == 0 && min.compareTo(zero) == 0;
                    boolean compTo2 = q1.compareTo(q2) == 0 && q2.compareTo(zero) == 0;
                    if (!(compTo1 || compTo2)) {
                        returnList.set(returnList.indexOf(vo), null);
                        errorData.add(vo);
                    }

                }

            }
            if (errorData.size() > 0) {
                log.info("使用boxplot准则进行过滤,该数组中的" + JSON.toJSONString(errorData) + "属于异常值! \n"+ data );
            }
        } catch (Exception e) {
            log.error("error data:" + data);
            e.printStackTrace();
        }
        return returnList;
    }

    public static void main(String[] args) {
        //原始数组
//        double[] data =new double[] {1,2,8,10,8,5,2,4,6,11,15,1,2,8,10,8,5,2,4,6,11,15,1000,1000};
//        double[] data =new double[] {1,2,8,10,8,5,2,4,6,11,15,1,2,8,10,8,5,2,4,6,11,15,10,10};
        double[] data = new double[]{0, 0, 0, 10, 0};
        List<Double> dataList = new ArrayList<>();
        for (double vo : data) {
            dataList.add(vo);
        }
        List<Double> outliersList = boxPlotFilterListDouble(dataList, null, null);
        log.debug(JSON.toJSONString(outliersList));
    }
}

Connected to the target VM, address: '127.0.0.1:58441', transport: 'socket'
11:14:03.359 [main] DEBUG org.jeecg.modules.data.analysis.BoxPlotFilter - 排序前data:[]
11:14:03.418 [main] DEBUG org.jeecg.modules.data.analysis.BoxPlotFilter - 
下四分位:4.5 中位数:7.7 上四分位:11.2 
最大值:38.0 最小值:-122.79999999999998
11:14:03.556 [main] INFO org.jeecg.modules.data.analysis.BoxPlotFilter - 使用boxplot准则进行过滤,该数组中的[38.8]属于异常值! 

11:14:03.872 [main] INFO org.jeecg.modules.data.analysis.WeatherBaseAnalysis - weather:{"clean":"F","cloud":"2","dataTime":"2022-11-17 18:49:02","dataType":"LGH","elavation":0.0,"gustSpeed":0.0,"huminity":15.0,"light":105.0,"pressure":90442.54,"rainFall":0.0,"remark":"{\"clean\":\"F\",\"cloud\":\"2\",\"dataTime\":\"2022-11-17 18:49:02\",\"dataType\":\"LGH\",\"elavation\":0.0,\"gustSpeed\":0.0,\"huminity\":15.0,\"light\":105.0,\"pressure\":90442.54,\"rainFall\":0.0,\"syncStatus\":1,\"temperature\":38.8,\"uv\":0.0,\"uvi\":0,\"windSpeed\":0.42}","syncStatus":3,"uv":0.0,"uvi":0,"windSpeed":0.42}
Disconnected from the target VM, address: '127.0.0.1:58441', transport: 'socket'

用法 成功过滤了38度以上数据 可以自行设置 multiplierMax 控制最大值范围 multiplierMin 最小值范围

            List<Double> listTemperature = dataWeatherList.stream().map(DataWeather::getTemperature).collect(Collectors.toList());
            List<Double> reTemperature = null;
            if (temperature) {
                reTemperature = BoxPlotFilter.boxPlotFilterListDouble(listTemperature, 4.0,19.0);
            }

maven依赖

        <!-- math3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.6.1</version>
        </dependency>

参考
https://blog.csdn.net/zhongkaigood/article/details/113887879
并优化的方法可以控制最大值 最小值范围

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容