#r #dataframe #repeat #long-integer
Вопрос:
У меня есть фрейм данных, подобный следующему:
Name School Weight Days
Antoine Bach 0.03 5
Antoine Ken 0.02 7
Barbara Franklin 0.04 3
Я хотел бы получить результат, подобный следующему:
Name School 1 2 3 4 5 6 7
Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Воспроизводимые Выборочные Данные:
df <- tribble(
~Name, ~School, ~Weight, ~Days,
"Antoine", "Bach", 0.03, 5,
"Antoine", "Ken", 0.02, 7,
"Barbara", "Franklin", 0.04, 3
)
Ответ №1:
Используя data.table, вы можете создать длинную версию, rep
съев Weight
значение Days
несколько раз для каждой строки, а затем dcast
перейдя в широкий формат с rowid
новой переменной в качестве столбца.
library(data.table)
setDT(df)
dcast(df[, .(rep(Weight, Days)), .(Name, School)],
Name School ~ rowid(V1))
# Name School 1 2 3 4 5 6 7
# 1: Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
# 2: Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
# 3: Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Вы также можете rep
Weight
указать количество повторений Days
, а затем NA
повторить их достаточно раз, чтобы завершить строку.
max_days <- max(df$Days)
df[, as.list(rep(c(Weight, NA), c(Days, max_days - Days))),
.(Name, School)]
# Name School V1 V2 V3 V4 V5 V6 V7
# 1: Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
# 2: Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
# 3: Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Комментарии:
1. Очень хорошее решение. У меня никогда не было возможности ознакомиться с
data.table
пакетом. У вас есть какие-либо предложения о том, с чего начать? Например, документация или, возможно, другой источник. Заранее спасибо.2. Я поддерживаю вышеприведенную просьбу. Даже мне еще предстоит найти учебник/книгу, посвященную этому пакету. Единственное, что я знаю, это то, что это очень быстро
3. Вингетты на их странице и учебник Фрэнка Нарфа по R , в котором используются данные.таблица, оба хороши
4. Этот powerpoint из выступления одного из создателей хорош для ознакомления с основами. Существует также этот учебник , в котором описываются методы для dplyr и data.table бок о бок
5. @IceCreamToucan Действительно большое вам спасибо. Я не знаю, как тебя благодарить. Я обязательно проверю их все. Желаю вам всего наилучшего.
Ответ №2:
Вы можете использовать pmap_dfr
, чтобы применить функцию к строкам, а затем связать полученный список строк в объект tibble. Функция сопоставит аргументы с именами столбцов, остальные значения строк будут записаны многоточием ...
.
library(purrr)
library(dplyr)
pmap_dfr(df, function(Weight, Days, ...) c(..., setNames(rep(Weight, Days), 1:Days))) %>%
mutate(across(3:last_col(), as.numeric))
Поскольку векторы являются атомарными в R c()
, все в строке должно быть символьным. Таким образом, мутация преобразует вновь созданные столбцы обратно в числовые.
setNames
используется для именования вновь созданных столбцов, которые необходимо связать по строкам.
Выход
Name School `1` `2` `3` `4` `5` `6` `7`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
2 Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
3 Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Примечание: pmap_dfr
это из purrr
упаковки, и mutate
, across
, и last_col
все из dplyr
.
Как это работает
При использовании pmap
вышеописанным способом аргументы именованной функции будут сопоставлены столбцам с тем же именем. Weights
Days
Аргументы функции So и as сопоставляются тем столбцам с одинаковыми именами в каждой строке.
...
Собирает оставшиеся столбцы, которые все еще передаются функции, но не используются (по имени) в функции. По сути, многоточие собирает Name
и School
в вашем случае.
Поскольку Name
и School
у них уже есть имена, они передаются в c()
первую очередь для поддержания порядка столбцов. Кроме того, мы объединяем другие значения и даем им имена. Вывод для одной строки, то это:
Name School 1 2 3 4 5 6
"Antoine" "Bach" "0.03" "0.03" "0.03" "0.03" "0.03" NA
7
NA
Результатом pmap
является список. _dfr
является определенной функцией для привязки строк (отсюда r
) этих элементов списка в фрейм данных/тиббл (отсюда df
).
Комментарии:
1. Мне очень понравилось ваше решение. На самом деле функции from
purrr
всегда являются моим первым выбором для работы по строкам, и яpmap
часто используюc(...)
их для сбора нескольких аргументов. Но здесь я не могу полностью понять, как мы можем передавать функцииc(...)
и как это получается так, как должно быть. Не могли бы вы, пожалуйста, немного объяснить мне это, мне очень любопытно знать.2. @AnoushiravanR Конечно, никаких проблем. Я обновил это решение более подробно. Надеюсь, это даст больше понимания того, как это работает. Для получения дополнительной информации и другого примера см. Последовательность примеров в документации
?pmap_dfr
в нижней части страницы, начинающуюся с комментария «# Использовать...
для поглощения неиспользуемых компонентов списка ввода .l».3. Большое вам спасибо за ваше подробное объяснение. Мне просто любопытно узнать, почему следующий код не работает. Поскольку я просто заменяю анонимную функцию формулой:
df %>% pmap_dfr(., ~ c(..., setNames(rep(Weight, Days), 1:Days)))
4. Используйте .x$ перед именами столбцов в невидимой функции, и это должно сработать. Замените эллипсы аналогичным способом
5. @AnilGoyal Большое тебе спасибо. Я действительно ценю наши обсуждения. Они очень полезны и богаты.
Ответ №3:
Одно tidyverse
решение.
- Сначала мы
tidyr::nest
две колонны. Результирующий столбец будет столбцом списка с именемd
dummy. - затем мы мутируем
d
в векторweights
доdays
времени, используяrep
функцию. Эта итерация выполняется с использованиемpurrr::map
. Примечание:map_dbl
на данном этапе это не требуется, так как на следующем шаге это будет не нужно. - мы также заключаем этот аргумент в
setNames
так, чтобыd
он мутировал в именованный список (имена, как и ожидалось). Для именseq
используется функция. - наконец, мы
tidyr::unnest_wider
d
разделяем столбец на столбцы с именами, уже сохраненными в списке на предыдущем шаге
library(dplyr)
library(tidyr)
library(purrr)
df %>% nest(d = c(Weight, Days)) %>%
mutate(d = map(d, ~setNames( rep(.x$Weight, .x$Days), seq(1, .x$Days, 1)))) %>%
unnest_wider(d)
# A tibble: 3 x 9
Name School `1` `2` `3` `4` `5` `6` `7`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
2 Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
3 Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Комментарии:
1. Анил, я только что нашел очень интересную идею. Мы можем использовать функцию map в вызове
mutate
, не называя имя выходного столбца. Я уже знал, что мы можем управлять именованием с помощью спискаmap_dfr
,map_dfc
но эта идея была для меня совершенно новой, и я нашел ее весьма полезной.2. Не могли бы вы, пожалуйста, объяснить немного подробнее?
3. Большое вам спасибо, вы тоже наверняка заслуживаете повышенного голоса.
Ответ №4:
Вы можете использовать следующий код для получения требуемых выходных данных:
library(dplyr)
library(tidyr)
df %>%
select(Weight, Days) %>%
uncount(Days, .remove = FALSE) %>%
group_by(Days) %>%
mutate(id = row_number()) %>%
pivot_wider(Days, names_from = id, values_from = Weight) %>%
right_join(df, by = "Days") %>%
relocate(Name, School) %>%
ungroup() %>%
select(-c(Weight, Days))
# A tibble: 3 x 9
Name School `1` `2` `3` `4` `5` `6` `7`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
2 Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
3 Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Данные:
df <- tribble(
~Name, ~School, ~Weight, ~Days,
"Antoine", "Bach", 0.03, 5,
"Antoine", "Ken", 0.02, 7,
"Barbara", "Franklin", 0.04, 3
)
Обновленный
Поскольку наши дорогие друзья по праву предложили использовать pmap
и map
из purrr
пакета, вот еще один вариант, думаю, было бы здорово узнать:
library(purrr)
df %>%
mutate(map2_dfr(Weight, Days, ~ set_names(rep(.x, .y), 1:.y))) %>%
select(-c(Weight, Days))
# A tibble: 3 x 9
Name School `1` `2` `3` `4` `5` `6` `7`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
2 Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
3 Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Ответ №5:
Мне нравится tidyr::uncount
делать x
количество копий каждой строки. Мы можем поворачиваться дольше, не считать, а затем снова поворачиваться шире.
library(tidyr)
my_data %>%
pivot_longer(Weight) %>%
uncount(Days, .id = "colnum") %>%
dplyr::select(-name) %>%
pivot_wider(names_from = colnum, values_from = value)
# A tibble: 3 x 9
Name School `1` `2` `3` `4` `5` `6` `7`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach 0.03 0.03 0.03 0.03 0.03 NA NA
2 Antoine Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
3 Barbara Franklin 0.04 0.04 0.04 NA NA NA NA
Комментарии:
1. подсчет действительно потрясающая функция