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

Я искал StockOverflow и нашел здесь похожие вопросы: здесь: Использование списков для изменения столбцов в нескольких фреймах данных в R и здесь: применение функции для списка фреймов данных. Однако я застрял, пытаясь использовать несколько имен столбцов для преобразования дат. Вот пример данных, иллюстрирующий мою проблему:

df1 <- data.frame("t1" = c(20070103, 20070104, 20070105, 20070108, 20070109), "t2" = c(20070110,20070111, 20070112, 20070113, 20070114), A = 1:5)
df2 <- data.frame("t1" = c(20080103, 20080104, 20080105, 20080108, 20080109), "t2" = c(20080110,20080111, 20080112, 20080113, 20080114), B = 1:5)
l <- list(df1 = df1, df2=df2)

Пока что я нашел два решения, которые могу повторить для каждого столбца, который хочу преобразовать в дату:

#1
l2 <-lapply(l, function(x) transform(x, t1 = as.Date(as.character(t1), "%Y%m%d")))

#2
f <- function(df){
    within(df, t1 <- as.date(date))
}
l2 <- lapply(l, f)

Однако есть ли способ использовать любой метод для получения нескольких столбцов (а не всего фрейма данных или списка) за один снимок и с использованием имен столбцов? Я безрезультатно пробовал следующие коды:

periods <- c( "t1", "t2" )
ls2 <-lapply(ls, function(x) transform(x, periods = as.Date(as.character(periods), "%Y%m%d")) 

f <- function(df) {
     within(df, t1 <- as.Date(as.character(t1), "%Y%m%d"))
     within(df, t2 <- as.Date(as.character(t2), "%Y%m%d"))
         }
l2 <- lapply(l, f)

for (i in periods)
    l2 <-lapply(l, function(x) transform(x, i = as.Date(as.character(i), "%Y%m%d")))
r
2
Limsanity82 28 Мар 2014 в 01:38
Это довольно легко. Как вы хотите программно идентифицировать свои столбцы?
 – 
stanekam
28 Мар 2014 в 01:58
У вас также есть фундаментальное непонимание концепции применения, с которой мы можем помочь.
 – 
stanekam
28 Мар 2014 в 01:59
Я новичок в R, поэтому я не уверен, что отвечаю на ваш вопрос, но я хотел бы просто указать имена столбцов, которые я хотел бы преобразовать в даты. Смогу ли я использовать вектор символов, «периоды», в моей функции или с помощью lapply?
 – 
Limsanity82
28 Мар 2014 в 07:16

2 ответа

Лучший ответ
  • Предложение №1, простое:

    lapply(l, function(dfrm, periods, fmt) {
        for (ff in which(colnames(dfrm) %in% periods))
            dfrm[,ff] <- as.Date(as.character(dfrm[,ff]), fmt)
        dfrm
    }, periods=c('t1', 't2'), fmt='%Y%m%d')
    

    Использование ff in which(...) позволяет нам указывать заголовки столбцов, которые могут быть включены или не включены, изменения не внесены, если некоторые или все из них свободны в конкретном data.frame.

    Второй и третий аргументы в пользу lapply, periods=c('t1','t2'), позволяет указать формат и имена столбцов и (чисто) ввести их во внутренние петли (не имея внутренней части петли выходят за пределы для данных, что-то, что укусит вас, если / когда вы копируете / вставляете код в другой проект).

  • Предложение №2, попробуйте преобразовать все столбцы:

    lapply(l, function(dfrm, fmt) {
        for (cc in seq.int(ncol(dfrm)))
            if (! is.na(as.Date(as.character(dfrm[1,cc]), format=fmt)))
                dfrm[,cc] <- as.Date(as.character(dfrm[,cc]), format=fmt)
        dfrm
    }, fmt='%Y%m%d')
    

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

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

  • Предложение №3, то же самое, но более устойчивое к ложным срабатываниям:

    lapply(l, function(dfrm, fmt) {
        for (cc in seq.int(ncol(dfrm))) {
            tmp <- as.Date(as.character(dfrm[,cc]), format=fmt)
            if (! any(is.na(tmp))) dfrm[,cc] <- tmp
        }
        dfrm
    }, fmt='%Y%m%d')
    

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

  • Предложение №4 с использованием регулярных выражений в именах столбцов:

    lapply(l, function(dfrm, regex, fmt) {
        for (cc in grep(regex, colnames(dfrm)))
            dfrm[,cc] <- as.Date(as.character(dfrm[,cc]), format=fmt)
        dfrm
    }, regex='^t[0-9]+$', fmt='%Y%m%d')
    

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

Это можно было сделать с вложенным *apply вместо for цикл, но поскольку R теперь довольно хорошо работает с такими циклами, я не думаю, что это большая проблема. (Это будет зависеть от размера вашего данные.)

Если вам удобнее соглашение об именах для заголовков столбцов, то ответом может быть №4. Если нет (или вас не устраивают регулярные выражения), но вы уверены, что столбцы без даты не будут истолкованы неправильно, то №2 или №3 тоже подойдут.

3
r2evans 28 Мар 2014 в 04:10
l.new <- lapply(l, function(x) {x[periods] <- lapply(x[periods], as.character); x})
str(l.new)

Производит

List of 2
 $ df1:'data.frame':    5 obs. of  3 variables:
  ..$ t1: chr [1:5] "20070103" "20070104" "20070105" "20070108" ...
  ..$ t2: chr [1:5] "20070110" "20070111" "20070112" "20070113" ...
  ..$ A : int [1:5] 1 2 3 4 5
 $ df2:'data.frame':    5 obs. of  3 variables:
  ..$ t1: chr [1:5] "20080103" "20080104" "20080105" "20080108" ...
  ..$ t2: chr [1:5] "20080110" "20080111" "20080112" "20080113" ...
  ..$ B : int [1:5] 1 2 3 4 5

ОБНОВЛЕНИЕ: чтобы получить даты, вы можете использовать:

lapply(
  l, 
  function(x) {
    x[periods] <- lapply(x[periods], function(x) as.Date(as.character(x), format="%Y%m%d")); 
    x
} )
1
BrodieG 29 Мар 2014 в 04:04
BrodieG, код, кажется, ломается, когда я использую as.Date вместо as.character после запуска вашего исходного кода. Где в коде я могу указать нестандартный однозначный формат, если я использую as.Date? Пример: l.new <- lapply(l.new, function(x) {x[периоды] <- lapply(x[периоды], as.Date); x})
 – 
Limsanity82
28 Мар 2014 в 22:09
@ Hakeem82 Hakeem82, он не работает, потому что ваши даты не в стандартном однозначном формате (гггг-мм-дд). Вы можете указать as.Date, в каком формате они находятся, с помощью аргумента format. Смотрите обновление.
 – 
BrodieG
29 Мар 2014 в 04:04