У меня есть две таблицы данных, например, dt и dt1
dt <- data.table(s=c("AA-AA-1", "BB-BB-2", "CC-CC-3"))
s
1 AA-AA-1
2 BB-BB-2
3 CC-CC-3
dt1 <- data.table(x=c(1,2,3), name=c("AA", "BB", "CC"))
x name
1: 1 AA
2: 2 BB
3: 3 CC
Мне нужно заменить часть строки в столбце s столбца dt на столбец имени dt1, сопоставив номер после последнего дефиса s & x col в столбце dt1 в dt, чтобы dt стал таким.
s
1: AA-AA-AA
2: BB-BB-BB
3: CC-CC-CC
Я знаю, что мы можем сделать это, разделив s и сопоставив
split <- lapply(strsplit(as.character(dt$s), split = "-"), tail, n=1)
dt1$name[match(dt$split, dt1$x)
Есть ли способ элегантно ускорить его?
Строки в dt могут достигать миллиона, и в нем много столбцов. Это повторяющаяся операция, поэтому мне нужно сэкономить столько времени, сколько смогу.
Вот базовый подход R. Мы можем создать столбец x
в первой таблице данных dt
, используя цифру справа от последнего тире. Затем мы можем merge
две таблицы данных на x
и, наконец, объединить ожидаемый результат s
.
dt$x <- sub(".*-", "", dt$s)
result <- merge(dt, dt1, by = "x")
result$s <- paste0(sub("\\d+", "", result$s), result$name)
result$s
[1] "AA-AA-AA" "BB-BB-BB" "CC-CC-CC"
Вот несколько более общий подход.
mapply(function(pat, repl, src){ sub(pat, repl, src) }, pat = dt1$x, repl = dt1$name, src = dt$s )
#[1] "AA-AA-AA" "BB-BB-BB" "CC-CC-CC"
Если вы говорите, что всегда хотите заменить после последнего - (дефис), вы можете упростить как:
mapply(function(repl, src){ sub("(?<=-)[^-]+$", repl, src, perl = T) }, repl = dt1$name, src = dt$s )
Обратите внимание: мое решение работает, только если dt
и dt1
заказаны, как в примере. Это означает, что все первые строки связаны и т. д. Если это не так, рассмотрите комбинацию @Tims (слияние ...) и моего решения.
Итак, вот надежное решение, основанное на некоторых идеях Тима:
dt <- data.table(s=c("AA-AA-1", "BB-BB-2", "CC-CC-3"))
dt1 <- data.table(x=3:1, name=c("CC", "BB", "AA")) # note the order is not right.
dt$x <- sub(".*-", "", dt$s)
dt <- merge.default(dt, dt1, by = "x")
dt$endResult <- mapply(function(repl, src){ sub("(?<=-)[^-]+$", repl, src, perl = T) }, repl = dt$name, src = dt$s )
мои dt и dt1 могут быть не в порядке. ваше решение выглядит хорошо, оно не вводит новый столбец в таблице dt. Я нуждаюсь в этом. не могли бы вы изучить это также.
mapply(sprintf, sub("[^-]+$", "%s", dt$s), dt1$name)
# AA-AA-%s BB-BB-%s CC-CC-%s
# "AA-AA-AA" "BB-BB-BB" "CC-CC-CC"
Я предположил, что оба фрейма данных находятся в соответствующем порядке (как в примере). Если нет, вам нужно сопоставить их раньше, например:
mapply(sprintf, sub("-.?$", "-%s", dt$s), dt1$name[match(gsub("[^0-9]","", dt$s), dt1$x)])
Мне нравится эта идея, но OP довольно четко указывает соответствие x
номеру
@eddi спасибо за редактирование. Я добавил строку, которая выполняет сопоставление перед sprintf
ing ..
Я бы выбрал простой подход:
dt1[dt[, .(x = as.integer(sub('.*-', '', s)), str = sub('[^-]+$', '', s))],
on = .(x), .(s = paste0(str, name))]
# s
#1: AA-AA-AA
#2: BB-BB-BB
#3: CC-CC-CC
Если они отсортированы соответствующим образом, как в вашем примере, вы можете использовать stringr::str_replace
:
library(stringr)
dt[,s := str_replace(s,as.character(dt1$x),dt1$name)]
dt
# s
# 1: AA-AA-AA
# 2: BB-BB-BB
# 3: CC-CC-CC
Поскольку вы просите ускорить код, сколько строк у
dt
иdt1
, соответственно, в ваших производственных условиях?