Преобразование данных из длинного формата в широкий формат с несколькими столбцами измерений
у меня возникли проблемы с определением наиболее элегантного и гибкого способа переключения данных из длинного формата в широкий формат, когда у меня есть более одной переменной измерения, которую я хочу взять с собой.
например, вот простой фрейм данных в длинном формате. ID
в этой теме TIME
- это переменная времени, а X и Y-измерения, сделанные из ID
at TIME
:
> my.df <- data.frame(ID=rep(c("A","B","C"), 5), TIME=rep(1:5, each=3), X=1:15, Y=16:30)
> my.df
ID TIME X Y
1 A 1 1 16
2 B 1 2 17
3 C 1 3 18
4 A 2 4 19
5 B 2 5 20
6 C 2 6 21
7 A 3 7 22
8 B 3 8 23
9 C 3 9 24
10 A 4 10 25
11 B 4 11 26
12 C 4 12 27
13 A 5 13 28
14 B 5 14 29
15 C 5 15 30
если бы я просто хотел превратить значения времени в заголовки столбцов, содержащие include X, я знаю, что могу использовать cast из пакета reshape (или dcast из reshape2):
> cast(my.df, ID ~ TIME, value="X")
ID 1 2 3 4 5
1 A 1 4 7 10 13
2 B 2 5 8 11 14
3 C 3 6 9 12 15
но то, что я действительно хочу сделать, это также принести Y в качестве другой переменной меры, и имена столбцов отражают как имя переменной меры, так и значение времени:
ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1 A 1 4 7 10 13 16 19 22 25 28
2 B 2 5 8 11 14 17 20 23 26 29
3 C 3 6 9 12 15 18 21 24 27 30
(FWIW, мне все равно, если все X сначала следуют Y, или если они чередуются как X_1
, Y_1
, X_2
, Y_2
, etc.)
я могу приблизиться к этому cast
-ing длинные данные дважды и слияние результатов, хотя имена столбцов нуждаются в некоторой работе, и мне нужно будет настроить его, если мне нужно добавить 3-ю или 4-ю переменную в дополнение к X и Y:
merge(
cast(my.df, ID ~ TIME, value="X"),
cast(my.df, ID ~ TIME, value="Y"),
by="ID", suffixes=c("_X","_Y")
)
похоже на некоторую комбинацию функций в reshape2
и/или plyr
должен быть в состоянии сделать это более элегантно, чем моя попытка, а также обрабатывать несколько переменных измерения более чисто. Что-то вроде cast(my.df, ID ~ TIME, value=c("X","Y"))
, что недопустимо. Но я не смог понять это из.
может ли любой R-wizards помочь мне? Спасибо.
4 ответов
для обработки нескольких переменных, как вы хотите, вам нужно melt
данные, которые у вас есть, прежде чем бросить его.
library("reshape2")
dcast(melt(my.df, id.vars=c("ID", "TIME")), ID~variable+TIME)
что дает
ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1 A 1 4 7 10 13 16 19 22 25 28
2 B 2 5 8 11 14 17 20 23 26 29
3 C 3 6 9 12 15 18 21 24 27 30
редактировать на основе комментария:
фрейм данных
num.id = 10
num.time=10
my.df <- data.frame(ID=rep(LETTERS[1:num.id], num.time),
TIME=rep(1:num.time, each=num.id),
X=1:(num.id*num.time),
Y=(num.id*num.time)+1:(2*length(1:(num.id*num.time))))
дает другой результат (все записи 2) потому что ID
/TIME
сочетание не указывает на уникальную строку. На самом деле, есть две строки с каждым ID
/TIME
комбинаций. reshape2
предполагает одно значение для всех возможных сочетаний переменных и применить функцию для создания одной переменной есть несколько записей. Вот почему есть предупреждение
Aggregation function missing: defaulting to length
вы можете получить что-то, что работает, если вы добавите другую переменную, которая нарушает эту избыточность.
my.df$cycle <- rep(1:2, each=num.id*num.time)
dcast(melt(my.df, id.vars=c("cycle", "ID", "TIME")), cycle+ID~variable+TIME)
это работает, потому что cycle
/ID
/ однозначно определяет строку в my.df
.
reshape(my.df,
idvar = "ID",
timevar = "TIME",
direction = "wide")
дает
ID X.1 Y.1 X.2 Y.2 X.3 Y.3 X.4 Y.4 X.5 Y.5
1 A 1 16 4 19 7 22 10 25 13 28
2 B 2 17 5 20 8 23 11 26 14 29
3 C 3 18 6 21 9 24 12 27 15 30
С помощью data.table_1.9.5
, Это можно сделать без melt
как он может обрабатывать несколько value.var
столбцы. Вы можете установить его из here
library(data.table)
dcast(setDT(my.df), ID~TIME, value.var=c('X', 'Y'))
# ID 1_X 2_X 3_X 4_X 5_X 1_Y 2_Y 3_Y 4_Y 5_Y
#1: A 1 4 7 10 13 16 19 22 25 28
#2: B 2 5 8 11 14 17 20 23 26 29
#3: C 3 6 9 12 15 18 21 24 27 30
вот решение с tidyr пакета, который по существу заменил изменить и reshape2. Как и в случае с этими двумя пакетами, стратегия заключается в том, чтобы сначала сделать набор данных длиннее, а затем шире.
library(magrittr); requireNamespace("tidyr"); requireNamespace("dplyr")
my.df %>%
tidyr::gather_(key="variable", value="value", c("X", "Y")) %>% # Make it even longer.
dplyr::mutate( # Create the spread key.
time_by_variable = paste0(variable, "_", TIME)
) %>%
dplyr::select(ID, time_by_variable, value) %>% # Retain these three.
tidyr::spread(key=time_by_variable, value=value) # Spread/widen.
после tidyr::gather()
вызов, промежуточный набор данных:
ID TIME variable value
1 A 1 X 1
2 B 1 X 2
3 C 1 X 3
...
28 A 5 Y 28
29 B 5 Y 29
30 C 5 Y 30
возможный результат:
ID X_1 X_2 X_3 X_4 X_5 Y_1 Y_2 Y_3 Y_4 Y_5
1 A 1 4 7 10 13 16 19 22 25 28
2 B 2 5 8 11 14 17 20 23 26 29
3 C 3 6 9 12 15 18 21 24 27 30
tidyr::unite()
альтернатива, предложенная @JWilliman. Это функционально эквивалентно dplyr::mutate()
и dplyr::select()
комбинация выше, когда имеет значение true (по умолчанию).
если вы не привыкли к такому типу манипуляций, то tidyr::unite()
может быть небольшое препятствие, потому что это еще одна функция, которую вы должны изучить и запомнить. Тем не менее, это преимущества включают (a) более краткий код (ie, четыре строки заменяются на одну) и (b) меньше мест для повторите имена переменных (ie, вам не нужно повторять / изменять переменные в dplyr::select()
предложения).
my.df %>%
tidyr::gather_(key="variable", value="value", c("X", "Y")) %>% # Make it even longer.
tidyr::unite("time_by_variable", variable, TIME, remove=T) %>% # Create the spread key `time_by_variable` while simultaneously dropping `variable` and `TIME`.
tidyr::spread(key=time_by_variable, value=value) # Spread/widen.