文字列の2つのベクトルを(Rで)準一致させる方法は?


36

私はこれがどのように呼ばれるべきかわからないので、より良い用語を知っているなら私を修正してください。

2つのリストがあります。55個のアイテムの1つ(例:文字列のベクトル)、92個のアイテム。アイテム名は似ていますが、同一ではありません。

55リストの項目から92リスト最適な候補を見つけたい(それからそれを調べて正しいフィッティングを選択する)。

どうすればできますか?

私が持っていたアイデア:

  1. 一致するものをすべて表示します(何かのリスト?matchを使用)
  2. 文字列ベクトル間の距離行列を試してみてください。しかし、それをどのように定義するのが最適かわかりません(同一の文字の数、文字列の順序はどうですか?)

では、そのようなタスクを処理するパッケージ/機能/研究分野は何ですか?

更新:一致させたいベクターの例を次に示します

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
こんにちはTal:>これらはタイプミスのない学名であると思われるので、まず(レーベンシュタインメトリックを(92行55列の距離行列のコンテキストで)試し、どのように出力されるかを確認します。
user603

2
しばらくして、stringdistパッケージはこの種のものにとって最適なリソースのように見えます。
みすぼらしいシェフ

回答:


19

同様の問題が発生しました。(ここで見られます:https : //stackoverflow.com/questions/2231993/merging-two-data-frames-using-fuzzy-approximate-string-matching-in-r

私が受け取った推奨事項の大部分は、次のとおりです。

pmatch()、およびagrep()grep()grepl()あなたを介して見て時間がかかる場合は、いずれかの近似文字列または近似正規表現によって近似文字列マッチングいくつかの洞察を提供することを三つの機能です。

文字列を見ずに、それらを一致させる方法の難しい例を提供することは困難です。いくつかのサンプルデータを提供していただければ、解決策が見つかるはずです。

私が見つけたもう1つのオプションは、文字列を平坦化tolower()することです。文字列内の各単語の最初の文字を見てから比較します。時々、それは問題なく動作します。次に、他の回答で言及された距離のような、より複雑なものがあります。時にはこれらの作品、時には恐ろしい-それは本当に文字列に依存します。

見えますか?

更新

agrep()がこれらのほとんどのトリックを行うようです。agrep()は、Rによるレーベンシュタイン距離の実装であることに注意してください。

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

計算しないものもありますが、Ferroplasm acidaramusがFerroglobus placidus DSM 10642と同じかどうかさえわかりません。たとえば:

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

これらのいくつかについては、あなたは少しSOLかもしれませんし、おそらく最初からインデックスを作成することが最善の策だと思います。すなわち。vec55のID番号を持つテーブルを作成してから、vec91のvec55のIDへの参照を手動で作成します。痛みを伴いますが、agrep()で多くのことができます。


こんにちは、ブランドン-データのサンプルを追加しました。ありがとう!
タルガリリ

こんにちはブランドン-あなたのソリューションはうまくいきました-ありがとう。
タルガリリ

SEのサブジェクトに関する前の質問へのリンクに対して+1(agrep()へのポインタのタグ)。
user603

15

2つの文字列間の距離を測定する方法は多数あります。Rで広く実装されている2つの重要な(標準)アプローチは、レーベンシュタインとハミング距離です。前者はパッケージ「MiscPsycho」で、後者は「e1071」で利用可能です。これらを使用して、私は単純に92対55のペアワイズ距離の行列を計算し、そこから先に進みます(つまり、リスト1の文字列 "1"の一致候補は、リスト2から文字列 "1までの最短距離の文字列" x " ")。

または、パッケージRecordLinkageに関数compare()があります。これは、必要なことを行うように設計されているようで、手元のタスクに適していると思われるいわゆるJaro-Winkler 距離を使用しますが、私はそれを経験していません。

編集:BrandecのコメントとTalのコードを含めるように回答を編集し、vec55の最初のエントリである「Aeropyrum pernix」に一致するものを見つけます。

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

8
+1。また、便利な場合は、文字列を比較するときにグーグルを指す用語は「編集距離」です:en.wikipedia.org/wiki/Edit_distance
ars

@ars:>ありがとう、これはR検索エンジンにフィードして何が出てくるかを見るのに便利なリストです!
user603

2
レーベンシュタインの編集距離は、ベースパッケージの一部としてagrep()を介して実装されます
ブランドンバーテルセン

すばらしい答えクワク-私は将来それを見ます!
タルガリリ

個人的には、これはタルの質問に対するより完全な答えだと思います。RecordLinkageを指すための+1-私は間違いなくそれを試してみる必要があります。
ブランドンバーテルセン

7

Kwakの有用な答えを補足するために、いくつかの簡単な原則とアイデアを追加できるようにします。メトリックを決定する良い方法は、文字列がターゲットとどのように異なるかを考慮することです。「距離の編集」は、バリエーションが隣人の転置や単一のキーの入力ミスなどのタイポグラフィエラーの組み合わせである場合に役立ちます。

別の有用なアプローチ(わずかに異なる哲学)は、すべての文字列を関連する文字列のクラスの1つの代表にマップすることです。「Soundex」メソッドはこれを行います。単語のSoundexコードは、主要な子音と同様の音の内部結果のグループをエンコードする4文字のシーケンスです。単語が表音のミススペルまたは互いの変形である場合に使用されます。サンプルアプリケーションでは、Soundexコードが各プローブワードのSoundexコードと等しいすべてのターゲットワードをフェッチします。(この方法でフェッチされるターゲットはゼロまたは複数存在する可能性があります。)


3

また、クワックの他の提案に加えて、N-gramDamerau-Levenshtein距離を確認することをお勧めします。

このホワイトペーパーでは、ここで言及したいくつかの異なる編集距離の精度を比較します(Googleの学者によると非常に引用されています)。

ご覧のとおり、これにアプローチする方法は多数あり、さまざまなメトリックを組み合わせることもできます(このリンクについて少しリンクした論文)。特に人間のタイピングが原因でエラーが発生する場合、レーベンシュタインおよび関連するベースのメトリックは最も直感的な意味を持っていると思います。N-gramも単純で、名前や単語ごとの単語ではないデータに意味があります。

soundexはオプションですが、私が見たほんの少しの作業(確かに非常に少ない量)のsoundexは、Levenshsteinまたは一致する名前の他の編集距離と同様に機能しません。また、Soundexは人間のタイパーによって入力される可能性のある音声フレーズに限定されています。レーベンシュタインとN-gramは潜在的に広い範囲を持ちます(特にN-gram。

私はパッケージまで手伝うことはできませんが、N-gramの概念は非常に単純です(最近N-gramを行うためにSPSSマクロを作成しましたが、そのような小さなプロジェクトでは、すでに作成されたパッケージでR他のポスターが示唆しています)。ここで Pythonでレーベンシュタイン距離を算出する例です。


ありがとう、アンディ-私は将来それを見ます。
タルガリリ

1

私はいくつかのパッケージとこの問題を解決する方法を調査しましたが、最良の候補はfuzzywuzzyRパッケージだと思います。

fuzzywuzzyRパッケージは、fuzzywuzzy pythonパッケージの実装に一致するファジー文字列です。レーベンシュタイン距離を使用して、シーケンス間の差を計算します。fuzzywuzzyRの機能の詳細は、ブログ投稿とパッケージVignetteにあります。

私はあなたの問題に対して簡単な解決策を行いましたが、ちょっとした落とし穴があります。pythonをインストールする必要があり、winodowsを使用する場合は、Visual Studioのビルドツールもインストールする必要があります。これらを選択する必要があります。

  • Windows 10 SDK 10.0.17763.0およびMSVC v140
  • VS 2015 C ++ビルドツール(v 14v00)

解決策は簡単です。メイン関数ExtractOneは、2つの値のリストを返します。最初は1つの文字列の一致で、2番目は対応するスコア(範囲0〜100)です。このfuzzywuzzyRパッケージは、役に立つ他の機能も提供します。主なドキュメントはこちらにあります。このコードが問題の解決に役立つことを願っています。

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

出力:

[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

機能に基づいて adist

文字ベクトル間のおおよその文字列距離を計算します。距離は、一般化されたレーベンシュタイン(編集)距離であり、1つの文字列を別の文字列に変換するために必要な挿入、削除、および置換の最小限の可能性のある重みを与えます。

stringdist同じ名前のパッケージの関数には、いくつかのメソッドがあります(を参照?stringdist)。

method = c( "osa"、 "lv"、 "dl"、 "hamming"、 "lcs"、 "qgram"、 "cosine"、 "jaccard"、 "jw"、 "soundex")

これにより、最大の発散(しきい値)を選択できます。

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

このデータフレームは、列firstvectorの最初のベクトル、列一致のsecondvectorのベストマッチ、列発散の距離、およびOPのように列sortmatchesで順序付けられたすべての重要な一致を示します。


2
多くの場合、実装には問題の実質的な内容が混在しますが、コードではなく、統計、機械学習などに関する情報を提供するサイトになるはずです。コードを提供することもできますが、コードから回答を認​​識して抽出するのに十分なほどこの言語を読まない人のために、テキストで実質的な回答を詳しく説明してください。
GUNG -復活モニカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.