Как заменить столбец со строками с кодами поиска в R - PullRequest
3 голосов
/ 20 марта 2020

Представьте, что у меня есть столбец данных или таблица данных со строками, в которой одна строка выглядит следующим образом:

a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4

и справочная таблица с кодами для сопоставления каждой из этих строк. Например:

string code
a1     10
b1     20
b2     30
b3     40
c1     50
c2     60
...

Я хотел бы иметь функцию отображения, которая отображает эту строку в код:

10; b: 20, 30, 40; c: 50, 60, 70; d: 80, 90, 100

У меня есть столбец этих строк в data.table / data. кадр (более 100 тыс.), поэтому любое быстрое решение будет очень цениться. Обратите внимание, что эта длина строки не всегда одинакова ... например, в одной строке я могу иметь строки a до d, в других a до f.

РЕДАКТИРОВАТЬ :

Мы получили решение для описанного выше случая, однако представьте, что у меня есть строка, подобная этой:

a; b: peter, joe smith, john smith; c: luke, james, john smith

Как заменить это, зная, что john smith может иметь два разных кода в зависимости от того, принадлежит ли он к категории b или c? Кроме того, строка может содержать слова с пробелом между ними.

РЕДАКТИРОВАТЬ 2 :

   string     code
    a          10
    peter      20
    joe smith  30
    john smith 40
    luke       50
    james      60
    john smith 70
...

Окончательное решение:

10; b: 20, 30, 40; c: 50, 60, 70

РЕДАКТИРОВАТЬ 3 Как было предложено, я открыл новый вопрос для следующего выпуска: Как заменить повторяющиеся строки и пробел между ними с помощью кодов поиска в R

Ответы [ 3 ]

5 голосов
/ 20 марта 2020

Мы можем использовать gsubfn

library(gsubfn)
gsubfn("([a-z]\\d+)", setNames(as.list(df1$code), df1$string), str1)
#[1] "10; b: 20, 30, 40; c: 50, 60, 70; d: 80, 90, 100, 110"

Для отредактированной версии

gsubfn("(\\w+ ?\\w+?)",  setNames(as.list(df2$code), df2$string), str2)
#[1] "a; b: 20, 30, 40; c: 50, 60, 40"

data

str1 <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
df1 <- structure(list(string = c("a1", "b1", "b2", "b3", "c1", "c2", 
 "c3", "d1", "d2", "d3", "d4"), code = c(10L, 20L, 30L, 40L, 50L, 
 60L, 70L, 80L, 90L, 100L, 110L)), class = "data.frame",
  row.names = c(NA, -11L))

str2 <- "a; b: peter, joe smith, john smith; c: luke, james, john smith"

df2 <- structure(list(string = c("a", "peter", "joe smith", "john smith", 
"luke", "james", "john smith"), code = c(10L, 20L, 30L, 40L, 
50L, 60L, 70L)), class = "data.frame", row.names = c(NA, -7L))
2 голосов
/ 20 марта 2020

Вот некоторые базовые решения R.

  • Подход 1: использование Reduce
res <- Reduce(function(x,k) gsub(df$string[k],df$code[k],x),
              c(s,as.list(1:nrow(df))))

такое, что

> res
[1] "10; b: 20, 30, 40; c: 50, 60, c3; d: d1, d2, d3, d4"
  • Подход 2: определить пользовательскую рекурсивную функцию f, чтобы сделать ее
f <- function(k) ifelse(k==0,s,gsub(df$string[k],df$code[k],f(k-1)))
res <- f(nrow(df))

такой, что

> res
[1] "10; b: 20, 30, 40; c: 50, 60, c3; d: d1, d2, d3, d4"

ДАННЫЕ

s <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
df <-structure(list(string = c("a1", "b1", "b2", "b3", "c1", "c2"), 
    code = c(10L, 20L, 30L, 40L, 50L, 60L)), class = "data.frame", row.names = c(NA, 
-6L))
2 голосов
/ 20 марта 2020

Гораздо более быстрой альтернативой было бы использование stringr::str_replace_all():

library(stringr)
library(gsubfn)

mystring <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
mystrings <- rep(mystring, 10000)

str_replace_all(mystrings, setNames(as.character(df$code), df$string))

microbenchmark::microbenchmark(gsubfn = gsubfn("([a-z]\\d+)", setNames(as.list(df$code), df$string), mystrings),
                               stringr = str_replace_all(mystrings, setNames(as.character(df$code), df$string)), check = "equal", times = 50)

Unit: milliseconds
    expr        min         lq      mean     median         uq        max neval cld
  gsubfn 4846.19633 5584.54845 5923.5042 5939.49794 6261.29821 7479.04022    50   b
 stringr   29.01798   29.94274   31.6118   30.80002   31.72871   50.57533    50  a 
...