Klaidingų duomenų paieška Tabibitosan metodu

data_islandDarbe iš nutolusių įrenginių kelis kartus per valanda surenkam įvairių meteorologinių jutiklių duomenis. Mums ganėtinai svarbu, kad įrenginys veiktų tvarkingai ir visą laiką fiksuotų realią informaciją. Žinoma, visko pasitaiko: lūžta ryšio įrenginiai ar valdikliai, dingsta elektra arba sugenda pavieniai jutikliai. Kaip identifikuoju viso įrenginio veikimą, jau esu rašęs ankščiau poste apie hierarchines SQL užklausas. Su pavienių jutiklių veikimu-neveikimu yra šiek tiek sudėtingesnė situacija. Kartais pasitaiko, kad sugedus vienam ar kitam įrenginio jutikliui, įrenginio valdiklis „užsiciklina” ir kiekvienos tolimesnės iteracijos metu gražina paskutinę iš jutiklio surinktą reikšmę, kas labai iškreipią suvokimą apie įrenginio duomenų kokybę.

Problema kaip ir aiški: norint išsiaiškinti, ar jutiklio duomenys yra „geri”, reikia tikrinti, ar duomenys iš vis yra ir ar reikšmės nesikartoja ilgą laiko periodą (pačių reikšmių teisingumas yra kitas klausimas). Norint, kad toks patikrinimas atneštų kažkokį žmogui suprantamą rezultatą, geriausia būtų identifikuoti laiko periodus (nuo-iki), kada duomenys buvo „blogi”. Tam puikiai tinka Tabibitosan metodas. Metodas leidžia labai greitai ir paprastai surasti nuoseklius duomenų intervalus.

Pavyzdžiui, turime keliolika eilučių duomenų, kuriuose skaičiai didėja su pertrūkiais (kairėje). Tabibitosan leidžia identifikuoti šiuos intervalus ir gauti gražią jų suvestinę (dešinėje):

1
2
3
5
6
7
10
11
12
20
21

MIN(NUMVAL)   MAX(NUMVAL)
------------- -------------
1             3
5             7
10            12
20            21

Smagiausia, kad užklausa yra genialiai paprasta ir lengvai suprantama:

with Ex1 as(
select  1 NumVal from dual union
select  2 NumVal from dual union
select  3 NumVal from dual union
select  5 NumVal from dual union
select  6 NumVal from dual union
select  7 NumVal from dual union
select 10 NumVal from dual union
select 11 NumVal from dual union
select 12 NumVal from dual union
select 20 NumVal from dual union
select 21 NumVal from dual
)
select min(NumVal),max(NumVal)
from (select NumVal
            ,NumVal-Row_Number() over(order by NumVal) as disTance
from Ex1)
group by disTance
order by min(NumVal);

Užklausos paprastumas leidžia ją pritaikyti pagal savo poreikius. Be to, užklausa veikia su bet kokio tipo duomenų intervalais ir bet kokiu stulpelių skaičiumi. Pateikiu užklausą, kurią naudoju jutiklių duomenų klaidoms aptikti:

with Ex2 as(
select date'2016-08-01' DT, 1 NumVal from dual union
select date'2016-08-02' DT, 2 NumVal from dual union
select date'2016-08-03' DT, 1 NumVal from dual union
select date'2016-08-04' DT, 3 NumVal from dual union
select date'2016-08-05' DT, 4 NumVal from dual union
select date'2016-08-06' DT, 1 NumVal from dual union
select date'2016-08-07' DT, 0 NumVal from dual union
select date'2016-08-08' DT, 0 NumVal from dual union
select date'2016-08-09' DT, 0 NumVal from dual union
select date'2016-08-10' DT, 0 NumVal from dual union
select date'2016-08-11' DT, 0 NumVal from dual union
select date'2016-08-12' DT, 1 NumVal from dual union
select date'2016-08-13' DT, 2 NumVal from dual union
select date'2016-08-14' DT, 3 NumVal from dual union
select date'2016-08-15' DT, null NumVal from dual union
select date'2016-08-16' DT, null NumVal from dual union
select date'2016-08-17' DT, null NumVal from dual union
select date'2016-08-18' DT, 1 NumVal from dual union
select date'2016-08-19' DT, 2 NumVal from dual union
select date'2016-08-20' DT, 1 NumVal from dual 
)
,tabibitosan as(
select NumVal, min(DT) minDT, max(DT) maxDT
  from (select DT,NumVal,
        (row_number() over (order by DT) 
        - row_number() over (partition by NumVal order by DT) 
        ) as disTance
        from Ex2)
group by disTance, NumVal
order by min(NumVal)
)
select * from tabibitosan
where maxDT - minDT > 1

Užklausa veikia analogiškai pirmajai, t. y. subužklausa „tabibitosan” ieško nuoseklių „NumVal” stulpelio reikšmių intervalo, tačiau gražina nebe patį intervalą, bet datas nuo kada iki kada tam „NumVal” tikra reikšmė nesikeitė (dešinėje).

query_1
query_2

Toks rezultatas, žinoma, yra labai painus. Atsirinkti kas ir kaip padeda paskutinė užklausos dalis, kuri gražina tik tuos laiko intervalus, kurie buvo didesni nei, šiuo atveju, viena para. Taip gaunam intervalus, kad duomenys buvo klaidingi. T. y. kur jų nebuvo arba nesikeitė pakankamai ilgą laiką:

query_3