Удалить цикл for из алгоритма stringdist в R

Я сделал алгоритм для определения оценок совпадающих строк из 2 фреймов данных в R. Он будет искать для каждой строки в test_ech совпадающие строки, оценка которых превышает 0,75 в test_data (на основе совпадения 3 столбцов из каждого фрейма данных) .

Ну, мой код отлично работает с небольшим фреймом данных, но я имею дело с фреймами данных из 12 миллионов строк, и процесс займет не менее 5 дней. Поэтому я думаю, что если я откажусь от «циклов», это сработает, но я действительно не знаю, как это сделать. (и если есть дополнительные изменения, которые мне нужно сделать, чтобы облегчить процесс)

Спасибо.

#score function :

library(stringdist)

score <- function(i,j) 
{  
s_n<-stringsim(test_ech[j,3],test_data[i,5],method = "jw",p=0.15)
s_v<-stringsim(test_ech[j,5],test_data[i,4],method = "jw",p=0.15)
s_c<-stringsim(test_ech[j,4],test_data[i,3],method = "jw",p=0.15)

 return(s_n*0.6+s_v*0.25+s_c*0.15)
}

#initialize result data frame :

resultat<-data.frame(nom_AS400=character(),ville_AS400=character(),cp_AS400=character(),                nom_SIRENE=character(),ville_SIRENE=character(),cp_SIRENE=character(),score=double())

#algo textmining :

system.time(for (j in 1:nrow(test_ech)) {

  for (i in 1:nrow(test_data)) {

    x<-score(i,j)

    if (x>0.75) {

ligne<-data.frame(nom_AS400=test_ech[j,3],
       ville_AS400=test_ech[j,5],
       cp_AS400=test_ech[j,4],
       nom_SIRENE=test_data[i,5],
       ville_SIRENE=test_data[i,4],
       cp_SIRENE=test_data[i,3],
       score=x)

      resultat<-rbind(resultat,ligne)      
    }  
  } 
})

test_ech: 65 тыс. строк и test_data: 12 млн строк

#test_ech (5 rows)
structure(list(societe_code = c("01", "01", "01", "01", "01"), 
    client_code = c("00048I", "00059Z", "00070Q", "00080W", "00131L"
    ), client_lib = c("CFA VAUBAN", "ALLRIM SA", "ATS CULLIGAN", 
    "AHSSEA", "ETS BRUNEAU P"), client_cp = c("25001", "25401", 
    "25480", "70002", "94700"), client_ville = c("BESANCON CEDEX", 
    "AUDINCOURT CEDEX", "ECOLE VALENTIN", "VESOUL CEDEX", "MAISONS ALFORT"
    )))

#test_data (5 rows)
structure(list(siren = c("005450093", "005450095", "005541552", 
"005580501", "005620117"), siret = c("00545009300033", "00545009300041", 
"00554155200039", "00558050100012", "00562011700019"), codePostalEtablissement = c("04800", 
"04802", "04260", "44600", "80100"), libelleCommuneEtablissement = c("GREOUX LES BAINS", 
"BAINS", "ALLOS", "SAINT NAZAIRE", "ABBEVILLE"), ref = c("PASSIONNEMENT GLAMOUR", 
"GLAMOUR", "LE SYMPA SNACK", "STEF", "DUBOIS")))

ожидаемый результат - это кадр данных с 3 опорными столбцами из test_ech с 3 соответствующими столбцами из test_data и оценкой, которая должна быть> 0,75.

выходная ссылка


person Amine96    schedule 04.06.2019    source источник
comment
Привет, Амин, не могли бы вы дать нам образец вашей базы данных, используя функцию dput()?   -  person Luis    schedule 04.06.2019
comment
Боюсь, вам придется вызвать score 12m·12m раз, что составляет 1,4·10^14, что очень много. Независимо от того, используете ли вы циклы for или массивы…   -  person ziggystar    schedule 04.06.2019
comment
Можно запустить его параллельно, с хорошим компом заглянуть в будущий пакет   -  person Bruno    schedule 04.06.2019
comment
@ziggystar Я обновил сообщение в конце ... test_ech : 65 тыс. строк и test_data : 12 млн строк ... Так что да, тебе нужно вызывать score 12 млн раз для 65 тыс. строк всего   -  person Amine96    schedule 04.06.2019
comment
@Luis Я обновил сообщение, вы можете найти 2 фрейма данных, используя функцию dput ()   -  person Amine96    schedule 04.06.2019
comment
Пожалуйста, дважды проверьте данные вашего примера из dput, они кажутся неполными. И если вы можете убедиться, что некоторые записи соответствуют вашему порогу баллов.   -  person emilliman5    schedule 04.06.2019
comment
@ Amine96, я не могу заставить ваш пример работать. После запуска do.call(cbind.data.frame, test_ech), чтобы превратить ваш объект dput из списка в фрейм данных (то же самое для test_data), я запускаю score(1, 1), и это не работает. Сама функция вызывает четвертый и пятый столбцы test_ech и test_data, но объекты dput имеют только 3 столбца.   -  person Luis    schedule 04.06.2019
comment
Код вывода @emilliman5 обновлен (извините за задержку)   -  person Amine96    schedule 04.06.2019
comment
Код @Luis dput обновлен (извините за задержку)   -  person Amine96    schedule 04.06.2019


Ответы (2)


Я не уверен, что это полностью решит вашу проблему, учитывая размеры ваших исходных данных, но вы можете существенно сократить свое время, выполнив это за один цикл for вместо двух. Вы можете сделать это, потому что функция stringsim принимает объект с одним символом с одной стороны и вектор с другой.

    score_2 <- function(j) 
{  
  s_n <- stringsim(test_ech[[j,3]], test_data[[5]], method = "jw", p = 0.15)
  s_v <- stringsim(test_ech[[j,5]], test_data[[4]], method = "jw", p = 0.15)
  s_c <- stringsim(test_ech[[j,4]], test_data[[3]], method = "jw", p = 0.15)

  return(s_n * 0.6 + s_v * 0.25 + s_c * 0.15)
}

    stringsim (test_ech[,3], test_data[,5])

    resultat<-data.frame(nom_AS400=character(),ville_AS400=character(),cp_AS400=character(),                nom_SIRENE=character(),ville_SIRENE=character(),cp_SIRENE=character(),score=double())

    for (j in 1:nrow(test_ech)) {

      x <- score_2(j)

      x_75 = which(x > 0.75)

      if(length(x_75) > 0){
        for(i in x_75){

         ligne<-data.frame(nom_AS400=test_ech[[j,3]],
                           ville_AS400=test_ech[[j,5]],
                           cp_AS400=test_ech[[j,4]],
                           nom_SIRENE=test_data[[i,5]],
                           ville_SIRENE=test_data[[i,4]],
                           cp_SIRENE = test_data[[i,3]],                       
                           score = x[i])

      resultat<-rbind(resultat,ligne)

    }
   }
  }

Ваша функция, повторяющая оба ваших тестовых объекта 60 раз:

  usuário   sistema decorrido 
     9.59      1.43     11.12 

Эта функция, повторяющая оба тестовых объекта 60 раз:

  usuário   sistema decorrido 
     0.21      0.08      0.18 

Чуть быстрее :)

(примечание: есть stringdistmatrix, который принимает векторы с обеих сторон и возвращает матрицу, но, к сожалению, нет stringsimmatrix. Если вы можете определить разницу между stringdist и stringsim, запуск stringdistmatrix и его настройка, вероятно, будут еще быстрее).

person Luis    schedule 04.06.2019
comment
Спасибо за ваш ответ, мне нравится использовать только один цикл вместо двух. Тем не менее, есть проблема с функцией score: она вычисляет соответствие одной строки из test_ech[,3] вектору, содержащему все строки в test_data[,5], и в конце возвращает среднее значение оценка при сопоставлении всех строк в test_data, а не каждой оценки для каждого сопоставления. - person Amine96; 05.06.2019
comment
Я обновил ваш код в сообщении с ответом... Большое спасибо за вашу помощь. - person Amine96; 05.06.2019
comment
@ Amine96 А, вы правы, это должно было быть x[i] вместо x[x_75]. В любом случае, по крайней мере, ваша проблема решена. Вы не против принять мой ответ? - person Luis; 05.06.2019
comment
Не беспокойтесь, но вам нужно обновить свой код моим, чтобы пометить его как ответ. (Я также изменил функцию оценки с помощью [[]] в test_data и test_ech) - person Amine96; 05.06.2019

Наконец, я решил проблему благодаря @Luis, используя только один цикл вместо двух.

Код ниже:

    score_2 <- function(j) 
{  
  s_n <- stringsim(test_ech[[j,3]], test_data[[5]], method = "jw", p = 0.15)
  s_v <- stringsim(test_ech[[j,5]], test_data[[4]], method = "jw", p = 0.15)
  s_c <- stringsim(test_ech[[j,4]], test_data[[3]], method = "jw", p = 0.15)

  return(s_n * 0.6 + s_v * 0.25 + s_c * 0.15)
}

    stringsim (test_ech[,3], test_data[,5])

    resultat<-data.frame(nom_AS400=character(),ville_AS400=character(),cp_AS400=character(),                nom_SIRENE=character(),ville_SIRENE=character(),cp_SIRENE=character(),score=double())

    for (j in 1:nrow(test_ech)) {

      x <- score_2(j)

      x_75 = which(x > 0.75)

      if(length(x_75) > 0){
        for(i in x_75){

         ligne<-data.frame(nom_AS400=test_ech[[j,3]],
                           ville_AS400=test_ech[[j,5]],
                           cp_AS400=test_ech[[j,4]],
                           nom_SIRENE=test_data[[i,5]],
                           ville_SIRENE=test_data[[i,4]],
                           cp_SIRENE = test_data[[i,3]],                       
                           score = x[i])

      resultat<-rbind(resultat,ligne)

    }
   }
  }
person Amine96    schedule 05.06.2019