R中基于用户的最近邻实现?

数据挖掘 r 推荐系统 相似
2022-03-07 16:19:27

我刚刚开始学习使用 R 并且不知道如何找到最好的包。我正在寻找一个包,它允许我计算基于用户的最近邻居作为推荐的输入。

我的原始数据大致如下:

User    Item    Rating
Anna    Apple   2
Bob Apple   4
Carlos  Apple   1
Ethan   Apple   1
Gene    Apple   2
Anna    Banana  4
Dana    Banana  3
Ethan   Banana  5
Frank   Banana  5
Bob Carrot  1
Ethan   Carrot  2
Gene    Carrot  4

我使用包 reshape2 和 dcast 方法来生成不同的数据结构,其中用户是行,项目是列,评级在值部分(类似于 Excel 数据透视表)。

User    Apple   Banana  Carrot
Anna    2   4   n/a
Bob 4   n/a 1
Carlos  1   n/a n/a
Dana    n/a 3   n/a
Ethan   1   5   2
Frank   n/a 5   n/a
Gene    2   n/a 4

是否有任何 R 包可以让我根据用户向量计算用户之间的相似度?最好我想使用 Jaccard 系数作为我的相似性度量。我试图用基于用户的最近邻实现在谷歌上搜索 R 包,但结果是空的。

任何替代建议的帮助将不胜感激。

谢谢!

2个回答

也许您的问题的最佳答案是CRAN 提供的 R 的推荐实验室包。这是小插图的链接。它实现了 CF 算法和 Jaccard 系数。对于 0/1 用户反馈案例。

只有 base stats R 包中的相似度(距离)计算才有dist函数。但它仅限于少数距离测量。我知道当我在名为vegan的 R 包中搜索距离度量时,声称已实现了许多方法。但我从未使用过它。

恕我直言,最基本的推荐系统算法非常简单。值得花几分钟实施它,以更好地理解和直觉它是如何工作的。尽管如此,在我针对您的案例的快速而肮脏的基于用户的 kNN 实现之下。代码可以以更好的方式重写(Adv.R 是非常好的资源) - 但这只是用于学习目的的示例,我不对任何损害负责:-)

require(reshape2)

ratings <- read.csv(con <- textConnection('
User,Item,Rating
Anna,Apple,2
Bob,Apple,4
Carlos,Apple,1
Ethan,Apple,1
Gene,Apple,2
Anna,Banana,4
Dana,Banana,3
Ethan,Banana,5
Frank,Banana,5
Bob,Carrot,1
Ethan,Carrot,2
Gene,Carrot,4
'), header = T)

R <- ratings %>% dcast(Item ~ User)
ProductNames <- R[,1]
R <- R[,-1] # only ratings

# tanimoto similarity (~Jaccard coefficiet)
tanimoto <- function(v1,v2) length(intersect(v1,v2))/length(union(v1,v2))

# return top-k similar users
getKNN <- function(R, i, k, sim = tanimoto) {
  similarity <- array(0,length(R))
  for(j in 1:length(R)) {
    if(i!=j) similarity[j] = sim(which(!is.na(R[,i])),which(!is.na(R[,j])))
  }
  idx <- order(similarity, decreasing = T)[1:k]
  data.frame(idx = idx, similarity = similarity[idx])
}

kNNRecommender <- function(R, k) {
  reco <- data.frame()
  for(u in 1:length(R)) {
    userItems <- which(!is.na(R[,u])) #items rated by user
    nn <- getKNN(R,u, k) # get user neighbours

    # rating prediction - wiegted by similarity
    r <- array(0, c(dim(R)[1], k))
    for(i in 1:k) {
      r[,i] = R[,nn[i,]$idx] * nn[i,]$similarity
    }
    r[is.na(r)] <- 0
    userReco <- rowSums(r) / sum(nn$similarity)
    userRecoIdx <- order(userReco, decreasing = T)

    # remove items already rated by user
    userRecoIdx <- userRecoIdx[-userItems]

    # add to recommendations result
    reco <- rbind(reco, data.frame(
      User=u,
      Item=(if(length(userRecoIdx)==0) NA else userRecoIdx),
      Prediction=(if(length(userRecoIdx)==0) NA else userReco[userRecoIdx])
    ))
  }
  reco
}

kNNRecommender(R, 2)

补充Bartłomiej Twardowski

由于索引已重新排序,因此无法应用已被用户评分的行删除项目,请替换:

userRecoIdx <- userRecoIdx[-userItems]

经过

userRecoIdx <- setdiff(userRecoIdx,userItems)

为了考虑在另一行/列中与 NA 的相似性总和

userReco <- rowSums(r) / sum(nn$similarity)

经过

userReco <- rowSums(r, na.rm = TRUE) / sum(nn$similarity, na.rm = TRUE)