Bagaimana kuasi mencocokkan dua vektor string (dalam R)?

36

Saya tidak yakin bagaimana ini harus disebut, jadi tolong perbaiki saya jika Anda tahu istilah yang lebih baik.

Saya punya dua daftar. Salah satu dari 55 item (misalnya: vektor string), yang lain dari 92. Nama-nama item serupa tetapi tidak identik.

Saya berharap untuk menemukan yang terbaik kandidat s di 92 daftar untuk item dalam 55 daftar (Saya kemudian akan melalui itu dan memilih pas benar).

Bagaimana itu bisa dilakukan?

Ide-ide saya punya tempat untuk:

  1. Lihat semua yang cocok (menggunakan daftar sesuatu? Cocok)
  2. Coba jarak matriks antara vektor string, tapi saya tidak yakin bagaimana cara terbaik mendefinisikannya (jumlah huruf identik, bagaimana dengan urutan string?)

Jadi apa yang berkaitan dengan paket / fungsi / bidang penelitian dengan tugas seperti itu, dan bagaimana?

Pembaruan: Ini adalah contoh dari vektor yang ingin saya cocokkan

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"
) 
Tal Galili
sumber
2
Hai Tal:> Mengingat bahwa ini sepertinya nama ilmiah bebas kesalahan ketik, saya akan mencoba metrik Levenshtein terlebih dahulu (dalam konteks matriks jarak 92-by-55) dan melihat bagaimana hasilnya.
user603
2
Beberapa waktu kemudian, stringdistpaket itu sepertinya sumber daya terbaik untuk hal semacam ini.
shabbychef

Jawaban:

19

Saya punya masalah serupa. (lihat di sini: https://stackoverflow.com/questions/2231993/merging-two-data-frames-using-fuzzy-approximate-string-matching-in-r )

Sebagian besar rekomendasi yang saya terima ada di sekitar:

pmatch(), Dan agrep(), grep(), grepl()tiga fungsi bahwa jika Anda mengambil waktu untuk melihat melalui akan memberikan beberapa wawasan string matching perkiraan baik oleh tali perkiraan atau regex perkiraan.

Tanpa melihat string, sulit untuk memberi Anda contoh keras tentang bagaimana mencocokkannya. Jika Anda dapat memberi kami beberapa contoh data, kami yakin kami dapat menemukan solusi.

Pilihan lain yang saya temukan berfungsi dengan baik adalah untuk meratakan string tolower(), melihat huruf pertama dari setiap kata dalam string dan kemudian membandingkan. Terkadang itu berhasil tanpa hambatan. Lalu ada hal-hal yang lebih rumit seperti jarak yang disebutkan dalam jawaban lain. Kadang-kadang ini bekerja, kadang-kadang mereka mengerikan - itu benar-benar tergantung pada string.

Bisakah kita melihatnya?

Memperbarui

Sepertinya agrep () akan melakukan trik untuk sebagian besar dari ini. Perhatikan bahwa agrep () hanyalah implementasi R dari jarak Levenshtein.

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

Beberapa tidak menghitung, saya bahkan tidak yakin apakah Ferroplasm acidaramus sama dengan Ferroglobus placidus DSM 10642, misalnya:

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

Saya pikir Anda mungkin sedikit SOL untuk beberapa ini dan mungkin membuat indeks dari awal adalah yang terbaik. yaitu,. Membuat tabel dengan nomor id untuk vec55, dan kemudian secara manual membuat referensi ke id di vec55 di vec91. Sakit, saya tahu, tapi banyak yang bisa dilakukan dengan agrep ().

Brandon Bertelsen
sumber
Hai Brandon - Saya menambahkan sampel data. Terima kasih!
Tal Galili
Hai Brandon - solusi Anda berhasil - terima kasih.
Tal Galili
+1 untuk tautan ke pertanyaan sebelumnya tentang subjek dalam SE (thaks untuk pointer ke agrep ()).
user603
15

Ada banyak cara untuk mengukur jarak antara dua string. Dua pendekatan penting (standar) yang diterapkan secara luas di R adalah Levenshtein dan jarak Hamming. Yang pertama tersedia dalam paket 'MiscPsycho' dan yang terakhir dalam 'e1071'. Dengan menggunakan ini, saya hanya akan menghitung 92 dengan 55 matriks jarak berpasangan, kemudian melanjutkan dari sana (yaitu pasangan kandidat terbaik untuk string "1" dalam daftar 1 adalah string "x" dari daftar 2 dengan jarak terkecil ke string "1 ").

Atau, ada fungsi membandingkan () dalam paket RecordLinkage yang tampaknya dirancang untuk melakukan apa yang Anda inginkan dan menggunakan apa yang disebut jarak Jaro-Winkler yang tampaknya lebih sesuai untuk tugas yang sedang dikerjakan, tetapi saya tidak punya pengalaman dengannya .

EDIT: saya sedang mengedit jawaban saya untuk memasukkan komentar Brandon dan juga kode Tal, untuk menemukan kecocokan dengan "Aeropyrum pernix", entri pertama vec55 :

agrep(vec55[1],vec91,ignore.case=T,value=T,max.distance = 0.1, useBytes = FALSE)
[1] "Aeropyrum pernix K1"
pengguna603
sumber
8
+1. Juga, jika itu membantu, istilah untuk google ketika membandingkan string adalah "edit jarak": en.wikipedia.org/wiki/Edit_distance
ars
@ars:> terima kasih, itu daftar yang berguna untuk memberi makan ke mesin pencari R dan melihat apa yang keluar!
user603
2
Jarak edit Levenshtein diimplementasikan sebagai bagian dari paket dasar via agrep ()
Brandon Bertelsen
Jawaban Hebat Kwak - Saya akan melihatnya di masa depan!
Tal Galili
Secara pribadi, saya merasa bahwa ini adalah jawaban yang lebih lengkap untuk pertanyaan Tal. +1 untuk menunjukkan RecordLinkage kami - Saya pasti harus mencobanya.
Brandon Bertelsen
7

Untuk melengkapi jawaban berguna Kwak, izinkan saya menambahkan beberapa prinsip dan gagasan sederhana. Cara yang baik untuk menentukan metrik adalah dengan mempertimbangkan bagaimana string mungkin berbeda dari target mereka. "Edit jarak" berguna ketika variasi adalah kombinasi dari kesalahan tipografi seperti mentransposisi tetangga atau salah ketik satu kunci.

Pendekatan lain yang bermanfaat (dengan filosofi yang sedikit berbeda) adalah memetakan setiap string menjadi satu perwakilan dari kelas string terkait. Metode " Soundex " melakukan ini: kode Soundex untuk sebuah kata adalah urutan empat karakter yang mengkode konsonan utama dan kelompok-kelompok konsekuensi internal yang terdengar serupa. Ini digunakan ketika kata-kata merupakan salah ejaan fonetis atau varian satu sama lain. Dalam contoh aplikasi, Anda akan mengambil semua kata target yang kode Soundex-nya sama dengan kode Soundex untuk setiap kata probe. (Mungkin ada nol atau beberapa target yang diambil dengan cara ini.)

whuber
sumber
3

Saya juga menyarankan Anda memeriksa N-gram dan jarak Damerau-Levenshtein selain saran lain dari Kwak.

Makalah ini membandingkan keakuratan dari beberapa jarak sunting yang disebutkan di sini (dan sangat dikutip menurut google scholar).

Seperti yang Anda lihat ada banyak cara berbeda untuk mendekati ini, dan Anda bahkan dapat menggabungkan metrik yang berbeda (makalah yang saya tautkan dengan pembicaraan tentang bit alittle ini). Saya pikir Levenshtein dan metrik berbasis terkait masuk akal paling intuitif, terutama jika kesalahan terjadi karena mengetik manusia. N-gram juga sederhana dan masuk akal untuk data yang bukan nama atau kata per kata.

Sementara soundex adalah sebuah pilihan, sedikit pekerjaan yang saya lihat (yang diakui dalam jumlah yang sangat kecil) soundex tidak berkinerja sebaik Levenshstein atau jarak edit lainnya untuk nama yang cocok. Dan Soundex terbatas pada frasa fonetik yang kemungkinan diinput oleh pengetik manusia, di mana Levenshtein dan N-gram memiliki cakupan yang berpotensi lebih luas (terutama N-gram, tetapi saya berharap jarak Levenshtein berkinerja lebih baik untuk non-kata juga).

Saya tidak dapat membantu sejauh paket, tetapi konsep N-gram cukup sederhana (saya memang membuat makro SPSS untuk melakukan N-gram baru-baru ini, tetapi untuk proyek sekecil itu saya hanya akan pergi dengan paket yang sudah dibuat di R poster lainnya telah menyarankan). Berikut adalah contoh penghitungan jarak Levenshtein dengan python.

Andy W
sumber
Terima kasih Andy - Saya akan melihatnya nanti.
Tal Galili
1

Saya meneliti beberapa paket dan cara bagaimana mengatasi masalah ini dan saya pikir kandidat terbaik adalah fuzzywuzzyRpaket.

Paket fuzzywuzzyR adalah implementasi pencocokan string fuzzy dari paket python fuzzywuzzy . Ini menggunakan Levenshtein Distance untuk menghitung perbedaan antara urutan. Rincian lebih lanjut tentang fungsi fuzzywuzzyR dapat ditemukan di posting blog dan dalam paket Vignette.

Saya melakukan solusi sederhana untuk masalah Anda, tetapi ada sedikit tangkapan. Anda harus menginstal python dan jika Anda menggunakan winodows juga harus menginstal beberapa alat membangun untuk studio visual . Anda harus memilih ini:

  • Windows 10 SDK 10.0.17763.0 dan MSVC v140
  • VS 2015 C ++ build tools (v 14v00)

Solusinya sederhana. Fungsi utama ExtractOnemengembalikan daftar dua nilai. Pertama adalah satu kecocokan string dan yang kedua adalah skor yang sesuai (dalam kisaran 0 - 100). The fuzzywuzzyRpaket menyediakan juga fungsi lainnya yang dapat berguna. Dokumentasi utama dapat ditemukan di sini . Saya harap kode ini membantu menyelesaikan masalah.

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)   
}

Keluaran:

[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"       
peteruherek
sumber
0

Berdasarkan fungsi adist

Hitung jarak string perkiraan antara vektor karakter. Jarak adalah jarak Levenshtein (sunting) yang digeneralisasi, memberikan jumlah minimal penyisipan, penghapusan, dan penggantian yang diperlukan untuk mengubah satu string menjadi string lainnya.

Fungsi stringdistdari paket dengan nama yang sama memiliki beberapa metode (lihat ?stringdist):

metode = c ("osa", "lv", "dl", "hamming", "lcs", "qgram", "cosine", "jaccard", "jw", "soundex")

Dengan ini, Anda dapat memilih divergensi maksimum (ambang batas):

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

Kerangka data ini menunjukkan vektor pertama dalam kolom firstvector, yang terbaik dari vektor kedua dalam kecocokan kolom, jaraknya dalam divergensi kolom, dan semua kecocokan signifikan yang dipesan dalam kolom diurutkan sesuai dengan OP.

Ferroao
sumber
2
Meskipun implementasi sering dicampur dengan konten substantif dalam pertanyaan, kami seharusnya menjadi situs untuk menyediakan informasi tentang statistik, pembelajaran mesin, dll., Bukan kode. Mungkin baik untuk memberikan kode juga, tetapi tolong uraikan jawaban substantif Anda dalam teks untuk orang-orang yang tidak membaca bahasa ini dengan cukup baik untuk mengenali & mengekstrak jawaban dari kode.
gung - Reinstate Monica