【问题标题】:How to create a new column based on values from rows that match values in other columns?如何根据与其他列中的值匹配的行中的值创建新列?
【发布时间】:2018-09-22 17:34:24
【问题描述】:

假设我有一个数据框,其中包含一些分类变量和一些字符串值的列。我想创建一个新列,如果分类列中的某些值匹配(或不匹配),则对于每一行,粘贴来自其他行的字符串值。这是一个玩具示例。

toy <- data.frame("id" = c(1,2,3,2), "year" = c(2000,2000,2004,2004), "words" = c("a b", "c d", "e b", "c d"))

如果满足两个条件,我想创建一个从其他行的words 列粘贴的变量word_pool:行的id 值不同于当前行的id 值和行的year value 小于当前行的年份值。

结果应该是

id year words word_pool
1 2000    a b
2 2000    c d
3 2004    e b   a b c d
2 2004    c d       a b

新列的前两行将是空白的,因为在玩具示例中没有少于 2000 年的年份。最后一行将只有“a b”作为新列中的值,因为它的 id 重复了。

我尝试了各种applygroup_by 方法,但似乎没有一个完全符合要求。将不胜感激任何和所有的想法!

【问题讨论】:

    标签: r string match


    【解决方案1】:

    我使用sqldfplyr 包来实现解决方案。虽然我不会称这是一个优雅的解决方案,但它确实有效。希望从其他人那里看到更有效的解决方案。

    library(sqldf)
    
    toy <- data.frame("id" = c(1,2,3,2), 
                       "year" = c(2000,2000,2004,2004), 
                       "words" = c("a b", "c d", "e b", "c d"))
    
    toy
    
    #  id year words
    #1  1 2000   a b
    #2  2 2000   c d
    #3  3 2004   e b
    #4  2 2004   c d
    
    df <- sqldf('SELECT t1.*,t2.words AS word_pool FROM toy t1 LEFT JOIN toy t2 
           ON t1.year > t2.year AND
           t1.words <> t2.words')
    
    df
    #  id year words word_pool
    #1  1 2000   a b      <NA>
    #2  2 2000   c d      <NA>
    #3  3 2004   e b       a b
    #4  3 2004   e b       c d
    #5  2 2004   c d       a b
    
    result <- plyr::ddply(df,c("id","year","words"), 
                          function(dfx)paste(dfx$word_pool, 
                                             collapse = " "))
    
    result
    #  id year words      V1
    #1  1 2000   a b      NA
    #2  2 2000   c d      NA
    #3  2 2004   c d     a b
    #4  3 2004   e b a b c d
    

    【讨论】:

    • 这行得通,我也有兴趣看看其他人是否有不同的解决方案。您认为这会有效地扩展到数千行吗?
    • sqldf 非常有效。如果有的话,瓶颈可能是 plyr 部分。但是,我已经使用类似的代码进行了至少一百万次观察,没有任何问题。因此,对于数千个订单,您应该没问题。
    【解决方案2】:

    使用 for 和 which,这必须像 apply 一样写,并且不能使用 extern 库

            ## Create data
            toy <-
              data.frame(
                "id" = c(1, 2, 3, 2),
                "year" = c(2000, 2000, 2004, 2004),
                "words" = c("a b", "c d", "e b", "c d")
              )
    
            toy$word_pool <- 0
            for (i in 1:length(toy)) {
              # Recognize index from condition
              condition_index <- which(toy$year[i] > toy$year
                                            & toy$id[i] != toy$id)
              # assign
              if (length(condition_index) == 0){# case no index
                toy$word_pool[i] = ""
              }
              else{# paste with collapse join array
                toy$word_pool[i] = paste(toy$words[condition_index],
                                         collapse = " ", sep = " ")
              }
            }
            toy
            # id year words word_pool
            # 1  2000   a b          
            # 2  2000   c d          
            # 3  2004   e b   a b c d
            # 2  2004   c d       a b
    

    【讨论】:

    • 谢谢。当然可以,但理想情况下该解决方案不会使用循环(我正在使用的实际数据有数千行)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2014-12-04
    • 2021-10-03
    • 1970-01-01
    相关资源
    最近更新 更多