Я использую список из двух фреймов данных, которые имеют несколько одинаковых столбцов, и я хочу иметь возможность преобразовать класс из нескольких столбцов в каждом фрейме данных за один раз, используя их имена столбцов, а не положение столбца.
Я искал 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")))
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 тоже подойдут.
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
} )
as.Date
, в каком формате они находятся, с помощью аргумента format
. Смотрите обновление.
Похожие вопросы
Новые вопросы
r
R — это бесплатный язык программирования с открытым исходным кодом и программная среда для статистических вычислений, биоинформатики, визуализации и общих вычислений. Пожалуйста, используйте минимально воспроизводимые примеры, которые другие могут запустить с помощью копирования и вставки. Показать желаемый результат полностью. Используйте dput() для данных и укажите все небазовые пакеты с помощью library(). Не вставляйте изображения для данных или кода, вместо этого используйте блоки кода с отступом. Для вопросов по статистике используйте https://stats.stackexchange.com.