r пометить разные значения для одного и того же пользователя в строке

У меня есть данные следующим образом:

userID  <-c(1,1,1,2,2,2,3,3,3)
product <-c("a","a","a","b","b","c","a","b","c")
result  <-c(0,0,0,0,0,0,0,0,0)

df<-data.frame(userID,product,result)

Я хочу заполнить результат 1, если у userID есть разные продукты, и 0, если у пользователя все такие же продукты.

так что мой заполненный вектор будет выглядеть так:

result<-c(0,0,0,1,1,1,1,1,1)

Я понятия не имею, как сделать это без обширного цикла, но я знаю, что должен быть более быстрый путь.

4 ответа

Решение

Вы могли бы использовать ave от base R

 df$result <- with(df, ave(as.character(product), userID, 
                 FUN=function(x) length(unique(x)))>1) +0 
 df$result
 [1] 0 0 0 1 1 1 1 1 1

Или, как предлагает @David Arenburg, вы можете использовать transform и создайте новую переменную result в пределах df

  transform(df, result = (ave(as.character(product), 
          userID, FUN = function(x) length(unique(x)))>1)+0)

Или же

tbl <- rowSums(!!table(df[,-3]))>1
(df$userID %in% names(tbl)[tbl])+0
 #[1] 0 0 0 1 1 1 1 1 1

Вот один из способов добиться этого

library(data.table)
setDT(df)[, result := as.integer(length(unique(product)) > 1), by = userID]
df
#    userID product result
# 1:      1       a      0
# 2:      1       a      0
# 3:      1       a      0
# 4:      2       b      1
# 5:      2       b      1
# 6:      2       c      1
# 7:      3       a      1
# 8:      3       b      1
# 9:      3       c      1

Или же

library(dplyr)
df %>%
  group_by(userID) %>%
  mutate(result = as.integer(n_distinct(product) > 1))

Вы можете использовать пакеты data.table или же dplyr чтобы решить этот вид задачи "разделить-применить-объединить". Вот как вы могли бы сделать это с помощью data.table:

library(data.table)
setDT(df)    ## convert to the new format
df[, result:=as.integer(length(unique(product)) > 1), by=userID]

Вот мой:

# table of users x number_of_products
myTable <- table(userID, product)
# one line from there:
(result <- ifelse(rowSums(myTable!=0)==1, 0, 1)[userID])
1 1 1 2 2 2 3 3 3 
0 0 0 1 1 1 1 1 1
Другие вопросы по тегам