(Acest articol a fost publicat pentru prima dată pe Date de Johnși cu amabilitate a contribuit la R-bloggeri). (Puteți raporta problema legată de conținutul acestei pagini aici)
Doriți să vă distribuiți conținutul pe R-bloggeri? dați clic aici dacă aveți un blog, sau aici dacă nu aveți.
Echipa noastră a lucrat cu datele recensământului din 2022 din Scoția. Există mai multe modalități de a descărca informațiile – puteți face clic pe hărți sau puteți utiliza un generator de tabel pentru a vă concentra asupra specificului sau există o descărcare zip mare care oferă toate datele în format CSV. Ajungi cu 71 de fișiere, cu aproximativ 46K de rânduri și un număr variabil de coloane.
- Primele 3 rânduri ale fiecărui fișier conțin informații generice despre setul de date și pot fi aruncate pentru analiză. Deoarece acestea au lățimi diferite, diferite cititoare de fișiere se pot declanșa atunci când le citesc.
data.table
sugerează utilizareafill = TRUE
la utilizarefread
dar asta provoacă eșec imediat în unele cazuri. - Ultimele 8 rânduri conțin text care poate fi, de asemenea, eliminat. (În adevăr, aceste rânduri nu au fost citite niciodată pentru că singura coloană a aruncat
fread
care a fost o binecuvântare deghizată) - Odată ce aceste rânduri au fost aruncate, multe fișiere au antete pe mai multe rânduri care trebuie extrase, combinate și adăugate înapoi ca anteturi de coloană.
- Trebuie să țineți cont de faptul că aveți între 0-5 rânduri de anteturi de coloană, cu unele rânduri goale între ele, de obicei în jurul liniei 4 sau 5
- Unele fișiere au delimitatori suplimentari în primele 3 rânduri
Evident, pentru unul sau două fișiere ad-hoc, puteți ocoli acest lucru manual sau prin alte mijloace nefaste. A face acest lucru în mod programatic este o altă problemă. Este doar tipul potrivit de problemă – suficient de complicată încât să nu te poți opri din gândire la asta și suficient de ușor încât să poți realiza cu adevărat ceva.
Abordarea mea inițială a implicat 2 citiri per fișier – am citit fișierul și l-am salvat ca fișier temporar, apoi l-am folosit scan
pe fișierul temporar pentru a găsi primul cod de zonă de ieșire din prima coloană – acesta este primul rând de date. Apoi am creat niște vectori de indici pentru unde au început datele și unde credeam că este prima linie reală de rânduri de antet, după ce am sărit peste primele 3 rânduri.
Am incercat sa folosesc {vroom}
. Pentru ca acest lucru să funcționeze, trebuia să ofer un skip
valoare și setată col_names
la FALS. Nu a existat nicio modalitate de a obține o valoare exactă de ignorare fără a face o citire sau o scanare prealabilă.
Apoi am decis să mă întorc la fread
și nu sări peste nimic, setează header
la FALSE
și efectuați o singură citire. data.table a fost suficient de inteligent pentru a elimina oricum primele trei rânduri, așa că am rămas cu mai multe rânduri care conțineau antetele coloanelor chiar la începutul tabelului.
Le-am îndepărtat folosind grep
pentru a găsi prima zonă de ieșire și scăzând 1 pentru a obține numărul corect de rânduri de antet
# find the row with the start_target value, and retrieve all the rows above it headers <- int_dt(,head(.SD,grep(start_target,V1) - 1L))
Folosind coada pe date, cu un index negativ pentru a ține cont de numărul de rânduri de antet, mi-a dat datele reale. Tocmai am folosit dim
a antetelor data.table pentru a obține numărul de rânduri, pentru a salva efectuarea altuia grep
# remove the first n header rows - the rest of the rows are the data we need to process int_dt <- int_dt(,tail(.SD, -dim(headers)(1)))
După aceea, a fost o chestiune de a combina rândurile antetelor și de a le restrânge într-un vector de caractere și de a le seta ca nume de coloane. Apoi am pivotat datele în format lung, am copiat coloana cu valori, am înlocuit cratimele cu NA
și forțat la numeric. Am adăugat opțiuni pentru a scrie fișierul, sau pentru a tipări sau pentru a-l returna în cazul în care ar fi nevoie de procesare ulterioară.
Iată cum am folosit operația set data.table pentru a elimina cazurile de 2 sau mai multe caractere de subliniere din variable
coloană. Observați utilizarea .I
pentru a returna un vector întreg de rânduri de actualizat
# replace any multiple underscores in variable column col_name <- "variable" rows_to_change <- out_dt(variable %like% "_{2,}",.I) set(out_dt, i = rows_to_change, j = col_name, value = stri_replace_all_regex(out_dt((col_name))(rows_to_change), pattern = "_{2,}", replacement = ""))
Deoarece acestea sunt date la un nivel geografic foarte mic, pentru toată Scoția, nu dorim să le scriem într-un fișier CSV (deși, funcția mea le salvează ca .TSV în mod implicit). Am folosit arrow
pachet pentru a le scrie la parchet. Și, folosit duckdb
pentru a crea o bază de date duckdb.
Codul pentru toate acestea este pe github-ul meu aici tidy_scotland_census
Evoluții ulterioare ar fi să filtreze acest lucru pentru anumite zone – sunt cu adevărat interesat doar de Highland și Argyll și Bute – totuși am lăsat acest lucru pentru moment, așa că codul ar trebui să fie de folos oricui dorește să-l folosească.
Există un exemplu de cod despre cum să utilizați funcția cu purrr pentru a scrie fișierele sau a vizualiza ieșirile în format ordonat. Le puteți, de asemenea, să le inserați într-o listă imbricată (1,7 GB), dar reacția mea imediată la acest lucru este să încerc să o scot imediat înapoi. Recomand să folosiți purrr’s safely
functie pentru asa ceva.
După ce am rezolvat abordarea, am petrecut ceva timp încercând să fac lucrurile puțin mai repede. Folosind gsub
încetineau lucrurile, așa că am înlocuit-o cu unele stringi
. Constrângerea la numeric a durat, de asemenea, ceva timp, dar chiar și folosind a set
abordarea în data.table nu a accelerat lucrurile. Asta pentru că am creat un vector de indici pentru a trece la sintaxa setată (for j in cols
), și funcționarea a fost destul de lentă. Trecerea înapoi la subsetare și utilizare let
actualizarea prin referință a fost mult mai rapidă. Nu sunt sigur că acest lucru ar trebui să fie în general cazul, dar am încercat ambele metode cu mai multe fișiere. Am folosit profvis
pachet pentru a afla unde erau blocajele și a fost foarte util să confirm că abordarea mea inițială a fost mai rapidă.
În general, toată această abordare poate fi folosită în altă parte, nu doar pentru aceste dosare de recensământ.
Deși fișierele dvs. CSV pot fi neregulate, există o modalitate de a le trata și de a obține o formă utilă a datelor.
Sfaturile mele de top:
- nu intrați în panică: căutați un punct comun, chiar dacă numărul de rânduri/coloane, antete variază în funcție de fișier. În acest caz, vedeam că primul rând de date reale începea cu aceeași valoare și că va apărea în primele zece rânduri.
- nu încerca să mănânci elefantul. Este ușor să introduceți o funcție
map
sau o altă funcție toarce și aplicați-o în masă. Dar este mai ușor să faci lucrurile să funcționeze câte un pas pe același fișier și apoi să te ramifică la alții. - folosește torc
safely
. Consultați codul pentru unele funcții pentru a extrage datele din lista rezultată - funcțiile șirurilor de bază sunt foarte utile și trecute cu vederea