如何在R语言模拟中避免用户重复的Facebook帖子反应
问题:模拟Facebook用户互动时的单一反应逻辑失效
我正在构建一个模拟用户与虚假/真实Facebook新闻帖子互动的仿真程序,要求每个用户仅能对帖子留下一种反应(点赞、爱心、哇、哈哈、悲伤、愤怒或关怀),例如若用户点赞,就不能再留下其他类型的反应。但现有代码运行后仍偶尔出现用户同时有多种反应的情况,恳请解决。
我的代码
1. 创建空数据框
#Creating empty dataframe fake_id<-1:1000 like<-rep(NA,max(fake_id)) love<-rep(NA,max(fake_id)) wow<-rep(NA,max(fake_id)) haha<-rep(NA,max(fake_id)) sad<-rep(NA,max(fake_id)) angry<-rep(NA,max(fake_id)) care<-rep(NA,max(fake_id)) comment<-rep(NA,max(fake_id)) shares<-rep(NA,max(fake_id)) fake<-data.frame(fake_id,like,love,wow,haha,sad,angry,care,comment,shares)
2. 定义互动概率分布
#Probability distribution for user interaction with a given FB post misinformation_prob<-c(0.090637966,0.015194195,0.023018674,0.013500845,0.001573673,0.017003550,0.002058321,0.003093388,0.001312486) authentic_prob<-c(0.0275070460,0.0103958123,0.0060707537,0.0034785282,0.0007527044,0.0088240139,0.0020064930,0.0019195168,0.0006860144) prob.dist<-data.frame(misinformation_prob,authentic_prob) colnames(prob.dist)<-c("Misinformation","Authentic") rownames(prob.dist)<-c("Likes","Comments","Shares","Loves","Wows","Hahas","Sads","Angrys","Cares") prob.dist
3. 生成模拟数据的循环代码
#For loop used to create a simulated data for(i in fake_id){ fake$like[i]<-sample(x=c(0,1), size=1,prob=c(1-prob.dist[1,'Misinformation'],prob.dist[1,'Misinformation'])) fake$comment[i]<-sample(x=c(0,1), size=1,prob=c(1-prob.dist[2,'Misinformation'],prob.dist[2,'Misinformation'])) fake$shares[i]<-sample(x=c(0,1), size=1,prob=c(1-prob.dist[3,'Misinformation'],prob.dist[3,'Misinformation'])) if(fake$like[i]==1){ fake[i,3:8]=0 }else for(j in 3:8){ if(is.na(fake[i,j])==TRUE){ fake[i,j]<-sample(x=c(0,1),size=1,prob=c(1-prob.dist[j+1,'Misinformation'],prob.dist[j+1,'Misinformation'])) } if(fake[i,j]==1){ fake[i,-j]==0 # 我原本以为这行能避免重复反应,但没用 } } }
问题原因分析
- 赋值运算符错误:
fake[i,-j]==0是比较操作,不是赋值,应该用fake[i,-j] <- 0或者fake[i,-j] = 0,这是导致其他反应列没被清零的核心原因。 - 循环逻辑漏洞:即使某个反应被设为1,内层循环仍会继续处理下一列,可能再次生成1(因为之前的清零操作没生效),最终出现多个反应同时为1的情况。
- 范围错误:
fake[i,-j]会把fake_id列也设为0,不符合需求,应该只针对反应列(like到care,即第2到第8列)操作。
解决方案
方案1:重构逻辑,先抽样选择互动类型(更高效)
直接为每个用户抽样选择一种互动类型(包括“无反应”),再对应设置为1,其他反应列设为0。这种方式避免嵌套循环的逻辑混乱,代码更简洁可靠:
# 定义所有反应类型及对应概率(以虚假新闻为例) reaction_types <- c("like", "love", "wow", "haha", "sad", "angry", "care") reaction_probs <- prob.dist[c("Likes", "Loves", "Wows", "Hahas", "Sads", "Angrys", "Cares"), "Misinformation"] # 加入"无反应"的概率 total_reaction_prob <- sum(reaction_probs) reaction_types <- c(reaction_types, "none") reaction_probs <- c(reaction_probs, 1 - total_reaction_prob) # 重新生成数据框 fake <- data.frame( fake_id = 1:1000, like = 0, love = 0, wow = 0, haha = 0, sad = 0, angry = 0, care = 0, comment = 0, shares = 0 ) # 遍历每个用户生成数据 for(i in 1:nrow(fake)){ # 抽样选择反应类型 selected_reaction <- sample(reaction_types, size = 1, prob = reaction_probs) if(selected_reaction != "none"){ fake[i, selected_reaction] <- 1 } # 单独处理评论和分享(若需和反应互斥,可调整逻辑) fake$comment[i] <- sample(c(0,1), size=1, prob=c(1-prob.dist["Comments","Misinformation"], prob.dist["Comments","Misinformation"])) fake$shares[i] <- sample(c(0,1), size=1, prob=c(1-prob.dist["Shares","Misinformation"], prob.dist["Shares","Misinformation"])) }
方案2:修复原有循环逻辑
如果想保留原有循环结构,需修正赋值错误并调整循环逻辑:
for(i in fake_id){ # 先初始化所有反应列为0,避免NA残留 fake[i, 2:8] <- 0 fake$comment[i] <- 0 fake$shares[i] <- 0 # 抽样是否点赞 if(sample(c(TRUE, FALSE), size=1, prob=c(prob.dist[1,'Misinformation'], 1-prob.dist[1,'Misinformation']))){ fake$like[i] <- 1 } else { # 抽样其他反应,选中后立即跳出循环 selected <- FALSE for(j in 3:8){ if(sample(c(TRUE, FALSE), size=1, prob=c(prob.dist[j+1,'Misinformation'], 1-prob.dist[j+1,'Misinformation']))){ fake[i,j] <- 1 selected <- TRUE break } } } # 处理评论和分享 fake$comment[i] <- sample(c(0,1), size=1, prob=c(1-prob.dist[2,'Misinformation'], prob.dist[2,'Misinformation'])) fake$shares[i] <- sample(c(0,1), size=1, prob=c(1-prob.dist[3,'Misinformation'], prob.dist[3,'Misinformation'])) }
关键改进点
- 先初始化所有反应列为0,避免NA残留和重复赋值
- 使用
break在选中一个反应后立即停止内层循环,防止生成多个反应 - 修正赋值运算符错误,确保其他反应列被正确清零
- 明确只针对反应列操作,避免影响
fake_id、评论和分享列
内容的提问来源于stack exchange,提问作者Boogie Ly
相关产品推荐
相关产品推荐

