[英]R, dplyr: cumulative version of n_distinct
我有一個如下的數據框。 它按列time
排序。
輸入 -
df = data.frame(time = 1:20,
grp = sort(rep(1:5,4)),
var1 = rep(c('A','B'),10)
)
head(df,10)
time grp var1
1 1 1 A
2 2 1 B
3 3 1 A
4 4 1 B
5 5 2 A
6 6 2 B
7 7 2 A
8 8 2 B
9 9 3 A
10 10 3 B
我想創建另一個變量var2
到目前為止,它不計算不同的var1
值,即直到每個組grp
那個time
點。 這與我使用n_distinct
得到的結果略有不同。
預期輸出 -
time grp var1 var2
1 1 1 A 1
2 2 1 B 2
3 3 1 A 2
4 4 1 B 2
5 5 2 A 1
6 6 2 B 2
7 7 2 A 2
8 8 2 B 2
9 9 3 A 1
10 10 3 B 2
我想為此創建一個函數cum_n_distinct
並將其用作 -
d_out = df %>%
arrange(time) %>%
group_by(grp) %>%
mutate(var2 = cum_n_distinct(var1))
dplyr
解決方案 - 這個邏輯基本上是將var1
的每個唯一值的第一次出現設置為1
並將每個組grp
其余值設置為0
,然后對其應用cumsum
-
df = df %>%
arrange(time) %>%
group_by(grp,var1) %>%
mutate(var_temp = ifelse(row_number()==1,1,0)) %>%
group_by(grp) %>%
mutate(var2 = cumsum(var_temp)) %>%
select(-var_temp)
head(df,10)
Source: local data frame [10 x 4]
Groups: grp
time grp var1 var2
1 1 1 A 1
2 2 1 B 2
3 3 1 A 2
4 4 1 B 2
5 5 2 A 1
6 6 2 B 2
7 7 2 A 2
8 8 2 B 2
9 9 3 A 1
10 10 3 B 2
假設東西已經按time
排序,首先定義一個累積不同的函數:
dist_cum <- function(var)
sapply(seq_along(var), function(x) length(unique(head(var, x))))
然后是使用ave
創建組的基本解決方案(注意,假設var1
是因子),然后將我們的函數應用於每個組:
transform(df, var2=ave(as.integer(var1), grp, FUN=dist_cum))
一個data.table
解決方案,基本上做同樣的事情:
library(data.table)
(data.table(df)[, var2:=dist_cum(var1), by=grp])
和dplyr
,同樣的事情:
library(dplyr)
df %>% group_by(grp) %>% mutate(var2=dist_cum(var1))
嘗試:
使用您的新數據集,一種基於 R 的方法
df$var2 <- unlist(lapply(split(df, df$grp),
function(x) {x$var2 <-0
indx <- match(unique(x$var1), x$var1)
x$var2[indx] <- 1
cumsum(x$var2) }))
head(df,7)
# time grp var1 var2
# 1 1 1 A 1
# 2 2 1 B 2
# 3 3 1 A 2
# 4 4 1 B 2
# 5 5 2 A 1
# 6 6 2 B 2
# 7 7 2 A 2
這是另一個使用 data.table 的解決方案,速度非常快。
cum_n_distinct <- function(x, na.include = TRUE){
# Given a vector x, returns a corresponding vector y
# where the ith element of y gives the number of unique
# elements observed up to and including index i
# if na.include = TRUE (default) NA is counted as an
# additional unique element, otherwise it's essentially ignored
temp <- data.table(x, idx = seq_along(x))
firsts <- temp[temp[, .I[1L], by = x]$V1]
if(na.include == FALSE) firsts <- firsts[!is.na(x)]
y <- rep(0, times = length(x))
y[firsts$idx] <- 1
y <- cumsum(y)
return(y)
}
cum_n_distinct(c(5,10,10,15,5)) # 1 2 2 3 3
cum_n_distinct(c(5,NA,10,15,5)) # 1 2 3 4 4
cum_n_distinct(c(5,NA,10,15,5), na.include = FALSE) # 1 1 2 3 3
d_out = df %>%
arrange(time) %>%
group_by(grp) %>%
mutate(var2 = cum_n_distinct(var1))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.