LoginSignup
0
0

More than 3 years have passed since last update.

Java Artery-数値配列の処理-集約(最大値、最小値、合計、平均、標準偏差)、変換(移動平均、変化率など)、ヒストグラムなど

Posted at

目次 ⇒ Javaアルゴリズムライブラリ-Artery-サンプル

概要

ArMathには計算処理のメソッドが集められている。その中から配列に関係するものを紹介する。本稿の内容は次の通り。

・関連enum、インターフェースの説明
・集約処理-最大値、最小値、合計、平均、標準偏差
・変換処理-指定数値との加減乗除
・変換処理-指定ロジックによる変換
・変換処理-移動平均、変化率など
・二つの配列の合成-二つの配列の値の加減乗除
・二つの配列の合成-二つの配列の値の計算(ロジックは外部指定)
・ヒストグラムの作成-境界値は直接またはArValidatorで指定する
・おまけ(逆順とランダマイズ、Map・二次元Map上の数値の加減乗除)

ラッパー型配列とプリミティブ型の配列を変換することが必要だったりするが、(紹介はしないが)このようなメソッドも含まれている。一次元配列と二次元配列の変換もできる。

なおArListには、ここに示したようなメソッドが用意されているが、ArListではオブジェクトの中の特定フィールドを指定して、このような計算を行うことができる。

一次元配列と二次元配列について同様の機能があるが、サンプルは一次元配列で示している。

関連enum、インターフェースの説明

ArReduceTo

ArReduceTo.java
/** 数値列の集約方法の定義 */
public enum ArReduceTo {
  /** 最大値 */
  MAX,
  /** 最小値 */
  MIN,
  /** 合計値 */
  SUM,
  /** 平均値 */
  AVG,
  /** 標準偏差 */
  STD_DEV,
  /** 予備-0 */
  YOBI_0,
  /** 予備-1 */
  YOBI_1,
}

ArCalc

ArCalc.java
/** 演算の種類の定義 */
public enum ArCalc {
  /** 加算 */
  ADD,
  /** 減算 */
  SUB,
  /** 乗算 */
  MUL,
  /** 除算 */
  DIV,
  /** 代入 */
  SET,
  /** 逆減算 */
  ISUB,
  /** 逆除算 */
  IDIV,
  /** 交換 */
  SWAP,
}

ArConvertTo

ArConvertTo.java
/** 数値列の変換方法の定義 */
public enum ArConvertTo {
  /** 移動平均 */
  MOVING_AVG,
  /** 変動率 */
  CHANGE_RATE,
  /** 変動幅 */
  CHANGE_WIDTH,
  /** パーセント */
  PERCENT,
  /** 累積値 */
  ACCUMULATION,
  /** ヒストグラム */
  HISTOGRAM,
  /** 期間集計(四半期⇒年間などに使用) */
  PERIOD_SUM,
  /** 予備-0 */
  YOBI_0,
  /** 予備-1 */
  YOBI_1,
}

ArTableRC

ArTableRC.java
/** ROW,COLUMNの区別の定義 */
public enum ArTableRC {
  /** ROW */
  ROW,
  /** COLUMN */
  COLUMN;
}

ArCreator

ArCreator.java
/** 指定されたオブジェクトから別のオブジェクトを生成するインタフェース. */
public interface ArCreator<T0,T1> {
  /** argからT1クラスのオブジェクトを生成する.実装がどのような処理をしているか分からないのでExceptionをthrowしておく. */
  public T1 convert(T0 arg) throws Exception;
}

ArCreator2d

ArCreator2d.java
/** 二つの値を元に別の値を生成するインターフェース */
public interface ArCreator2D<$arg0,$arg1,$value> {
  /** arg0とarg1から$valueクラスのオブジェクトを生成する.実装がどのような処理をしているか分からないのでExceptionをthrowしておく. */
  public $value convert($arg0 arg0,$arg1 arg1) throws Exception;
}

ArValidator

ArValidator.java
/** 指定されたオブジェクトの正当性を判定するインターフェース. */
public interface ArValidator<T> {
  /**
   *  指定されたオブジェクトを判定する.
   *  @param value 判定対象オブジェクト.
   */
  public boolean check(T value);
}

集約処理-最大値、最小値、合計、平均、標準偏差

メソッド定義

二次元配列ではROWまたはCOLUMNを指定して集約することができる。

ArMath.java
  /** 一次元配列の集約(最大最小など) */
  public static double calc(double[] doubleArray,ArReduceTo kind) 
  /** 二次元配列の集約(最大最小など) */
  public static double[] calc(double[][] doubleArray,ArReduceTo kind,ArTableRC rowCol) 

サンプルプログラム

Q09_00.java
package jp.avaj.lib.algo;

import jp.avaj.lib.def.ArReduceTo;
import jp.avaj.lib.test.L;

/**
数値配列の集約処理-最大値、最小値、合計、平均、標準偏差
 */
public class Q09_00 {
  public static void main(String[] args) {
    double[] array = new double[]{1,2,3,4,5,6,7,8,9,10};
    // 念のためランダマイズ
    array = ArMath.randomize(array);
    L.p("array="+ArObj.toString(array));

    L.p("最大値="+ArMath.calc(array,ArReduceTo.MAX));
    L.p("最小値="+ArMath.calc(array,ArReduceTo.MIN));
    L.p("合計="+ArMath.calc(array,ArReduceTo.SUM));
    L.p("平均="+ArMath.calc(array,ArReduceTo.AVG));
    L.p("標準偏差="+ArMath.calc(array,ArReduceTo.STD_DEV));

  }
}

サンプル実行結果

result.txt
array=[1, 5, 10, 7, 2, 4, 6, 8, 3, 9]
最大値=10.0
最小値=1.0
合計=55.0
平均=5.5
標準偏差=2.8722813232690143

変換処理-指定数値との加減乗除

メソッド定義

ArMath.java
  /** 一次元配列の変換 */
  public static double[] convert(double[] doubleArray,ArCalc kind,double value) 
  /** 二次元配列の変換 */
  public static double[][] convert(double[][] doubleArray,ArCalc kind,double value) 

サンプルプログラム

Q09_01.java
package jp.avaj.lib.algo;

import jp.avaj.lib.def.ArCalc;
import jp.avaj.lib.test.L;

/**
数値配列の変換処理-指定数値との加減乗除.

本サンプルでは加算処理のみを行う.
 */
public class Q09_01 {
  public static void main(String[] args) {

    double[] array = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(10D,20D));
    L.p("array="+ArObj.toString(array));

    array = ArMath.convert(array,ArCalc.ADD,3D);
    L.p("array="+ArObj.toString(array));

  }
}

サンプル実行結果

result.txt
array=[13.348039840199508, 14.896592857645892, 17.333929293408378, 13.588269785858508, 16.036528261560278, 13.983347639430207, 12.002249957043128, 19.93148386395162, 12.337109159822935, 14.39586704149562]
array=[16.348039840199508, 17.896592857645892, 20.333929293408378, 16.588269785858508, 19.036528261560278, 16.983347639430207, 15.002249957043128, 22.93148386395162, 15.337109159822935, 17.39586704149562]

変換処理-指定ロジックによる変換

ロジックはArCreatorで指定するものと、ArCreator2Dで指定するものがある。前者は当該の数値を使って変換し、後者は当該の数値と配列のインデックスを使って変換する。

メソッド定義

ArMath.java
  /** 一次元配列の変換 */
  public static double[] convert(double[] doubleArray,ArCreator<Double,Double> creator) throws Exception 
  /** 一次元配列の変換.creatorのInteger引数には配列のインデックスが指定される. */
  public static double[] convert(double[] doubleArray,ArCreator2D<Double,Integer,Double> creator) throws Exception 
  /** 二次元配列の変換 */
  public static double[][] convert(double[][] doubleArray,ArCreator<Double,Double> creator) throws Exception 
  /** 二次元配列の変換 */
  public static double[][] convert(double[][] doubleArray,ArCreator2D<Double,Integer,Double> creator) throws Exception 

サンプルプログラム

Q09_02.java
package jp.avaj.lib.algo;

import jp.avaj.lib.test.L;

/**
数値配列の変換処理-指定ロジックによる変換
 */
public class Q09_02 {
  public static void main(String[] args) throws Exception {
    // テストデータ
    double[] array = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array="+ArObj.toString(array));
    double[] newArray;
    // ArCreator<Double,Double>を利用した変換
    {
      // ArCreatorの定義
      ArCreator<Double,Double> creator = new ArCreator<Double,Double>() {
        @Override
        public Double convert(Double obj) throws Exception {
          return obj+2D;
        }
      };
      newArray = ArMath.convert(array,creator);
      L.p("newArray="+ArObj.toString(newArray));
    }

    // ArCreator2D<Double,Integer,Double>を利用した変換
    {
      // ArCreator2Dの定義、Integerのパラメータには配列のインデックスが渡される
      ArCreator2D<Double,Integer,Double> creator = new ArCreator2D<Double,Integer,Double>() {
        @Override
        public Double convert(Double arg0,Integer arg1) throws Exception {
          return ((arg1%2)==0) ? arg0 : -arg0;
        }
      };
      newArray = ArMath.convert(array,creator);
      L.p("newArray="+ArObj.toString(newArray));
    }
  }
}

サンプル実行結果

result.txt
array=[5.852138560225006, 9.75606457517308, 5.4476666795846, 8.750541556076218, 9.276962894000498, 5.419291907356978, 8.018936197987681, 9.885258841879601, 8.406859998877785, 5.572972359413988]
newArray=[7.852138560225006, 11.75606457517308, 7.4476666795846, 10.750541556076218, 11.276962894000498, 7.419291907356978, 10.018936197987681, 11.885258841879601, 10.406859998877785, 7.572972359413988]
newArray=[5.852138560225006, -9.75606457517308, 5.4476666795846, -8.750541556076218, 9.276962894000498, -5.419291907356978, 8.018936197987681, -9.885258841879601, 8.406859998877785, -5.572972359413988]

変換処理-移動平均、変化率など

メソッド定義

ArMath.java
  /**
   *  ArConvertToを利用した数値の変換.ただしkindのうちヒストグラムは指定不可.
   *  paramは次の場合のみ意味がある.MOVING_AVG,CHANGE_RATE,CHANGE_WIDTH,PERIOD_SUM.
   *  またCHANGE_RATE,CHANGE_WIDTHでparamに0を指定した場合は初期値に対する変動となる.
   */
  public static double[] convert(double[] array,ArConvertTo kind,int param) throws Exception 
  /**
   *  ArConvertToを利用した数値の変換.ただしkindのうちヒストグラムは指定不可.
   *  paramは次の場合のみ意味がある.MOVING_AVG,CHANGE_RATE,CHANGE_WIDTH,PERIOD_SUM.
   *  またCHANGE_RATE,CHANGE_WIDTHでparamに0を指定した場合は初期値に対する変動となる.
   */
  public static double[] convert(double[][] array,ArConvertTo kind,int param,ArTableRC rowCol,int index) throws Exception 

サンプルプログラム

Q09_03.java
package jp.avaj.lib.algo;

import jp.avaj.lib.def.ArConvertTo;
import jp.avaj.lib.test.L;

/**
数値配列の変換処理-移動平均、変化率など

本サンプルでは移動平均と合計値に対するパーセントを求める.
 */
public class Q09_03 {
  public static void main(String[] args) throws Exception {
    // テストデータ
    double[] array = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array="+ArObj.toString(array));
    double[] newArray;

    // 四個の移動平均
    {
      newArray = ArMath.convert(array,ArConvertTo.MOVING_AVG,4);
      L.p("newArray="+ArObj.toString(newArray));
    }

    // 合計値に対するパーセント
    {
      newArray = ArMath.convert(array,ArConvertTo.PERCENT,0); // 最後の引数は無意味
      L.p("newArray="+ArObj.toString(newArray));
    }
  }
}

サンプル実行結果

result.txt
array=[5.985380144134761, 7.575355662184555, 7.8352635477308015, 5.149850038794702, 9.389332364999511, 5.035633917916596, 7.484603584807374, 6.847220004442178, 8.15795801332105, 9.350653554342703]
newArray=[5.985380144134761, 6.780367903159658, 7.131999784683373, 6.6364623482112055, 7.487450403427393, 6.8525199673604025, 6.764854976629546, 7.189197468041415, 6.881353880121799, 7.960108789228326]
newArray=[8.220405604471235, 10.40410043166721, 10.761061591616974, 7.072876759979646, 12.895441648951902, 6.916010726815919, 10.27946024716367, 9.404068637932905, 11.204254727155634, 12.842319624244903]

二つの配列の合成-二つの配列の値の加減乗除

メソッド定義

ArMath.java
  /** 一次元配列同士の演算 */
  public static double[] calc(double[] doubleArray0,double[] doubleArray1,ArCalc kind) 
  /** 二次元配列の行・列と一次元配列の加減乗除.元の二次元配列が書き換えられる. */
  public static void calc(double[][] doubleArray0,double[] doubleArray1,int index,ArCalc kind,ArTableRC rowCol) 

サンプルプログラム

Q09_04.java
package jp.avaj.lib.algo;

import jp.avaj.lib.def.ArCalc;
import jp.avaj.lib.test.L;

/**
数値配列の変換処理-二つの配列の合成-二つの配列の値の加減乗除

本サンプルでは加算と乗算を行う
 */
public class Q09_04 {
  public static void main(String[] args) {
    // テストデータ
    double[] array0 = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array0="+ArObj.toString(array0));
    double[] array1 = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array1="+ArObj.toString(array1));
    double[] newArray;

    // 加算処理
    {
      newArray = ArMath.calc(array0,array1,ArCalc.ADD);
      L.p("newArray="+ArObj.toString(newArray));
    }

    // 乗算処理
    {
      newArray = ArMath.calc(array0,array1,ArCalc.MUL);
      L.p("newArray="+ArObj.toString(newArray));
    }

  }
}

サンプル実行結果

result.txt
array0=[8.786527250579027, 8.475128917235198, 6.570863590659945, 9.095151921000824, 5.810489054840153, 8.574370760082004, 9.513894390034038, 7.928853544903687, 8.364814697141732, 7.496390955771403]
array1=[9.825877092616365, 6.40624152776633, 8.091125641955209, 7.508234716238508, 9.254752954661516, 6.5836384715162835, 6.050855953787648, 5.823069794583683, 7.791340507005785, 7.748951867821722]
newArray=[18.612404343195394, 14.881370445001528, 14.661989232615154, 16.603386637239332, 15.06524200950167, 15.158009231598289, 15.564750343821686, 13.75192333948737, 16.156155204147517, 15.245342823593125]
newArray=[86.3353368351139, 54.29372282276542, 53.16568288817856, 68.28853540272175, 53.77464074831031, 56.450557205120205, 57.56720451364436, 46.170267583006414, 65.1731195834377, 58.08917269864668]

二つの配列の合成-二つの配列の値の計算(ロジックは外部指定)

メソッド定義

変換ロジックArCreator2Dの引数には、それぞれの配列の値が指定される。

ArMath.java
  /** 一次元配列同士の演算 */
  public static double[] calc(double[] doubleArray0,double[] doubleArray1,ArCreator2D<Double,Double,Double> creator) throws Exception 
  /** 二次元配列の行・列と一次元配列の演算.元の二次元配列が書き換えられる.*/
  public static void calc(double[][] doubleArray0,double[] doubleArray1,int index,ArCreator2D<Double,Double,Double> creator,ArTableRC rowCol) throws Exception 

サンプルプログラム

Q09_05.java
package jp.avaj.lib.algo;

import jp.avaj.lib.def.ArCalc;
import jp.avaj.lib.test.L;

/**
数値配列の変換処理-二つの配列の合成-二つの配列の値の計算(ロジックは外部指定)

 */
public class Q09_05 {
  public static void main(String[] args) throws Exception {
    // テストデータ
    double[] array0 = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array0="+ArObj.toString(array0));
    double[] array1 = ArMath.doubleArray(10,new ArGeneratorDoubleRandom(5D,10D));
    L.p("array1="+ArObj.toString(array1));

    double[] newArray;
    ArCreator2D<Double,Double,Double> creator;

    // サンプル0
    {
      creator = new     ArCreator2D<Double,Double,Double>() {
        @Override
        public Double convert(Double arg0,Double arg1) throws Exception {
          return arg0 + arg1;
        }
      };
      newArray = ArMath.calc(array0,array1,creator);
      L.p("newArray="+ArObj.toString(newArray));
    }

    // サンプル1
    {
      creator = new     ArCreator2D<Double,Double,Double>() {
        @Override
        public Double convert(Double arg0,Double arg1) throws Exception {
          return arg0 * arg1;
        }
      };
      newArray = ArMath.calc(array0,array1,ArCalc.MUL);
      L.p("newArray="+ArObj.toString(newArray));
    }

  }
}

サンプル実行結果

result.txt
array0=[5.494186340089856, 7.735644497901893, 9.972069234046275, 7.934361174668532, 9.917112507755494, 5.151322997853091, 9.188756724966979, 6.353024733860859, 8.223364931681369, 5.106895727778108]
array1=[9.363650797189873, 7.332827778640875, 9.2790020579598, 5.158181941401007, 5.763411771209263, 6.158648211398885, 9.22867429510773, 9.781626836497223, 8.291547420073364, 5.795528562585907]
newArray=[14.857837137279729, 15.068472276542767, 19.251071292006074, 13.092543116069539, 15.680524278964757, 11.309971209251977, 18.41743102007471, 16.13465157035808, 16.51491235175473, 10.902424290364014]
newArray=[51.44564230329209, 56.72414885990545, 92.530850944833, 40.926878527728505, 57.15640296360463, 31.725186167065885, 84.80004299170105, 62.142917229664, 68.18442028360442, 29.597160056485965]

ヒストグラムの作成-境界値は直接またはArValidatorで指定する

なおArListでは数値ではなく、任意のオブジェクトに対するヒストグラムを作成するメソッドが用意されている。境界値はArValidatorで指定する。

メソッド定義

ArMath.java
  /**
   * ヒストグラムを作成する.doudaryのlength-1の値が戻される.
   * boundary[0],boundary[length-1]にはnullを指定できる.その場合は無限値と解釈される.
   */
  public static int[] histogram(double[] array,Double[] boundary) throws Exception 
  /** ヒストグラムを作成する.validators.lendth-1の値が戻される. */
  public static int[] histogram(double[] array,ArValidator<Double>[] validators) throws Exception 

サンプルプログラム

Q09_06.java
package jp.avaj.lib.algo;

import jp.avaj.lib.test.L;

/**
ヒストグラムの作成-境界値は直接またはArValidatorで指定する

 */
public class Q09_06 {
  public static void main(String[] args) throws Exception {
    // テストデータ ⇒ 1.0~10.0の配列を作成する
    double[] array = ArMath.doubleArray(10,new ArGeneratorDoubleSeq(1D,1D));
    // あらぬ疑いを避けるためにランダマイズする
    array = ArMath.randomize(array);
    L.p("array="+ArObj.toString(array));
    int[] result;

    // 境界値を直接指定する
    {
      // 両端をnullにすれば無限値となる
      Double[] boundary = new Double[] {
        null,4.0D,8.0D,null
      };
      result = ArMath.histogram(array,boundary);
      L.p("result="+ArObj.toString(result));
    }

    // 境界値をArValidatorで指定する
    {
      // ArValidatorの配列の作成 ⇒ 一例なのでよろしく
      Double[][] boundary = new Double[][] {
        {null,4.0D},
        {4.0D,8.0D},
        {8.0D,null},
      };
      ArValidator<Double>[] validators = ArValidatorUtil.createArValidatorDoubleRangeArray(boundary);
      result = ArMath.histogram(array,validators);
      L.p("result="+ArObj.toString(result));
    }
  }
}

サンプル実行結果

result.txt
array=[6, 9, 1, 3, 5, 7, 2, 8, 10, 4]
result=[3, 4, 3]
result=[3, 4, 3]

おまけ

逆順とランダマイズ

ArMath.java
  /** 一次元配列の逆順 */
  public static double[] reverse(double[] doubleArray) 
  /** 一次元配列のランダマイズ */
  public static double[] randomize(double[] doubleArray) 
  /** 二次元配列の行・列の逆順,indexが-1の時はすべてのRowまたはColumn */
  public static double[][] reverse(double[][] doubleArray,int index,ArTableRC rowCol) 
  /** 二次元配列の行・列のランダマイズ,indexが-1の時はすべてのRowまたはColumn */
  public static double[][] randomize(double[][] doubleArray,int index,ArTableRC rowCol) 
Q09_07.java
package jp.avaj.lib.algo;

import jp.avaj.lib.test.L;

/**
逆順とランダマイズ
 */
public class Q09_07 {
  public static void main(String[] args) {
    // テストデータ ⇒ 1.0~10.0の配列を作成する
    double[] array = ArMath.doubleArray(10,new ArGeneratorDoubleSeq(1D,1D));
    L.p("array="+ArObj.toString(array));
    double[] newArray;

    // 逆順
    newArray = ArMath.reverse(array);
    L.p("newArray="+ArObj.toString(newArray));

    // ランダマイズ
    newArray = ArMath.randomize(array);
    L.p("newArray="+ArObj.toString(newArray));

  }
}
result.txt
array=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
newArray=[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
newArray=[8, 1, 7, 9, 10, 6, 5, 4, 2, 3]

Map、二次元Map上の数値の加減乗除

Mapあるいは二次元Map(ArMatrix)上のInteger値を更新する。返値は更新後の値。

ArMath.java
  /**
   *  値がIntegerのMapの加減乗除.指定されたキーに対応する値とvalueの演算を行う.
   *  除算の場合は、切り捨てて整数化する.
   *  Mapにキーが存在しない場合は、加減の場合は0、乗除の場合は1を仮定する.
   */
  public static <$Key> int calc(Map<$Key,Integer> map,$Key key,ArCalc op,Integer value) 
  /**
   *  値がIntegerのArMatrixの加減乗除.指定されたキーに対応する値とvalueの演算を行う.
   *  除算の場合は、切り捨てて整数化する.
   *  ArMatrixにキーが存在しない場合は、加減と交換の場合は0、乗除の場合は1を仮定する.
   */
  public static <$Key0, $Key1> int calc(ArMatrix<$Key0,$Key1,Integer> mat,$Key0 key0,$Key1 key1,ArCalc op,Integer value) 
Q09_08.java
package jp.avaj.lib.algo;

import java.util.HashMap;
import java.util.Map;

import jp.avaj.lib.def.ArCalc;
import jp.avaj.lib.test.L;

/**
Map、二次元Map上の数値の加減乗除
 */
public class Q09_08 {
  public static void main(String[] args) {
    int result;
    L.p("Mapの場合");
    {
      // 初期値を適当に入れておく
      Map<String,Integer> map = new HashMap<String,Integer>();
      map.put("a",3);
      map.put("b",4);
      map.put("c",5);

      // 加減算では存在しないキーでは0が設定されていると仮定、徐除算では1.
      result = ArMath.calc(map,"d",ArCalc.ADD,2);
      L.p("result="+result);
      result = ArMath.calc(map,"d",ArCalc.MUL,3);
      L.p("result="+result);
    }

    L.p("二次元Map(ArMatrix)の場合");
    {
      // 初期値を適当に入れておく
      ArMatrix<String,String,Integer> mat = new ArMatrix<String,String,Integer>();
      mat.put("a0","b0",7);
      mat.put("a1","b1",3);
      mat.put("a0","b1",5);
      mat.put("a1","b0",6);

      result = ArMath.calc(mat,"a","b",ArCalc.ADD,2);
      L.p("result="+result);
      result = ArMath.calc(mat,"a","b",ArCalc.MUL,3);
      L.p("result="+result);
    }
  }
}
result.txt
Mapの場合
result=2
result=6
二次元Map(ArMatrix)の場合
result=2
result=6

==== end.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0