我正在努力遍历数据帧中某一列的行,然后使用当前行来定义将在函数中使用的参数。这是示例数据框:
df <-
structure(list(child = c("A268", "A268497", "A268497BOX", "A268497BOX2",
"A268497BOX218", "A277", "A277A79", "A277A79091", "A277A790911",
"A277A79091144", "A492", "A492586", "A492586BOX", "A492586BOX1",
"A492586BOX144", "A492A69", "A492A69027", "A492A690271", "A492A69027144",
"A492A6902715K", "A492A6902719Y", "A492A690271BH", "A492A690271BI",
"A492A690271CQ", "A492A690271CS", "A492A690271CT", "A492A690271CU",
"A492A690271CV", "A492A690271CW", "A492A690271CX", "A492A690271CY",
"A492A690271DA", "A492A69028", "A492A690281", "A492A69028144",
"A492A69402", "A492A694021", "A492A69402144", "A492A70", "A492A70033",
"A492A700331", "A492A70033144", "A492A700332", "A492A70033244",
"A492A70034", "A492A700341", "A492A70034144", "A492A70035", "A492A700351",
"A492A70035144"), clvl = c(2, 3, 4, 5, 6, 2, 3, 4, 5, 6, 2, 3,
4, 5, 6, 3, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4,
5, 6, 4, 5, 6, 3, 4, 5, 6, 5, 6, 4, 5, 6, 4, 5, 6), parent = c("A",
"A268", "A268497", "A268497BOX", "A268497BOX2", "A", "A277",
"A277A79", "A277A79091", "A277A790911", "A", "A492", "A492586",
"A492586BOX", "A492586BOX1", "A492", "A492A69", "A492A69027",
"A492A690271", "A492A690271", "A492A690271", "A492A690271", "A492A690271",
"A492A690271", "A492A690271", "A492A690271", "A492A690271", "A492A690271",
"A492A690271", "A492A690271", "A492A690271", "A492A690271", "A492A69",
"A492A69028", "A492A690281", "A492A69", "A492A69402", "A492A694021",
"A492", "A492A70", "A492A70033", "A492A700331", "A492A70033",
"A492A700332", "A492A70", "A492A70034", "A492A700341", "A492A70",
"A492A70035", "A492A700351"), plvl = c(1, 2, 3, 4, 5, 1, 2, 3,
4, 5, 1, 2, 3, 4, 5, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 3, 4, 5, 3, 4, 5, 2, 3, 4, 5, 4, 5, 3, 4, 5, 3, 4, 5
)), row.names = c(NA, 50L), class = "data.frame")
我的目标是生成此:
我尝试通过循环并在循环内使用不同版本的apply
函数来执行此操作,但我无法正确执行。在这里,我需要定义每次迭代时x和y将是当前行中的child
和pathString
。有没有一种干净便捷的方法?
df[] <- apply(df,1,function(x,y) sub(x,y,x))
假设child
(或pathString
)中的字符数将继续增加,如共享数据所示,一种方式是使用purrr::accumulate
,该方法允许从先前的输出中获取输入并按组应用它。
library(dplyr)
df %>%
group_by(gr = cumsum(c(TRUE, diff(nchar(child)) < 0))) %>%
mutate(ans = purrr::accumulate(pathString, ~sub(".*(/.*)",paste0(.x, "\\1"),.y)))
# child pathString gr ans
# <chr> <chr> <int> <chr>
# 1 A268 A/268 1 A/268
# 2 A268497 A268/497 1 A/268/497
# 3 A268497BOX A268497/BOX 1 A/268/497/BOX
# 4 A268497BOX2 A268497BOX/2 1 A/268/497/BOX/2
# 5 A268497BOX218 A268497BOX2/18 1 A/268/497/BOX/2/18
# 6 A277 A/277 2 A/277
# 7 A277A79 A277/A79 2 A/277/A79
# 8 A277A79091 A277A79/091 2 A/277/A79/091
# 9 A277A790911 A277A79091/1 2 A/277/A79/091/1
#10 A277A79091144 A277A790911/44 2 A/277/A79/091/1/44
保留最终输出中组的gr
列以阐明如何创建组。
我们也可以使用Reduce
在基R中实现相同的逻辑
apply_fun <- function(x, y) sub(".*(/.*)", paste0(x, "\\1"), y)
df$ans <- with(df, ave(pathString, cumsum(c(TRUE, diff(nchar(child)) < 0)),
FUN = function(x) Reduce(apply_fun, x, accumulate = TRUE)))
我设法使用下面的代码块完成了它,但是循环需要75-80秒,我想可能会有更快的方法:
for(row in 1:nrow(df5)) {
x=df5[row,2] #child
y=df5[row,3] #pathString
g=df5[row,c('gr')]
df5$pathString[df5$gr==g] <- sub(x,y,df5$pathString[df5$gr==g])
df5$child[df5$gr==g] <- sub(x,y,df5$child[df5$gr==g])
}
注意,gr
是根据clvl=2
填充的:
library(zoo)
df4$gr <- ifelse(df4$clvl==2,df4$child,NA)
df4$gr <- na.locf(df4$gr)
这就是df4
的制作方式:
df4 <- sqldf("select *, parent || replace(child,parent,'/') AS pathString FROM df ORDER BY child")