2013-04-11 17 views
8

voglio calcolare la Median di y in sottogruppi di questa semplice xy_table:SQL classifica query per calcolare ranghi e mediana in sottogruppi

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 

In questo esempio ogni x è unico e la tabella è già ordinata per x. Ora voglio GROUP BY round(x) e ottenere la tupla che contiene la mediana di in ciascun gruppo.

posso già calcolare la mediana per l'intera tavola con questa classifica interrogazione:

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) 

uscita: 0.1, 4.0

ma ho fatto non ancora riescono a scrivere una query per calcolare la mediana per sub gruppi.

Attenzione: Non ho una funzione di aggregazione median() disponibile. Si prega inoltre di non proporre soluzioni con speciali PARTITION, RANK o QUANTILE dichiarazioni (come quelle trovate nello stesso esempio ma anche specifiche del fornitore SO questions). Ho bisogno SQL pianura (vale a dire, compatibile con SQLite senza median() funzione)

Edit: realtà stavo cercando il Medoid e non il Median.

+0

qual è la relazione tra 2a e 3a tabella nell'esempio? 1 ° 'gid' cambiato in 0.1 e né i valori di 'x' né' y' sono mediani per il gruppo 0.0 – Aprillion

+0

Scusa se ho avuto un errore di battitura lì. La terza tabella dovrebbe presentare le mediane dei gruppi 'gid' (gruppo ID). E presumo che la mediana di [4,3] sia 4 (il valore maggiore) in questi casi. La mediana di [4,3] di – Juve

+0

è in genere di 3,5 come suggerito nel primo paragrafo del tuo link wikipedia, vuoi esplicitamente ottenere il valore maggiore? – Aprillion

risposta

3

io suggerisco di fare il calcolo nel linguaggio di programmazione:

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

Ma se si è bloccato con SQLite, è possibile ordinare ogni gruppo da y e selezionare i record in mezzo come questo http://sqlfiddle.com/#!5/d4c68/55/0:

UPDATE: solo il valore "mediano" più grande è importato e anche per nr. di righe, in modo che nessun avg() è necessario:

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 
+0

Thx, la tua risposta fornisce la base per la mia soluzione. Perché non usare semplicemente 'SELECT round (x) gid, 1+ (count (*))/2 mid_row' per ottenere la riga" middle "? Funziona almeno per me. – Juve

+0

sì è stato lasciato dal calcolo del punto centrale a sinistra :) – Aprillion

0

OK, questo si basa su una tabella temporanea:.

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

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

Ma si potrebbe potenzialmente creare questo per una serie di dati si erano interessati in un altro modo sarebbe per garantire che il xy_table avesse questo ordinamento, invece di ordinare solo su x. La ragione di ciò è la mancanza della capacità di numerazione delle righe di SQLite.

Poi:

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 

Se si voleva trattare la mediana come il più grande dei due valori medi, che non rientra nella definizione di @Aprillion già sottolineato, allora si dovrebbe semplicemente prendere il più grande dei due valori , anziché la loro media, sulla terza riga della query.

Problemi correlati