Мой примерный набор данных широк и содержит следующие значения:
olddata_wide <- read.table(header=TRUE, text='
subject sex a b c a1 b1 c1 a2 b2 c2
1 M 7.9 12.3 10.7 7.5 12.1 10.3 8.1 12.5 10.9
2 F 6.3 10.6 11.1 6.0 10.4 11.0 6.5 10.9 11.4
3 F 9.5 13.1 13.8 9.3 13.0 13.5 9.8 13.5 13.9
4 M 11.5 13.4 12.9 11.2 13.5 12.7 11.7 13.6 13.9
')
Я хотел бы преобразовать его в длинный набор данных. Проблема в том, что я хочу использовать несколько ключей одновременно - я хочу, чтобы столбцы a
и b
и c
стали одним длинным столбцом с именем value
, а также столбцы a1
и b1
и c1
в value1
и a2
и b2
и c2
к value3
. Итак, желаемый результат:
subject sex value valueType value1 valueType1 value2 valueType2
1: 1 M 7.9 a 7.5 a1 8.1 a2
2: 2 F 6.3 a 6.0 a1 6.5 a2
3: 3 F 9.5 a 9.3 a1 9.8 a2
4: 4 M 11.5 a 11.2 a1 11.7 a2
5: 1 M 12.3 b 12.1 b1 12.5 b2
6: 2 F 10.6 b 10.4 b1 10.9 b2
7: 3 F 13.1 b 13.0 b1 13.5 b2
8: 4 M 13.4 b 13.5 b1 13.6 b2
9: 1 M 10.7 c 10.3 c1 10.9 c2
10: 2 F 11.1 c 11.0 c1 11.4 c2
11: 3 F 13.8 c 13.5 c1 13.9 c2
12: 4 M 12.9 c 12.7 c1 13.9 c2
Я знаю, как программно получить желаемый результат для одного ключевого столбца:
keycol <- "valueType"
valuecol <- "value"
gathercols <- c("a", "b", "c")
gather_(olddata_wide, keycol, valuecol, gathercols)
Но как я могу сделать это для нескольких ключей одновременно?
Есть разные решения.
Если имена ваших столбцов не соответствуют определенному шаблону, я бы выбрал пакет sjmisc.
sjmisc::reshape_longer(
olddata_wide ,
columns = list(
c("a", "b", "c"),
c("a1", "b1", "c1"),
c("a2", "b2", "c2")),
values.to = c("value", "value1", "value2"))
Вы также можете использовать data.table.
melt(setDT(olddata_wide),
measure = patterns("^[^0-9]$","[abc]+1", "[abc]+2"),
variable.name = c("id"),
value.name = c("value","value1", "value2"))
Я отредактировал свой ответ с помощью решения data.table
Вот один из подходов с использованием melt()
и dcast()
из пакета data.table
.
setDT(olddata_wide)
setnames(olddata_wide, old=c("a","b","c"), new=c("a0", "b0", "c0"))
df_long = dcast(
melt(olddata_wide,c("subject","sex"),variable.name = "v")[,(c("v", "t")):=tstrsplit(v,"")],
subject+sex+v~t, value.var = "value",
)
df_long[, .(subject, sex, value=`0`,valueType=v, value1=`1`,valueType1 = paste0(v,"1"), value2=`2`,valueType2 = paste0(v,"2"))]
Вывод:
subject sex value valueType value1 valueType1 value2 valueType2
1: 1 M 7.9 a 7.5 a1 8.1 a2
2: 1 M 12.3 b 12.1 b1 12.5 b2
3: 1 M 10.7 c 10.3 c1 10.9 c2
4: 2 F 6.3 a 6.0 a1 6.5 a2
5: 2 F 10.6 b 10.4 b1 10.9 b2
6: 2 F 11.1 c 11.0 c1 11.4 c2
7: 3 F 9.5 a 9.3 a1 9.8 a2
8: 3 F 13.1 b 13.0 b1 13.5 b2
9: 3 F 13.8 c 13.5 c1 13.9 c2
10: 4 M 11.5 a 11.2 a1 11.7 a2
11: 4 M 13.4 b 13.5 b1 13.6 b2
12: 4 M 12.9 c 12.7 c1 13.9 c2
Вот еще один подход (также использует data.table
)
do.call(cbind,lapply(1:3, \(i) {
res = melt(setDT(olddata_wide[,c(1:2,((i*3):(i*3+2)))]),c("subject","sex"),variable.name = "valueType")
if (i>1) res <- setNames(res[,-c(1,2)],paste0(names(res)[-c(1,2)],i-1))
res
}))
Вот (довольно неуклюжий) tidyverse
подход:
olddata_wide %>%
pivot_longer(matches("^[abc]"), names_to = "valueType") %>%
mutate(suffix = str_remove(valueType, "^.")) %>%
pivot_wider(
names_from = "suffix", values_from = c("value", "valueType"), names_sep = "", values_fn = list) %>%
unnest(matches("value"))
## A tibble: 12 × 8
# subject sex value value1 value2 valueType valueType1 valueType2
# <int> <chr> <dbl> <dbl> <dbl> <chr> <chr> <chr>
# 1 1 M 7.9 7.5 8.1 a a1 a2
# 2 1 M 12.3 12.1 12.5 b b1 b2
# 3 1 M 10.7 10.3 10.9 c c1 c2
# 4 2 F 6.3 6 6.5 a a1 a2
# 5 2 F 10.6 10.4 10.9 b b1 b2
# 6 2 F 11.1 11 11.4 c c1 c2
# 7 3 F 9.5 9.3 9.8 a a1 a2
# 8 3 F 13.1 13 13.5 b b1 b2
# 9 3 F 13.8 13.5 13.9 c c1 c2
#10 4 M 11.5 11.2 11.7 a a1 a2
#11 4 M 13.4 13.5 13.6 b b1 b2
#12 4 M 12.9 12.7 13.9 c c1 c2
Общая идея состоит в том, чтобы изменить форму всех столбцов, соответствующих «^[abc]», из широких в длинные, а затем перестроить в широкий формат в соответствии с ожидаемым результатом.
data.table
Идея:
olddata_wide[, melt(
.SD,
id.vars = c("subject", "sex"),
measure.vars = patterns(valueType = "[a-c]$", valueType1 = '1$', valueType2 = '2$'))
][, variable := letters[variable]][]
# subject sex variable valueType valueType1 valueType2
# <int> <char> <char> <num> <num> <num>
# 1: 1 M a 7.9 7.5 8.1
# 2: 2 F a 6.3 6.0 6.5
# 3: 3 F a 9.5 9.3 9.8
# 4: 4 M a 11.5 11.2 11.7
# 5: 1 M b 12.3 12.1 12.5
# 6: 2 F b 10.6 10.4 10.9
# 7: 3 F b 13.1 13.0 13.5
# 8: 4 M b 13.4 13.5 13.6
# 9: 1 M c 10.7 10.3 10.9
# 10: 2 F c 11.1 11.0 11.4
# 11: 3 F c 13.8 13.5 13.9
# 12: 4 M c 12.9 12.7 13.9
не могли бы объяснить, что делает последняя часть? [, переменная := буквы[переменная]][] На самом деле я использую разные имена столбцов для своих реальных данных, но в итоге у меня есть столбец переменных с содержимым a, b и c
Хорошо, какие шаблоны вы используете для valueType
? Возможно, вы могли бы повторно использовать его так: [, variable := grep("[a-c]$", names(olddata_wide), value = TRUE)[variable]][]
Я использую подход data.table, аналогичный некоторым другим, но разбиваю процесс на более мелкие дискретные шаги, что позволяет (я думаю) легко модифицировать процедуру, хотя и с большим количеством строк кода.
Исходный набор данных преобразован в объект data.table.
library(data.table)
olddata_wide <- read.table(header = TRUE, text = '
subject sex a b c a1 b1 c1 a2 b2 c2
1 M 7.9 12.3 10.7 7.5 12.1 10.3 8.1 12.5 10.9
2 F 6.3 10.6 11.1 6.0 10.4 11.0 6.5 10.9 11.4
3 F 9.5 13.1 13.8 9.3 13.0 13.5 9.8 13.5 13.9
4 M 11.5 13.4 12.9 11.2 13.5 12.7 11.7 13.6 13.9
')
setDT(olddata_wide)
Преобразование в форму блочной записи со всеми числовыми значениями в одном столбце.
DT <- melt(olddata_wide,
id.vars = c("subject", "sex"),
variable.name = "type",
value.name = "value",
variable.factor = FALSE)
DT
#> subject sex type value
#> <int> <char> <char> <num>
#> 1: 1 M a 7.9
#> 2: 2 F a 6.3
#> 3: 3 F a 9.5
#> 4: 4 M a 11.5
#> 5: 1 M b 12.3
#> 6: 2 F b 10.6
#> ---
#> 31: 3 F b2 13.5
#> 32: 4 M b2 13.6
#> 33: 1 M c2 10.9
#> 34: 2 F c2 11.4
#> 35: 3 F c2 13.9
#> 36: 4 M c2 13.9
Назначьте valueType
соответствующие нижним индексам на type
(например, a, a1, a2 и т. д.)
DT[, valueType := fcase(
type %ilike% "1", "value1",
type %ilike% "2", "value2",
default = "value0"
)]
DT
#> subject sex type value valueType
#> <int> <char> <char> <num> <char>
#> 1: 1 M a 7.9 value0
#> 2: 2 F a 6.3 value0
#> 3: 3 F a 9.5 value0
#> 4: 4 M a 11.5 value0
#> 5: 1 M b 12.3 value0
#> 6: 2 F b 10.6 value0
#> ---
#> 31: 3 F b2 13.5 value2
#> 32: 4 M b2 13.6 value2
#> 33: 1 M c2 10.9 value2
#> 34: 2 F c2 11.4 value2
#> 35: 3 F c2 13.9 value2
#> 36: 4 M c2 13.9 value2
Удалите индексы из a1, a2, b1 и т. д., чтобы облегчить следующее преобразование.
DT[, type := substr(type, 1, 1)]
DT
#> subject sex type value valueType
#> <int> <char> <char> <num> <char>
#> 1: 1 M a 7.9 value0
#> 2: 2 F a 6.3 value0
#> 3: 3 F a 9.5 value0
#> 4: 4 M a 11.5 value0
#> 5: 1 M b 12.3 value0
#> 6: 2 F b 10.6 value0
#> ---
#> 31: 3 F b 13.5 value2
#> 32: 4 M b 13.6 value2
#> 33: 1 M c 10.9 value2
#> 34: 2 F c 11.4 value2
#> 35: 3 F c 13.9 value2
#> 36: 4 M c 13.9 value2
Трансформируйте в желаемую форму.
DT <- dcast(DT, subject + sex + type ~ valueType, value.var = "value")
setorderv(DT, c("type", "subject"))
DT
#> subject sex type value0 value1 value2
#> <int> <char> <char> <num> <num> <num>
#> 1: 1 M a 7.9 7.5 8.1
#> 2: 2 F a 6.3 6.0 6.5
#> 3: 3 F a 9.5 9.3 9.8
#> 4: 4 M a 11.5 11.2 11.7
#> 5: 1 M b 12.3 12.1 12.5
#> 6: 2 F b 10.6 10.4 10.9
#> 7: 3 F b 13.1 13.0 13.5
#> 8: 4 M b 13.4 13.5 13.6
#> 9: 1 M c 10.7 10.3 10.9
#> 10: 2 F c 11.1 11.0 11.4
#> 11: 3 F c 13.8 13.5 13.9
#> 12: 4 M c 12.9 12.7 13.9
к сожалению, я не могу установить этот пакет (политика компании), какой другой вариант?