Нечеткое сопоставление со строками, содержащими числа

Я пытаюсь приблизить совпадения между эталонной и целевой строками.

Я пробовал adist и stringdist в R с различными доступными расстояниями.

Хотя алгоритмы хорошо справляются со строками, содержащими только буквы алфавита, они не могут сопоставлять строки, в которых присутствуют числа и специальные символы (% и т. д.).

Как можно обработать этот случай?

Ниже приведен мой код.

library(stringdist)

dist.name <- outer(tolower(WW_name),tolower(Px_name),
                   stringdist::stringdist, method = "lcs")

# We now take the pairs with the minimum distance
min.name<-apply(dist.name, 1, min)

match.s1.s2<-NULL

chk <- function(x){
  s2.i<-match(min.name[x],dist.name[x,])
  s1.i<-x
  match.s1.s2<-rbind(data.frame(s2.i=s2.i,s1.i=s1.i,WWname=WW_lookup[s1.i],
                                Pxname=Px_lookup[s2.i],
                                adist=min.name[x]),match.s1.s2)
  return(match.s1.s2)
}

outDf <- lapply(1:nrow(dist.name),FUN = chk)

outDf <- do.call(rbind.data.frame, outDf)

Примеры, где совпадение не правильное ниже -

введите здесь описание изображения

Pxname соответствует алгоритму | MappedPxName сопоставляется вручную

Мы ценим любые предложения.


person darkage    schedule 04.02.2020    source источник
comment
Можете ли вы привести краткий пример, когда сопоставление не работает должным образом?   -  person Allan Cameron    schedule 04.02.2020
comment
Очень интересная проблема, но, пожалуйста, предоставьте больше данных. Какие струны? Какой код вы используете, чтобы найти расстояние/сходство?   -  person Roman Luštrik    schedule 04.02.2020
comment
@AllanCameron добавил примеры   -  person darkage    schedule 04.02.2020
comment
@RomanLuštrik Я не могу обнародовать данные. Я добавил пример в пост. Надеюсь, это даст вам некоторое представление.   -  person darkage    schedule 04.02.2020
comment
Это хорошее начало. Можете ли вы сделать фрагмент данных копируемым/вставляемым? Вы всегда можете составить некоторые записи, которые имитируют ваши фактические данные.   -  person Roman Luštrik    schedule 04.02.2020


Ответы (1)


Вот моя попытка. Поскольку вы не можете поделиться своими данными, то, что я могу сделать для вас, ограничено. Но я надеюсь, что следующее поможет вам в некоторой степени. Я думаю, что задача здесь состоит в том, чтобы преобразовать числа в письменные числа. Это то, с чем мы можем справиться с английским пакетом. Я создал два вектора (например, WWname и PXname). Я также создал свою собственную функцию под названием myfun. Это обрабатывает некоторые манипуляции со строками, такие как преобразование % в проценты и преобразование чисел в записанные числа. Я использовал эту функцию и создал два фрейма данных (т. е. ww и px).

library(tidyverse)
library(english)
library(stringi)
library(stringdist)

WWname <- c("Excellence Dark 85% Cocoa 100g", "Excellence Dark 78% COCOA 100G",
            "ZDEL Excellence Dark 85% Cocoa 100g", "Excellence Dark 50% Cocoa 100g")

PXname <- c("Excellence Dark 85% Cocoa 100g", "Excellence Dark 80% Cocoa 200g",
            "ZDEL Excellence Dark 80% Cocoa 100g", "ZDEL Excellence Dark 85% Cocoa 100g",
            "ZDEL Excellence Dark 78% Cocoa 100g", "Excellence Dark 78% Cocoa 100g",
            "Excellence Dark 50% Cocoa 100g")

myfun <- function(myvec) {

  sub(x = myvec, pattern = "%", replacement = " percent") %>%
  sub(pattern = "(?<=[0-9])g|G", replacement = " grams", perl = T) %>%
  tolower %>% 
  stri_split_regex(pattern = "\\s") %>% 
  enframe %>% 
  unnest(value) %>% 
  mutate(word = if_else(grepl(x = value, pattern = "[0-9]+"),
                        as.character(english(as.numeric(value))),
                        value)) %>% 
  group_by(name) %>% 
  summarize(string = paste0(word, collapse = " ")) -> out
  return(out)
}

# Convert numbers to alphabets abd create new strings.

ww <- myfun(myvec = WWname)
px <- myfun(myvec = PXname)

# Calculate distance.
mymat <- stringdistmatrix(a = ww$string, b = px$string, method = "lcs")

rownames(mymat) <- WWname
colnames(mymat) <- PXname

# Check if there is any non-match.
mymat %>%
as.data.frame(stringsAsFactors = F) %>%
rownames_to_column(var = "WWname") %>%
pivot_longer(cols = -WWname, names_to = "PXname", values_to = "ranking") %>% 
group_by(WWname) %>%
mutate(check = if_else(any(ranking == 0),
                       TRUE,
                       FALSE)) -> out

Теперь проверяем соответствие шаблонов.

filter(out, check == TRUE) %>%
slice(which.min(ranking))

  WWname                              PXname                              ranking check
  <chr>                               <chr>                                 <dbl> <lgl>
1 Excellence Dark 50% Cocoa 100g      Excellence Dark 50% Cocoa 100g            0 TRUE 
2 Excellence Dark 78% COCOA 100G      Excellence Dark 78% Cocoa 100g            0 TRUE 
3 Excellence Dark 85% Cocoa 100g      Excellence Dark 85% Cocoa 100g            0 TRUE 
4 ZDEL Excellence Dark 85% Cocoa 100g ZDEL Excellence Dark 85% Cocoa 100g       0 TRUE

Если мы хотим проверить несовпадение, мы можем сделать следующее. Это вернет кратчайшее несоответствие. В данном случае соответствия нет.

filter(out, check == FALSE) %>%
slice(which.min(ranking)) 
person jazzurro    schedule 04.02.2020