改善R数据帧之间的采样

时间:2017-11-23 16:05:51

标签: r data.table

在我需要使用来自数据帧的数据和来自其他数据帧的样本的情况下,我正在尝试提高速度。 首先,我需要从df_obs中抽取我想要的样本数量。 然后我需要根据我所在的月份(子命令)确定从哪里采样的子集。 然后我想从相应的示例数据帧中进行采样。 最后将它们放在一个新的数据框中。

下面的代码是Works,但是当我不得不重复这1000次时,它很慢。是否有更好的使用应用函数的替代方法?或者也许是一些data.table函数?

#Sample function to sample correct in case of only one value to sample from
resample <- function(x, ...) x[sample.int(length(x), ...)]

#Creating dummy data
no_pr_month <- sort(sample(67:120, 20))
df_obs <- data.frame(replicate(20,sample(0:5,1000,rep=TRUE)))
colnames(df_obs) <- no_pr_month
amount <- sample(50:50000,200)
month <- sample(no_pr_month,200, rep=TRUE)
df <- data.frame(month,amount)
df_sum <- data.frame(matrix(NA, ncol = 20, nrow=1000))

#The far too slow loop
for (k in 1:length(no_pr_month)){
  a <- df_obs[,k]
  df_sample <- subset(df, df$month == names(df_obs[k]))
  df_out <- sapply(a, function(x) sum(resample(df_sample$amount, x,replace = TRUE)))
  df_sum[,k] <- df_out
}

1 个答案:

答案 0 :(得分:0)

注意:在创建数据之前,我插入了set.seed(000)以获得一致的结果

即使使用data.table软件包,最好以“整洁”的方式组织数据:基于行。

因此,我们首先将您的df_obs数据集更改为长格式data.table。

library(data.table)

df_obs_long <- data.table(
  month     = as.integer(rep(names(df_obs), each = nrow(df_obs))),
  obs_count = unlist(df_obs)
)

df_obs_long
#        month obs_count
#     1:    69         4
#     2:    69         5
#     3:    69         1
#     4:    69         3
#     5:    69         0
#    ---                
# 19996:   116         4
# 19997:   116         1
# 19998:   116         2
# 19999:   116         3
# 20000:   116         5

接下来,我们将定义一个函数,该函数采用样本大小的向量和从中抽取样本的月份数。该函数将返回给定的每个尺寸的样本总和向量。

使df成为data.table并不会为编写代码节省太多,但可以减少运行时间。

setDT(df)


sample_and_sum_month <- function(sizes, month_number) {
  choices <- df[month == month_number, amount]
  vapply(
    sizes,
    FUN.VALUE = numeric(1),
    FUN = function(s) {
      sum(resample(choices, size = s, replace = TRUE))
    }
  )
}


sample_and_sum_month(1:3, 69)
# [1] 12729 55068 28605

最后,我们可以将这些总和添加为df_obs_long中的新列。

df_obs_long[
  ,
  sample_sum := sample_and_sum_month(obs_count, .BY[["month"]]),
  by = "month"
]

df_obs_long
#        month obs_count sample_sum
#     1:    69         4      82662
#     2:    69         5     160761
#     3:    69         1       5743
#     4:    69         3     108783
#     5:    69         0          0
#    ---                           
# 19996:   116         4      56792
# 19997:   116         1      22570
# 19998:   116         2      35337
# 19999:   116         3      64734
# 20000:   116         5      69075