data.table
Przekształcanie, układanie w stosy i dzielenie
Szukaj…
Uwagi
Oficjalna winieta „Skuteczne przekształcanie przy użyciu data.tables” jest najlepszym wprowadzeniem do tego tematu.
Wiele zadań przekształcania wymaga przejścia między długimi i szerokimi formatami:
- Szerokie dane to dane, przy czym każda kolumna reprezentuje oddzielną zmienną, a wiersze reprezentują osobne obserwacje
- Długie dane to dane o identyfikatorze formularza | zmienna | wartość, gdzie każdy wiersz reprezentuje parę zmiennych obserwacyjnych
stop i odlewaj z tabelą danych
data.table
oferuje szeroki zakres możliwości przekształcania danych zarówno w sposób wydajny, jak i łatwy
Na przykład, zmieniając kształt z długiego na szeroki, możesz jednocześnie przekazać kilka zmiennych do value.var
i do parametrów fun.aggregate
w tym samym czasie
library(data.table) #v>=1.9.6
DT <- data.table(mtcars)
Długa do szerokiej
dcast(DT, gear ~ cyl, value.var = c("disp", "hp"), fun = list(mean, sum))
gear disp_mean_4 disp_mean_6 disp_mean_8 hp_mean_4 hp_mean_6 hp_mean_8 disp_sum_4 disp_sum_6 disp_sum_8 hp_sum_4 hp_sum_6 hp_sum_8
1: 3 120.100 241.5 357.6167 97 107.5 194.1667 120.1 483.0 4291.4 97 215 2330
2: 4 102.625 163.8 NaN 76 116.5 NaN 821.0 655.2 0.0 608 466 0
3: 5 107.700 145.0 326.0000 102 175.0 299.5000 215.4 145.0 652.0 204 175 599
Spowoduje to ustawienie gear
jako kolumny indeksu, podczas gdy mean
i sum
zostaną obliczone dla disp
i hp
dla każdej kombinacji gear
i cyl
. W przypadku, gdy niektóre kombinacje nie istnieją, możesz podać dodatkowe parametry, takie jak na.rm = TRUE
(które zostaną przekazane do funkcji mean
i sum
) lub podać wbudowany argument fill
. Możesz także dodawać marginesy, usuwać brakujące kombinacje i podzbiór danych. Zobacz więcej w ?data.table::dcast
Szeroki do długiego
Podczas przekształcania z szerokiego na długi, możesz na przykład przekazywać kolumny do parametru measure.vars
za pomocą wyrażeń regularnych
print(melt(DT, c("cyl", "gear"), measure = patterns("^d", "e")), n = 10)
cyl gear variable value1 value2
1: 6 4 1 160.00 16.46
2: 6 4 1 160.00 17.02
3: 4 4 1 108.00 18.61
4: 6 3 1 258.00 19.44
5: 8 3 1 360.00 17.02
---
60: 4 5 2 3.77 5.00
61: 8 5 2 4.22 5.00
62: 6 5 2 3.62 5.00
63: 8 5 2 3.54 5.00
64: 4 4 2 4.11 4.00
To melt
dane według cyl
i gear
jak kolumny indeksu, podczas gdy wszystkie wartości zmiennych, które rozpoczynają się d
( disp
& drat
) będzie obecny w value1
i wartości dla zmiennych, które zawierają litery e
w nich ( qsec
i gear
) będą obecne w kolumnie value2
.
Można również zmienić nazwę wszystkich kolumn w wyniku podczas określania variable.name
i value.name
argumentów lub zdecydować, czy chcesz, aby character
kolumny mają być automatycznie konwertowane na factor
s lub nie, określając variable.factor
i value.factor
argumenty. Zobacz więcej w ?data.table::melt
Przekształć używając „data.table”
data.table
rozszerza reshape2
melt
& dcast
( Odniesienie: Wydajne przekształcanie przy użyciu data.tables )
library(data.table)
## generate some data
dt <- data.table(
name = rep(c("firstName", "secondName"), each=4),
numbers = rep(1:4, 2),
value = rnorm(8)
)
dt
# name numbers value
# 1: firstName 1 -0.8551881
# 2: firstName 2 -1.0561946
# 3: firstName 3 0.2671833
# 4: firstName 4 1.0662379
# 5: secondName 1 -0.4771341
# 6: secondName 2 1.2830651
# 7: secondName 3 -0.6989682
# 8: secondName 4 -0.6592184
Długa do szerokiej
dcast(data = dt,
formula = name ~ numbers,
value.var = "value")
# name 1 2 3 4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814
W wielu kolumnach (od data.table
1.9.6)
## add an extra column
dt[, value2 := value * 2]
## cast multiple value columns
dcast(data = dt,
formula = name ~ numbers,
value.var = c("value", "value2"))
# name value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078 0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814 -1.6409368 0.9748581 1.476649 1.1515627
Szeroki na długi
## use a wide data.table
dt <- fread("name 1 2 3 4
firstName 0.1836433 -0.8356286 1.5952808 0.3295078
secondName -0.8204684 0.4874291 0.7383247 0.5757814", header = T)
dt
# name 1 2 3 4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814
## melt to long, specifying the id column, and the name of the columns
## in the resulting long data.table
melt(dt,
id.vars = "name",
variable.name = "numbers",
value.name = "myValue")
# name numbers myValue
# 1: firstName 1 0.1836433
# 2: secondName 1 -0.8204684
# 3: firstName 2 -0.8356286
# 4: secondName 2 0.4874291
# 5: firstName 3 1.5952808
# 6: secondName 3 0.7383247
# 7: firstName 4 0.3295078
# 8: secondName 4 0.5757814
Przechodzenie z szerokiego na długi format za pomocą stopu
Topienie: podstawy
Topienie służy do przekształcania danych z szerokiego na długi format.
Począwszy od szerokiego zestawu danych:
DT = data.table(ID = letters[1:3], Age = 20:22, OB_A = 1:3, OB_B = 4:6, OB_C = 7:9)
Możemy stopić nasze dane za pomocą funkcji melt
w data.table. Zwraca to kolejną tabelę danych w długim formacie:
melt(DT, id.vars = c("ID","Age"))
1: a 20 OB_A 1
2: b 21 OB_A 2
3: c 22 OB_A 3
4: a 20 OB_B 4
5: b 21 OB_B 5
6: c 22 OB_B 6
7: a 20 OB_C 7
8: b 21 OB_C 8
9: c 22 OB_C 9
class(melt(DT, id.vars = c("ID","Age")))
# "data.table" "data.frame"
Wszelkie kolumny, które nie są ustawione w parametrze id.vars
, są uważane za zmienne. Alternatywnie możemy ustawić je jawnie za pomocą argumentu measure.vars
:
melt(DT, measure.vars = c("OB_A","OB_B","OB_C"))
ID Age variable value
1: a 20 OB_A 1
2: b 21 OB_A 2
3: c 22 OB_A 3
4: a 20 OB_B 4
5: b 21 OB_B 5
6: c 22 OB_B 6
7: a 20 OB_C 7
8: b 21 OB_C 8
9: c 22 OB_C 9
W takim przypadku zakłada się, że dowolne kolumny nie ustawione w measure.vars
są identyfikatorami.
Jeśli ustawimy oba jawnie, zwróci tylko wybrane kolumny:
melt(DT, id.vars = "ID", measure.vars = c("OB_C"))
ID variable value
1: a OB_C 7
2: b OB_C 8
3: c OB_C 9
Nazwy zmiennych i wartości w wyniku
Możemy manipulować nazwami kolumny zwracanej tabeli za pomocą variable.name
i value.name
melt(DT,
id.vars = c("ID"),
measure.vars = c("OB_C"),
variable.name = "Test",
value.name = "Result"
)
ID Test Result
1: a OB_C 7
2: b OB_C 8
3: c OB_C 9
Ustawianie typów zmiennych miary w wyniku
Domyślnie stopienie data.table konwertuje wszystkie measure.vars
czynników:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT[, variable])
# "factor"
Aby ustawić jako znak zamiast użyć variable.factor
argumentu:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), variable.factor = FALSE)
class(M_DT[, variable])
# "character"
Wartości zazwyczaj dziedziczą po typie danych źródłowej kolumny:
class(DT[, value])
# "integer"
class(M_DT[, value])
# "integer"
W przypadku konfliktu typy danych zostaną wymuszone. Na przykład:
M_DT <- melt(DT,id.vars = c("Age"), measure.vars = c("ID","OB_C"))
class(M_DT[, value])
# "character"
Podczas topienia wszelkie zmienne czynnikowe zostaną wymuszone na typ znaku:
DT[, OB_C := factor(OB_C)]
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT)
# "character"
Aby tego uniknąć i zachować wstępne pisanie, użyj argumentu value.factor
:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), value.factor = TRUE)
class(M_DT)
# "factor"
Obsługa brakujących wartości
Domyślnie wszelkie wartości NA
są zachowywane w stopionych danych
DT = data.table(ID = letters[1:3], Age = 20:22, OB_A = 1:3, OB_B = 4:6, OB_C = c(7:8,NA))
melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
ID variable value
1: a OB_C 7
2: b OB_C 8
3: c OB_C NA
Jeśli należy je usunąć z danych, ustaw na.rm = TRUE
melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), na.rm = TRUE)
ID variable value
1: a OB_C 7
2: b OB_C 8
Przechodzenie z długiego do szerokiego formatu za pomocą rzutowania
Casting: podstawy
Przesyłanie służy do przekształcania danych z długiego na szeroki format.
Począwszy od długiego zestawu danych:
DT = data.table(ID = rep(letters[1:3],3), Age = rep(20:22,3), Test = rep(c("OB_A","OB_B","OB_C"), each = 3), Result = 1:9)
Możemy rzucić nasze dane za pomocą dcast
funkcję w data.table. Zwraca to kolejną tabelę data.table w szerokim formacie:
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
class(dcast(DT, formula = ID ~ Test, value.var = "Result"))
[1] "data.table" "data.frame"
Rzucanie wartości
Argument value.var
jest niezbędny do prawidłowego przesyłania - jeśli nie zostanie podany, dcast przyjmie założenie na podstawie twoich danych.
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
ID OB_A OB_B OB_C
1: a 20 20 20
2: b 21 21 21
3: c 22 22 22
Na value.var
można podać wiele wartości. value.var
dcast(DT, formula = ID ~ Test, value.var = list("Result","Age"))
ID Result_OB_A Result_OB_B Result_OB_C Age_OB_A Age_OB_B Age_OB_C
1: a 1 4 7 20 20 20
2: b 2 5 8 21 21 21
3: c 3 6 9 22 22 22
Formuła
Rzutowanie jest kontrolowane za pomocą argumentu formuły w dcast
. Ma to postać RZĘDY ~ KOLUMNY
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
dcast(DT, formula = Test ~ ID, value.var = "Result")
Test a b c
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
Zarówno wiersze, jak i kolumny można rozwinąć o kolejne zmienne za pomocą +
dcast(DT, formula = ID + Age ~ Test, value.var = "Result")
ID Age OB_A OB_B OB_C
1: a 20 1 4 7
2: b 21 2 5 8
3: c 22 3 6 9
dcast(DT, formula = ID ~ Age + Test, value.var = "Result")
ID 20_OB_A 20_OB_B 20_OB_C 21_OB_A 21_OB_B 21_OB_C 22_OB_A 22_OB_B 22_OB_C
1: a 1 4 7 NA NA NA NA NA NA
2: b NA NA NA 2 5 8 NA NA NA
3: c NA NA NA NA NA NA 3 6 9
#order is important
dcast(DT, formula = ID ~ Test + Age, value.var = "Result")
ID OB_A_20 OB_A_21 OB_A_22 OB_B_20 OB_B_21 OB_B_22 OB_C_20 OB_C_21 OB_C_22
1: a 1 NA NA 4 NA NA 7 NA NA
2: b NA 2 NA NA 5 NA NA 8 NA
3: c NA NA 3 NA NA 6 NA NA 9
Przesyłanie może często tworzyć komórki, w których nie ma obserwacji w danych. Domyślnie jest to oznaczone przez NA
, jak wyżej. Możemy to zastąpić argumentem fill=
.
dcast(DT, formula = ID ~ Test + Age, value.var = "Result", fill = 0)
ID OB_A_20 OB_A_21 OB_A_22 OB_B_20 OB_B_21 OB_B_22 OB_C_20 OB_C_21 OB_C_22
1: a 1 0 0 4 0 0 7 0 0
2: b 0 2 0 0 5 0 0 8 0
3: c 0 0 3 0 0 6 0 0 9
Możesz także użyć dwóch specjalnych zmiennych w obiekcie formuły
-
.
nie reprezentuje żadnych innych zmiennych -
...
reprezentuje wszystkie inne zmienne
dcast(DT, formula = Age ~ ., value.var = "Result")
Age .
1: 20 3
2: 21 3
3: 22 3
dcast(DT, formula = ID + Age ~ ..., value.var = "Result")
ID Age OB_A OB_B OB_C
1: a 20 1 4 7
2: b 21 2 5 8
3: c 22 3 6 9
Agregowanie naszej wartości. Var
Możemy także rzutować i agregować wartości w jednym kroku. W tym przypadku mamy trzy obserwacje na każdym przecięciu Wiek i ID. Aby ustawić fun.aggregate
agregację, używamy argumentu fun.aggregate
:
#length
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = length)
ID 20 21 22
1: a 3 0 0
2: b 0 3 0
3: c 0 0 3
#sum
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = sum)
ID 20 21 22
1: a 12 0 0
2: b 0 15 0
3: c 0 0 18
#concatenate
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = function(x){paste(x,collapse = "_")})
ID 20 21 22
1: a 1_4_7
2: b 2_5_8
3: c 3_6_9
Możemy również przekazać listę do fun.aggregate
aby korzystać z wielu funkcji
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = list(sum,length))
ID Result_sum_20 Result_sum_21 Result_sum_22 Result_length_20 Result_length_21 Result_length_22
1: a 12 0 0 3 0 0
2: b 0 15 0 0 3 0
3: c 0 0 18 0 0 3
Jeśli przekażemy więcej niż jedną funkcję i więcej niż jedną wartość, możemy obliczyć wszystkie kombinacje, przekazując wektor value.vars
dcast(DT, formula = ID ~ Age, value.var = c("Result","Test"), fun.aggregate = list(function(x){paste0(x,collapse = "_")},length))
ID Result_function_20 Result_function_21 Result_function_22 Test_function_20 Test_function_21 Test_function_22 Result_length_20 Result_length_21
1: a 1_4_7 OB_A_OB_B_OB_C 3 0
2: b 2_5_8 OB_A_OB_B_OB_C 0 3
3: c 3_6_9 OB_A_OB_B_OB_C 0 0
Result_length_22 Test_length_20 Test_length_21 Test_length_22
1: 0 3 0 0
2: 0 0 3 0
3: 3 0 0 3
gdzie każda para jest obliczana w kolejności value1_formula1, value1_formula2, ... , valueN_formula(N-1), valueN_formulaN
.
Alternatywnie możemy ocenić nasze wartości i funkcje jeden do jednego, przekazując „value.var” jako listę:
dcast(DT, formula = ID ~ Age, value.var = list("Result","Test"), fun.aggregate = list(function(x){paste0(x,collapse = "_")},length))
ID Result_function_20 Result_function_21 Result_function_22 Test_length_20 Test_length_21 Test_length_22
1: a 1_4_7 3 0 0
2: b 2_5_8 0 3 0
3: c 3_6_9 0 0 3
Nazwy kolumn w wyniku
Domyślnie komponenty nazw kolumn są oddzielone znakiem podkreślenia _
. Można to ręcznie zastąpić za pomocą argumentu sep=
:
dcast(DT, formula = Test ~ ID + Age, value.var = "Result")
Test a_20 b_21 c_22
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
dcast(DT, formula = Test ~ ID + Age, value.var = "Result", sep = ",")
Test a,20 b,21 c,22
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
Spowoduje to oddzielenie każdego fun.aggregate
lub value.var
którego używamy:
dcast(DT, formula = Test ~ ID + Age, value.var = "Result", fun.aggregate = c(sum,length), sep = ",")
Test Result,sum,a,20 Result,sum,b,21 Result,sum,c,22 Result,length,a,20 Result,length,b,21 Result,length,c,22
1: OB_A 1 2 3 1 1 1
2: OB_B 4 5 6 1 1 1
3: OB_C 7 8 9 1 1 1
Układanie wielu tabel przy użyciu rbindlist
Powszechny refren w R przebiega następująco:
Nie powinieneś mieć wielu powiązanych tabel o nazwach takich jak
DT1
,DT2
, ...,DT11
. Iteracyjne czytanie i przypisywanie obiektów według nazwy jest nieporządne. Rozwiązaniem jest lista tabel danych!
Taka lista wygląda
set.seed(1)
DT_list = lapply(setNames(1:3, paste0("D", 1:3)), function(i)
data.table(id = 1:2, v = sample(letters, 2)))
$D1
id v
1: 1 g
2: 2 j
$D2
id v
1: 1 o
2: 2 w
$D3
id v
1: 1 f
2: 2 w
Inną perspektywą jest to, że powinieneś przechowywać te tabele razem jako jeden stół , układając je w stos. Jest to łatwe do zrobienia przy użyciu rbindlist
:
DT = rbindlist(DT_list, id="src")
src id v
1: D1 1 g
2: D1 2 j
3: D2 1 o
4: D2 2 w
5: D3 1 f
6: D3 2 w
Ten format ma o wiele większy sens w przypadku składni data.table, w której operacje „według grupy” są powszechne i proste.
Dla głębszego spojrzenia odpowiedź Gregora może być dobrym początkiem. Oczywiście sprawdź też ?rbindlist
. Istnieje osobny przykład obejmujący czytanie w kilku tabelach z CSV, a następnie układanie ich w stosy .