Duomenys apibrėžti nestandartiniu žodynu
level
= 5.
Normalizavimas
Duomenų normalizavimas iš esmės yra duomenų pasikartojimo mažinimas. Štai pavyzdys, kaip atrodo denormalizuoti duomenys:
https://example.com/1/miestai.csv |
|
---|---|
šalis |
miestas |
Lietuva |
Vilnius |
Lietuva |
Kaunas |
Lietuva |
Klaipėda |
Kaip matote, stulpelyje šalis
daug kartų pakartota reikšmė Lietuva
. Toks
duomenų pasikartojimas kelia daug problemų, jei duomenys keičiasi arba tas pats
objektas gali būti pavadintas keliais skirtingais pavadinimais. Tarkime, turime
duomenis iš kito tiekėjo, kurie atrodo taip:
https://example.com/2/miestai.csv |
|
---|---|
šalis |
miestas |
Lietuvos respublika |
Šiauliai |
Lietuvos respublika |
Panevėžys |
Matome, kad šioje vietoje ta pati šalis pavadinta skirtingai.
Tokias besikeičiančių ir pasikartojančių duomenų problemas padeda spręsti unikalūs identifikatoriai arba pirminiai raktai.
Norint normalizuoti duomenis, mūsų lentelę reikia išskaidyti į dvi atskiras lenteles:
Šalys
id |
šalis |
---|---|
1 |
Lietuva |
Miestai
id |
šalis |
miestas |
---|---|---|
1 |
1 |
Vilnius |
2 |
1 |
Kaunas |
3 |
1 |
Klaipėda |
4 |
1 |
Šiauliai |
5 |
1 |
Panevėžys |
Turinti tokią normalizuotą duomenų bazę, galima nesunkiai keisti šalies pavadinimą, galima į šalies lentelę įtraukti daugiau atributų ir visa tai užtenka padaryti vienoje vietoje, kadangi šalies pirminis raktas niekada nesikeičia.
Pirminius duomenis visada rekomenduojama saugoti normalizuotoje formoje, o denormalizuotos duomenų bazės kuriamos normalizuotos duomenų bazės pagrindu, jei norima atlikti duomenų analizę išvengiant skirtingų lentelių jungimo kainos.
Duomenų struktūrų aprašai turėtų būti kiek įmanoma normalizuoti. Jei pirminis duomenų šaltinis yra denormalizuotas, duomenų aprašuose nesunkiai galima atlikti normalizavimą, su sąlyga, jei įmanoma unikaliai identifikuoti objektus.
Mūsų aprašytą miestų pavyzdį normalizuoti galima šio duomenų struktūros aprašo pagalba:
d |
r |
b |
m |
property |
type |
ref |
source |
prepare |
---|---|---|---|---|---|---|---|---|
datasets/example/norm |
||||||||
enum |
country |
Lietuvos respublika |
"Lietuva" |
|||||
miestai |
csv |
https://example.com/{}.csv |
||||||
Country |
1/miestai |
|||||||
name |
string |
šalis |
||||||
City |
1/miestai |
|||||||
name |
string |
miestas |
||||||
country |
ref |
country |
šalis |
|||||
Country |
proxy |
name |
||||||
Country2 |
name |
2/miestai |
||||||
name |
šalis |
choose(self, self, country) |
||||||
City |
proxy |
name |
||||||
City2 |
name |
2/miestai |
||||||
name |
string |
miestas |
||||||
country |
ref |
country |
šalis |
choose(self, self, country) |
Iš šio pavyzdžio matome, kad miestų duomenys iš pirmojo šaltinio miestai1
skaitomi du kartus ir paskirstomi dviejose lentelėse. Pirmą kartą skaitome tik
šalis, generuojant pirminį raktą iš šalies pavadinimo, antrą kartą skaitome tik
miestus ir prijungiame šalį panaudojant šalies pirminį raktą.
Antram duomenų šaltiniui darome tą patį, tik normalizuojame šalies pavadinimus panaudodami Klasifikatoriai reikšmių normalizavimo sąrašą.
Abiejų duomenų šaltinių modeliai turi vieną country
bazę ir vieną city
bazę. O kadangi base.type
yra proxy
, tai duomenų saugykloje, bus
saugoma tik viena lentelė, bendra abiem šaltiniams. Šaltinių duomenys š
bazines lenteles bus apjungiami sutapatinant objektus, pagal miesto ir šalies
pavadinimus.
Galutiniame rezultate gauname tokias lenteles:
datasets/example/norm/country |
|
---|---|
_id |
pavadinimas |
1 |
Lietuva |
datasets/example/norm/city |
||
---|---|---|
_id |
šalis |
miestas |
1 |
1 |
Vilnius |
2 |
1 |
Kaunas |
3 |
1 |
Klaipėda |
4 |
1 |
Šiauliai |
5 |
1 |
Panevėžys |
Lentelių apjungimas
Kartais yra poreikis, skirtingas šaltinio lenteles apjungti į vieną. Pavyzdžiui:
APSKRITYS |
|
---|---|
id |
pavadinimas |
1 |
Vilniaus |
2 |
Kauno |
3 |
Klaipėdos |
SAVIVALDYBES |
||
---|---|---|
id |
apskritis |
pavadinimas |
1 |
1 |
Vilniaus miesto |
2 |
1 |
Vilniaus rajono |
3 |
1 |
Trakų rajono |
Kadangi skirtingos šalis naudoja skirtingus administracinius suskirstymus, tai mes norime normalizuoti šias lenteles, ir padaryti iš jų vieną administracijų lentelė.
Tarkime, apskrities administracinis vienetas bus žymimas skaičiumi 1
, o
savivaldybės skaičiumi 2
. Turime dvi konstantas administraciniam vienetui.
Mūsų pradinė inventorizacijos lentelė atrodys taip:
id |
d |
r |
b |
m |
property |
type |
ref |
source |
level |
---|---|---|---|---|---|---|---|---|---|
datasets/gov/dc/administracijos |
|||||||||
sql |
|||||||||
Apskritys |
id |
APSKRITYS |
|||||||
id |
integer |
id |
4 |
||||||
pavadinimas |
string |
pavadinimas |
2 |
||||||
Savivaldybes |
id |
SAVIVALDYBES |
|||||||
id |
integer |
id |
4 |
||||||
apskritis |
ref |
apskritys |
apskritis |
4 |
|||||
pavadinimas |
string |
pavadinimas |
2 |
Mums reikia pertvarkyti inventorizacijos lentelę taip, kad gautume tokį duomenų pavidalą:
ADMINISTRACIJOS |
|||
---|---|---|---|
id |
priklauso |
lygis |
pavadinimas |
1 |
NULL |
1 |
Vilniaus |
2 |
NULL |
1 |
Kauno |
3 |
NULL |
1 |
Klaipėdos |
4 |
1 |
2 |
Vilniaus miesto |
5 |
1 |
2 |
Vilniaus rajono |
6 |
1 |
2 |
Trakų rajono |
Kad tai gautume, mums reikia atlikti tokius pakeitimus:
Primiausiai, apsirašome naują modelį
administracijos
, kadangi galutiniame rezultate norime turėti viską vienoje lentelėje.Tada nurodome, kad
apskritys
irsavivaldybes
yra modelioadministracijos
dalis. Tai reiškia, kad galiausiai duomenys išapskritys
irsavivaldybes
bus apjungti į vieną modelįadministracijos
.Keičiame lauko
savivaldybes.apskritis
pavadinimą įpriklauso
, kad lauko pavadinimas sutaptu suadministracijos.priklauso
.Kai du modeliai siejamie per
base
lauką, apjungtieji modeliai tampa vieno modelio dalimi ir turi tokias pačias savybes, kaip ir bazinis modelis. Šiuo atveju bazinis modelis yraadministracijos
.Paskutinis pakeitimas, tiek apskritims, tiek savivaldybėms pridėti
lygis
savybę nurodant konstantas1
ir2
.
Po pertvarkymų, mūsų inventorizacijos lentelė turėtų atrodyti taip:
id |
d |
r |
b |
m |
property |
type |
ref |
source |
level |
---|---|---|---|---|---|---|---|---|---|
datasets/gov/dc/administracijos |
|||||||||
sql |
|||||||||
Administracijos |
|||||||||
priklauso |
ref |
administracijos |
|||||||
lygis |
integer |
||||||||
pavadinimas |
string |
||||||||
Administracijos |
proxy |
||||||||
Apskritys |
id |
APSKRITYS |
|||||||
id |
integer |
id |
4 |
||||||
lygis |
integer |
1 |
4 |
||||||
pavadinimas |
string |
pavadinimas |
4 |
||||||
Savivaldybes |
id |
SAVIVALDYBES |
|||||||
id |
integer |
id |
4 |
||||||
priklauso |
ref |
apskritys |
apskritis |
4 |
|||||
lygis |
integer |
2 |
4 |
||||||
pavadinimas |
string |
pavadinimas |
4 |
administracijos
modelis neturi level
reikšmių, taip yra todėl, kad
administracijos
modelis yra išvestinis ir neturi tiesioginio šaltinio, o
duomenų brandos lygis nurodomas duomenų laukams kurie tiesiogiai gaunami iš tam
tikro duomenų šaltinio.
Kadangi base
administracijos
eilutėje ref
stulpelio yra reikšmė, tai
susiejimas bus daromas pagal vidinį modelio identifikatorių. Tai reiškia, kad
modeliai apskritys
ir savivaldybes
nepersidengs.
base
administracijos
eilutėje type
sulpelio reikšmė proxy
reiškia,
kad modeliai apskritys
ir savivaldybes
jokių duomenų nesaugos, o veiks kaip
perlaidos režimu ir duomenis rašys tik į administracijos
modelį.
Lentelės skaidymas
Prieš tai aptarėme kaip apjungti kelias lenteles į vieną modelį. O dabar aptarsime, kaip daryti atvirkštinį procesą, kaip skaidyti vieną lentelę į kelis modelius.
Tarkime turime tokią lentelę:
ADMINISTRACIJOS |
|||
---|---|---|---|
id |
priklauso |
lygis |
pavadinimas |
1 |
NULL |
1 |
Vilniaus |
2 |
NULL |
1 |
Kauno |
3 |
NULL |
1 |
Klaipėdos |
4 |
1 |
2 |
Vilniaus miesto |
5 |
1 |
2 |
Vilniaus rajono |
6 |
1 |
2 |
Trakų rajono |
Norime šią lentelę suskaidyti į dvi atskiras lenteles. Įrašai, kurių lygis
reikšmė yra 1
turėtų keliauti į apskričių modelį, o įrašai, kurių lygis
reikšmė yra 2
turėtų keliauti į savivaldybių modelį.
Pirminė inventorizacijos lentelė atrodo taip:
id |
d |
r |
b |
m |
property |
type |
ref |
source |
level |
---|---|---|---|---|---|---|---|---|---|
datasets/gov/dc/administracijos |
|||||||||
sql |
|||||||||
Administracijos |
id |
ADMINISTRACIJOS |
|||||||
id |
integer |
id |
4 |
||||||
priklauso |
ref |
administracijos |
priklauso |
4 |
|||||
lygis |
integer |
lygis |
2 |
||||||
pavadinimas |
string |
pavadinimas |
2 |
Tam, kad suskaidyti vienos lentelės duomenis į kelis skirtingus modelius, mums
reikia panaudoti filtrus lentelės lygmenyje. Metaduomenys lentelės lygmenyje
taikomi tada, kai property
reikšmė yra tuščia.
source
stulpelyje galima nurodyti užklausą duomenims filtruoti. Duomenų
filtras pateikiamas tarp []
skliaustelių.
Šiuo atveju, mums reikia filtruoti duomenis pagal stulpelio lygis
reikšmes.
Galutinė inventorizacijos lentelė, po pertvarkymų atrodo taip:
id |
d |
r |
b |
m |
property |
type |
ref |
source |
prepare |
level |
---|---|---|---|---|---|---|---|---|---|---|
datasets/gov/dc/administracijos |
||||||||||
sql |
||||||||||
Apskritys |
id |
ADMINISTRACIJOS |
lygis=1 |
|||||||
id |
integer |
id |
4 |
|||||||
pavadinimas |
string |
pavadinimas |
4 |
|||||||
Savivaldybes |
id |
ADMINISTRACIJOS |
lygis=2 |
|||||||
id |
integer |
id |
4 |
|||||||
apskritis |
ref |
apskritys |
priklauso |
4 |
||||||
pavadinimas |
string |
pavadinimas |
4 |
Vieningo žodyno naudojimas
Tam, kad iš pirminio duomenų chaoso padaryti aukščiausio brandos lygio atvirus
duomenis, būtina išversti model
ir property
stulpelių pavadinimus į
pavadinimus iš vieningo žodyno.
Kaip pavyzdį galime imti tokius duomenis:
COUNTRIES |
||
---|---|---|
id |
code |
country |
1 |
lt |
Lietuva |
2 |
lv |
Latvija |
3 |
ee |
Estija |
Šiuose duomenyse yra šalių kodai ir pavadinimai. Kadangi, tai gan dažnai naudojami duomenys, tikėtina, kad skirtinguose duomenų šaltiniuose panaši lentelė ir jos laukai turės kitokius pavadinimus.
Tam, kad suvienodinti pavadinimus, mums reikia pasitelkti vieningą žodyną.
Žodynų sudarymas, yra gan sudėtingas darbas, todėl, jei tik yra galimybė reikėtų remtis egzistuojančiais žodynais. Egzistuojančius žodynus galima rasti LOV svetainėje, WikiData dažniausiai taip pat būna labai naudingas.
Tačiau nebūtina tiksliai atkartoti tai, kas pateikiama žodynuose, nes dažnai žodynai yra labai bendro pobūdžio ir ne viską apimantys. Todėl sudarant žodynus yra laisvė
Vieningas žodyno DSA atrodo taip:
id |
m |
property |
type |
ref |
uri |
title |
---|---|---|---|---|---|---|
prefix |
esco |
http://data.europa.eu/esco/model# |
||||
prefix |
og |
http://ogp.me/ns# |
||||
place/Country |
esco:Country |
Šalis |
||||
code |
string |
esco:isoCountryCodeA2 |
ISO 3166-1 A2 kodas |
|||
name |
string |
og:country-name |
Pavadinimas |
Toliau, įprastai aprašome duomenų šaltinį ir įtraukiame base
dimensiją, kurios pagalba duomenis nukreipiame į standartų vardų erdvę.
id |
d |
r |
b |
m |
property |
type |
ref |
source |
level |
---|---|---|---|---|---|---|---|---|---|
1 |
datasets/gov/dc/countries |
||||||||
2 |
sql |
||||||||
3 |
/place/Country |
code |
|||||||
4 |
Countries |
id |
COUNTRIES |
||||||
5 |
id |
integer |
id |
3 |
|||||
6 |
code |
string |
code |
3 |
|||||
7 |
name |
string |
country |
3 |
Duomenų rinkinių modeliai siejami su žodynu base
stulpelyje pateikiant
susiejamo modelio pavadinimą iš standartų vardų erdvės. Tada atitinkamai reikia
pakeisti property
reikšmes, kad jos atitiktų base
modelio pavadinimus.
Dar vienas svarbus momentas yra code
reikšmė base.source
stulpelyje,
3-ioje eilutėje. Ši reikšmė nurodo kaip datasets/gov/dc/countries/countries
modelio objektai turi būti identifikuojami place/country
modelyje. Šiuo atveju nurodyta, kad objektų siejimas turi būti daromas per
code
lauką. Toks objektų susiejimas leidžia turėti vienodus identifikatorius
visiems duomenų rinkiniams kurie yra place/country
modelio dalis.