İki dizi dizgenin (R cinsinden) eşleştirilmesi nasıl yapılır?


36

Bunun nasıl adlandırılması gerektiğinden emin değilim, bu yüzden daha iyi bir terim biliyorsanız lütfen beni düzeltin.

İki listem var. 55 öğeden biri (örneğin: bir dizge vektörü), diğer 92 dizisi. Öğe adları benzer ancak aynı değil.

Ben en iyi aday bulmak istediğiniz s 55 listedeki öğeleri (Ben o zaman geçmesi ve doğru uydurma alacaktır) için 92 listesinde.

Nasıl yapılabilir?

Nerede olduğunu düşündüm fikirler:

  1. Uygun olanları gör (bir şey listesi kullanarak mı?
  2. Dizeler vektörleri arasında bir mesafe matrisi deneyin, ancak onu en iyi nasıl tanımlayacağımı bilmiyorum (aynı harf sayısı, dizelerin sırası ne olacak?)

Peki hangi paket / fonksiyonlar / araştırma alanları böyle bir görevle ilgileniyor ve nasıl?

Güncelleme: İşte eşleştirmek istediğim vektörlere bir örnek

vec55 <- c("Aeropyrum pernix", "Archaeoglobus fulgidus", "Candidatus_Korarchaeum_cryptofilum", 
"Candidatus_Methanoregula_boonei_6A8", "Cenarchaeum_symbiosum", 
"Desulfurococcus_kamchatkensis", "Ferroplasma acidarmanus", "Haloarcula_marismortui_ATCC_43049", 
"Halobacterium sp.", "Halobacterium_salinarum_R1", "Haloferax volcanii", 
"Haloquadratum_walsbyi", "Hyperthermus_butylicus", "Ignicoccus_hospitalis_KIN4", 
"Metallosphaera_sedula_DSM_5348", "Methanobacterium thermautotrophicus", 
"Methanobrevibacter_smithii_ATCC_35061", "Methanococcoides_burtonii_DSM_6242"
)
vec91 <- c("Acidilobus saccharovorans 345-15", "Aciduliprofundum boonei T469", 
"Aeropyrum pernix K1", "Archaeoglobus fulgidus DSM 4304", "Archaeoglobus profundus DSM 5631", 
"Caldivirga maquilingensis IC-167", "Candidatus Korarchaeum cryptofilum OPF8", 
"Candidatus Methanoregula boonei 6A8", "Cenarchaeum symbiosum A", 
"Desulfurococcus kamchatkensis 1221n", "Ferroglobus placidus DSM 10642", 
"Halalkalicoccus jeotgali B3", "Haloarcula marismortui ATCC 43049", 
"Halobacterium salinarum R1", "Halobacterium sp. NRC-1", "Haloferax volcanii DS2", 
"Halomicrobium mukohataei DSM 12286", "Haloquadratum walsbyi DSM 16790", 
"Halorhabdus utahensis DSM 12940", "Halorubrum lacusprofundi ATCC 49239", 
"Haloterrigena turkmenica DSM 5511", "Hyperthermus butylicus DSM 5456", 
"Ignicoccus hospitalis KIN4/I", "Ignisphaera aggregans DSM 17230", 
"Metallosphaera sedula DSM 5348", "Methanobrevibacter ruminantium M1", 
"Methanobrevibacter smithii ATCC 35061", "Methanocaldococcus fervens AG86", 
"Methanocaldococcus infernus ME", "Methanocaldococcus jannaschii DSM 2661", 
"Methanocaldococcus sp. FS406-22", "Methanocaldococcus vulcanius M7", 
"Methanocella paludicola SANAE", "Methanococcoides burtonii DSM 6242", 
"Methanococcus aeolicus Nankai-3", "Methanococcus maripaludis C5", 
"Methanococcus maripaludis C6", "Methanococcus maripaludis C7", 
"Methanococcus maripaludis S2", "Methanococcus vannielii SB", 
"Methanococcus voltae A3", "Methanocorpusculum labreanum Z", 
"Methanoculleus marisnigri JR1", "Methanohalobium evestigatum Z-7303", 
"Methanohalophilus mahii DSM 5219", "Methanoplanus petrolearius DSM 11571", 
"Methanopyrus kandleri AV19", "Methanosaeta thermophila PT", 
"Methanosarcina acetivorans C2A", "Methanosarcina barkeri str. Fusaro", 
"Methanosarcina mazei Go1", "Methanosphaera stadtmanae DSM 3091", 
"Methanosphaerula palustris E1-9c", "Methanospirillum hungatei JF-1", 
"Methanothermobacter marburgensis str. Marburg", "Methanothermobacter thermautotrophicus str. Delta H", 
"Nanoarchaeum equitans Kin4-M", "Natrialba magadii ATCC 43099", 
"Natronomonas pharaonis DSM 2160", "Nitrosopumilus maritimus SCM1", 
"Picrophilus torridus DSM 9790", "Pyrobaculum aerophilum str. IM2", 
"Pyrobaculum arsenaticum DSM 13514", "Pyrobaculum calidifontis JCM 11548", 
"Pyrobaculum islandicum DSM 4184", "Pyrococcus abyssi GE5", "Pyrococcus furiosus DSM 3638", 
"Pyrococcus horikoshii OT3", "Staphylothermus hellenicus DSM 12710", 
"Staphylothermus marinus F1", "Sulfolobus acidocaldarius DSM 639", 
"Sulfolobus islandicus L.D.8.5", "Sulfolobus islandicus L.S.2.15", 
"Sulfolobus islandicus M.14.25", "Sulfolobus islandicus M.16.27", 
"Sulfolobus islandicus M.16.4", "Sulfolobus islandicus Y.G.57.14", 
"Sulfolobus islandicus Y.N.15.51", "Sulfolobus solfataricus P2", 
"Sulfolobus tokodaii str. 7", "Thermococcus gammatolerans EJ3", 
"Thermococcus kodakarensis KOD1", "Thermococcus onnurineus NA1", 
"Thermococcus sibiricus MM 739", "Thermofilum pendens Hrk 5", 
"Thermoplasma acidophilum DSM 1728", "Thermoplasma volcanium GSS1", 
"Thermoproteus neutrophilus V24Sta", "Thermosphaera aggregans DSM 11486", 
"Vulcanisaeta distributa DSM 14429", "uncultured methanogenic archaeon RC-I"
) 

2
Merhaba Tal:> Bunların yazımsız bilimsel isimler olarak gözüktüğü göz önüne alındığında, önce Levenshtein metrik değerini (92'ye 55'lik bir mesafe matrisi bağlamında) dener ve nasıl ortaya çıktığını görürdüm.
user603

2
Bir süre sonra, stringdistpaket bu tür şeyler için en iyi kaynak gibi görünüyor.
shabbychef

Yanıtlar:


18

Ben de benzer problemler yaşadım. (burada görüldü: https://stackoverflow.com/questions/2231993/merging-two-data-frames-using-fuzzy-approximate-string-matching-in-r )

Aldığım önerilerin çoğu düştü:

pmatch()Ve agrep(), grep(), grepl()üç fonksiyonlar Eğer yaklaşık dize veya yaklaşık regex yoluyla yaklaşık dize eşleştirme içine bazı fikir sağlayacaktır bakmak için zaman ayırın eğer.

İpleri görmeden, onlara nasıl eşleştirileceğine dair sert bir örnek vermek zordur. Bize bir örnek veri verebilirseniz, bir çözüme ulaşabileceğimize eminim.

Bulduğum bir diğer seçenek de dizgelerin düzleştirilmesi, dizgideki tolower()her kelimenin ilk harfine bakılması ve sonra karşılaştırılması. Bazen bu bir aksamadan çalışır. O zaman diğer cevaplarda belirtilen mesafeler gibi daha karmaşık şeyler var. Bazen bu işler, bazen korkunçlar - bu gerçekten dizelere bağlı.

Onları görebilir miyiz?

Güncelleştirme

Agrep (), bunların çoğu için hile yapacak gibi görünüyor. Agrep () uygulamasının sadece R'nin Levenshtein mesafesinin uygulanması olduğunu unutmayın.

agrep(vec55[1],vec91,value=T)

Bazıları hesaplanmaz, ancak Ferroplasm acidaramus'un Ferroglobus placidus DSM 10642 ile aynı olup olmadığından bile emin değilim, örneğin:

agrep(vec55[7],vec91,value=T) 

Bunların bazıları için biraz SOL olabilir ve belki de sıfırdan bir endeks oluşturmak en iyi bahis olduğunu düşünüyorum. yani ,. Vec55 için kimlik numaralarına sahip bir tablo oluşturun ve sonra vec91'deki vec55'teki kimliğe manuel olarak bir başvuru oluşturun. Acı verici, biliyorum ama birçoğu agrep () ile yapılabilir.


Merhaba Brandon - Verilerin bir örneğini ekledim. Teşekkürler!
Tal Galili

Merhaba Brandon - çözümünüz harika çalıştı - teşekkür ederim.
Tal Galili 10:10

SE'deki konuyla ilgili önceki soruya bağlantı için +1 (işaretçinin agrep () işlevini uygular).
user603

15

İki dize arasındaki mesafeleri ölçmek için birçok yol vardır. R'de yaygın olarak uygulanan iki önemli (standart) yaklaşım Levenshtein ve Hamming mesafesidir. İlki 'MiscPsycho' paketinde ve sonuncusu 'e1071'de bulunuyor. Bunları kullanarak, basitçe 92 x 55'lik çift mesafeli bir matris hesaplayacağım, sonra oradan devam edeceğim (yani, 1. listedeki "1" dizesi için en iyi aday eşleşmesi, 2. dizgiden "x" dizesi "1" dizesine en küçük mesafeye sahip olacaktı. ").

Alternatif olarak, RecordLinkage paketinde, istediğinizi yapmak için tasarlanmış ve eldeki görev için daha uygun görünen Jaro-Winkler mesafesini kullanan, ancak eldeki görev için daha uygun görünen bir işlev () işlevi vardır. .

EDIT: Cevabımı, vec55'in ilk girişi olan "Aeropyrum pernix" ile eşleşmeyi bulmak üzere Brandon'ın yorumunu ve Tal'in kodunu içerecek şekilde düzenleyeceğim :

agrep(vec55[1],vec91,ignore.case=T,value=T,max.distance = 0.1, useBytes = FALSE)
[1] "Aeropyrum pernix K1"

8
+1. : Dizeleri karşılaştırarak "düzenlemek mesafesi" olduğunda Ayrıca, yararlı olur durumunda, terim google en.wikipedia.org/wiki/Edit_distance
ars

@ ars:> teşekkürler, bu bir R arama motoruna beslemek ve çıkanları görmek için kullanışlı bir liste!
user603,

2
Levenshtein düzenleme mesafesi, baz paketinin bir parçası olarak agrep () ile gerçekleştirilir
Brandon Bertelsen

Harika cevap Kwak - Gelecekte bir göz atacağım!
Tal Galili 10:10

Şahsen, bunun Tal'in sorusuna daha eksiksiz bir cevap olduğunu hissediyorum. RecordLinkage'ımızı işaret etmek için +1 - Bunu kesinlikle denemek zorundayım.
Brandon Bertelsen

7

Kwak'ın faydalı cevabını tamamlamak için bazı basit ilkeler ve fikirler eklememe izin verin. Metriği belirlemenin iyi bir yolu, dizgilerin hedeflerinden nasıl farklılık gösterebileceğini düşünmektir. "Mesafeyi düzenle", varyasyon komşuları transpoze etmek veya tek bir tuşu yanlış yazmak gibi tipografik hataların bir birleşimi olduğunda faydalıdır.

Başka bir faydalı yaklaşım (biraz farklı bir felsefeyle), her dizgiyi ilgili dizgelerin bir sınıfının temsilcisine eşlemektir. " Soundex " yöntemi bunu yapar: Bir kelime için Soundex kodu, asıl ünsüz kodunu ve benzer şekilde içsel sonuç veren grupları kodlayan dört karakterden oluşan bir sekanstır. Kelimelerin fonetik yazım hataları veya birbirlerinin türevleri olması durumunda kullanılır. Örnek uygulamada, Soundex kodu her prob kelimesi için Soundex koduna eşit olan tüm hedef kelimeleri alırsınız. (Bu şekilde getirilen sıfır veya birden fazla hedef olabilir.)


3

Ayrıca Kwak'ın önerileri dışında N- gram'ı ve Damerau-Levenshtein mesafesini de kontrol etmeni öneririm .

Bu makale burada belirtilen birkaç farklı düzenleme mesafesinin doğruluğunu karşılaştırmaktadır (ve google alimine göre oldukça alıntılanmıştır).

Gördüğünüz gibi, buna yaklaşmanın birçok farklı yolu var ve hatta farklı ölçümleri (bu küçük bit hakkında konuştuğum makaleyi bile) birleştirebilirsiniz. Levenshtein ve ilgili ölçümlerin, özellikle de insan yazması nedeniyle hatalar meydana gelirse, en sezgisel bir anlam ifade ettiğini düşünüyorum. N gramları da basittir ve söylenilen ad veya sözcük olmayan veriler için anlamlıdır.

Soundex bir seçenek olmakla birlikte, gördüğüm çalışmaların bir kısmı (kuşkusuz çok küçük bir miktar) soundex, eşleştirme adları için Levenshstein veya diğer düzenleme mesafeleri kadar iyi performans göstermiyor. Ve Soundex, muhtemelen Levenshtein ve N-gramlarının potansiyel olarak daha geniş bir kapsama sahip oldukları (özellikle N-gram, ancak Levenshtein mesafesinin de kelimeler olmayanlar için daha iyi performans göstermesini beklerdim) gibi, insan tiperleri tarafından girilen fonetik ifadelerle sınırlıdır.

Paketlere kadar yardım edemiyorum, ancak N gram kavramı oldukça basit (Son zamanlarda N gram yapmak için bir SPSS makrosu yaptım, fakat bu kadar küçük bir proje için hazırladığım paketlerle birlikte gidecektim. R diğer afişler önerdi). Burada python Levenshtein mesafenin hesaplanması bir örnektir.


Teşekkürler Andy - Gelecekte bir göz atacağım.
Tal Galili 10:10

1

Bazı paketleri ve bu problemi nasıl çözeceğimizi araştırdım ve en iyi adayın fuzzywuzzyRpaket olduğunu düşünüyorum .

BulanıkwuzzyR paketi, bulanıkwuzzy python paketinin uygulaması ile eşleşen bir bulanık dizedir . Diziler arasındaki farkları hesaplamak için Levenshtein Mesafe kullanır. FuzzywuzzyR'nin işlevselliği hakkında daha fazla ayrıntıyı blog postasında ve Vignette paketinde bulabilirsiniz.

Sorununuz için basit bir çözüm yaptım, ancak küçük bir yakalama var. Python'u kurmanız ve winodows kullanıyorsanız görsel stüdyosu için bazı derleme araçlarını da kurmanız gerekir . Bunları seçmelisin:

  • Windows 10 SDK 10.0.17763.0 ve MSVC sürüm1
  • VS 2015 C ++ oluşturma araçları (v 14v00)

Çözüm basit. Ana işlev ExtractOneiki değerin listesini döndürür. Birincisi bir dize eşleşmesi ve ikincisi karşılık gelen skordur (0 - 100 aralığında). fuzzywuzzyRPaket de yararlı olabilir, diğer fonksiyonları sağlar. Ana belgeler burada bulunabilir . Umarım bu kod sorunu çözmeye yardımcı olur.

library(fuzzywuzzyR)

# The Fuzzy initialization
init_proc = FuzzUtils$new()
PROC = init_proc$Full_process # class process-method
PROC1 = tolower # base R function
init_scor = FuzzMatcher$new()
SCOR = init_scor$WRATIO    
init <- FuzzExtract$new()

match_strings <- function(vector_to_process, base_vector){  
  new_vec = c()
  for(i in 1:length(vector_to_process)){      
    new_word <- init$ExtractOne(string = vector_to_process[i], sequence_strings = base_vector, processor = PROC1, scorer = SCOR, score_cutoff = 0L)
    new_vec[i] <- new_word[[1]]
  }     
  return(new_vec)
}

# Check if all python modules are available
if (check_availability()){    
  new_vec <- match_strings(vec55, vec91)
  print(new_vec)   
}

Çıktı:

[1] "Aeropyrum pernix K1"                                 "Archaeoglobus fulgidus DSM 4304"                    
[3] "Candidatus Korarchaeum cryptofilum OPF8"             "Candidatus Methanoregula boonei 6A8"                
[5] "Cenarchaeum symbiosum A"                             "Desulfurococcus kamchatkensis 1221n"                
[7] "Thermoplasma volcanium GSS1"                         "Haloarcula marismortui ATCC 43049"                  
[9] "Halobacterium sp. NRC-1"                             "Halobacterium salinarum R1"                         
[11] "Haloferax volcanii DS2"                              "Haloquadratum walsbyi DSM 16790"                    
[13] "Hyperthermus butylicus DSM 5456"                     "Ignicoccus hospitalis KIN4/I"                       
[15] "Metallosphaera sedula DSM 5348"                      "Methanothermobacter thermautotrophicus str. Delta H"
[17] "Methanobrevibacter smithii ATCC 35061"               "Methanococcoides burtonii DSM 6242"       

0

Fonksiyonuna göre adist

Karakter vektörleri arasındaki yaklaşık dize mesafesini hesaplayın. Mesafe, genelleştirilmiş bir Levenshtein (düzenleme) mesafesidir ve bir dizgiyi diğerine dönüştürmek için gerekli olan minimum muhtemel ağırlıklı ekleme, silme ve ikame sayısını verir.

stringdistAynı isimdeki bir paketten gelen fonksiyonun birkaç yöntemi vardır (bakınız ?stringdist):

method = c ("osa", "lv", "dl", "hamming", "lcs", "qgram", "kosinüs", "jaccard", "jw", "soundex")

Bununla, maksimum sapma (eşik) seçebilirsiniz:

firstvector<-vec55
secondvector<-vec91

match<-character()
threshold<-14 # max 14 characters of divergence
mindist<-integer()
sortedmatches<-character()

for (i in 1:length(firstvector) ) {
  matchdist<-adist(firstvector[i],secondvector)[1,]
  # matchdist<-stringdist(firstvector[i],secondvector) # several methods available

  matchdist<-ifelse(matchdist>threshold,NA,matchdist)
  sortedmatches[i]<-paste(secondvector[order(matchdist, na.last=NA)], collapse = ", ")
  mindist[i]<- tryCatch(ifelse(is.integer(which.min(matchdist)),matchdist[which.min(matchdist)],NA), error = function(e){NA})
  match[i]<-ifelse(length(secondvector[which.min(matchdist)])==0,NA,
                  secondvector[which.min(matchdist)] )
}
res<-data.frame(firstvector=firstvector,match=match,divergence=mindist, sortedmatches=sortedmatches, stringsAsFactors = F)
res

Bu veri çerçevesi, ilk vektör sütununda birinci vektörü, ikinci vektörün sütun eşleşmesinde en iyi eşleşmesini, sütun farklılığındaki mesafesini ve OP'de olduğu gibi sütun sıralama eşleşmelerinde sıralanan tüm önemli eşleşmeleri gösterir.


2
Her ne kadar uygulama çoğu zaman sorulardaki önemli içerikle karıştırılsa da, kodlar yerine istatistikler, makine öğrenmeleri vb. Hakkında bilgi sağlayan bir site olmamız gerekiyor. Kod sağlamak da iyi olabilir, ancak lütfen bu dili iyi okumayan ve koddaki yanıtı okuyacak kadar iyi okuyabilen kişiler için metindeki cevabınızı ayrıntılı bir şekilde hazırlayın.
dediklerinin - Eski Monica
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.