Java数据分析类,使用基本的数据分组透视方法,处理二维数组结构的数据

一、问题:怎么用Java语言从一张二维数据表中做出数据透视和筛选的操作呢

在Excel中拿到一张二维的数据表,要提取其中的有用的信息,常用的方法就是根据条件筛选和数据透视获取聚合后的信息,但现在在Java环境中分析类似的结构,思路其实是一样的。如果学过sql语言,会发现,和sql语言的分组聚合也是一个思路。

怎样从一张二维数据表中做出类似的分析效果呢?

二、思路:

1.分组和聚合,筛选行和列,这些操作都是在原数组上操作的,一套简单的完整的流程是:选取想要的列,对目标列进行分组,聚合结果列。这是一个传递的关系,每一步操作都是在上一步操作结果接着做的。因此,在写分析类时,要每个对应的方法最终返回的值是执行完所有操作后的对象本身,这是第一个注意点。

2.操作数组时,如果在原数组中直接操作的话,会涉及到很多删掉无用列无用行的操作,简洁的方法是新建一个数组,把操作得到的数据填到新数组里,再把新数组赋值给原数组。这是第二个注意点。

3.拆分步骤,数据透视,第一步是先选取对应的列,其他列不要;第二部,对目标列(聚合列)进行聚合,把结果列根据目标列的值进行列转行的操作;第三部,对结果列的数据进行计数、求和、平均值等操作;第四步,调用方法读取结果

三、新建Array类

1.定义一个二维数组
2.把大于等于小于介于等定义为常量,这样在输入过滤参数时更方便
3.构造方法

    public static final int A=0;//<
    public static final int B=1;//<=
    public static final int C=2;//=
    public static final int D=3;//>=
    public static final int E=4;//>
    public static final int F=5;//介于
    private String[][] data;

    public Array(String[][] data) {
        this.data = data;
    }

四、优化:

为避免输入参数不规范导致异常报错,最好写几个方法提前验证下参数的有效性:
1.验证选取的列的范围是否超出
2.使用常量时,验证是否在常量范围内
3.验证条件范围参数是否符合规则(大于等于小于介于等的规则)
4.列过滤去重

    private boolean isIxInValid(int ix){
        final int COL=data[0].length;
        return ix<0||ix>=COL;
    }
    private boolean isSignInValid(int sign){
        return sign<0||sign>5;
    }
    private <T> boolean isArgInValid(T[] t){
        return null==t||t.length==0;
    }

五、具体方法类

1.列过滤

单独写个方法,保留不重复列,重复值过滤掉

获取不重复的列的编号,存在数组里,这里使用了动态参数

新建二维数组操作

把原数组里的列赋值给新数组

把新数组传给原数组(避免在原数组上操作,麻烦)

    //列过滤
    public Array map(int...ixs){
        ixs = distinct(ixs);
        String[][] copy = new String[data.length][ixs.length];
        for (int i = 0; i < copy.length; i++) {
            for (int j = 0; j < ixs.length; j++) {
                copy[i][j] = data[i][ixs[j]];
            }
        }
        data = copy;
        return this;
    }

2.行过滤-数值

思路:过滤行,一般是提取满足某项条件的值或文本
传入3个参数:哪一列,满足哪些条件
把表里对应列的数字强转为double
遍历每一行的该列,如果满足条件,传到新数组里

    //行过滤
    public Array filter(int ix,int sign,Double...t) throws Exception {
        if (isIxInValid(ix)||isSignInValid(sign)||isArgInValid(t)){
            throw new Exception("输入的参数异常");
        }
        int size = 0;
        for (int i = 0; i < data.length; i++) {
            double d = Double.parseDouble(data[i][ix]);
            double a = t[0];
            if (sign==A&&d<a||sign==B&&d<=a||sign==C&&d==a||sign==D&&d>=a||sign==E&&d>a||sign==F&&d>a&&d<t[1]){
                data[size++] = data[i];
            }
        }
        String[][] copy = new String[size][];
        System.arraycopy(data,0,copy,0,size);
        data = copy;
        return this;
    }

3.行过滤-字符串

思路:字符串一般是判断是否等于
传入两个参数:哪一列,要输入的动态参数数组
遍历二维数组,如果满足条件,提取出

    //字符串列过滤
    public  Array filter(int ix,String...t) throws  Exception{//这一行的ix列下面的值要符合sign特征的t的值
        if (isIxInValid(ix)||isArgInvalid(t)){
            throw new Exception("ix或t异常");
        }
        //验证数据是不是字符串或者数字
        int size = 0;
        for (int i = 0; i < data.length; i++) {
            boolean exist = false;//是否重复、是否存在,就用布尔值
            for (String s : t) {
                if (data[i][ix].equals(s)){
                    data[size++] = data[i];
                    break;
                }
            }
        }
        String[][] copy = new String[size][];
        System.arraycopy(data,0,copy,0,size);
        data = copy;
        return this;
    }

4.分组(数据透视,但不计算)

思路:过滤出要透视的列,对分组列去重;然后再生成一个数组,所有项放进去(可能和分组列是一列)
遍历数组先去重,保留不重复项,生成一个不重复项的数组,作为输出结果的第一个值
再次遍历数组,如果等于第一个distinct数组,则在后面添加一个1(如果是两列,则添加第二列的值),生成一个新数组;多个新数组放到二维数组中,生成一个新的二维数组

   //分组,数据透视
    public Array groupBy(int ix) throws Exception {
        if (data[0].length==0||data[0].length>2){
            throw new Exception("分组列为1-2");
        }
        String[] distinct = new String[data.length];
        int size = 0;
        for (int i = 0; i < data.length; i++) {
            if (size==0){
                distinct[size++]=data[i][ix];
            }else {
                boolean notrepeat = true;
                for (int j = 0; j <size ; j++) {
                    if (data[i][ix].equals(distinct[j])){
                        notrepeat = false;
                        break;
                    }
                }
                if (notrepeat){
                    distinct[size++] = data[i][ix];
                }
            }
        }
        String[][] copy = new String[size][];
        for (int i = 0,_ix = ix==0 ? 1 : 0 , col = data[0].length; i <size ; i++) {
            String[] row = new String[data.length+1];
            int _size = 0;
            row[_size++] = distinct[i];
            for (int j = 0; j <data.length ; j++) {
                if (data[j][ix].equals(distinct[i])){
                    row[_size++] = col==1?"1":data[j][_ix];
                }
            }
            String[] _row = new String[_size];
            System.arraycopy(row,0,_row,0,_size);
            copy[i] = _row;
        }
        data=copy;
        return this;
    }

5.分组数据透视后的计数

思路:新建一个两列的数组,第一列就是分组后的第一列(分组名),第二列是对应行的长度减去第一个

    //计数
    public Array count(){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            copy[i][1] = data[i].length-1+"";
        }
        data = copy;
        return this;
    }

6.分组后找到最大最小值

思路:新建一个两列的数组,遍历分组后的行,把第一个数字拿出来和后面的依次比较,如果更大,赋给他。最小值同理。然后把得到的值放在新数组的第二列

    //最大最小值,布尔值判断
    private Array limit(boolean max){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            double limit = Double.parseDouble(data[i][1]),val;
            for (int j = 2; j < data.length; j++) {
                val = Double.parseDouble(data[i][j]);
                if (max?val>limit:val<limit){
                    limit = val;
                }
            }
            copy[i][1] = limit+"";
        }


        data = copy;
        return this;
    }
    //最大值
    public Array max(){
        return limit(true);
    }
    //最小值
    public Array min(){
        return limit(false);
    }

7.分组后求和求平均数

思路:新建一个两列的数组,遍历分组后的行,从第二个开始累加,得到的值赋给新数组的第二列;平均数则除以数组的长度减一

    //求和求平均数
    private Array cal(boolean avg){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            double sum = 0;
            for (int j = 1; j <data[i].length ; j++) {
                sum += Double.parseDouble(data[i][j]);
            }
            copy[i][1] = (avg?Math.round(sum/data[i].length-1):sum)+"";
        }
        data = copy;
        return this;
    }
    //求平均数
    public Array avg(){
        return cal(true);
    }
    //求和
    public Array sum(){
        return cal(false);
    }

六、执行

本地的数据调整为逗号为分隔符的txt文档,放在工程项目下,表结构为:
“姓名,性别,年龄,入学日期,毕业日期,就业薪资,籍贯,就业城市”
读取本地文档,需要用到字符输入流BufferReader,使用完关闭流即可,代码如下

public class Analysisy {
    public static void main(String[] args) throws Exception {
        String[][] data = new String[25][];
        BufferedReader br =new BufferedReader(new FileReader("D:\\JavaProjects\\ClassStudy\\Javahigh\\JavahighTest\\analyasis\\myfile\\kb10.txt"));
        String line = null;
        int size =0;
        while (null!=(line=br.readLine())){
            data[size++] = line.split(",");
        }
        br.close();

        Array array = new Array(data);
        //输出第一列和第三列
//        array.map(0,2).println();
        //输出年龄大于28岁的人
//        array.map(0,2).filter(1,4,28.0).println();
        //输出籍贯为安徽的人的姓名性别
//        array.map(0,1,6).filter(2,"安徽").println();
        //求:不同性别的人数
//        array.map(1).groupBy(0).count().println();
        //求:不同籍贯的人数
//        array.map(6).groupBy(0).count().println();
        //求:不同就业地的人数
//        array.map(7).groupBy(0).count().println();
        //求:不同性别的平均薪资
//        array.map(1,5).groupBy(0).avg().println();
        //求不同籍贯的人的平均薪资
//        array.map(6,5).groupBy(0).avg().println();
        //求:不同就业地的人的平均薪资
//        array.map(7,5).groupBy(0).avg().println();

    }
}

七、方法类完整代码如下:

public class Array {
    public static final int A=0;//<
    public static final int B=1;//<=
    public static final int C=2;//=
    public static final int D=3;//>=
    public static final int E=4;//>
    public static final int F=5;//介于
    private String[][] data;

    public Array(String[][] data) {
        this.data = data;
    }

    //验证是否重复
    private int[] distinct(int[] ixs){
        int[] copy = new int[ixs.length];
        int size = 0;
        for (int ix : ixs) {
            if (size==0){
                copy[size++]=ix;
            }else {
                boolean isrepeat = false;
                for (int i = 0; i <size ; i++) {
                    if (ix==copy[i]){
                        isrepeat = true;
                        break;
                    }
                }
                if (isrepeat){
                    break;
                }
                copy[size++] = ix;
            }
        }
        int[] copy2 = new int[size];
        System.arraycopy(copy,0,copy2,0,size);
        copy = null;
        return copy2;
    }
    //判断列 参数 范围是否无效
    private boolean isIxInValid(int ix){
        final int COL=data[0].length;
        return ix<0||ix>=COL;
    }
    private boolean isSignInValid(int sign){
        return sign<0||sign>5;
    }
    private <T> boolean isArgInValid(T[] t){
        return null==t||t.length==0;
    }

    //列过滤
    public Array map(int...ixs){
        ixs = distinct(ixs);
        String[][] copy = new String[data.length][ixs.length];
        for (int i = 0; i < copy.length; i++) {
            for (int j = 0; j < ixs.length; j++) {
                copy[i][j] = data[i][ixs[j]];
            }
        }
        data = copy;
        return this;
    }
    //行过滤
    public Array filter(int ix,int sign,Double...t) throws Exception {
        if (isIxInValid(ix)||isSignInValid(sign)||isArgInValid(t)){
            throw new Exception("输入的参数异常");
        }
        int size = 0;
        for (int i = 0; i < data.length; i++) {
            double d = Double.parseDouble(data[i][ix]);
            double a = t[0];
            if (sign==A&&d<a||sign==B&&d<=a||sign==C&&d==a||sign==D&&d>=a||sign==E&&d>a||sign==F&&d>a&&d<t[1]){
                data[size++] = data[i];
            }
        }
        String[][] copy = new String[size][];
        System.arraycopy(data,0,copy,0,size);
        data = copy;
        return this;
    }
    //分组,数据透视
    public Array groupBy(int ix) throws Exception {
        if (data[0].length==0||data[0].length>2){
            throw new Exception("分组列为1-2");
        }
        String[] distinct = new String[data.length];
        int size = 0;
        for (int i = 0; i < data.length; i++) {
            if (size==0){
                distinct[size++]=data[i][ix];
            }else {
                boolean notrepeat = true;
                for (int j = 0; j <size ; j++) {
                    if (data[i][ix].equals(distinct[j])){
                        notrepeat = false;
                        break;
                    }
                }
                if (notrepeat){
                    distinct[size++] = data[i][ix];
                }
            }
        }
        String[][] copy = new String[size][];
        for (int i = 0,_ix = ix==0 ? 1 : 0 , col = data[0].length; i <size ; i++) {
            String[] row = new String[data.length+1];
            int _size = 0;
            row[_size++] = distinct[i];
            for (int j = 0; j <data.length ; j++) {
                if (data[j][ix].equals(distinct[i])){
                    row[_size++] = col==1?"1":data[j][_ix];
                }
            }
            String[] _row = new String[_size];
            System.arraycopy(row,0,_row,0,_size);
            copy[i] = _row;
        }
        data=copy;
        return this;
    }
    //计数
    public Array count(){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            copy[i][1] = data[i].length-1+"";
        }
        data = copy;
        return this;
    }
    //最大最小值,布尔值判断
    private Array limit(boolean max){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            double limit = Double.parseDouble(data[i][1]),val;
            for (int j = 2; j < data.length; j++) {
                val = Double.parseDouble(data[i][j]);
                if (max?val>limit:val<limit){
                    limit = val;
                }
            }
            copy[i][1] = limit+"";
        }
        data = copy;
        return this;
    }
    //最大值
    public Array max(){
        return limit(true);
    }
    //最小值
    public Array min(){
        return limit(false);
    }
    //求和求平均数
    private Array cal(boolean avg){
        String[][] copy = new String[data.length][2];
        for (int i = 0; i < copy.length; i++) {
            copy[i][0] = data[i][0];
            double sum = 0;
            for (int j = 1; j <data[i].length ; j++) {
                sum += Double.parseDouble(data[i][j]);
            }
            copy[i][1] = (avg?Math.round(sum/data[i].length-1):sum)+"";
        }
        data = copy;
        return this;
    }
    //求平均数
    public Array avg(){
        return cal(true);
    }
    //求和
    public Array sum(){
        return cal(false);
    }
	//输出
    public void println(){
        for (String[] datum : data) {
            for (String s : datum) {
                System.out.print(s+"\t");
            }
            System.out.println();
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值