2016-11-16 4 views
0

У меня есть таблица в формате на следующем:сплит колонки на основе 2-х столбцов

 t_id  date1  date2 date3 email 
100678318 2016-09-05  <NA> <NA> [email protected] 
100678319  <NA> 2016-10-05 <NA> [email protected] 
100732587 2016-11-01  <NA> <NA> [email protected] 
100689822 2016-09-18  <NA> <NA> [email protected] 
100640340 2016-08-01  <NA> <NA> [email protected] 
100641415  <NA> 2016-08-02 <NA> [email protected] 

Теперь я хочу, чтобы изменить данные в другой формат. (широкий) Чтобы собрать немного больше писем, необходимо сгруппировать их в 1 строку. И если у нас есть t_id, который происходит более чем один раз, мы хотим, чтобы они закончили, как t_id_1 date1 t_id_2 date2, и так далее.

Так что таблица выглядит следующим образом (только первая запись для примера):

email    t_id_1  date1  t_id_2  date2  t_id_3 date3 
[email protected] 100678318 2016-09-05 100678319 2016-10-05 NA NA 

Так, наверное, мне нужно некоторое условное форматирование или что-то. Я надеялся на решение с Dpylr и plyr.

Try других вопросов:

library(data.table) 
tst <- setDT(tstDF)[, lapply(.SD, function(x) toString(na.omit(x))), by = t_id] 

Надеюсь кто-то есть с решением этой проблемы.

+0

Вы хотите широкий формат, а не длинный формат. возможно [этот пост] (http://stackoverflow.com/questions/5890584/how-to-reshape-data-from-long-to-wide-format) будет полезен. – lmo

+0

Спасибо, изменил его! –

ответ

2

Детали t_id и date не описаны в этом вопросе, так что в (1) мы предположили, что существуют до 3 t_id значений в email и что они появляются в порядке, соответствующем date1, date2 и date3 соответственно со всеми остальными date значениями, являющимися NA. Например, если есть значения 2 t_id для определенного адреса электронной почты, тогда первый будет иметь date1 в качестве даты и date2 и date3 будет NA. Второй будет иметь date2 в качестве даты и date1 и date3 будет NA. В (2) мы принимаем то же самое, за исключением того, что мы обобщаем значение k вместо 3.

Пакеты не используются.

1) Использование by разделить на email, а затем вручную построить строку для каждого. Наконец rbind строки вместе.

do.call("rbind", 
    by(DF, DF$email, function(x) { 
    t_id <- c(x$t_id, NA, NA, NA)[1:3] 
    date <- c(na.omit(c(x$date1, x$date2, x$date3)), NA, NA, NA)[1:3] 
    data.frame(email = x$email[1], 
       t_id1 = t_id[1], date1 = date[1], 
       t_id2 = t_id[2], date2 = date[2], 
       t_id3 = t_id[3], date3 = date[3] 
    ) 
    } 
)) 

дает:

       email  t_id1  date1  t_id2  date2 
[email protected]     [email protected] 100689822 2016-09-18  NA  <NA> 
[email protected] [email protected] 100732587 2016-11-01  NA  <NA> 
[email protected]  [email protected] 100640340 2016-08-01 100641415 2016-08-02 
[email protected]  [email protected] 100678318 2016-09-05 100678319 2016-10-05 
        t_id3 date3 
[email protected]   NA <NA> 
[email protected] NA <NA> 
[email protected]  NA <NA> 
[email protected]  NA <NA> 

2) При желании мы могли бы обобщить это до k даты и t_id значений. В этом случае rbind/by создает новый кадр данных long, который имеет k строк для каждого email. Первая строка для каждого email в long соответствует первым и date и так далее до kth. long впоследствии изменен на широкий.

is.date <- grepl("date", names(DF)) 
k <- sum(is.date) 

long <- do.call("rbind", 
    by(DF, DF$email, function(x) 
    data.frame(email = x$email[1], 
     time = 1:k, 
     t_id = c(x$t_id, rep(NA, k))[1:k], 
     date = c(na.omit(do.call("c", x[is.date])), rep(NA, k))[1:k] 
    ) 
) 
) 
reshape(long, dir = "wide", idvar = "email") 

дает:

        email t_id.1  date.1 t_id.2  date.2 t_id.3 date.3 
[email protected]     [email protected] 100689822 2016-09-18  NA  <NA>  NA <NA> 
[email protected] [email protected] 100732587 2016-11-01  NA  <NA>  NA <NA> 
[email protected] [email protected] 100640340 2016-08-01 100641415 2016-08-02  NA <NA> 
[email protected] [email protected] 100678318 2016-09-05 100678319 2016-10-05  NA <NA> 

Примечание: Входные сигналы DF в воспроизводимой форме предполагалась:

Lines <- "t_id  date1  date2 date3 email 
100678318 2016-09-05  <NA> <NA> [email protected] 
100678319  <NA> 2016-10-05 <NA> [email protected] 
100732587 2016-11-01  <NA> <NA> [email protected] 
100689822 2016-09-18  <NA> <NA> [email protected] 
100640340 2016-08-01  <NA> <NA> [email protected] 
100641415  <NA> 2016-08-02 <NA> [email protected]" 

DF <- transform(read.table(text = Lines, header = TRUE, na.strings = "<NA>"), 
      date1 = as.Date(date1), 
      date2 = as.Date(date2), 
      date3 = as.Date(date3)) 
+0

Спасибо за очень обширную разработку. Я начну пытаться! Может быть, я вернусь для дополнительной разработки, если вы хотите мне помочь. –

2

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

library(reshape2) 

coalesce <- function(...) { 
    apply(cbind(...), 1, function(x) x[which(!is.na(x))[1]]) 
} 

df$date <- as.Date(coalesce(df$date1, df$date2, df$date3), origin = '1970-01-01') 
df$id <- 1 
for (i in 2:nrow(df)) { 
    if (df$email[i] == df$email[i - 1]) { 
    df$id[i] <- df$id[i] + 1 
    } 
} 

reshape(df[ c('id', 'date', 't_id', 'email')], idvar = 'email', timevar = 'id', direction = 'wide') 
+0

Хорошее решение действительно, но это только дает мне первые 2 t_id и даты. В моем полном DF у меня в среднем около 8 транзакций. Могли бы вы, возможно, немного соединить цикл for, чтобы я мог изменить код на мои требования? –