【问题标题】:R - How to subset and sum over a dataframe with list of vectors that contain ids?R - 如何使用包含 id 的向量列表对数据帧进行子集化和求和?
【发布时间】:2021-05-22 01:43:33
【问题描述】:

我有一个数据框如下:

nearby_ids <- NULL

for (i in 1:10){
string <- paste(as.character(sample(setdiff(1:10,i), sample(setdiff(1:10,i)))), collapse = ",")
nearby_ids <- c(nearby_ids, string)}

my_df <- data.frame(school_id=1:10, classes=sample(1:50, 10), nearby_schools_id = nearby_ids, stringsAsFactors = FALSE)

看起来是这样的:

变量“school_id”和“classes”是整数,nearth_schools_id 是字符。

我想要的是以下内容(希望不经过循环):

对于每一行,我想获取 near_schools_ids,将它们用作索引来对数据框进行子集化,对于那个子集化的数据框,我想对“类”求和。

这个想法是,我想知道附近所有学校的班级总数。

预期: 所以以第 1 行为例,我想输出 122 (= 46+8+44+24)。

我知道我需要在这里使用strsplit。但我试图避免循环和应用()(我有大约 300 万行,我想要最有效的方式)。 当我实现strsplit(my_df$nearby_schools_id, ",") 时,我立即得到了一个向量列表,这使得事情变得稍微复杂了。

对此有矢量化解决方案吗? 最好的解决方法是什么?

感谢任何帮助

【问题讨论】:

    标签: r list dataframe subset vectorization


    【解决方案1】:

    类似于@Ronak 的逻辑,但匹配过程可以批量完成。
    现在更新以考虑附近学校的空列表

    spl <- strsplit(my_df$nearby_schools_id, ",", fixed=TRUE)
    sa <- seq_along(spl)
    my_df$result <- tapply(
        my_df$classes[match(unlist(spl),my_df$school_id)],
        factor(rep(sa, lengths(spl)), levels=sa),
        FUN=sum
    )
    

    测试 300 万行:

    my_df <- my_df[rep(1:10,3e5),]
    my_df$school_id <- 1:3e6
    
    system.time({
    spl <- strsplit(my_df$nearby_schools_id, ",", fixed=TRUE)
    tapply(
        my_df$classes[match(unlist(spl),my_df$school_id)],
        rep(seq_along(spl), lengths(spl)),
        FUN=sum
    )
    })
    ##   user  system elapsed 
    ## 10.206   0.492  10.698
    

    【讨论】:

    • 我认为这是一个非常有用的。但我的身份证是随机的,不是连续的。有没有办法编辑你的代码以适应非连续的“school_id”?
    • @Am95 - 我认为应该。我只是遍历数据集的每一行并按数据集中显示的顺序返回该行的总和。尝试像my_df &lt;- my_df[sample(nrow(my_df)),] 那样打乱输入 - 它仍然应该给出正确的结果。
    • 非常感谢!这太棒了。我在 0.0000212 秒时对我的数据的每一行进行了基准测试!很好的解决方案。
    • 操作!还有一件事刚刚出来。我的一些 near_schools_id 是空的(附近没有学校)。但这并没有显示在 tapply 输出中。所以最后我得到的行数少于我的数据集中的行数(即 my_df)。有没有办法填补缺失值?
    【解决方案2】:

    我认为如果不进行任何拆分,您实际上无法做到这一点。试试这个方法:

    my_df$result <- sapply(strsplit(my_df$nearby_schools_id, ','), function(x) 
                           sum(my_df$classes[as.numeric(x)]))
    

    如果您的数据未按学校 ID 排序,或者您没有连续的 ID 序列,您可以使用 match 获取正确的 ID。

    my_df$result <- sapply(strsplit(my_df$nearby_schools_id, ','), function(x)
                      sum(my_df$classes[match(as.numeric(x), my_df$school_id)]))
    

    【讨论】:

    • 我明白了,谢谢。但是使用带有“match()”和 300 万行的 apply() 是一个杀手!我以每行 0.03732 秒对您的方法进行了基准测试,这使其运行时间约为 31.1 小时!因此,我正在寻找更有效的东西:'(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多