想象一下,我有一个这样的数据框(或所有月份的名称)
set.seed(1)
mydata <- data.frame()
mydata <- rbind(mydata,c(1,round(runif(20),3)))
mydata <- rbind(mydata,c(2,round(runif(20),3)))
mydata <- rbind(mydata,c(3,round(runif(20),3)))
colnames(mydata) <- c("id", paste0(rep(c('Mary', 'Bob', 'Dylan', 'Tom', 'Jane', 'Sam', 'Tony', 'Luke', 'John', "Pam"), each=2), 1:2))
id Mary1 Mary2 Bob1 Bob2 Dylan1 Dylan2 Tom1 Tom2 Jane1 Jane2 Sam1 Sam2 Tony1 Tony2 Luke1 Luke2 John1 John2 Pam1 Pam2
1 0.266 0.372 0.573 0.908 0.202 0.898 0.945 0.661 0.629 0.062 0.206 0.177 0.687 0.384 0.770 0.498 0.718 0.992 0.380 0.777
2 0.935 0.212 0.652 0.126 0.267 0.386 0.013 0.382 0.870 0.340 0.482 0.600 0.494 0.186 0.827 0.668 0.794 0.108 0.724 0.411
3 0.821 0.647 0.783 0.553 0.530 0.789 0.023 0.477 0.732 0.693 0.478 0.861 0.438 0.245 0.071 0.099 0.316 0.519 0.662 0.407
通常包含更多列。
我想添加列(由您决定将它们添加到右侧,或者使用这些新列创建一个新的数据帧)每两个减去...(*)
id, Mary1-Mary2, Bob1-Bob2, Dylan1-Dylan2, Tom1-Tom2, Jane1-Jane2,...
此操作很常见。
我希望通过姓名,而不是按职位来做,以防止问题,如果他们不连续。 甚至可能会发生一些列没有它的“双”列,只是保持原样,或者现在忽略这种复杂性。
(*)列的名称有前缀和数字。 而不是仅仅减去两列我可以有5个组,我可能想要做一些事情,比如添加所有数字。通用解决方案会很棒。
我首先尝试将其转换为长格式,然后使用聚合操作,然后将其转换回宽格式,但也许更容易直接以宽格式执行。我知道问题主要与使用有效的正则表达式有关。
R, data.table or dplyr, long format splitting colnames
我不介意速度,而是最简单的解决方案。 任何套餐都很好。
PD:如果我添加一个孤独的列,所有代码都会失败。 set.seed(1)
mydata <- data.frame()
mydata <- rbind(mydata,c(1,round(runif(21),3)))
mydata <- rbind(mydata,c(2,round(runif(21),3)))
mydata <- rbind(mydata,c(3,round(runif(21),3)))
colnames(mydata) <- c(c("id", paste0(rep(c('Mary', 'Bob', 'Dylan', 'Tom', 'Jane', 'Sam', 'Tony', 'Luke', 'John', "Pam"), each=2), 1:2)),"Lola" )
我知道我可以手动过滤掉它但如果结果是每对的差异(*)并且单独留下孤独的列会更好。 (如果是第2组的差异)
最佳选择不是手动删除第一列,而是将所有列拆分为单列和多列。
答案 0 :(得分:3)
如何使用基数R:
onClick
您可以将此方法用于任意数量的组。例如,这将返回带有后缀1或2的名称的行和。:
cn <- unique(gsub("\\d", "", colnames(mydata)))[-1]
sapply(cn, function(x) mydata[[paste0(x, 1)]] - mydata[[paste0(x, 2)]] )
此粘贴方法可以替换为更常规应用程序的正则表达式。
答案 1 :(得分:2)
你可以做点什么,
sapply(unique(sub('\\d', '', names(mydata[,-1]))),
function(i) Reduce('-', mydata[,-1][,grepl(i, sub('\\d', '', names(mydata[,-1])))]))
# Mary Bob Dylan Tom Jane Sam Tony Luke John Pam
#[1,] -0.106 -0.335 -0.696 0.284 0.567 0.029 0.303 0.272 -0.274 -0.397
#[2,] 0.723 0.526 -0.119 -0.369 0.530 -0.118 0.308 0.159 0.686 0.313
#[3,] 0.174 0.230 -0.259 -0.454 0.039 -0.383 0.193 -0.028 -0.203 0.255
根据您的评论,我们可以轻松地对列进行排序,然后应用上面的公式
sorted.names <- names(mydata)[order(nchar(names(mydata)), names(mydata))]
mydata <- mydata[,sorted.names]
答案 2 :(得分:1)
此解决方案处理任意数量的双胞胎。
## return data frame
twin.vars <- function(prefix, df) {
df[grep(paste0(prefix, '[0-9]+$'), names(df))]
}
pfx <- unique(sub('[0-9]*$', '', names(mydata[-1])))
tmp <- lapply(pfx, function(x) Reduce(`-`, twin.vars(x, mydata)))
cbind(id=mydata$id, as.data.frame(setNames(tmp, pfx)))
答案 3 :(得分:1)
好的,我选择了@NBATrends解决方案,因为它几乎总能运作良好,而且他是第一个。
无论如何,我加上我的小贡献,以防万一有兴趣:
runs <- rle(sort(sub('\\d$', '', names(mydata))))
sapply(runs[[2]][runs[[1]]>1], function(x) mydata[[paste0(x, 1)]] - mydata[[paste0(x, 2)]] )
唯一的问题&#34;是它改变了最终的顺序,但你不需要手动删除孤立的列,也适用于无序列。
我感到困惑,因为没有人发布dplyr或data.table的解决方案:)