在R中分组/合并几个列

时间:2018-12-06 00:56:29

标签: r rstudio

我有一个数据文件,其中n列分为3组,每组有几个主题。因此,标题为G1S1,G1S2 ... G2S1,G2S2。。每一列中的数据均独立于其他列。我想做的是将第1组中的所有数据放在column1中。第2栏中的第2组,依此类推。 我尝试了以下代码,该代码可用于将第1组的所有数据放入一个以G1列为标题的新数据帧中。

dt <-TestFile [1:5] # extract data from group 1
dt2 <- NULL
tmp1 <- NULL

for (i in 1:ncol(dt)) {
  ColName <- names(dt)[i] #Get the column mame
  tmp1 <- dt[ColName] #copy data to tmp1
  GrpName <- substring(ColName,1,2) #get group name from column name
  names(tmp1)[names(tmp1)==ColName]<-GrpName #rename column header to match column in dt2 '
  dt2 <- rbind (dt2,tmp1) # merge data together
}

此代码适用于一个组,但是,如果我想添加另一个组,我会陷入困境,因为rbin函数现在不再起作用,因为列数不再相同。

如果我在循环中添加这些iff语句,则可以创建3个data.frames,但是如果我获得的文件具有不同的组号,那么我将不得不添加更多的if语句,从长远来看这是不可行的。

if (GrpName == 'G1'){
    dt1 <- rbind (dt1,tmp1) # merge data together}
  }
  if(GrpName == 'G2'){
    dt2 <- rbind (dt2,tmp1) # merge data together}
  }

  if(GrpName == 'G3'){
    dt3 <- rbind (dt3,tmp1) # merge data together}

  }

有人建议从这里去吗?

1 个答案:

答案 0 :(得分:0)

使用一些示例数据:

dat <- read.table(stringsAsFactors=FALSE, header=TRUE, text="
G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
111  121  131  211  221  231  311  321  331
112  122  132  212  222  232  312  322  332
113  123  133  213  223  233  313  323  333")

想要仅在每个名称的第二个字符上进行分割,让我们首先知道如何将其与其他字符分开:

gsub("S.*", "", names(dat))
# [1] "G1" "G1" "G1" "G2" "G2" "G2" "G3" "G3" "G3"

现在,我们要做的是将列提取到这三组中。一种方法是split。如果我们单独使用split,则无法使用:

split(dat, gsub("S.*", "", names(dat)))
# Warning in split.default(x = seq_len(nrow(x)), f = f, drop = drop, ...) :
#   data length is not a multiple of split variable
# $G1
#   G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# 1  111  121  131  211  221  231  311  321  331
# 2  112  122  132  212  222  232  312  322  332
# 3  113  123  133  213  223  233  313  323  333
# $G2
# [1] G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# <0 rows> (or 0-length row.names)
# $G3
# [1] G1S1 G1S2 G1S3 G2S1 G2S2 G2S3 G3S1 G3S2 G3S3
# <0 rows> (or 0-length row.names)

这是因为split看到它正在data.frame上工作,并尝试逐行执行操作,这不是我们想要的。如果您四处搜寻,可以使用许多split S3方法(具有基于第一个参数的特定版本的函数):

methods("split")
# [1] split.data.frame split.Date       split.default    split.POSIXct   
# see '?methods' for accessing help and source code

我们默默使用的版本是split.data.frame,因为dat是框架。我们可以覆盖:

lodf <- split.default(dat, gsub("S.*", "", names(dat)))
lodf
# $G1
#   G1S1 G1S2 G1S3
# 1  111  121  131
# 2  112  122  132
# 3  113  123  133
# $G2
#   G2S1 G2S2 G2S3
# 1  211  221  231
# 2  212  222  232
# 3  213  223  233
# $G3
#   G3S1 G3S2 G3S3
# 1  311  321  331
# 2  312  322  332
# 3  313  323  333

从此处开始:我个人建议您将其保留在这种list帧(按我的临时名称lodf)的结构中。这是基于这样的假设:您对一个人所做的事情也将对其他人做(同一件事),在这种情况下,lapply是操作的自然选择。

如果您确实需要对此进行分类(再次,不鼓励使用),可以使用以下命令将它们作为组名称分配给呼叫环境:

for (nm in names(lodf)) assign(nm, lodf[[nm]])
ls()
# [1]  "dat"   "G1"   "G2"   "G3" "lodf"

与列表内框架相关的答案是How do I make a list of data frames?


您可能会更进一步,也许以“整洁”(即“长”)格式处理此问题:将组合并到框架本身中:

do.call(rbind.data.frame, c(mapply(function(nm, x) {
  # remove the 'G#' from each column name, allows row-binding later
  names(x) <- gsub(paste0("^", nm), "", names(x))
  # add the group name as a new column
  transform(x, Grp = nm)
}, names(lodf), lodf, SIMPLIFY = FALSE), list(stringsAsFactors = FALSE)))
#       S1  S2  S3 Grp
# G1.1 111 121 131  G1
# G1.2 112 122 132  G1
# G1.3 113 123 133  G1
# G2.1 211 221 231  G2
# G2.2 212 222 232  G2
# G2.3 213 223 233  G2
# G3.1 311 321 331  G3
# G3.2 312 322 332  G3
# G3.3 313 323 333  G3

(我不喜欢行名G1.1等,但它们无害。)说明:

  • mapplylapply相似,不同之处在于每个函数调用需要1个或更多参数,从而有效地将列表参数“压缩”在一起。它的参数是names(lodf)lodf,因此第一个调用(调用anonfunc(nm, x)作为其参数的匿名函数)看起来像anonfunc(names(lodf)[[1]], lodf[[1]]),第二anonfunc(names(lodf)[[2]], lodf[[2]]),等等。
  • 在该匿名函数中,我首先从列名称中删除G#。这样,我们以后就可以将它们行绑定为S1S2等。
  • 我将组名添加为框架Grp

这种格式允许您一次执行一次操作,只需根据需要应用Grp变量进行分组。例如,如果您使用的是dplyrdata.table,分别进行... %>% dplyr::group_by(Grp) %>% ...DT[, .(...), by="Grp"]很容易。