Din in-nota se tkun ta 'interess għal dawk li jużaw il-librerija ta' l-ipproċessar tad-dejta tabulari għal R - data.table, u jista 'jkun kuntent li tara l-flessibilità ta' l-użu tagħha f'diversi eżempji.
Ispirat minn eżempju tajjeb , u bit-tama li diġà qrajt l-artiklu tiegħu, nipproponi li nħaffer aktar fil-fond lejn l-ottimizzazzjoni u l-prestazzjoni tal-kodiċi bbażati fuq data.table.
Introduzzjoni: Minn fejn ġej data.table?
L-aħjar huwa li tibda tiffamiljarizza ruħha mal-librerija ftit mill-bogħod, jiġifieri, bl-istrutturi tad-dejta li minnhom jista 'jinkiseb l-oġġett tad-data.table (minn hawn 'il quddiem imsejjaħ DT).
Array
Kodiċi
## arrays ---------
arrmatr <- array(1:20, c(4,5))
class(arrmatr)
typeof(arrmatr)
is.array(arrmatr)
is.matrix(arrmatr)
Struttura waħda bħal din hija firxa (?base::array). Bħal f'lingwi oħra, arrays hawn huma multidimensjonali. Madankollu, il-ħaġa interessanti hija li, pereżempju, firxa bidimensjonali tibda tirret proprjetajiet mill-klassi matriċi (?bażi::matriċi), u firxa ta' dimensjoni waħda, li hija wkoll importanti, ma jiretx minn vettur (?base::vector).
Għandu jiġi mifhum li t-tip ta 'data li tinsab fi kwalunkwe oġġett għandu jiġi ċċekkjat bl-użu tal-funzjoni bażi::typeof, li jirritorna d-deskrizzjoni tat-tip intern skond R Interni - il-protokoll ġenerali tal-lingwa assoċjata mal-oriġinal C.
Kmand ieħor biex tiddetermina l-klassi ta 'oġġett huwa bażi::klassi, fil-każ ta 'vectors, jirritorna t-tip ta' vettur (tvarja fl-isem minn dak intern, iżda jippermettilek ukoll tifhem it-tip ta 'data).
Lista
Minn firxa bidimensjonali, magħrufa wkoll bħala matriċi, tista 'tmur għal-lista (?base::lista).
Kodiċi
## lists ------------------
mylist <- as.list(arrmatr)
is.vector(mylist)
is.list(mylist)
Jiġru diversi affarijiet f'daqqa:
- It-tieni dimensjoni tal-matriċi tiġġarraf, jiġifieri, niksbu kemm lista kif ukoll vettur fl-istess ħin.
- Il-lista għalhekk tirret minn dawn il-klassijiet. Wieħed irid iżomm f'moħħu li element tal-lista se jikkorrispondi għal valur wieħed (skalari) minn ċellula tal-matriċi tal-firxa.
Minħabba li lista hija wkoll vettur, xi funzjonijiet tal-vettur jistgħu jiġu applikati għaliha.
Dataframe
Tista' tmur minn lista, matriċi jew vettur għal dataframe (?base::data.frame).
Kodiċi
## data.frames ------------
df <- as.data.frame(arrmatr)
df2 <- as.data.frame(mylist)
is.list(df)
df$V6 <- df$V1 + df$V2
X'hemm interessanti dwarha: id-dataframe jiret mil-lista! Kolonni tad-dataframe huma ċelluli tal-lista. Dan se jkun importanti aktar tard meta nużaw funzjonijiet applikati għal-listi.
data.table
Ikseb DT (?data.table::data.table) jistgħu jkunu minn dataframe, lista, vettur jew matriċi. Per eżempju, bħal dan (fil-post).
Kodiċi
## data.tables -----------------------
library(data.table)
data.table::setDT(df)
is.list(df)
is.data.frame(df)
is.data.table(df)
Huwa utli li, bħal dataframe, DT jiret il-proprjetajiet ta' lista.
DT u memorja
B'differenza mill-oġġetti l-oħra kollha fil-bażi R, id-DTs huma mgħoddija b'referenza. Jekk għandek bżonn tagħmel kopja għal żona ta 'memorja ġdida, għandek bżonn funzjoni data.table::kopja jew għandek bżonn tagħmel għażla mill-oġġett antik.
Kodiċi
df2 <- df
df[V1 == 1, V2 := 999]
data.table::fsetdiff(df, df2)
df2 <- data.table::copy(df)
df[V1 == 2, V2 := 999]
data.table::fsetdiff(df, df2)
Dan jikkonkludi l-introduzzjoni. DT hija kontinwazzjoni tal-iżvilupp tal-istrutturi tad-dejta f'R, li sseħħ prinċipalment minħabba l-espansjoni u l-aċċelerazzjoni tal-operazzjonijiet imwettqa fuq oġġetti tal-klassi dataframe. Fl-istess ħin, il-wirt minn primittivi oħra huwa ppreservat.
Xi eżempji ta 'użu ta' data.table proprjetajiet
Bħal lista...
L-iterazzjoni fuq ir-ringieli ta' dataframe jew DT mhix idea tajba, peress li l-kodiċi tal-linja fil-lingwa R ħafna aktar bil-mod C, iżda huwa pjuttost possibbli li wieħed jgħaddi mill-kolonni, li ġeneralment ikunu ħafna iżgħar. Tgħaddi mill-kolonni, ftakar li kull kolonna hija element ta 'lista, li ġeneralment ikun fiha vettur. U l-operazzjonijiet fuq il-vettori huma vettorizzati sew fil-funzjonijiet bażiċi tal-lingwa. Tista' wkoll tuża operaturi ta' għażla komuni għal-listi u vettori: `[[`, `$`.
Kodiċi
## operations on data.tables ------------
#using list properties
df$'V1'[1]
df[['V1']]
df[[1]][1]
sapply(df, class)
sapply(df, function(x) sum(is.na(x)))
Vettorizzazzjoni
Jekk ikun hemm bżonn li tgħaddi mill-linji ta 'DT kbir, l-aħjar soluzzjoni tkun li tikteb funzjoni b'vettorizzazzjoni. Imma jekk dan ma jaħdimx, allura għandek tiftakar li ċ-ċiklu внутри DT għadu aktar mgħaġġel miċ-ċiklu R, peress li titwettaq fuq C.
Ejja nippruvawha fuq eżempju akbar b'100K ringieli. Se niġbdu l-ewwel ittra mill-kliem inklużi fil-kolonna tal-vettur w.
Aġġornata
Kodiċi
library(magrittr)
library(microbenchmark)
## Bigger example ----
rown <- 100000
dt <-
data.table(
w = sapply(seq_len(rown), function(x) paste(sample(letters, 3, replace = T), collapse = ' '))
, a = sample(letters, rown, replace = T)
, b = runif(rown, -3, 3)
, c = runif(rown, -3, 3)
, e = rnorm(rown)
) %>%
.[, d := 1 + b + c + rnorm(nrow(.))]
# vectorization
microbenchmark({
dt[
, first_l := unlist(strsplit(w, split = ' ', fixed = T))[1]
, by = 1:nrow(dt)
]
})
# second
first_l_f <- function(sd)
{
strsplit(sd, split = ' ', fixed = T) %>%
do.call(rbind, .) %>%
`[`(,1)
}
dt[, first_l := NULL]
microbenchmark({
dt[
, first_l := .(first_l_f(w))
]
})
# third
first_l_f2 <- function(sd)
{
strsplit(sd, split = ' ', fixed = T) %>%
unlist %>%
matrix(nrow = 3) %>%
`[`(1,)
}
dt[, first_l := NULL]
microbenchmark({
dt[
, first_l := .(first_l_f2(w))
]
})
L-ewwel ġirja itera fuq ringieli:
Unità: millisekondi
expr min
{ dt[, `:=`(first_l, unlist(strsplit(w, split = " ", fiss = T))[1]), by = 1:nrow(dt)] } 439.6217
lq medjan uq max neval
451.9998 460.1593 456.2505 460.9147 621.4042 100
It-tieni ġirja, fejn il-vettorizzazzjoni sseħħ billi ddawwar il-lista f'matriċi u tieħu elementi fuq il-porzjon bl-indiċi 1 (din tal-aħħar hija l-vettorizzazzjoni nnifisha). Korrezzjoni: vettorizzazzjoni fil-livell tal-funzjoni strsplit, li jistgħu jaċċettaw vettur bħala input. Jirriżulta li l-proċedura biex tinbidel lista f'matriċi hija ħafna aktar diffiċli mill-vettorizzazzjoni nnifisha, iżda f'dan il-każ hija ħafna aktar mgħaġġla mill-verżjoni mhux vettorizzata.
Unità: millisekondi
expr min lq mean median uq max neval
{ dt[, `:=`(first_l, .(first_l_f(w)))] } 93.07916 112.1381 161.9267 149.6863 185.9893 442.5199 100
Aċċelerazzjoni bil-medjan in 3 darbiet.
It-tielet ġirja, fejn l-iskema ta 'trasformazzjoni fil-matriċi nbidlet.
Unità: millisekondi
expr min lq mean median uq max neval
{ dt[, `:=`(first_l, .(first_l_f2(w)))] } 32.60481 34.13679 40.4544 35.57115 42.11975 222.972 100
Aċċelerazzjoni bil-medjan in 13 darbiet.
Ikollok bżonn tesperimenta b'din il-materja, aktar ma tkun aħjar.
Eżempju ieħor bil-vettorizzazzjoni, fejn hemm ukoll it-test, iżda huwa qrib il-kundizzjonijiet reali: tulijiet differenti ta 'kliem, numru differenti ta' kliem. Għandek bżonn tikseb l-ewwel 3 kelmiet. Bħal dan:

Hawnhekk il-funzjoni preċedenti ma taħdimx, peress li l-vettori huma ta 'tulijiet differenti, u nissettjaw id-daqs tal-matriċi. Ejja nerġgħu nagħmlu dan billi nħaffru madwar l-Internet.
Kodiċi
# fourth
rown <- 100000
words <-
sapply(
seq_len(rown)
, function(x){
nwords <- rbinom(1, 10, 0.5)
paste(
sapply(
seq_len(nwords)
, function(x){
paste(sample(letters, rbinom(1, 10, 0.5), replace = T), collapse = '')
}
)
, collapse = ' '
)
}
)
dt <-
data.table(
w = words
, a = sample(letters, rown, replace = T)
, b = runif(rown, -3, 3)
, c = runif(rown, -3, 3)
, e = rnorm(rown)
) %>%
.[, d := 1 + b + c + rnorm(nrow(.))]
first_l_f3 <- function(sd, n)
{
l <- strsplit(sd, split = ' ', fixed = T)
maxl <- max(lengths(l))
sapply(l, "length<-", maxl) %>%
`[`(n,) %>%
as.character
}
microbenchmark({
dt[
, (paste0('w_', 1:3)) := lapply(1:3, function(x) first_l_f3(w, x))
]
})
dt[
, (paste0('w_', 1:3)) := lapply(1:3, function(x) first_l_f3(w, x))
]
Unità: millisekondi
expr min lq medjan medju
{ dt[, `:=`((paste0(“w_”, 1:3)), strsplit(w, split = " ", fiss = T))] } 851.7623 916.071 1054.5 1035.199
uq max neval
1178.738 1356.816 100
L-iskrittura dam b'veloċità medja ta' sekonda. Mhux ħażin.
Konnessi b'katina waħda...
Tista 'taħdem ma' oġġetti DT billi tuża l-katina. Jidher qisu twaħħal is-sintassi tal-parentesi fuq il-lemin, essenzjalment iz-zokkor.
Kodiċi
# chaining
res1 <- dt[a == 'a'][sample(.N, 100)]
res2 <- dt[, .N, a][, N]
res3 <- dt[, coefficients(lm(e ~ d))[1], a][, .(letter = a, coef = V1)]
Jiċċirkola mill-pajpijiet...
L-istess operazzjonijiet jistgħu jsiru permezz ta 'pajpijiet, jidher simili, iżda huwa funzjonalment aktar sinjuri, peress li tista' tuża kwalunkwe metodu, mhux biss DT. Ejja nieħdu koeffiċjenti ta 'rigressjoni loġistika għad-dejta sintetika tagħna b'numru ta' filtri fuq DT.
Kodiċi
# piping
samplpe_b <- dt[a %in% head(letters), sample(b, 1)]
res4 <-
dt %>%
.[a %in% head(letters)] %>%
.[,
{
dt0 <- .SD[1:100]
quants <-
dt0[, c] %>%
quantile(seq(0.1, 1, 0.1), na.rm = T)
.(q = quants)
}
, .(cond = b > samplpe_b)
] %>%
glm(
cond ~ q -1
, family = binomial(link = "logit")
, data = .
) %>%
summary %>%
.[[12]]
Statistika, tagħlim bil-magni u aktar ġewwa DT
Tista 'tuża funzjonijiet lambda, iżda kultant ikun aħjar li toħloqhom separatament, tikteb il-pipeline kollu tal-analiżi tad-dejta, u tkompli - jaħdmu ġewwa d-DT. L-eżempju huwa arrikkit bil-karatteristiċi kollha ta 'hawn fuq, flimkien ma' diversi affarijiet utli mill-armament DT (bħal aċċess għad-DT innifsu ġewwa d-DT permezz ta 'link, xi drabi mdaħħal mhux sekwenzjali, iżda b'tali mod li jkun).
Kodiċi
# function
rm(lm_preds)
lm_preds <- function(
sd, by, n
)
{
if(
n < 100 |
!by[['a']] %in% head(letters, 4)
)
{
res <-
list(
low = NA
, mean = NA
, high = NA
, coefs = NA
)
} else {
lmm <-
lm(
d ~ c + b
, data = sd
)
preds <-
stats::predict.lm(
lmm
, sd
, interval = "prediction"
)
res <-
list(
low = preds[, 2]
, mean = preds[, 1]
, high = preds[, 3]
, coefs = coefficients(lmm)
)
}
res
}
res5 <-
dt %>%
.[e < 0] %>%
.[.[, .I[b > 0]]] %>%
.[, `:=` (
low = as.numeric(lm_preds(.SD, .BY, .N)[[1]])
, mean = as.numeric(lm_preds(.SD, .BY, .N)[[2]])
, high = as.numeric(lm_preds(.SD, .BY, .N)[[3]])
, coef_c = as.numeric(lm_preds(.SD, .BY, .N)[[4]][1])
, coef_b = as.numeric(lm_preds(.SD, .BY, .N)[[4]][2])
, coef_int = as.numeric(lm_preds(.SD, .BY, .N)[[4]][3])
)
, a
] %>%
.[!is.na(mean), -'e', with = F]
# plot
plo <-
res5 %>%
ggplot +
facet_wrap(~ a) +
geom_ribbon(
aes(
x = c * coef_c + b * coef_b + coef_int
, ymin = low
, ymax = high
, fill = a
)
, size = 0.1
, alpha = 0.1
) +
geom_point(
aes(
x = c * coef_c + b * coef_b + coef_int
, y = mean
, color = a
)
, size = 1
) +
geom_point(
aes(
x = c * coef_c + b * coef_b + coef_int
, y = d
)
, size = 1
, color = 'black'
) +
theme_minimal()
print(plo)
Konklużjoni
Nittama li kont kapaċi noħloq stampa kompluta, iżda, ovvjament, mhux kompluta, ta 'oġġett bħal data.table, li tibda mill-proprjetajiet tagħha assoċjati mal-wirt minn klassijiet R u tispiċċa bil-karatteristiċi u l-ambjent tagħha stess minn elementi tidyverse. . Nittama li dan jgħinek titgħallem u tuża aħjar din il-librerija għax-xogħol u divertiment.

Grazzi!
Kodiċi sħiħ
Kodiċi
## load libs ----------------
library(data.table)
library(ggplot2)
library(magrittr)
library(microbenchmark)
## arrays ---------
arrmatr <- array(1:20, c(4,5))
class(arrmatr)
typeof(arrmatr)
is.array(arrmatr)
is.matrix(arrmatr)
## lists ------------------
mylist <- as.list(arrmatr)
is.vector(mylist)
is.list(mylist)
## data.frames ------------
df <- as.data.frame(arrmatr)
is.list(df)
df$V6 <- df$V1 + df$V2
## data.tables -----------------------
data.table::setDT(df)
is.list(df)
is.data.frame(df)
is.data.table(df)
df2 <- df
df[V1 == 1, V2 := 999]
data.table::fsetdiff(df, df2)
df2 <- data.table::copy(df)
df[V1 == 2, V2 := 999]
data.table::fsetdiff(df, df2)
## operations on data.tables ------------
#using list properties
df$'V1'[1]
df[['V1']]
df[[1]][1]
sapply(df, class)
sapply(df, function(x) sum(is.na(x)))
## Bigger example ----
rown <- 100000
dt <-
data.table(
w = sapply(seq_len(rown), function(x) paste(sample(letters, 3, replace = T), collapse = ' '))
, a = sample(letters, rown, replace = T)
, b = runif(rown, -3, 3)
, c = runif(rown, -3, 3)
, e = rnorm(rown)
) %>%
.[, d := 1 + b + c + rnorm(nrow(.))]
# vectorization
# zero - for loop
microbenchmark({
for(i in 1:nrow(dt))
{
dt[
i
, first_l := unlist(strsplit(w, split = ' ', fixed = T))[1]
]
}
})
# first
microbenchmark({
dt[
, first_l := unlist(strsplit(w, split = ' ', fixed = T))[1]
, by = 1:nrow(dt)
]
})
# second
first_l_f <- function(sd)
{
strsplit(sd, split = ' ', fixed = T) %>%
do.call(rbind, .) %>%
`[`(,1)
}
dt[, first_l := NULL]
microbenchmark({
dt[
, first_l := .(first_l_f(w))
]
})
# third
first_l_f2 <- function(sd)
{
strsplit(sd, split = ' ', fixed = T) %>%
unlist %>%
matrix(nrow = 3) %>%
`[`(1,)
}
dt[, first_l := NULL]
microbenchmark({
dt[
, first_l := .(first_l_f2(w))
]
})
# fourth
rown <- 100000
words <-
sapply(
seq_len(rown)
, function(x){
nwords <- rbinom(1, 10, 0.5)
paste(
sapply(
seq_len(nwords)
, function(x){
paste(sample(letters, rbinom(1, 10, 0.5), replace = T), collapse = '')
}
)
, collapse = ' '
)
}
)
dt <-
data.table(
w = words
, a = sample(letters, rown, replace = T)
, b = runif(rown, -3, 3)
, c = runif(rown, -3, 3)
, e = rnorm(rown)
) %>%
.[, d := 1 + b + c + rnorm(nrow(.))]
first_l_f3 <- function(sd, n)
{
l <- strsplit(sd, split = ' ', fixed = T)
maxl <- max(lengths(l))
sapply(l, "length<-", maxl) %>%
`[`(n,) %>%
as.character
}
microbenchmark({
dt[
, (paste0('w_', 1:3)) := lapply(1:3, function(x) first_l_f3(w, x))
]
})
dt[
, (paste0('w_', 1:3)) := lapply(1:3, function(x) first_l_f3(w, x))
]
# chaining
res1 <- dt[a == 'a'][sample(.N, 100)]
res2 <- dt[, .N, a][, N]
res3 <- dt[, coefficients(lm(e ~ d))[1], a][, .(letter = a, coef = V1)]
# piping
samplpe_b <- dt[a %in% head(letters), sample(b, 1)]
res4 <-
dt %>%
.[a %in% head(letters)] %>%
.[,
{
dt0 <- .SD[1:100]
quants <-
dt0[, c] %>%
quantile(seq(0.1, 1, 0.1), na.rm = T)
.(q = quants)
}
, .(cond = b > samplpe_b)
] %>%
glm(
cond ~ q -1
, family = binomial(link = "logit")
, data = .
) %>%
summary %>%
.[[12]]
# function
rm(lm_preds)
lm_preds <- function(
sd, by, n
)
{
if(
n < 100 |
!by[['a']] %in% head(letters, 4)
)
{
res <-
list(
low = NA
, mean = NA
, high = NA
, coefs = NA
)
} else {
lmm <-
lm(
d ~ c + b
, data = sd
)
preds <-
stats::predict.lm(
lmm
, sd
, interval = "prediction"
)
res <-
list(
low = preds[, 2]
, mean = preds[, 1]
, high = preds[, 3]
, coefs = coefficients(lmm)
)
}
res
}
res5 <-
dt %>%
.[e < 0] %>%
.[.[, .I[b > 0]]] %>%
.[, `:=` (
low = as.numeric(lm_preds(.SD, .BY, .N)[[1]])
, mean = as.numeric(lm_preds(.SD, .BY, .N)[[2]])
, high = as.numeric(lm_preds(.SD, .BY, .N)[[3]])
, coef_c = as.numeric(lm_preds(.SD, .BY, .N)[[4]][1])
, coef_b = as.numeric(lm_preds(.SD, .BY, .N)[[4]][2])
, coef_int = as.numeric(lm_preds(.SD, .BY, .N)[[4]][3])
)
, a
] %>%
.[!is.na(mean), -'e', with = F]
# plot
plo <-
res5 %>%
ggplot +
facet_wrap(~ a) +
geom_ribbon(
aes(
x = c * coef_c + b * coef_b + coef_int
, ymin = low
, ymax = high
, fill = a
)
, size = 0.1
, alpha = 0.1
) +
geom_point(
aes(
x = c * coef_c + b * coef_b + coef_int
, y = mean
, color = a
)
, size = 1
) +
geom_point(
aes(
x = c * coef_c + b * coef_b + coef_int
, y = d
)
, size = 1
, color = 'black'
) +
theme_minimal()
print(plo)
Sors: www.habr.com
