9

この単純なサブグループ中央値を計算したい:yxy_table

  x | y --groups--> gid |   x | y --medians-->  gid |   x | y
-------             -------------               -------------
0.1 | 4             0.0 | 0.1 | 4               0.0 | 0.1 | 4
0.2 | 3             0.0 | 0.2 | 3                   |     |
0.7 | 5             1.0 | 0.7 | 5               1.0 | 0.7 | 5
1.5 | 1             2.0 | 1.5 | 1                   |     |
1.9 | 6             2.0 | 1.9 | 6                   |     |
2.1 | 5             2.0 | 2.1 | 5               2.0 | 2.1 | 5
2.7 | 1             3.0 | 2.7 | 1               3.0 | 2.7 | 1

この例では、everyxは一意であり、テーブルは既に でソートされていxます。私は今、各グループGROUP BY round(x)の中央値を保持するタプルを取得したいと考えています。y

このランキング クエリを使用して、テーブル全体の中央値を既に計算できます。

SELECT a.x, a.y FROM xy_table a,xy_table b
WHERE a.y >= b.y
GROUP BY a.x, a.y
HAVING count(*) = (SELECT round((count(*)+1)/2) FROM xy_table)

出力:0.1, 4.0

しかし、サブグループの中央値を計算するクエリの作成にはまだ成功していません。

注意:median()利用可能な集計関数がありません。PARTITIONまた、特別な、RANK、またはQUANTILEステートメントを使用して解決策を提案しないでください(類似しているがベンダー固有すぎるSO の質問に見られるように)。プレーンな SQL が必要です (つまり、median()関数のない SQLite と互換性がある)

編集:私は実際にはMedianではなくMedoidを探していました。

4

2 に答える 2

4

プログラミング言語で計算を行うことをお勧めします。

for each group:
  for each record_in_group:
    append y to array
  median of array

しかし、SQLite に行き詰まっている場合は、次のように各グループを並べ替えてy、真ん中のレコードを選択できますhttp://sqlfiddle.com/#!5/d4c68/55/0 :

更新: より大きな「中央値」値のみが重要であり、nr についても重要です。行の数なので、noavg()は必要ありません:

select groups.gid,
  ids.y median
from (
  -- get middle row number in each group (bigger number if even nr. of rows)
  -- note the integer divisions and modulo operator
  select round(x) gid,
    count(*) / 2 + 1 mid_row_right
  from xy_table
  group by round(x)
) groups
join (
  -- for each record get equivalent of
  -- row_number() over(partition by gid order by y)
  select round(a.x) gid,
    a.x,
    a.y,
    count(*) rownr_by_y
  from xy_table a
  left join xy_table b
    on round(a.x) = round (b.x)
    and a.y >= b.y
  group by a.x
) ids on ids.gid = groups.gid
where ids.rownr_by_y = groups.mid_row_right
于 2013-04-11T12:36:57.653 に答える
0

OK、これは一時テーブルに依存しています:

create temporary table tmp (x float, y float);

insert into tmp
  select * from xy_table order by round(x), y

しかし、関心のある範囲のデータに対してこれを作成できる可能性があります。別の方法はxy_table、 で並べ替えるだけでなく、 がこの並べ替え順序になっていることを確認することxです。この理由は、SQLite に行番号付け機能がないためです。

それで:

select tmp4.x as gid, t.* from (
  select tmp1.x, 
         round((tmp2.y + coalesce(tmp3.y, tmp2.y)) / 2) as y -- <- for larger of the two, change to: (case when tmp2.y > coalesce(tmp3.y, 0) then tmp2.y else tmp3.y end)
  from (
    select round(x) as x, min(rowid) + (count(*) / 2) as id1, 
           (case when count(*) % 2 = 0 then min(rowid) + (count(*) / 2) - 1 
                 else 0 end) as id2
    from (  
      select *, rowid from tmp
    ) t
    group by round(x)
  ) tmp1
  join tmp tmp2 on tmp1.id1 = tmp2.rowid
  left join tmp tmp3 on tmp1.id2 = tmp3.rowid
) tmp4
join xy_table t on tmp4.x = round(t.x) and tmp4.y = t.y

中央値を2つの中間値のうち大きい方として扱いたい場合は、@Aprillionがすでに指摘したように定義に適合しません。3y番目の平均ではなく、2つの値のうち大きい方を単純に取得しますクエリの行。

于 2013-04-11T13:22:55.763 に答える