R: развернуть фрейм данных по столбцам со сдвинутыми строками данных - PullRequest
0 голосов
/ 05 мая 2018

- Пример данных для работы с:

Чтобы создать сокращенный пример, это вывод dput (df):

df <- structure(list(SubjectID = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 
3L, 3L), .Label = c("1", "2", "3"), class = "factor"), EventNumber = structure(c(1L, 
1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L), .Label = c("1", "2"), class = "factor"), 
    EventType = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 
    1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L
    ), .Label = c("A", "B"), class = "factor"), Param1 = c(0.3, 
    0.21, 0.87, 0.78, 0.9, 1.2, 1.4, 1.3, 0.6, 0.45, 0.45, 0.04, 
    0, 0.1, 0.03, 0.01, 0.09, 0.06, 0.08, 0.09, 0.03, 0.04, 0.04, 
    0.02), Param2 = c(45, 38, 76, 32, 67, 23, 27, 784, 623, 54, 
    54, 1056, 487, 341, 671, 859, 7769, 2219, 4277, 4060, 411, 
    440, 224, 57), Param3 = c(1.5, 1.7, 1.65, 1.32, 0.6, 0.3, 
    2.5, 0.4, 1.4, 0.67, 0.67, 0.32, 0.1, 0.15, 0.22, 0.29, 0.3, 
    0.2, 0.8, 1, 0.9, 0.8, 0.3, 0.1), Param4 = c(0.14, 0, 1, 
    0.86, 0, 0.6, 1, 1, 0.18, 0, 0, 0.39, 0, 1, 0.29, 0.07, 0.33, 
    0.53, 0.29, 0.23, 0.84, 0.61, 0.57, 0.59), Param5 = c(0.18, 
    0, 1, 0, 1, 0, 0.09, 1, 0.78, 0, 0, 1, 0.2, 0, 0.46, 0.72, 
    0.16, 0.22, 0.77, 0.52, 0.2, 0.68, 0.58, 0.17), Param6 = c(0, 
    1, 0.75, 0, 0.14, 0, 1, 0, 1, 0.27, 0, 1, 0, 0.23, 0.55, 
    0.86, 1, 0.33, 1, 1, 0.88, 0.75, 0, 0), AbsoluteTime = structure(c(1522533600, 
    1522533602, 1522533604, 1522533604, 1525125600, 1525125602, 
    1525125604, 1519254000, 1519254002, 1519254004, 1519254006, 
    1521759600, 1521759602, 1521759604, 1521759606, 1521759608, 
    1517353224, 1517353226, 1517353228, 1517353230, 1517439600, 
    1517439602, 1517439604, 1517439606), class = c("POSIXct", 
    "POSIXt"), tzone = "")), row.names = c(NA, -24L), class = "data.frame")
df

Реальные данные имеют 20 субъектов, EventNumbers от 1 до 100, а параметры от Param1 до Param40 (в зависимости от эксперимента). Количество строк около 60 000 наблюдений.

- Чего я хочу достичь:

Для df создайте n * 40 новых столбцов. # (40 или любое количество параметров, которые будут выбраны позже.)

Думайте о n как о «шагах в будущее». Назовите 40 * n вновь созданных столбцов:

Param1_2, Param2_2, Param3_2, ..., Param39_2, Param40_2, ...,

Param1_3, Param2_3, Param3_3, ..., Param39_3, Param40_3, ...,

...

Param1_n, Param2_n, Param3_n, ..., Param39_n, Param40_n

В результате в столбцах

Param1_1, Param2_1, Param1_2, Param2_2, Param1_3, Param2_3, Param1_4, Param2_4, ... Param1_n, Param2_n

Таким образом, каждое наблюдение подмножества df[X, c(4:9)] будет получать дополнительный набор переменных со значениями от df[X+1, c(4:9)] до df[X+n, c(4:9)].

Вот как должен выглядеть новый файл df.extended для n = 1:

df.extended <- structure(list(SubjectID = c(1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3), EventNumber = c(1, 1, 
1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 
2), EventType = c("A", "A", "A", "A", "B", "B", "B", "A", "A", 
"A", "A", "B", "B", "B", "B", "B", "A", "A", "A", "A", "B", "B", 
"B", "B"), Param1 = c(0.3, 0.21, 0.87, 0.78, 0.9, 1.2, 1.4, 1.3, 
0.6, 0.45, 0.45, 0.04, 0, 0.1, 0.03, 0.01, 0.05, 0.07, 0.06, 
0.01, 0.01, 0.01, 0.07, 0.04), Param2 = c(45, 38, 76, 32, 67, 
23, 27, 784, 623, 54, 54, 1056, 487, 341, 671, 859, 1858, 640, 
8181, 220, 99, 86, 170, 495), Param3 = c(1.5, 1.7, 1.65, 1.32, 
0.6, 0.3, 2.5, 0.4, 1.4, 0.67, 0.67, 0.32, 0.1, 0.15, 0.22, 0.29, 
1.5, 0.9, 0.8, 0.9, 0.1, 0, 0.8, 0.1), Param4 = c(0.14, 0, 1, 
0.86, 0, 0.6, 1, 1, 0.18, 0, 0, 0.39, 0, 1, 0.29, 0.07, 0.64, 
0.11, 0.12, 0.32, 0.55, 0.67, 0.83, 0.82), Param5 = c(0.18, 0, 
1, 0, 1, 0, 0.09, 1, 0.78, 0, 0, 1, 0.2, 0, 0.46, 0.72, 0.27, 
0.14, 0.7, 0.67, 0.23, 0.44, 0.61, 0.76), Param6 = c(0, 1, 0.75, 
0, 0.14, 0, 1, 0, 1, 0.27, 0, 1, 0, 0.23, 0.55, 0.86, 1, 0.56, 
0.45, 0.5, 0, 0, 0.89, 0.11), AbsoluteTime = c("2018-04-01 00:00:00", 
"2018-04-01 00:00:02", "2018-04-01 00:00:04", "2018-04-01 00:00:04", 
"2018-05-01 00:00:00", "2018-05-01 00:00:02", "2018-05-01 00:00:04", 
"2018-02-22 00:00:00", "2018-02-22 00:00:02", "2018-02-22 00:00:04", 
"2018-02-22 00:00:06", "2018-03-23 00:00:00", "2018-03-23 00:00:02", 
"2018-03-23 00:00:04", "2018-03-23 00:00:06", "2018-03-23 00:00:08", 
"2018-01-31 00:00:24", "2018-01-31 00:00:26", "2018-01-31 00:00:28", 
"2018-01-31 00:00:30", "2018-02-01 00:00:00", "2018-02-01 00:00:02", 
"2018-02-01 00:00:04", "2018-02-01 00:00:06"), Param1_2 = c(0.21, 
0.87, 0.78, NA, 1.2, 1.4, NA, 0.6, 0.45, 0.45, NA, 0, 0.1, 0.03, 
0.01, NA, 0.07, 0.07, 0.08, NA, 0.09, 0.06, 0.01, NA), Param2_2 = c(38, 
76, 32, NA, 23, 27, NA, 623, 54, 54, NA, 487, 341, 671, 859, 
NA, 6941, 4467, 808, NA, 143, 301, 219, NA), Param3_2 = c(1.7, 
1.65, 1.32, NA, 0.3, 2.5, NA, 1.4, 0.67, 0.67, NA, 0.1, 0.15, 
0.22, 0.29, NA, 1, 1, 0.1, NA, 0.5, 1, 0.3, NA), Param4_2 = c(0, 
1, 0.86, NA, 0.6, 1, NA, 0.18, 0, 0, NA, 0, 1, 0.29, 0.07, NA, 
0.31, 0.16, 0.68, NA, 0.86, 0.47, 0.47, NA), Param5_2 = c(0, 
1, 0, NA, 0, 0.09, NA, 0.78, 0, 0, NA, 0.2, 0, 0.46, 0.72, NA, 
0.29, 0.26, 0.1, NA, 0.88, 0.86, 0.95, NA), Param6_2 = c(1, 0, 
0, NA, 0, 1, NA, 1, 0.27, 0, NA, 0, 0.23, 0.55, 0.86, NA, 0.68, 
0.66, 0, NA, 0.44, 1, 0.22, NA)), row.names = c(NA, 24L), class = "data.frame")
df.extended

Как это можно решить без использования циклов, написания индексов столбцов вручную и т. Д .? Напишите функцию для пробной версии 2 и используйте doBy?

Мои мысли и что я уже сделал, чтобы решить это:

  1. Пробная версия 1:

    1. Цикл по SubjectIDs в цикле for
    2. Во внутреннем цикле for циклически перебирать EventNumber
    3. В другом внутреннем цикле for циклически перебирайте строки
    4. Получить первую строку, взяв df [1,] и сохранить в df.temp
    5. Объединить df.temp с df [2, параметры] #
    6. Объединить объединить df.temp с df [3, параметры] и т. Д.
    7. Сохранить все полученные df.temps в df.final

    Проблемы, с которыми я столкнулся: Шаг 5:

    df.temp <- df[1,]
    df.temp <- merge(df.temp, df[2, !(colnames(df) == "AbsoluteTime")], by = c("SubjectID", "EventNumber", "EventType"))
    df.temp <- merge(df.temp, df[3, !(colnames(df) == "AbsoluteTime")], by = c("SubjectID", "EventNumber", "EventType"))
    df.temp <- merge(df.temp, df[4, !(colnames(df) == "AbsoluteTime")], by = c("SubjectID", "EventNumber", "EventType"))
    Warning:
    In merge.data.frame(df.temp, df[4, ], by = c("SubjectID", "EventNumber",  :
      column names ‘Param1.x’, ‘Param2.x’, ‘Param3.x’, ‘Param4.x’, ‘Param5.x’, ‘Param6.x’, ‘AbsoluteTime.x’, ‘Param1.y’, ‘Param2.y’,
    

    "Param3.y", "Param4.y", "Param5.y", "Param6.y", "AbsoluteTime.y" являются дублируется в результате.

    • Имена столбцов повторяются, см. Предупреждение.
    • Я не могу понять, как легко создавать имена столбцов / переименовывать новые столбцы на основе заданного имени столбца и переменной.

    Должен быть лучший путь, чем этот:

    n <- 3 
    names_vector <- c()
    for (n in seq(from = c(1), to = n)) {
      for (i in names(df[4:9])) {
      names_vector <- c(names_vector, paste0(i, "_", c(n+1)))
        }
    }
    names(df.temp)[c(4:9)] <- parameters
    names(df.temp)[c(11:ncol(df.temp))] <- names_vector
    names(df.temp)
    
    • Кроме того, как я могу предотвратить нарушение скрипта последними n-1 строками? Это большая работа, которую нужно выполнять вручную, и я думаю, что она может привести к ошибкам!
  2. Пробная версия 2:

    1. Цикл по SubjectIDs в цикле for
    2. Во внутреннем цикле for циклически перебирать EventNumber
    3. Получить все строки параметров в новом фрейме данных, кроме первой строки
    4. Добавить строку с NA
    5. используйте cbind () для объединения строк
    6. Повторите n раз.

    Это код для одного SubjectID и одного EventNumber:

    df.temp <- df[which(df$SubjectID == "1" & df$EventNumber == "1"), ]
    df.temp2 <- df.temp[2:nrow(df.temp)-1, parameters]
    df.temp2 <- rbind(df.temp2, NA)
    df.temp <- cbind(df.temp, df.temp2)
    df.temp2 <- df.temp[3:nrow(df.temp)-1, parameters]
    df.temp2 <- rbind(df.temp2, NA, NA)
    df.temp <- cbind(df.temp, df.temp2)
    df.temp2 <- df.temp[4:nrow(df.temp)-1, parameters]
    df.temp2 <- rbind(df.temp2, NA, NA, NA)
    df.temp <- cbind(df.temp, df.temp2)
    n <- 3
    names_vector <- c()
    for (n in seq(from = c(1), to = n)) {
      for (i in names(df[4:9])) {
        print(i)
        print(n)
        names_vector <- c(names_vector, paste0(i, "_", c(n+1)))
      }
    }
    names(df.temp)[c(4:9)] <- parameters
    names(df.temp)[c(11:ncol(df.temp))] <- names_vector
    df.temp
    
    • Это решает проблему с пропущенными строками (в моем случае допустимы NA).
    • Все еще много работы вручную / для циклов и ошибок!?

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

Для базы R рассмотрим by для нарезки на SubjectID , EventNumber и EventType , и запустите merge с помощью помощника group_num . И чтобы перейти к ряду параметров, оберните процесс by в lapply для списка кадров данных, которые вы объединяете в цепочку снаружи для окончательного слияния с исходным кадром данных:

df_list <- lapply(2:3, function(i) {
  # BUILD LIST OF DATAFRAMES
  by_list <- by(df, df[c("SubjectID", "EventNumber", "EventType")], FUN=function(sub){

    sub$grp_num <- 1:nrow(sub)
    row_less_sub <- transform(sub, AbsoluteTime=NULL, grp_num=grp_num-(i-1))

    merge(sub, row_less_sub, by=c("SubjectID", "EventNumber", "EventType", "grp_num"), 
          all.x=TRUE, suffixes = c("", paste0("_", i)))
  })

  # APPEND ALL DATAFRAMES IN LIST
  grp_df <- do.call(rbind, by_list)
  grp_df <- with(grp_df, grp_df[order(SubjectID, EventNumber),])
  # KEEP NEEDED COLUMNS
  grp_df <- grp_df[c("SubjectID", "EventNumber", "EventType", "grp_num",
                   names(grp_df)[grep("Param[0-9]_", names(grp_df))])]
  row.names(grp_df) <- NULL

  return(grp_df)
})

# ALL PARAMS_* CHAIN MERGE
params_df <- Reduce(function(x,y) merge(x, y, by=c("SubjectID", "EventNumber", "EventType", "grp_num")), df_list)

# ORIGINAL DF AND PARAMS MERGE
df$grp_num <- ave(df$Param1, df$SubjectID, df$EventNumber, df$EventType, 
                  FUN=function(x) cumsum(rep(1, length(x))))

final_df <- transform(merge(df, params_df, by=c("SubjectID", "EventNumber", "EventType", "grp_num")), grp_num=NULL)

выход

head(final_df, 10)

#    SubjectID EventNumber EventType Param1 Param2 Param3 Param4 Param5 Param6        AbsoluteTime Param1_2 Param2_2 Param3_2 Param4_2 Param5_2 Param6_2 Param1_3 Param2_3 Param3_3 Param4_3 Param5_3 Param6_3
# 1          1           1         A   0.30     45   1.50   0.14   0.18   0.00 2018-03-31 17:00:00     0.21       38     1.70     0.00     0.00     1.00     0.87       76     1.65     1.00     1.00     0.75
# 2          1           1         A   0.21     38   1.70   0.00   0.00   1.00 2018-03-31 17:00:02     0.87       76     1.65     1.00     1.00     0.75     0.78       32     1.32     0.86     0.00     0.00
# 3          1           1         A   0.87     76   1.65   1.00   1.00   0.75 2018-03-31 17:00:04     0.78       32     1.32     0.86     0.00     0.00       NA       NA       NA       NA       NA       NA
# 4          1           1         A   0.78     32   1.32   0.86   0.00   0.00 2018-03-31 17:00:04       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA
# 5          1           2         B   0.90     67   0.60   0.00   1.00   0.14 2018-04-30 17:00:00     1.20       23     0.30     0.60     0.00     0.00     1.40       27     2.50     1.00     0.09     1.00
# 6          1           2         B   1.20     23   0.30   0.60   0.00   0.00 2018-04-30 17:00:02     1.40       27     2.50     1.00     0.09     1.00       NA       NA       NA       NA       NA       NA
# 7          1           2         B   1.40     27   2.50   1.00   0.09   1.00 2018-04-30 17:00:04       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA       NA
# 8          2           1         A   1.30    784   0.40   1.00   1.00   0.00 2018-02-21 17:00:00     0.60      623     1.40     0.18     0.78     1.00     0.45       54     0.67     0.00     0.00     0.27
# 9          2           1         A   0.60    623   1.40   0.18   0.78   1.00 2018-02-21 17:00:02     0.45       54     0.67     0.00     0.00     0.27     0.45       54     0.67     0.00     0.00     0.00
# 10         2           1         A   0.45     54   0.67   0.00   0.00   0.27 2018-02-21 17:00:04     0.45       54     0.67     0.00     0.00     0.00       NA       NA       NA       NA       NA       NA
0 голосов
/ 12 мая 2018

Что-то вроде этого:

Вы можете использовать версию разработчика пакета dplyr для добавления и переименования переменных в соответствии с различными подмножествами ваших данных. dplyr также предоставляет функции lead() и lag(), которые можно использовать для поиска «следующих» или «предыдущих» значений в векторе (или здесь строке). Вы можете использовать lead() в сочетании с функцией mutate_at(), чтобы извлечь значения из следующей n-й строки и использовать их для создания нового набора переменных.

Здесь я использую данные, которые вы предоставили в своем примере:

# load dplyr package
require(dplyr)

# creacte new data frame "df.extended"
df.extended <- df

# number of observations per group (e.g., SubjectID)
# or desired number of successions
obs = 3

# loop until number of successions achieved
for (i in 1:obs) {

  # overwrite df.extended with new information
   df.extended <- df.extended %>% 
     # group by subjects and events
     group_by(SubjectID, EventNumber) %>%
     # create new variable for each parameter
     mutate_at( vars(Param1:Param6), 
                # using the lead function
                .funs = funs(step = lead),
                # for the nth followning row
                n = i) %>% 
     # rename the new variables to show the succession number
     rename_at(vars(contains("_step")), funs(sub("step", as.character(i), .)))

}

Это должно примерно воссоздать данные, которые вы разместили, как желаемый результат.

# Look at first part of "df.extended"
> head(df.extended)

# A tibble: 6 x 28
# Groups:   SubjectID, EventNumber [2]
  SubjectID EventNumber EventType Param1 Param2 Param3 Param4 Param5 Param6 AbsoluteTime        Param1_1 Param2_1 Param3_1 Param4_1 Param5_1 Param6_1
  <fct>     <fct>       <fct>      <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dttm>                 <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
1 1         1           A          0.300    45.  1.50   0.140  0.180  0.    2018-04-01 00:00:00    0.210      38.    1.70     0.      0.        1.00 
2 1         1           A          0.210    38.  1.70   0.     0.     1.00  2018-04-01 00:00:02    0.870      76.    1.65     1.00    1.00      0.750
3 1         1           A          0.870    76.  1.65   1.00   1.00   0.750 2018-04-01 00:00:04    0.780      32.    1.32     0.860   0.        0.   
4 1         1           A          0.780    32.  1.32   0.860  0.     0.    2018-04-01 00:00:04   NA          NA    NA       NA      NA        NA    
5 1         2           B          0.900    67.  0.600  0.     1.00   0.140 2018-05-01 00:00:00    1.20       23.    0.300    0.600   0.        0.   
6 1         2           B          1.20     23.  0.300  0.600  0.     0.    2018-05-01 00:00:02    1.40       27.    2.50     1.00    0.0900    1.00 
# ... with 12 more variables: Param1_2 <dbl>, Param2_2 <dbl>, Param3_2 <dbl>, Param4_2 <dbl>, Param5_2 <dbl>, Param6_2 <dbl>, Param1_3 <dbl>,
#   Param2_3 <dbl>, Param3_3 <dbl>, Param4_3 <dbl>, Param5_3 <dbl>, Param6_3 <dbl>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...