У меня есть следующие данные, и я хочу создать новую переменную, которая учитывает предыдущую информацию за предыдущий период. Например,
moviewatched<- c('Comedy', 'Horror', 'Comedy', 'Horror', 'Drama', 'Comedy', 'Drama')
name<- c('john', 'john', 'john', 'john', 'john','kate','kate')
time<- c('1-2018', '1-2018', '1-2018', '2-2018', '2-2018','1-2018' ,'2-2018')
df<- data.frame(moviewatched, name, time)
Теперь мне нужно создать переменную, которая будет рассказывать, какие фильмы нового жанра он смотрел в этом месяце. Например, в приведенном выше случае Джон смотрел 2 жанровых типа в первый месяц 2018 года и смотрел 1 новый дополнительный тип во втором месяце (поскольку он уже смотрел комедии и ужасы в первый месяц). Есть ли какой-нибудь способ создать текущий счет новых типов, которые начал смотреть человек? Я хочу создать переменную под названием movietypewatched, содержащую все типы жанров, которые человек смотрел до этого месяца. Ожидаемый результат будет следующим:
name time movietypewatched
john 1-2018 2
john 2-2018 3
kate 1-2018 1
kate 2-2018 2
Спасибо
Решение с использованием dplyr
. Мы можем удалить повторяющиеся строки на основе moviewatched
и name
, подсчитать уникальные moviewatched
, а затем использовать cumsum
для вычисления промежуточной суммы. df2
- это окончательный результат.
library(dplyr)
df2 <- df %>%
distinct(moviewatched, name, .keep_all = TRUE) %>%
group_by(name, time) %>%
summarise(movietypewatched = n_distinct(moviewatched)) %>%
mutate(movietypewatched = cumsum(movietypewatched)) %>%
ungroup()
df2
# # A tibble: 4 x 3
# name time movietypewatched
# <fct> <fct> <int>
# 1 john 1-2018 2
# 2 john 2-2018 3
# 3 kate 1-2018 1
# 4 kate 2-2018 2
А вот и решение data.table
по той же логике.
library(data.table)
setDT(df)
df2 <- df[!duplicated(df[, .(moviewatched, name)])][
, .(movietypewatched = uniqueN(moviewatched)), by = .(name, time)][
, movietypewatched := cumsum(movietypewatched), by = name]
df2[]
# name time movietypewatched
# 1: john 1-2018 2
# 2: john 2-2018 3
# 3: kate 1-2018 1
# 4: kate 2-2018 2
А какой бы эквивалент в data.table
? Я не могу этого сделать, хотя знал, что это так. например, df[,uniqueN(moviewatched),by = .(time,name)]
не будет работать, потому что uniqueN
находится в группе.
@denis Я считаю, что distinct
и group_by
до функции summarise
важны.
Это интересный подход. Важно, чтобы distinct
с .keep_all = TRUE
сохраняли первое из нескольких наблюдений, что вам и нужно здесь, при условии, что time
отсортирован. Наверное, безопаснее всего заранее вызвать в arrange
, чтобы убедиться в правильности порядка.
@denis Пожалуйста, смотрите мое обновление как решение data.table
.
@alistaire Спасибо. Я согласен с тем, что важно убедиться в правильности порядка.
@www Bravo за решение data.table
. duplicated
я тоже не знал. Я удалю свой дерьмовый ответ. Спасибо еще раз
К вашему сведению, поддерживается следующий синтаксис: duplicated (DT, by = c ("col1", "col2")), хотя в этом случае вы должны сделать unique (DT, by = c ("col1", "col2")) , Наверное.
@Frank Полезно знать duplicated(DT, by=c("col1", "col2"))
. Спасибо!
Сначала преобразуйте данные времени в класс, чтобы установить порядок, например с lubridate::myd
с truncated = 1
. Отсюда установите порядок строк, чтобы убедиться, что они в порядке, затем, сгруппированные по name
, используйте purrr::accumulate
для создания списка уникальных значений, замеченных до сих пор в moviewatched
, при вызове которых lengths
вернет количество просмотренных фильмов. точка. Агрегируйте по месяцам с помощью max
, чтобы получить общие кумулятивные типы за каждый месяц.
library(tidyverse)
df <- data_frame(
moviewatched = c('Comedy', 'Horror', 'Comedy', 'Horror', 'Drama', 'Comedy', 'Drama'),
name = c('john', 'john', 'john', 'john', 'john','kate','kate'),
time = lubridate::myd(c('1-2018', '1-2018', '1-2018', '2-2018', '2-2018','1-2018' ,'2-2018'), truncated = 1)
)
df %>%
group_by(name) %>%
arrange(name, time) %>%
mutate(n_types = lengths(accumulate(moviewatched, ~unique(c(...))))) %>%
group_by(name, time) %>%
summarise(n_types = max(n_types))
#> # A tibble: 4 x 3
#> # Groups: name [2]
#> name time n_types
#> <chr> <date> <dbl>
#> 1 john 2018-01-01 2
#> 2 john 2018-02-01 3
#> 3 kate 2018-01-01 1
#> 4 kate 2018-02-01 2
Как я могу узнать фактическое количество просмотров нового фильма? например, 2,1, 1,1 показаны только новые типы фильмов, которые он смотрел, большое спасибо.
может быть df %>% group_by(name) %>% arrange(name, time) %>% mutate(new = c(1, diff(lengths(accumulate(moviewatched, ~unique(c(...))))))) %>% group_by(name, time) %>% summarise(types = sum(new))
Использование data.table
:
library(data.table)
df <- unique(df)
setDT(df)[, movietypewatched := 1:.N, by = c("moviewatched", "name")]
df <- df[!(movietypewatched == 2), ]
df[, movietypewatched := .N, by = c("name", "time")][, moviewatched := NULL]
df <- unique(df)
df[, movietypewatched := cumsum(movietypewatched), by = name]
name time movietypewatched
1: john 1-2018 2
2: john 2-2018 3
3: kate 1-2018 1
4: kate 2-2018 2
Составьте таблицу первых посещенных свиданий; считать по месяцам; и возьмите кумулятивную сумму:
library(data.table)
setDT(df)
# fix bad date
df[, d := as.IDate(paste(time, "01", sep = "-"), "%m-%Y-%d")]
# identify month first watched
fw = df[, .(d = min(d)), by=.(name, moviewatched)]
# count new movies per month
nm = fw[, .N, keyby=.(name, d)]
# take cumulative count
nm[, cN := cumsum(N), by=name]
name d N cN
1: john 2018-01-01 2 2
2: john 2018-02-01 1 3
3: kate 2018-01-01 1 1
4: kate 2018-02-01 1 2
Вам нужно преобразовать дату; в противном случае min () будет неправильным и / или сломанным.
Здесь есть два этапа агрегации, но код должен быть быстрым благодаря оптимизации в data.table (см. ?GForce
).
Здесь вы можете выполнить промежуточные шаги, если хотите получить уникальные значения в genre_all
и счетчик в genre_count
.
Обратите внимание, что:
name, date
для накопления значений.lag()
, чтобы получить предыдущее значение. Поскольку первая запись для каждого name
не имеет предыдущего значения, будет получен NA
.n_distinct()
.>
library(dplyr)
library(purrr)
library(tidyr)
moviewatched <- c('Comedy', 'Horror', 'Comedy', 'Horror', 'Drama', 'Comedy', 'Drama')
name <- c('john', 'john', 'john', 'john','kate','kate', 'john')
time <- c( '1-2018', '1-2018', '2-2018', '2-2018','1-2018' ,'2-2018','1-2018')
df <- data.frame(moviewatched, name, time)
df_final <- df %>%
arrange(name, time) %>%
group_by(name, time) %>%
nest(.key= 'genre') %>%
group_by(name) %>%
mutate(genre_all = map2(genre, lag(genre), rbind) %>% map(unique)) %>%
ungroup() %>%
mutate(genre_count = map_int(genre_all, ~ lift(n_distinct)(.x, na.rm =TRUE)))
Результат:
> df_final
# A tibble: 4 x 5
name time genre genre_all genre_count
<fct> <fct> <list> <list> <int>
1 john 1-2018 <tibble [3 x 1]> <tibble [3 x 1]> 2
2 john 2-2018 <tibble [2 x 1]> <tibble [3 x 1]> 3
3 kate 1-2018 <tibble [1 x 1]> <tibble [2 x 1]> 1
4 kate 2-2018 <tibble [1 x 1]> <tibble [2 x 1]> 2
Вот это да. Я не понимаю, как
n_distinct
видят в группе .. Это из-заsummarise
?