在R数据帧中生成随机数,这些数字在相似行中是恒定的

时间:2015-11-25 19:51:04

标签: r dataframe split-apply-combine

我有一个数据框,每个用户都有X行,其中X在用户之间不是一成不变的。我想要做的是能够生成随机数来填充新列,但对于每个用户来说都是如此。对应于该用户的所有行中的随机数相同。例如,数据可能如下所示:

user    feature1     feature2
 1        "A"           "B"
 1        "L"           "L"
 1        "Q"           "B"
 1        "D"           "M"
 1        "D"           "M"
 1        "P"           "E"
 2        "A"           "B"
 2        "R"           "P"
 2        "A"           "F"
 3        "X"           "U"
...       ...           ...

我希望生成一个可能如下所示的新列:

user    feature1     feature2   new_rand
 1        "A"           "B"       0.183
 1        "L"           "L"       0.183
 1        "Q"           "B"       0.183
 1        "D"           "M"       0.183
 1        "D"           "M"       0.183
 1        "P"           "E"       0.183
 2        "A"           "B"       0.971
 2        "R"           "P"       0.971
 2        "A"           "F"       0.971
 3        "X"           "U"       0.302
...       ...           ...

我做的第一种方法是基本上使用s <- split(df, df$user),但数据框包含大量用户,我认为这可能是一种非常低效的方法。

非常感谢。

3 个答案:

答案 0 :(得分:6)

@ akrun的方法是一个很好的一次性,但它不利用矢量化(我们在rnorm的每个级别内反复调用user),所以它可能在缓慢的一面。更通用的方法是:

library(data.table)
setDT(df)
df[unique(df, by = "user")[ , new_rand := rnorm(.N)],
   new_rand := i.new_rand, on = "user"]

这里发生了什么? unique会返回一个新的data.table,其中删除了所有重复的观察结果(由by定义,此处为user);然后我们为这个新对象添加一列([, := ])。最后,这个增强的data.table将重新连接到原始表格。

请注意,这里我们只调用rnorm一次,返回一个大小合适的矢量。然后,我们将其加入到原始数据集中,根据需要在每个user的所有观察值中“传播”该值。

或者,如果要分配给更具体的群组,请说userfeature1以及feature2

grps <- c("user", "feature1", "feature2")
df[unique(df, by = grps)[ , new_rand := rnorm(.N)],
   new_rand := i.new_rand, on = grps]

答案 1 :(得分:3)

我们可以尝试data.table。我们转换了&#39; data.frame&#39;到&#39; data.table&#39; (setDT(df1)),按&#39;用户&#39;分组,我们得到一个随机数(rnorm(1))并指定(:=)来创建&#39; new_rand&#39 ;

library(data.table)
setDT(df1)[, new_rand := rnorm(1)  , by = user]

或者我们可以使用dplyr

library(dplyr)
df1 %>%
   group_by(user) %>%
   mutate(new_rand = rnorm(1)) 

left_join

的其他选项
distinct(df1, user) %>%
      mutate(new_rand=rnorm(n())) %>%
      left_join(df1, ., by='user')

答案 2 :(得分:3)

和基础R解决方案:

df_ <- data.frame(user = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 3), feature1  = c("A", "L", "Q", "D", "D", "P", "A", "R", "A", "X"), feature2 = c("B", "L", "B", "M", "M", "E", "B", "P", "F", "U"))

tmp <- by(df_, df_[, 'user'], FUN = function(x) data.frame(x, new_rand = rnorm(1)))
do.call(rbind, tmp)

#     user feature1 feature2   new_rand
# 1.1    1        A        B -0.6145338
# 1.2    1        L        L -0.6145338
# 1.3    1        Q        B -0.6145338
# 1.4    1        D        M -0.6145338
# 1.5    1        D        M -0.6145338
# 1.6    1        P        E -0.6145338
# 2.7    2        A        B -1.4292151
# 2.8    2        R        P -1.4292151
# 2.9    2        A        F -1.4292151
# 3      3        X        U -0.3309754

或根据akrun的建议:

df_[, 'new_rand'] <- ave(seq_along(df_$user), df_$user, FUN = function(x) rnorm(1))