【问题标题】:Extend an irregular sequence and add zeros to missing values扩展不规则序列并为缺失值添加零
【发布时间】:2017-01-12 12:51:13
【问题描述】:

我有一个数据框,其中包含“col1”中的序列和“col2”中的值:

col1 col2
2     0.02
5     0.12
9     0.91
13    1.13

我想用从 1 到 13 的常规序列扩展“col1”中的不规则序列。对于原始数据中缺少的“col1”中的值,我希望“col2”具有值0在最终输出中:

col1  col2
1     0
2     0.02
3     0
4     0
5     0.12
6     0
7     0
8     0
9     0.91
10    0
11    0
12    0
13    1.13

如何在 R 中做到这一点?

【问题讨论】:

标签: r


【解决方案1】:

为了完整起见,使用data.table 进行自二元连接(您将得到NAs 而不是零,但如果需要可以轻松更改)

library(data.table)
setDT(df)[.(seq(max(col1))), on = .(col1)]
#     col1 col2
#  1:    1   NA
#  2:    2 0.02
#  3:    3   NA
#  4:    4   NA
#  5:    5 0.12
#  6:    6   NA
#  7:    7   NA
#  8:    8   NA
#  9:    9 0.91
# 10:   10   NA
# 11:   11   NA
# 12:   12   NA
# 13:   13 1.13

【讨论】:

    【解决方案2】:
    library(tidyr)
    
    complete(d, col1 = 1:13, fill = list(col2 = 0))
    

    complete(d, col1 = seq(max(col1))), fill = list(col2 = 0))
    
    # A tibble: 13 × 2
        col1  col2
       <int> <dbl>
    1      1  0.00
    2      2  0.02
    3      3  0.00
    4      4  0.00
    5      5  0.12
    6      6  0.00
    7      7  0.00
    8      8  0.00
    9      9  0.91
    10    10  0.00
    11    11  0.00
    12    12  0.00
    13    13  1.13
    

    library(dplyr)
    
    left_join(data.frame(col1 = seq(max(d$col1)))), d)
    

    但这会留下NAs 而不是零。

    【讨论】:

      【解决方案3】:

      另一种方法如下。您的数据在此处称为mydf。您创建一个数据框,其中包含从 1 到最大值 col1 的列。然后,您使用将mydf 中的col2 的值分配给foo 中名为col2 的新列。执行此过程时,您使用mydf 中的col1 中的数字作为索引。此时,您在col2 中拥有NAfoo 中。您想将 NA 更改为 0。所以最后一步是执行此操作。您使用is.na()foo 中的col2 中查找NA 的位置,并为这些位置分配零。

      foo <- data.frame(col1 = 1:max(mydf$col1))
      foo$col2[mydf$col1] <- mydf$col2
      foo$col2[is.na(foo$col2)] <- 0
      

      将lmo的想法记入账,可以先创建一个0的数据框,避免第三步。

      foo <- data.frame(col1 = 1:max(mydf$col1), col2 = 0)
      foo$col2[mydf$col1] <- mydf$col2
      
      
      #   col1 col2
      #1     1 0.00
      #2     2 0.02
      #3     3 0.00
      #4     4 0.00
      #5     5 0.12
      #6     6 0.00
      #7     7 0.00
      #8     8 0.00
      #9     9 0.91
      #10   10 0.00
      #11   11 0.00
      #12   12 0.00
      #13   13 1.13
      

      数据

      mydf <- structure(list(col1 = c(2L, 5L, 9L, 13L), col2 = c(0.02, 0.12, 
      0.91, 1.13)), .Names = c("col1", "col2"), class = "data.frame", row.names = c(NA, 
      -4L))
      

      【讨论】:

      • foo &lt;- data.frame(col1 = 1:max(mydf$col1), col2=0) 在第一行。所以你不必做第三行。
      • @Imo 是的,这是真的。让我修改一下我的帖子。谢谢。
      • @jazzurro 我认为您的原始解决方案更适用于更多列。例如,如果原始数据集中有 10 列,您可以执行 foo[names(mydf)[-1]][mydf$col1] &lt;- mydf[-1] 或类似的操作(未经测试)。然后将所有零更改为一也非常简单。
      • @DavidArenburg 感谢您的反馈。 :)
      【解决方案4】:

      这是一个使用来自splitstackshape 包的expandRows 的函数,

      expand_seq <- function(x){
        x$new <- c(x$col1[1], diff(x$col1))
        new_df <- splitstackshape::expandRows(x, 'new')
        new_df$col1 <- seq(max(new_df$col1))
        new_df$col2[!new_df$col1 %in% x$col1] <- 0
        rownames(new_df) <- NULL
        return(new_df)
      }
      
      expand_seq(df)
      #   col1 col2
      #1     1 0.00
      #2     2 0.02
      #3     3 0.00
      #4     4 0.00
      #5     5 0.12
      #6     6 0.00
      #7     7 0.00
      #8     8 0.00
      #9     9 0.91
      #10   10 0.00
      #11   11 0.00
      #12   12 0.00
      #13   13 1.13
      

      【讨论】:

      • 您可以为包提出的新功能? :)
      • @jazzurro 当然。这真的很有帮助:)
      • 你可以问阿难他是否热衷。
      【解决方案5】:

      只是为了添加一个不同的观点,考虑到你所拥有的可以被看作是一个稀疏向量,即一个只定义了非零值的向量。稀疏向量由 R 中的 Matrix 包实现。如果 df 是您的初始 data.frame,请尝试:

      require(Matrix)
      data.frame(col1=seq_len(max(df$col1)),
            col2=as.vector(sparseVector(df$col2,df$col1,max(df$col1))))
      #   col1 col2
      #1     1 0.00
      #2     2 0.02
      #3     3 0.00
      #4     4 0.00
      #5     5 0.12
      #6     6 0.00
      #7     7 0.00
      #8     8 0.00
      #9     9 0.91
      #10   10 0.00
      #11   11 0.00
      #12   12 0.00
      #13   13 1.13
      

      单行base R 的结果相同:

      data.frame(col1=seq_len(max(df$col1)),
         col2=`[<-`(numeric(max(df$col1)),df$col1,df$col2))
      

      【讨论】:

        【解决方案6】:

        这里已经有一些有趣的答案。

        只是为了跳进去,我们可以创建一个从 1 到 max(col1) 的数字序列,然后使用 match 获取 col2 的相应值

        col1 = seq(1, max(df$col1))
        data.frame(col1, col2 = df$col2[match(col1, df$col1)])
        
        #   col1 col2
        #1     1   NA
        #2     2 0.02
        #3     3   NA
        #4     4   NA
        #5     5 0.12
        #6     6   NA
        #7     7   NA
        #8     8   NA
        #9     9 0.91
        #10   10   NA
        #11   11   NA
        #12   12   NA 
        #13   13 1.13
        

        这将给出NAs 而不是 0。如果我们需要 0,

        data.frame(col1,col2 = ifelse(is.na(match(col1,df$col1)), 0,
                                                     df$col2[match(col1,df$col1)]))
        
        #   col1 col2
        #1     1 0.00
        #2     2 0.02
        #3     3 0.00
        #4     4 0.00
        #5     5 0.12
        #6     6 0.00
        #7     7 0.00
        #8     8 0.00
        #9     9 0.91
        #10   10 0.00
        #11   11 0.00
        #12   12 0.00
        #13   13 1.13
        

        【讨论】:

          【解决方案7】:

          我们可以将base Rmergereplace 一起使用

          transform(merge(data.frame(col1= 1:13), df, all.x=TRUE),
                                col2 = replace(col2, is.na(col2), 0))
          #    col1 col2
          #1     1 0.00
          #2     2 0.02
          #3     3 0.00  
          #4     4 0.00
          #5     5 0.12
          #6     6 0.00
          #7     7 0.00
          #8     8 0.00
          #9     9 0.91
          #10   10 0.00
          #11   11 0.00
          #12   12 0.00
          #13   13 1.13
          

          【讨论】:

            【解决方案8】:

            我没有看到简单的merge 解决方案,所以这里有一个:

            res <- merge(data.frame(col1=1:max(df$col1)),df,by="col1",all.x=TRUE)
            res$col2 <- ifelse(is.na(res$col2),0,res$col2)
            

            第二行是用零替换merge(左外连接)中的NA。正如@Axeman 指出的那样,这也可以通过以下方式完成:

            res$col2[is.na(res$col2)] <- 0
            

            结果是:

            res
            ##   col1 col2
            ##1     1 0.00
            ##2     2 0.02
            ##3     3 0.00
            ##4     4 0.00
            ##5     5 0.12
            ##6     6 0.00
            ##7     7 0.00
            ##8     8 0.00
            ##9     9 0.91
            ##10   10 0.00
            ##11   11 0.00
            ##12   12 0.00
            ##13   13 1.13
            

            【讨论】:

            • 第二行可以是res$col2[is.na(res$col2)] &lt;- 0
            【解决方案9】:

            另一种方法是:

            for (i in 1:max(test$col1)) {
              if(!(i %in% test$col1)) (test <- rbind(test, c(i, 0)))
            }
            test <- test[order(test$col1),]
            

            不过,Axeman 的回答真的很贴心。

            编辑:使用的数据--

            test <- structure(list(col1 = c(2, 5, 9, 13), col2 = c(0.02, 0.12, 0.91, 
            1.13)), .Names = c("col1", "col2"), row.names = c(NA, -4L), class = "data.frame")
            

            免责声明:这确实不应该用于大型数据集。我尝试了 1k 行,并且很快就完成了,但是我的第二个 100k 行测试现在运行了几分钟,这确实强调了 Axeman 在他的评论中的担忧。

            【讨论】:

            • 在性能很重要的情况下,在其中增长对象的循环几乎是最坏的情况。矢量化解决方案要好得多。
            • 是的,我同意这一点。 OP 声明my data is,因此我并没有真正考虑计算时间。 // 编辑:为了好玩和咯咯笑,我只运行了 100k 行的代码......假设它仍在运行......
            猜你喜欢
            • 1970-01-01
            • 2021-12-12
            • 1970-01-01
            • 2011-08-10
            • 2021-12-29
            • 2020-06-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多