■CALENDAR■
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31     
<<前月 2019年12月 次月>>
■LOGIN■
現在のモード: ゲストモード
USER ID:
USER PW:
■ADMIN■
ADMIN ID:
ADMIN PW:
■NEW ENTRIES■
■RECENT COMMENTS■
■RECENT TRACKBACK■
  • 吉本隆明の死去に寄せて - 反「反核」ということ
  • 2010 年の釣り - 2010~11 シーズン開幕! 肝和えの素(カワハギ)
  • 震災後の復興を考える
  • 夏本番ですが ・・・ 水の事故を防ぐために
  • 2011 年の釣り - 遅ればせながらシーズン開幕 東京湾のマゴチ
■CATEGORIES■
■ARCHIVES■
■PROFILE■
■POWERED BY■
BLOGN(ぶろぐん)
BLOGNPLUS(ぶろぐん+)
■OTHER■

PL/pgSQL で、第xN分位数を得る集約関数を定義する(その3)
 前の記事で、レコード毎に投入されるフィールドデータをソートし、配列にエレメントとして格納して保持する集約関数 arraySorted() を定義しました。
 今度は、それを使って第xN分位数を求める関数と、ジニ係数を求める関数を、定義します。

 
 まず、第xN分位数を求める関数から。 与える引数は、ソート済配列とそこから取り出すデータ位置の2つです。 「その1」でも書いたとおり、取り出す位置については0~1の実数で与えることとし、配列中のどの位置の値でも取り出すことができるような仕様としています。
 なお、関数名 percentile は「百分位数」という意味の英語です。 第x四分位であろうが第x十分位であろうが、何でも取り出せる、という意味あいです。

-- percentile() : 昇順にソートされた配列から、百分位数を求める
CREATE FUNCTION percentile(float8[], float8) RETURNS float8 AS $BODY$
DECLARE
  arrySrtd  alias for $1;  -- 昇順ソート配列
  index  alias for $2;  -- 求める第n百分位(0~1の実数)
  head  integer;
  tail  integer;
  size  integer;
  nPos  integer;
  fPos  float8;
BEGIN
  head :=coalesce(array_lower(arrySrtd, 1), 0);
  tail :=coalesce(array_upper(arrySrtd, 1), -1);
  size :=tail - head +1;
  if size < 1 then
    /* ソート配列が空なら NULL を返す */
    return NULL;
  elsif size = 1 then
    /* 配列構成要素が1なら、それを返す */
    return arrySrtd[head];
  else
/* 上記のいずれでもない場合 */
    /* 第2引数(取り出す位置)のチェック */
    if (index < 0.0) or (1.0 < index) then
      return NULL;
    end if;
    /* 指定位置の値を取り出す */
    fPos :=head + (tail - head) * index;
    if index < 1.0 then
      nPos :=trunc(fPos);
      fPos :=fPos -nPos;
    else
      nPos :=trunc(fPos) -1;
      fPos :=1.0;
    end if;
    /* 指定された位置が連続する2つの要素の間を示す場合、両者を直線補間して指定位置の値を求める */
    return arrySrtd[nPos] + (arrySrtd[nPos +1] - arrySrtd[nPos]) * fPos;
  end if;
END;
$BODY$ LANGUAGE plpgsql;
 引数を前段チェックしたのち、ソート配列中の、実数で与えられた取り出し位置の値を求めます。
 多くの場合、指定された位置にちょうど配列要素が存在することはなく、配列の要素と要素の間が、位置として指定されていることでしょう。 この場合には、指定された位置を挟む2要素を直線補間して、指定位置の値を求めます。

 次に、ジニ係数を求める関数です。
 ジニ係数は、ある集団内における格差の大きさを表す数値です。 集団の個々の値について昇順に並べて、横軸に要素ならび、縦軸にその要素までの値の累計値を取った線グラフを思い浮かべて下さい。
 集団内の要素が完全に平等だった場合、すなわち全要素の値が同値であった場合には、グラフは右上がりの直線になります。 集団内に僅かでも格差がある場合には、右上がりの凹型曲線となり、この最も極端な場合は、1要素を除くその他の全要素の値がゼロだった場合で、このときのグラフは右端までゼロで平坦、最右端の要素に達して初めて累計値が0より大きくなりますから、形としては「逆L字型」になります。
 完全に平等だった場合のグラフの面積から被験集団のグラフの面積を減じ、それを完全に平等だった場合のグラフの面積で除したものが、ジニ係数です。
 完全に平等な状態ではゼロになり、格差が大きいほど1に近づきます。

-- gini() : 昇順にソートされた配列から、ジニ係数を求める
CREATE FUNCTION gini(float8[]) RETURNS float8 AS $BODY$
DECLARE
  arrySrtd  alias for $1;  -- 昇順ソート配列
  head  integer;
  tail  integer;
  size  integer;
  arryAccm  float8[];
  accmPrev  float8;
  sumArry   float8;
BEGIN
  head :=coalesce(array_lower(arrySrtd, 1), 0);
  tail :=coalesce(array_upper(arrySrtd, 1), -1);
  size :=tail - head +1;
  if size < 1 then
    return NULL;
  elsif size = 1 then
    return 0.0;
  else
    -- 累積値配列の生成
    arryAccm :=array[0.0];
    accmPrev :=arryAccm[1];
    for i in head..tail loop
      accmPrev :=accmPrev + arrySrtd[i];
      arryAccm :=arryAccm || accmPrev;
    end loop;
    -- ジニ係数の算出
    head :=array_lower(arryAccm, 1);
    tail :=array_upper(arryAccm, 1);
    size :=tail - head +1;
    sumArry :=0;
    for i in head..tail loop
      sumArry :=sumArry + arryAccm[i];
    end loop;
    return 1.0 - (sumArry / (arryAccm[tail] * size / 2.0));
  end if;
END;
$BODY$ LANGUAGE plpgsql;
 前述した定義どおりに算出しています。
 完全に平等だった場合のグラフ面積は、要素数(底辺)×全要素累積値(高さ)÷2。
 被験集団のグラフ面積は、要素値を昇順に並べて、要素毎にその要素までの累積値を算出したとき、その「各要素までの累計値」の総和(離散積分値)。
 この2つから、ジニ係数が求められます。

 第xN分位数とジニ係数を集約関数で算出する全ソースリスト、掲載完了です。
 次の記事に、上記両関数の使用例と、最適化に関する若干の実験結果を載せる予定です。載せました。
| http://blog.wakita.cc/index.php?e=88 |
| サーバ・Linux::PostgreSQL | 02:53 PM | comments (0) | trackback (0) |










http://blog.wakita.cc/tb.php/88
PAGE TOP ↑