【问题标题】:Is it possible to merge rows in R data.frame?是否可以合并 R data.frame 中的行?
【发布时间】:2017-03-14 18:49:15
【问题描述】:

如果我有以下data.frame:

> df <- data.frame(x = c('a', 'b*', 'c'), y = c('d', 'e', 'f'))
> df
   x y
1  a d
2 b* e
3  c f

是否有明确的方法来识别df$x条目中包含字符串值*的行,然后使用此条件强制该行的字符串条目与自身前面的行合并,从而导致data.frame 如下:

> df
     x   y
1 a b* d e
2    c   f

我假设问题的第一部分(识别包含 `* 的 x 行值)可以使用正则表达式以相当简单的方式完成。我无法确定如何强制 data.frame 行与其前面的行合并。

一个特别棘手的挑战是,如果一行中的多个条目具有该模式,例如

> df <- data.frame(x = c('a', 'b*', 'c*'), y = c('d', 'e', 'f'))
> df
   x y
1  a d
2 b* e
3 c* f

在这种情况下,生成的 data.frame 应该如下所示:

> df
        x     y
1 a b* c* d e f

我发现的主要问题是,在运行将字符串从 df[2,] 粘贴到 df[1,] 的循环的一次迭代后,data.frame 索引不适应新的 data.frame 大小:

> df
     x   y
1 a b* d e
3   c*   f

因此,随后的索引被中断。

【问题讨论】:

  • 您希望新的df 的输出是什么?
  • 我已经对我的问题进行了编辑,以显示所需的输出并更好地解释我遇到的困难。

标签: r string dataframe merge


【解决方案1】:

这里是一个初步的解决方案:

# Creating the data frame
df <- data.frame(x = c('a', 'b*', 'c'), y = c('d', 'e', 'f'),stringsAsFactors = FALSE)
df

# Creating a vector of rows with *
ast <- grepl("\\*",df$x)

# For loop
for(i in seq(length(ast),1,-1)){
  if(ast[i]){
    df[i-1,"x"] <- paste(df[i-1,"x"],df[i,"x"],sep=" ")
    df[i-1,"y"] <- paste(df[i-1,"y"],df[i,"y"],sep=" ")
    df <- df[-i,]
  }
}

这是一个初始解决方案,因为当第一行有 * 和其他类似情况时,您仍然需要进行管理。我希望这已经有所帮助。

【讨论】:

  • 关于如何处理具有条件的多个后续行的任何想法?向上合并一行后,索引就会混乱(例如,如果 c*df[3,1] 条目。
  • 这就是为什么在 for 循环中我倒数。例如,我将第 3 行与第 2 行合并,然后删除第 3 行。第 3 行顶部的所有内容保持不变,循环索引仍然有效。
【解决方案2】:

这里有 3 种替代方案(对于基本 R 之一,我假设 xy 是字符而不是因素。我还使您的数据更复杂以涵盖不同的场景)

(稍微复杂一点的数据集)

df <- data.frame(x = c('p','a', 'b*', 'c*', 'd', 'h*', 'j*', 'l*', 'n'), 
                 y = c('r','d', 'e', 'f', 'g', 'i', 'k', 'm', 'o'), 
                 stringsAsFactors = FALSE)

基础R

aggregate(. ~ ID, 
          transform(df, ID = cumsum(!grepl("*", x, fixed = TRUE))),
          paste, collapse = " ")
#   ID          x       y
# 1  1          p       r
# 2  2    a b* c*   d e f
# 3  3 d h* j* l* g i k m
# 4  4          n       o

数据表

library(data.table)
setDT(df)[, lapply(.SD, paste, collapse = " "), 
            by = .(ID = cumsum(!grepl("*", df[["x"]], fixed = TRUE)))]
#    ID          x       y
# 1:  1          p       r
# 2:  2    a b* c*   d e f
# 3:  3 d h* j* l* g i k m
# 4:  4          n       o

dplyr

library(dplyr)
df %>%
  group_by(ID = cumsum(!grepl("*", x, fixed = TRUE))) %>%
  summarise_all(funs(paste(., collapse = " ")))

# # A tibble: 4 x 3
#      ID          x       y
#   <int>      <chr>   <chr>
# 1     1          p       r
# 2     2    a b* c*   d e f
# 3     3 d h* j* l* g i k m
# 4     4          n       o

【讨论】:

    【解决方案3】:

    实际上并不合并行,但对于那些有 * 的行,它会将前一行的值粘贴到其中,然后删除下一行中带有 * 的行。

    library(dplyr)
    
    df <- data.frame(x = c('a', 'b*', 'c'), y = c('d', 'e', 'f'))
    
    df <- mutate(df, 
                 Operator = grepl("\\*",x), # Check for *
                 lagged.x = lag(x, n = 1),  # Get x value from 1 row ago
                 lagged.y = lag(y, n = 1),  # Get y value from 1 row ago
                 x = ifelse(Operator, paste(lagged.x, x),x), # if there is * paste lagged x
                 y = ifelse(Operator, paste(lagged.y, y),y), # if there is * paste lagged y
                 lead.Operator = lead(Operator, n = 1)       # Check if next row has a *
    )
    
    # keep only rows that had no * in following row and that had no following row (last row)
    df <- filter(df, !lead.Operator | is.na(lead.Operator))
    
    # Select just the x and y columns
    df <- select(df, x, y)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-04
      • 1970-01-01
      • 2013-01-24
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 2017-02-24
      相关资源
      最近更新 更多