4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

awkで標準出力から直接データを処理したい(統計編)

Last updated at Posted at 2018-12-15

はじめに

お仕事で、データに対してサクッと統計値求めたかったり、規格化したかったりする場合があります。
通常であればpythonとかRとかでサクッとやれちゃうんでしょうが、データ取得環境がオフラインでパッケージも入れられないような環境だったりしたので、awkでスクリプト作って標準出力のデータをパイプでつなげてサクッと求められるようにしました。

環境

GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.1-p11, GNU MP 6.1.2)

統計な計算

最大最小合計平均3σ

標準出力から直接

  • データ数
  • 最大値,index
  • 最小値,index
  • 合計値
  • 平均

を求めます。また、データ整形機能

  • 最大値で規格化(/max)
  • 最小値で規格化(-min)
  • オフセット差っ引く(-ave)
  • 0~1に規格化
  • 標準化

も追加しました。

こことかここを参考にさせてもらいました。

※2020/08/10 修正
option受け取りはbash部分を使うけど、基本的にawkの中だけで閉じるように修正しました。

tokei
#!/bin/bash
# option1 : exec row no
# option2 : '-ave':
#           '-max':
#           '-min':
#           '-stan1':
#           '-stan2':
awk -M '\
function ave_stan(data, ave,  i, N, T){
    for(i=1;i<=NR;i++){
        N = split(org[i],T)
        for(k=1;k<=N;k++){
            if(k==rec)printf"%s ",data[i] - ave
            else      printf"%s ",T[k]
        }
        printf"\n"
    }
    exit;
}
function max_stan(data, max,  i, N, T){
    for(i=1;i<=NR;i++){
        N = split(org[i],T)
        for(k=1;k<=N;k++){
            if(k==rec)printf"%s ",data[i]/max
            else      printf"%s ",T[k]
        }
        printf"\n"
    }
    exit;
}
function min_stan(data, min,  i, N, T){
    for(i=1;i<=NR;i++){
        N = split(org[i],T)
        for(k=1;k<=N;k++){
            if(k==rec)printf"%s ",data[i]-min
            else      printf"%s ",T[k]
        }
        printf"\n"
    }
    exit;
}
function stan1(data, min, max,  i, N, T){
    for(i=1;i<=NR;i++){
        N = split(org[i],T)
        for(k=1;k<=N;k++){
            if(k==rec)printf"%s ",(data[i]-min)/(max-min)
            else      printf"%s ",T[k]
        }
        printf"\n"
    }
    exit;
}
function stan2(data, ave, sig,  i, N, T){
    for(i=1;i<=NR;i++){
        N = split(org[i],T)
        for(k=1;k<=N;k++){
            if(k==rec)printf"%s ",(data[i]-ave)/sig
            else      printf"%s ",T[k]
        }
        printf"\n"
    }
    exit;
}
NR==1{
    rec = min_cnt = max_cnt = 1;
    # option setting
    split(arg, set)
    for(i in set){
        if(set[i]!~/[a-zA-Z]/){
            rec = set[i]
        }
        if(set[i]~/-/){
            opt = set[i]
        }
    }
    max = min = $rec;
}
{
    d[NR] = $rec;
    sum += $rec;
    org[NR] = $0;
}
min>$rec{
    min = $rec;
    min_cnt = NR;
}
max<$rec{
    max = $rec;
    max_cnt = NR;
}
END{
    ave = sum/NR;
    for(i=1;i<=NR;i++)sqsum+=(d[i]-ave)**2;
    sig = sqrt(sqsum/(NR-1));
    for(i=1;i<=NR;i++){
        z = (d[i]-ave)/sig;
        skewsum += z**3;
        kurtsum += z**4 - 3;
    }
    skew = skewsum / NR;
    kurt = kurtsum / NR;
    if(opt=="-ave")ave_stan(d,ave);
    if(opt=="-max")max_stan(d,max);
    if(opt=="-min")min_stan(d,min);
    if(opt=="-stan1")stan1(d,min,max);
    if(opt=="-stan2")stan2(d,ave,sig);
    print "#no   : ",NR;
    print "#max  : ",max," no : ",max_cnt;
    print "#min  : ",min," no : ",min_cnt;
    print "#sum  : ",sum;
    print "#ave  : ",ave;
    print "#3sig : ",sig*3;
    print "#skew : ",skew;
    print "#kurt : ",kurt
}' arg="`echo $@`"

説明
以下のような感じで使います。

使い方1統計値算出
$ cat gaus.dat | tokei
# no   :  10000
# max  :  1  no :  4997
# min  :  0.367879  no :  1
# sum  :  7467.86
# ave  :  0.746786
# 3sig :  0.603083

(gaus.datには$exp(-(x/10)^2) (-10<x<10)$のデータが入っています。)

空白区切りの整然としたデータであれば列指定もできます。
(引数無しなら1列目を扱う)

引数1に列を指定できます。
$ cat gaus.dat | tokei 2

指定がなければ1列目が処理されます。

データ整形

引数2はデータ整形できます。

  • -ave : 平均値で引く(data[i]-ave)
  • -max : 最大値で割る(data[i]/max)
  • -min : 最小値で引く(data[i]-min)
  • -stan1 : 0~1に規格化( (data[i]-min)/(max-min) )
  • -stan2 : 正規標準化( (data[i]-ave)/σ )
使い方2データ整形
$ cat test.dat
0
1
2
3
4
5
6
7
8
9
10

$ cat test.dat | tokei -ave
-5
-4
-3
-2
-1
0
1
2
3
4
5

$ cat test.dat | tokei -stan2
-1.50756
-1.20605
-0.904534
-0.603023
-0.301511
0
0.301511
0.603023
0.904534
1.20605
1.50756

例え1列データであったとしても引数1に列指定はいれなくてはいけません。
⇒入れなくても良くなりました。

最後に

これでイチイチ個人PCにデータを移動してexcelで処理していたのが大分楽になりました。
次は、統計値の次によく使う周波数解析について書きたいと思います。
またコードに不備、改善点があれば教えてください。

4
1
2

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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?