data.table
Перестроение, укладка и разбиение
Поиск…
замечания
Официальная виньетка «Эффективная перестройка с использованием data.tables» - лучшее введение в эту тему.
Многие задачи перестройки требуют перехода между длинными и широкими форматами:
- Широкие данные - это данные с каждым столбцом, представляющим отдельную переменную, а строки, представляющие отдельные наблюдения
- Длинные данные - это данные с идентификатором формы | переменная | значение, где каждая строка, представляющая пару переменных наблюдения
расплав и литье с данными.table
data.table
предлагает широкий спектр возможностей для изменения ваших данных как эффективно, так и легко
Например, при изменении формы от long to wide вы можете передавать несколько переменных в value.var
и в параметры fun.aggregate
одновременно
library(data.table) #v>=1.9.6
DT <- data.table(mtcars)
Длинные
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
Это установит gear
как индексную колонку, тогда как mean
и sum
будут рассчитаны для disp
и hp
для каждой комбинации gear
и cyl
. В случае, если некоторые комбинации не существуют, вы можете указать дополнительные параметры, такие как na.rm = TRUE
(которые будут переданы в mean
и sum
функции) или указать встроенный аргумент fill
. Вы также можете добавлять поля, оставлять недостающие комбинации и подмножество данных. См. Больше в ?data.table::dcast
Широкий и длинный
При перестройке с широким и длинным вы можете передавать столбцы в параметр measure.vars
используя регулярные выражения, например
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
Это будет melt
данные по cyl
и gear
в качестве столбцов индекса, в то время как все значения для переменных , которые начинаются с d
( disp
& drat
) будет присутствовать в value1
и значения для переменных , которые содержат букву e
в них ( qsec
и gear
) будут присутствовать в столбце value2
.
Вы также можете переименовать все имена столбцов в результате при указании аргументов variable.name
и value.name
или решить, хотите ли вы, чтобы столбцы character
были автоматически преобразованы в factor
s или нет при определении аргументов variable.factor
и value.factor
. См. Больше в ?data.table::melt
Изменить форму с помощью `data.table`
data.table
расширяет функции reshape2
melt
& dcast
( Ссылка: эффективная перестройка с использованием 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
Длинные
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
В нескольких столбцах (с 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
Широкий и длинный
## 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
Переход от широкого до длинного формата с использованием расплава
Плавление: основы
Плавление используется для преобразования данных с широкоформатного формата.
Начиная с широкого набора данных:
DT = data.table(ID = letters[1:3], Age = 20:22, OB_A = 1:3, OB_B = 4:6, OB_C = 7:9)
Мы можем расплавить наши данные, используя функцию melt
в таблице данных. Это возвращает другую таблицу данных в длинном формате:
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"
Любые столбцы, не заданные в параметре id.vars
считаются переменными. В качестве альтернативы мы можем установить их явно с measure.vars
аргумента 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
В этом случае любые столбцы, не установленные в measure.vars
считаются идентификаторами.
Если мы установим оба явно, он вернет только выбранные столбцы:
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
Именование переменных и значений в результате
Мы можем манипулировать именами столбцов возвращаемой таблицы с помощью variable.name
и 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
Настройка типов для переменных измерения в результате
По умолчанию, таяние data.table преобразует все measure.vars
в факторы:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT[, variable])
# "factor"
Чтобы вместо этого указать символ, используйте аргумент variable.factor
:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), variable.factor = FALSE)
class(M_DT[, variable])
# "character"
Значения, как правило, наследуются от типа данных исходного столбца:
class(DT[, value])
# "integer"
class(M_DT[, value])
# "integer"
Если есть конфликт, типы данных будут принуждаться. Например:
M_DT <- melt(DT,id.vars = c("Age"), measure.vars = c("ID","OB_C"))
class(M_DT[, value])
# "character"
При плавлении любые фактор-переменные будут принуждаться к типу символа:
DT[, OB_C := factor(OB_C)]
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT)
# "character"
Чтобы избежать этого и сохранить начальное типирование, используйте аргумент value.factor
:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), value.factor = TRUE)
class(M_DT)
# "factor"
Обработка отсутствующих значений
По умолчанию любые значения NA
сохраняются в расплавленных данных
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
Если они должны быть удалены из ваших данных, установите 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
Переход от длинного к широкому формату с использованием dcast
Кастинг: Основы
Кастинг используется для преобразования данных из длинного в широкий формат.
Начиная с длинного набора данных:
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)
Мы можем отливать наши данные , используя dcast
функции в data.table. Это возвращает другую таблицу данных в широком формате:
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"
Значение ценности
Аргумент value.var
необходим для правильного приведения - если не предусмотрено, dcast сделает предположение на основе ваших данных.
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
В value.var
можно value.var
несколько value.var
s
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
формула
Кастинг контролируется с помощью аргумента формулы в dcast
. Это имеет вид ROWS ~ COLUMNS
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
Как строки, так и столбцы можно развернуть с помощью дополнительных переменных, используя +
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
Кастинг часто может создавать ячейки, где в данных не наблюдается наблюдения. По умолчанию это обозначается NA
, как указано выше. Мы можем переопределить это с помощью аргумента 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
Вы также можете использовать две специальные переменные в объекте формулы
-
.
не представляет никаких других переменных -
...
представляет все остальные переменные
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
Совокупность нашего значения.
Мы также можем приводить и агрегировать значения за один шаг. В этом случае у нас есть три наблюдения на каждом из пересечений Age и ID. Чтобы установить то, что мы хотим, мы используем аргумент 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
Мы также можем передать список для fun.aggregate
чтобы использовать несколько функций
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
Если мы передаем более одной функции и более одного значения, мы можем вычислить все комбинации, передав вектор 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
где каждая пара вычисляется в value1_formula1, value1_formula2, ... , valueN_formula(N-1), valueN_formulaN
.
В качестве альтернативы мы можем оценивать наши значения и функции взаимно однозначно, передавая «value.var» в виде списка:
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
Именование столбцов в результате
По умолчанию компоненты имени столбца разделяются символом подчеркивания _
. Это можно вручную переопределить с помощью аргумента 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
Это будет fun.aggregate
любые fun.aggregate
или value.var
мы используем:
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
Укладка нескольких таблиц с помощью rbindlist
Общий рефрен в R идет по этим линиям:
У вас не должно быть кучи связанных таблиц с такими именами, как
DT1
,DT2
, ...,DT11
. Итеративное чтение и назначение объектов по имени беспорядочно. Решение представляет собой список таблиц данных!
Такой список выглядит
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
Другая перспектива заключается в том, что вы должны хранить эти таблицы вместе как одну таблицу , путем их укладки. Это просто сделать, используя 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
Этот формат дает больше смысла с синтаксисом data.table, где операции «по группам» являются общими и понятными.
Для более глубокого взгляда ответ Грегора может быть хорошим началом. ?rbindlist
, ?rbindlist
. Есть отдельный пример, охватывающий чтение в кучу таблиц из CSV, а затем их укладку .