Madwar data.table

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 Kollegi, 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:

Madwar data.table

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.

Madwar data.table

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

Żid kumment