(Acest articol a fost publicat pentru prima dată pe Rstats – cuantificatș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.
Într-o postare anterioară, am descris cum să desfășoare o sesiune pentru a preda R biologilor celulari. În această postare ne vom uita mai detaliat la unul dintre pași: cum să încărcați datele în R.
Ca o reamintire, o sarcină tipică de analiză în biologia celulară urmează acești pași:
- face experimentul(ele)
- colectați datele – de exemplu imagini microscopice
- analizați și obțineți o ieșire text simplu (csv) – de exemplu folosind Fiji
- încărcați datele în R
- ciocniți niște numere și complotați
Încărcarea datelor în R
În setul de date de testare din postarea anterioară avem un singur folder de 80 de fișiere csv. 4 experimente, 2 condiții, câte 10 fișiere din fiecare. Și fiecare fișier are 20 de rânduri de date. Vezi mai jos pentru alte scenarii.
Scopul este de a încărca datele în R și de a le asambla într-un singur cadru de date. Există multe moduri de a face acest lucru, voi arăta trei dintre cele mai populare.
- baza R
- tidyverse
- date.tabel
# Demonstrate three ways to load all CSV files in Data:
# 1) base R
# 2) tidyverse (readr + dplyr + purrr)
# 3) data.table
data_dir <- "Data"
csv_files <- list.files(data_dir, pattern = "\.csv$", full.names = TRUE)
# --- 1) base R ---------------------------------------------------------------
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_file <- basename(path)
df
})
base_all <- do.call(rbind, base_list)
# --- 2) tidyverse ------------------------------------------------------------
if (!requireNamespace("readr", quietly = TRUE) ||
!requireNamespace("dplyr", quietly = TRUE) ||
!requireNamespace("purrr", quietly = TRUE)) {
stop("Please install tidyverse components: readr, dplyr, purrr")
}
tidy_all <- purrr::map_dfr(
csv_files,
~ readr::read_csv(.x, show_col_types = FALSE) |>
dplyr::mutate(source_file = basename(.x))
)
# --- 3) data.table -----------------------------------------------------------
if (!requireNamespace("data.table", quietly = TRUE)) {
stop("Please install data.table")
}
dt_list <- lapply(csv_files, function(path) {
dt <- data.table::fread(path)
dt(, source_file := basename(path))
dt
})
dt_all <- data.table::rbindlist(dt_list, use.names = TRUE, fill = TRUE)
# Quick checks
cat("Files loaded:", length(csv_files), "n")
cat("Rows (base):", nrow(base_all), "n")
cat("Rows (tidyverse):", nrow(tidy_all), "n")
cat("Rows (data.table):", nrow(dt_all), "n")
În fiecare caz, listăm fișierele și apoi folosim această listă pentru a încărca fiecare articol și a asambla într-un singur cadru de date mare.
Trebuie să știm ce rânduri ale cadrului de date mari provin din ce fișier. Acest lucru este esențial dacă nu există un identificator în cadrul datelor. Deci, în fiecare caz, după încărcare, adăugăm numele fișierului ca o nouă coloană numită source_file. Apoi asamblam aceste cadre de date modificate într-un singur cadru de date mare.
Iată rezultatul din ultima parte a codului:
> # Quick checks
> cat("Files loaded:", length(csv_files), "n")
Files loaded: 80
> cat("Rows (base):", nrow(base_all), "n")
Rows (base): 1600
> cat("Rows (tidyverse):", nrow(tidy_all), "n")
Rows (tidyverse): 1600
> cat("Rows (data.table):", nrow(dt_all), "n")
Rows (data.table): 1600
>
Rezultatul este identic cu toate cele trei abordări.
Strategia mea preferată este să folosesc baza R pentru sarcini ca aceasta. În general, cel mai bine este să rămâneți la baza R decât să vă bazați pe biblioteci. În ceea ce privește viteza, {data.table} este renumit pentru că este rapid. Dacă datele dvs. sunt masive, merită să le folosiți {data.table}. Cu toate acestea, pentru un set de 80 de fișiere mici, viteza nu este o problemă, iar baza R funcționează foarte bine. S-ar putea să preferați sintaxa tidyverse și să vă fie mai ușor de înțeles, caz în care mergeți la ea. În caz contrar, sfatul meu este să rămâi la baza R.
Rețineți că, cu fiecare dintre aceste trei abordări, există mai multe moduri diferite de a realiza același lucru. Vă prezint doar unul. De exemplu, cu baza R, puteți vedea exemple în care o buclă for este folosită pentru a realiza același lucru ca lapply. Această abordare este mai lentă decât cea prezentată aici, deși este, probabil, mai lizibilă.
Vom continua cu baza R în timp ce ne uităm la câteva scenarii alternative pe care probabil le veți întâlni.
Scenarii alternative
Cazul de bază (mai detaliat)
Aveam un director plat (Data/) din 80 de fișiere, unde numele fișierelor codificau detaliile experimentale. Prin urmare, am folosit acest cod R de bază pentru a încărca datele.
data_dir <- "Data"
csv_files <- list.files(data_dir, pattern = "\.csv$", full.names = TRUE)
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_file <- basename(path)
df
})
base_all <- do.call(rbind, base_list)
După aceasta, trebuie să folosim source_file coloană pentru a adăuga coloane suplimentare care semnifică detaliile experimentale. Fișierele noastre se numesc lucruri de genul control_n2_9.csv sau rapa_n3_2.csv – cu alte cuvinte sunt de forma condition_experiment_x.csv. Literele de subliniere pot fi folosite pentru a împărți numele fișierului cu strsplit() și apoi putem lua primul sau al doilea element al rezultatului și le putem stoca în coloane noi.
# source_file column has name of the file, name is of the form foo_bar_1.csv
# extract foo and bar into two columns
base_all$cond <- sapply(strsplit(base_all$source_file, "_"), "(", 1)
base_all$expt <- sapply(strsplit(base_all$source_file, "_"), "(", 2)
După cum am spus mai sus, există întotdeauna alte abordări și aici s-ar putea să facem acești pași în interiorul originalului lapply() apel, adică înainte de a asambla cadrul mare de date.
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_file <- basename(path)
df$cond <- sapply(strsplit(df$source_file, "_"), "(", 1)
df$expt <- sapply(strsplit(df$source_file, "_"), "(", 2)
df
})
base_all <- do.call(rbind, base_list)
Cu toate acestea, dacă lăsați coloana frământată până când ați asamblat cadrul de date mare, este mai probabil ca partea de încărcare a codului să fie reutilizabilă.
Să ne uităm la alte câteva scenarii.
Sunt necesare doar un subset de coloane
Dacă fișierele au multe coloane, este posibil să aveți nevoie doar de un subset de coloane în cadrul de date. Mai rar, fișierele csv pot avea un număr diferit de coloane. În acest caz, nu este posibil să folosim codul de mai sus deoarece avem nevoie de un număr egal de coloane pentru a asambla cadrul de date mare.
Soluția în ambele cazuri este să specificați ce coloane să încărcați. Putem face:
> head(read.csv(csv_files(1))) X Area Mean StdDev Min Max IntDen RawIntDen 1 1 1248 48.83477 8.864353 0 255 60945 60945 2 2 1248 52.46805 10.564050 0 255 65480 65480 3 3 1248 72.14579 8.947991 0 255 90037 90037 4 4 1248 55.77559 9.542218 0 255 69607 69607 5 5 1248 56.42217 9.749921 0 255 70414 70414 6 6 1248 73.86571 7.626613 0 255 92184 92184
Pentru a vedea prima parte (head()) din primul dosar. Să presupunem că vrem doar Area, Mean (și source_file) coloane. Apoi putem face:
data_dir <- "Data"
csv_files <- list.files(data_dir, pattern = "\.csv$", full.names = TRUE)
my_columns <- c("Area", "Mean")
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df <- df(,my_columns)
df$source_file <- basename(path)
df
})
base_all <- do.call(rbind, base_list)
De aici, putem asambla cond şi expt coloane așa cum se arată mai sus.
Până acum, toate informațiile necesare sunt codificate în numele fișierului. Dacă nu este cazul, este mai bine în această etapă să modificați scriptul care a generat csvs, astfel încât informațiile necesare să poată fi citite din numele fișierului sau, alternativ, din calea fișierului.
Nume de fișiere identice (neunice) în foldere diferite
În exemplul de mai sus, starecel experiment si a diferențiator au fost codificate în numele fișierului. S-ar putea ca fișierele csv să fie organizate astfel:
- date/
- Controla/
- drog/
- celula1.csv
- cell2.csv
- cell3.csv
sau
- date/
- Expt1/
- Expt2/
- Controla/
- celula1.csv
- cell2.csv
- cell3.csv
- drog/
- Controla/
sau orice altă combinație. Ideea este că numele fișierului nu mai este unic. Există mai multe fișiere cu același diferențiere și pentru a accesa condiția sau experimentul, trebuie să manipulăm calea fișierului mai degrabă decât numele fișierului.
data_dir <- "Data"
csv_files <- list.files(data_dir,
pattern = "\.csv$",
full.names = TRUE, # get the full path (folder names)
recursive = TRUE) # ensures we look in subfolders of data_dir
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_path <- path
df
})
base_all <- do.call(rbind, base_list)
Acest bloc de cod se va ocupa de o mulțime de subdosare din interior data_dir și asamblați cadrul mare de date. De data aceasta, facem o coloană numită source_path și aici stocăm calea completă a fiecărui fișier.
Deci, un fișier numit cell2.csv în Expt1/Drug/ în cadrul Data/ folderul din proiect cu have the source_path de Data/Expt1/Drug/cell2.csv
Trebuie doar să ne frământăm această cale pentru a extrage informațiile despre stare și experiment.
# "experiment" folder
base_all$cond <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 2)
# folder enclosing file
base_all$expt <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 3)
base_all$source_file <- basename(base_all$source_path) # get the filename (differentiator)
Acest pas de dispută va trebui ajustat la nevoile dumneavoastră. Noi folosim .Platform$file.sep Mai degrabă decât "/" sau "" deoarece separatorul de fișiere diferă pe Windows.
Exerciții
Iată trei scenarii pe care le puteți întâlni. Cum le-ai rezolva?
- Fișierele sunt în
Data/și sunt organizate în trei foldere de condiții, în interiorul fiecăruia sunt patru subfoldere de experiment, fiecare cu 10 fișiere csv. - Fișierele sunt în
Data/și sunt organizate în patru foldere de experiment, în interiorul fiecăruia sunt 20 de fișiere csv. Ele sunt numite astfel:microscopy-analysis_Control_IF488_cell3.csvşimicroscopy-analysis_Drug_IF488_cell2.csv - Fișierele sunt localizate la
~/Desktop/și sunt organizate în 12 foldere numiteControl-Expt1sauDRUG1-Expt2(există trei condiții și patru experimente). Fișierele din interior sunt numitecell1.csvetc. - Fișierele sunt într-un singur folder, folosind etichetarea experiment_condition_differentiator, cu toate acestea, utilizatorul a fost inconsecvent. Uneori este numit Controlul
ControluneoriCtrlsauctrl
Răspunsuri
Faceți clic pentru a dezvălui.
Problema 1
data_dir <- "Data"
csv_files <- list.files(data_dir,
pattern = "\.csv$",
full.names = TRUE, # get the full path (folder names)
recursive = TRUE) # ensures we look in subfolders of data_dir
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_path <- path
df
})
base_all <- do.call(rbind, base_list)
base_all$cond <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 3)
base_all$expt <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 2)
base_all$source_file <- basename(base_all$source_path)
Problema 2
data_dir <- "Data"
csv_files <- list.files(data_dir,
pattern = "\.csv$",
full.names = TRUE, # get the full path (folder names)
recursive = TRUE) # ensures we look in subfolders of data_dir
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_path <- path
df
})
base_all <- do.call(rbind, base_list)
base_all$expt <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 2)
base_all$source_file <- basename(base_all$source_path)
base_all$cond <- sapply(strsplit(base_all$source_path, "_", fixed = TRUE), "(", 2)
Problema 3
# first relocate the files to "Data/" in the project folder and then...
data_dir <- "Data"
csv_files <- list.files(data_dir,
pattern = "\.csv$",
full.names = TRUE, # get the full path (folder names)
recursive = TRUE) # ensures we look in subfolders of data_dir
base_list <- lapply(csv_files, function(path) {
df <- read.csv(path)
df$source_path <- path
df
})
base_all <- do.call(rbind, base_list)
base_all$condexpt <- sapply(strsplit(base_all$source_path, .Platform$file.sep, fixed = TRUE), "(", 2)
base_all$source_file <- basename(base_all$source_path)
base_all$cond <- sapply(strsplit(base_all$condexpt, "-", fixed = TRUE), "(", 1)
base_all$expt <- sapply(strsplit(base_all$condexpt, "-", fixed = TRUE), "(", 2)
Problema 4
Există o serie de moduri de a face față acestei probleme. Dacă etichetarea este foarte inconsecventă, cel mai bine este să reluați analiza (care a dat fișierele csv) într-un mod care să ofere o etichetare consistentă. Dacă acest lucru nu este posibil, de exemplu, numele fișierelor originale sunt denumite inconsecvent, atunci puteți extrage etichetele de condiție și experimentați ca înainte și apoi să le redenumiți. Având în vedere amestecul de litere mari și mici, este recomandabil să alergați to.lower() mai întâi, apoi descoperă ce intrări sunt unice pentru ce grup și apoi atribuie-le în consecință. O altă abordare este de a crea un cadru de date care să arate cum ar trebui redenumite intrările și de a-l folosi pentru a redenumi etichetele. Partea bonus a confruntării cu o astfel de problemă este că, odată ce ați trecut prin durere, vă va face mai consecvent atunci când numiți lucruri în viitor!
—
Titlul postării vine de la Get Better de The New Fast Automatic Daffodils.
Parte dintr-o serie de dezvoltare a abilităților membrilor laboratorului.
