【问题标题】:Conditional creation of column with increment in data.table在 data.table 中有条件地创建具有增量的列
【发布时间】:2017-12-17 07:55:05
【问题描述】:

我有一个包含大约 100 万条记录和 80 列的大型数据集。为了加快处理速度,我使用了 data.table。我需要根据条件创建一个新列,我不知道如何在 data.table 中执行此操作

下面是示例数据的代码:

set.seed(1200)
N_Blocks = 1348
cyc=200
City1 <- vector()
City2 <- vector()
a1 <- vector()
a2 <- vector()

for (a in 1:cyc) {
City1 <- sample(paste("City", formatC(a, width=nchar(cyc), flag="0"), sep=""),N_Blocks,rep=T)
a1 <- sample(0:1,N_Blocks,rep = T)

City2 <- append(City2,City1)
a2 <- append(a2,a1)
}

df1 <- data.frame(City2,a2)

现在的要求是,对于每个城市(目前我们在这个样本数据中有 200 个城市)并且对于 a2 == 1,我需要创建一个新列,它将 1 的总数划分为 12 个月。因此,例如 City001 & a2 == 1 种子为 1200 我得到 671 条记录。所以新列Months需要有代码01-12。因此,a2 == 1 的第一个 56 条记录的代码为 01,然后接下来的 56 条记录的代码为 02,依此类推.....City001 的最后 55 条记录的代码为 a2 == 1 的代码为 12(所以总数添加到 671)。就像在 12 个月内为每个城市拆分 a2 的选择一样。

我们可以从命令中得到选择的城市级别摘要-

table(df1$City2,df1$a2)

我们可以使用 data.table 来实现吗?

【问题讨论】:

    标签: r data.table conditional increment


    【解决方案1】:

    使用:

    library(data.table)
    setDT(df1)[a2 == 1, mon := cut(seq_along(a2), 12, sprintf('%02d',1:12)), by = City2][]
    

    给予:

              City2 a2 mon
         1: City001  1  01
         2: City001  1  01
         3: City001  1  01
         4: City001  1  01
         5: City001  0  NA
        ---               
    269596: City200  1  12
    269597: City200  0  NA
    269598: City200  1  12
    269599: City200  1  12
    269600: City200  1  12
    

    这是做什么的:

    • 使用setDT(df1) 时,data.frame 会转换为 data.table(仍然是 data.frame)。
    • 使用a2 == 1 过滤数据并使用by = City2 分组。
    • 最后,通过引用为cut 选定的行创建一个新列mon。
    • 对于City2 列的每一组,cut 将一个序列(seq_along(a2);它以1 开始并以组大小结束,即第一组的671)在 12(几乎) 相等的部分。每个部分都有一个相应的标签 (sprintf('%02d',1:12))。有关详细信息,请参阅?cut。

    上述方法的缺点是记录数最少的中断不在末尾。可以通过总结结果来展示:

    > df1[a2 == 1, .N, by = .(City2,mon)][1:24]
          City2 mon  N
     1: City001  01 56
     2: City001  02 56
     3: City001  03 56
     4: City001  04 56
     5: City001  05 56
     6: City001  06 56
     7: City001  07 55
     8: City001  08 56
     9: City001  09 56
    10: City001  10 56
    11: City001  11 56
    12: City001  12 56
    13: City002  01 56
    14: City002  02 55
    15: City002  03 56
    16: City002  04 55
    17: City002  05 56
    18: City002  06 55
    19: City002  07 55
    20: City002  08 56
    21: City002  09 55
    22: City002  10 56
    23: City002  11 55
    24: City002  12 56
    

    要获得最后记录数较少的中断,您可以使用:

    setDT(df1)[a2 == 1, mon := cut(seq_along(a2),
                                   {n <- .N/12; 
                                    br <- c(0, rep(ceiling(n), round((n-floor(n))*12)), rep(floor(n), round((floor(n) - n + 1)*12)));
                                    unique(c(cumsum(br),.N))},
                                   sprintf('%02d',1:12)),
               by = City2][]
    

    第一个解决方案中的断点数 (12) 被一个断点向量替换,该向量在大括号 ({n &lt;- .N/12 .... unique(c(cumsum(br),.N))}) 之间的部分中分别为每组 City2 计算。这部分计算间隔的方式是观察数不是随机分布在几个月内,而是观察数较少的月份总是在末尾。

    除了上面的解释之外,大括号之间的部分做了以下事情:

    • 首先将每组中的观察数 (.N) 除以 12。这通常不是整数,而是小数点后的数值。
    • rep(ceiling(n), round((n-floor(n))*12)) 计算第一组 (ceiling(n)) 的较高观察次数,并重复一定次数,该次数由 n 的十进制值与 round((n-floor(n))*12) 确定。
    • 较小组(中断)的组大小由rep(floor(n), round((floor(n) - n + 1)*12)) 确定,方法类似。 floor(n) 确定组的大小,而round((floor(n) - n + 1)*12) 确定较小的组所需的组数。
    • 前两个向量以起始零 (c(0, rep ... )) 放在一起。
    • 通过在其上使用 cumsum,您会得到一个以 0 开头的向量,最后添加组大小 (.N)。通过将其包装在 unique 中,您可以确保获得唯一的中断值。这是必需的,因为当 n 是一个完全四舍五入的数字(例如 600/12 的结果)时,最后一个中断值在向量中出现两次。

    同样的检查现在表明已经实现了:

    > df1[a2 == 1, .N, by = .(City2,mon)][1:24]
          City2 mon  N
     1: City001  01 56
     2: City001  02 56
     3: City001  03 56
     4: City001  04 56
     5: City001  05 56
     6: City001  06 56
     7: City001  07 56
     8: City001  08 56
     9: City001  09 56
    10: City001  10 56
    11: City001  11 56
    12: City001  12 55
    13: City002  01 56
    14: City002  02 56
    15: City002  03 56
    16: City002  04 56
    17: City002  05 56
    18: City002  06 56
    19: City002  07 55
    20: City002  08 55
    21: City002  09 55
    22: City002  10 55
    23: City002  11 55
    24: City002  12 55
    

    【讨论】:

    • 如果我正确理解 OP 的问题,我仍然不确定这是否正确。如果以第 669 行为例:669: City001 1 07,应该是 12 而不是 07。City001 的最后十二行(其中 a2=1)都应该标记为 12。
    • @Mako212 是的,这是正确的结果。根据 OP 的规范,a2 == 0 的行不应获得月份值。当您执行df1[1:669, sum(a2==0)] 时,它将返回326,即未获得月份值的行数。只有a2 == 1 所在的行才能获得月份值。所以只有 669 - 326 = 343 行应该得到月份值,这意味着 07 是第 669 行的正确结果。
    • @Jaap,谢谢!!这行得通!如果您能详细说明 {....} 内部发生的事情,将会有很大帮助。这是一个非常有趣的代码,我试图理解但无法得到它。我还有很长的路要走......
    • @user1412 添加了解释,HTH
    • @Jaap,谢谢!!这很有帮助!
    猜你喜欢
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 2015-06-19
    • 2015-04-26
    • 2022-08-17
    • 2014-04-20
    • 2020-07-08
    • 2014-08-23
    相关资源
    最近更新 更多