Вставка строк в кадр данных при отсутствии значений в категории

Вопрос:

У меня есть фрейм данных, как показано ниже. Обратите внимание, что в day b нет записи для product 1. Это означает, что продукт 1 не был продан в тот день, но мне нужна строка, которая делает это ясным. То есть, я хочу добавить строку, где day=b, product=1 и sales=0. Я хочу сделать это для каждой пары day-product, которая не существует в кадре данных, например, day c и product 3. Как я могу это сделать?

  df <- data.frame(day=c(rep('a',3), rep('b',2), rep('c',2)),
product = c(1:3, 2:3,1:2),
sales = runif(7))

Лучший ответ:

Опция 1

Благодаря @Frank для лучшего решения, используя tidyr:

library(tidyr)
complete(df, day, product, fill = list(sales = 0))

Используя этот подход, вам больше не нужно беспокоиться о выборе названий продуктов и т.д.

Что дает вам:

  day product      sales
1   a       1 0.52042809
2   b       1 0.00000000
3   c       1 0.46373882
4   a       2 0.11155348
5   b       2 0.04937618
6   c       2 0.26433153
7   a       3 0.69100939
8   b       3 0.90596172
9   c       3 0.00000000

Вариант 2

Вы можете сделать это, используя пакет tidyrdplyr)

df %>% 
  spread(product, sales, fill = 0) %>% 
  gather('1':'3', key = "product", value = "sales")

Что дает тот же результат

Это работает, используя spread для создания широкого фрейма данных, причем каждый продукт является его собственным столбцом. Аргумент fill = 0 приведет к заполнению всех пустых ячеек 0 (по умолчанию — NA).

Затем gather работы, чтобы преобразовать «широкий» кадр данных в исходный «длинный» кадр данных. Первый аргумент — это столбцы продуктов (в этом случае '1':'3'). Затем мы устанавливаем key и value в имена исходных столбцов.

Я хотел бы предложить вариант 1, но вариант 2 может все же оказаться полезным в определенных обстоятельствах.


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

Ответ №1

Если скорость вызывает беспокойство, может быть вариант самостоятельного присоединения, чтобы заполнить недостающие уровни (см. Раздел 3.5.5 учебника Frank Quick R):

library(data.table)
setDT(df)[CJ(day = day, product = product, unique = TRUE), on = .(day, product)][
is.na(sales), sales := 0.0][]
   day product      sales
1:   a       1 0.57406950
2:   a       2 0.04390324
3:   a       3 0.63809278
4:   b       1 0.00000000
5:   b       2 0.01203568
6:   b       3 0.61310815
7:   c       1 0.19049274
8:   c       2 0.61758172
9:   c       3 0.00000000

эталонный тест

Создать контрольные данные в 1 миллион строк минус 10% отсутствует = 0,9 M строк:

n_day <-  1e3L
n_prod <- 1e3L
n_rows <- n_day * n_prod
# how many rows to remove?
n_miss <- n_rows / 10L
set.seed(1L)
df <- expand.grid(day = 1:n_day, product = 1:n_prod)
df$sales <- runif(n_rows)
#remove rows
df <- df[-sample.int(n_rows, n_miss), ]
str(df)
'data.frame': 900000 obs. of  3 variables:
$ day    : int  1 2 3 5 6 7 8 9 11 12 ...
$ product: int  1 1 1 1 1 1 1 1 1 1 ...
$ sales  : num  0.266 0.372 0.573 0.202 0.898 ...
- attr(*, "out.attrs")=List of 2
..$ dim     : Named int  1000 1000
.. ..- attr(*, "names")= chr  "day" "product"
..$ dimnames:List of 2
.. ..$ day    : chr  "day=   1" "day=   2" "day=   3" "day=   4" ...
.. ..$ product: chr  "product=   1" "product=   2" "product=   3" "product=   4" ...

Определить функцию проверки:

my_check <- function(values) {
all(sapply(values[-1], function(x) identical(as.data.frame(values[[1]]), as.data.frame(x))))
}

Запуск тестов:

library(data.table)
microbenchmark::microbenchmark(
tidyr = tidyr::complete(df, day, product, fill = list(sales = 0)),
dt = setDT(df)[CJ(day = day, product = product, unique = TRUE), on = .(day, product)][
is.na(sales), sales := 0.0][],
times = 3L,
check = my_check
)
Unit: milliseconds
expr       min        lq      mean    median        uq       max neval cld
tidyr 1253.3395 1258.0595 1323.5438 1262.7794 1358.6459 1454.5124     3   b
dt   94.4451  100.2952  155.4575  106.1452  185.9638  265.7823     3  a

Для данной задачи размер 1 М строк минус 10% отсутствует, решение tidyr на величину медленнее, чем метод data.table.

Оцените статью
TechArks.Ru
Добавить комментарий