У меня есть последовательность операций, для выполнения которых я использую канал R 4.3:
df <- alfred::get_alfred_series(CHAUVET_PIGER_PROB)
unrevised_df <- df |>
within(Delta <- realtime_period - date) |>
na.omit() |>
subset(Delta == ave(Delta, date, FUN = min)) |>
subset(Delta <60) |>
within(date <- zoo::as.yearmon(format(date, format = '%Y-%m-%d'))) |>
subset(select = -c(Delta, realtime_period))
> head(unrevised_df)
date UNRATE
146 1960-02-01 4.8
1065 1960-03-01 5.4
1984 1960-04-01 5.0
2903 1960-05-01 4.9
3822 1960-06-01 5.5
4741 1960-07-01 5.4
Я хотел бы добавить еще одну строку в свою последовательность, чтобы изменить имя первого столбца с date
на Date
без необходимости вызывать dplyr
. Я знаю, что могу просто добавить отдельную строку следующего содержания:
colnames(unrevised_df[1]) <- "Date"
но я хотел бы сделать это строкой в трубе. Как я могу это сделать?
Ваша помощь очень ценится.
Искренне
Томас Филипс
Хотя мне кажется странным писать Date = date, date = NULL
, использовать transform()
легко и просто:
1)
> df |>
+ transform(Date = date, date = NULL)
UNRATE Date
146 4.8 1960-02-01
1065 5.4 1960-03-01
1984 5.0 1960-04-01
2903 4.9 1960-05-01
3822 5.5 1960-06-01
4741 5.4 1960-07-01
2) Жестко запрограммировано с использованием colnames()
df |>
`colnames<-`(c("Date", "Unrate"))
3) Небольшая обертка
rename = \(df, pos, nm) {
stopifnot(is.data.frame(df))
names(df)[pos] = nm
df
}
df |>
rename(df=_, 1L, "Date")
4) Для более чем одного имени
rename2 = \(df, new, old) {
stopifnot(is.data.frame(df), length(old) == length(new), is.character(new))
# this check needs to be revised:
if (! inherits(old, c("numeric", "character", "integer")))
stop("Specify 'old' either as character or integer/numeric.")
if (is.character(old))
if (all(sapply(old, \(x) x %in% names(df))==FALSE))
stop("Names in 'old' do not match with names of df.") else
names(df)[match(old, names(df))] = new
else names(df)[old] = new
df
}
mtcars |>
rename2(c("lala", "blabla"), c("am", "disp"))
mtcars |>
rename2(c("lala", "blabla"), 7:8)
Данные
df = read.table(text = " date UNRATE
146 1960-02-01 4.8
1065 1960-03-01 5.4
1984 1960-04-01 5.0
2903 1960-05-01 4.9
3822 1960-06-01 5.5
4741 1960-07-01 5.4", header = TRUE)
df |>
colnames<-(c("Date", "Unrate"))
удивительно просто, но я, похоже, не могу изменить только первое имя (второе будет варьироваться в зависимости от того, что загружается. Я попробовал df |>
colnames[1]<-` ("Date" )` но ничего не получилось. Я хотел бы оставить Date
в качестве первого столбца, если смогу.
Используйте обертку.
ХОРОШО. Я надеялся, что в R будет встроенная функция, похожая на rename
.
@ThomasPhilips, у tidyverse, безусловно, есть недостатки - как вы упомянули в другом вопросе, API часто меняется. Одна из причин этого заключается в том, что много внимания уделяется тому, как создавать согласованные и удобные для пользователя функции. Если вы хотите обменять tidyverse на базу R, чтобы добиться стабильности, вам придется пожертвовать некоторым удобством.
Также @Friede, когда вы говорите, что ваш первый подход неэффективен, вы имеете в виду набор текста? Он не копирует данные.
@SamR, да, мне кажется неприятно печатать Date = date, date = NULL
. Спасибо, я внес ясность.
@SamR Хорошая мысль, и я готов на эту сделку. Я не особенно хороший программист (считайте меня одним из тех, кто поместил Overflow в StackOverflow), поэтому, как только я что-то разберу, я бы хотел, чтобы так и осталось. Я попробовал tidyverse несколько лет назад и был удивлен тем, как часто все меняется. Затем друг рассказал мне, что у него был такой же опыт, и посоветовал мне, насколько это возможно, придерживаться базы R и при необходимости использовать тщательно подобранные пакеты.
Вот еще несколько подходов. Первый использует анонимную функцию, а остальные используют трюк с помещением входных данных в список.
# 1
unrevised_df |>
(\(x) { names(x)[1] <- "Date"; x })()
# 2
unrevised_df |>
list(x = _) |>
with( { names(x)[1] <- "Date"; x } )
# 3
unrevised_df |>
list(x = _) |>
with( setNames(x, replace(names(x), 1, "Date")) )
# 4
unrevised_df |>
list(x = _) |>
with(cbind(Date = x[[1]], x[-1]))
Если вы хотите использовать magrittr (который очень стабилен и не требует использования dplyr, tidyr или любого другого пакета tidyverse), мы можем сделать это:
# 5
library(magrittr)
unrevised_df %>%
{ names(.)[1] <- "Date"; . }
Канал Bizarro на самом деле не канал, а просто базовый синтаксис R, который выглядит примерно так:
# 6
unrevised_df ->.;
{ names(.)[1] <- "Date"; . }
Другая возможность — определить свою собственную трубку. Этот канал не выполняет автоматическую замену, а вместо этого требует использования точки (.) с правой стороны. Для его определения требуется всего лишь одна дополнительная строка кода, сохраняющая независимость кода от каких-либо пакетов.
"%.>%" <- function(left, right) eval(substitute(right), list(. = left))
unrevised_df %.>%
{ names(.)[1] <- "Date"; . }
Ввод в воспроизводимой форме
unrevised_df <- data.frame(
date = c("1960-02-01", "1960-03-01", "1960-04-01", "1960-05-01", "1960-06-01", "1960-07-01"),
UNRATE = c(4.8, 5.4, 5, 4.9, 5.5, 5.4),
row.names = c("146", "1065", "1984", "2903", "3822", "4741")
)
Я бы использовал unrevised_df |> ``colnames<-``(c("Date", new_name))
, где newname
передается в качестве параметра в функцию, но я также попробую №1 и №5. (Только один обратный апостроф вокруг имен столбцов). Спасибо, как всегда.
@Томас, я думаю, ты имел в виду unrevised_df |> `colnames<-`(c("Date", "UNRATE"))
, но я подумал, что идея вопроса состоит в том, чтобы избежать необходимости заменять все имена. (В комментариях к SO вы можете использовать обратную косую черту для защиты обратных кавычек.)
Добавлен альтернативный вариант (7).
Глупый вопрос, но зачем его переименовывать, если можно сразу назвать правильно
within(Date <- zoo::as.yearmon(format(date, format = '%Y-%m-%d')))
?