R применяет пользовательскую функцию ко всем строкам кадра данных - PullRequest
0 голосов
/ 26 января 2020

Я изо всех сил пытаюсь сделать l oop через строки столбца в кадре данных, а затем использовать текущую строку, чтобы определить аргументы, которые будут использоваться в функции. Вот пример кадра данных:

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")

enter image description here

Моя цель - сгенерировать это:

enter image description here

Я пытался сделать это с помощью al oop и использовать разные версии функции apply внутри l oop, но я не смог сделать это правильно. Здесь мне нужно определить, что x и y будут child и pathString из текущей строки при каждой итерации. Есть ли чистый и простой способ сделать это?

df[] <- apply(df,1,function(x,y) sub(x,y,x))

Ответы [ 2 ]

1 голос
/ 26 января 2020

Предполагая, что количество символов в 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 в окончательном выводе, чтобы уточнить, как создаются группы.


Мы также можем реализовать ту же логику c в базе R, используя Reduce

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)))
0 голосов
/ 27 января 2020

Мне удалось это сделать, используя следующий блок кода, но l oop занимает 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")
...