Подтвердить что ты не робот

Преобразование данных из длинного формата в широкоформатный с несколькими столбцами измерения

У меня возникли проблемы с выяснением наиболее элегантного и гибкого способа переключения данных с длинного формата на широкий формат, когда у меня есть более одной переменной измерения, которую я хочу взять с собой.

Например, вот простой фрейм данных в длинном формате. ID - субъект, TIME - временная переменная, а X и Y - измерения, сделанные из ID в 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

Если бы я просто хотел повернуть значения TIME в заголовки столбцов, содержащие include X, я знаю, что могу использовать листинг из пакета 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 и т.д.)

Я могу приблизиться к этому, дважды вставляя длинные данные и объединяя результаты, хотя имена столбцов требуют некоторой работы, и мне нужно будет настроить его, если мне нужно добавить третью или четвертую переменную в дополнение к 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-мастера помочь мне? Спасибо.

4b9b3361

Ответ 1

Чтобы обрабатывать несколько переменных, как вы хотите, вам нужно 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

EDIT на основе комментария:

Кадр данных

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/TIME теперь однозначно определяет строку в my.df.

Ответ 2

   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

Ответ 3

Используя 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

Ответ 4

Здесь решение с пакетом tidyr, которое существенно заменило изменение и изменение2. Как и в случае с этими двумя пакетами, стратегия состоит в том, чтобы сначала взять набор данных, а затем шире.

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