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

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

например, вот простой фрейм данных в длинном формате. 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.