別々の csv ファイル内のフィールドから 3 つ以上の単語を一致させる

別々の csv ファイル内のフィールドから 3 つ以上の単語を一致させる

2 つの csv ファイルがあります:

これはcsv1です:

11, The Sun Still Shines in Reading, 64312, 464566
13, You and Me Together Again London, 564564, 131355
12, What's the Story Now Your Gone, 4545646, 1124545
17, Hello I love you, 456899, 1259898

これはcsv2です:

69, The Sun Shines, 6464, 52364
12, Tell me your name, 56456, 21345
17, From London to New York, 897944, 234655
97, Reading Still Shines, 545464, 16748967

タイトル (フィールド 2 にある) の一致を可能にするこのコードがあります。

cat $csv1 |cut -d, -f2 | while read p; do
  grep -i "$p" $csv2
  if [ $? -eq 0 ];then
    grep -i "$p" $csv1
  fi
done

現時点では、このコードは csv1 の各行を調べ、一致するタイトルを持つ行が csv2 にある場合は、一致する行を一緒に出力します。これは非常にうまく機能します。

ただし、正確なタイトルを検索するのではなく、3 つ以上の単語が一致するかどうかを確認するようにスクリプトを調整したいと思います。

したがって、このページの csv データの場合の出力は次のようになります。

11, The Sun Still Shines in Reading, 64312, 464566
69, The Sun Shines, 6464, 52364
97, Reading Still Shines, 545464, 16748967

これは、先頭に csv1 の行があり、その後にフィールド 2 (タイトル) に 3 つ以上の一致する単語がある csv2 の 2 行が続きます。一致する単語の数を指定するにはどうすればよいですか?

編集: 言及し忘れたことの 1 つは、csv1 は csv2 よりも行数でサイズが大幅に小さくなることです (数千行に対して 10 行程度)。ただし、考えてみると、最大のデータ セットを csv1 または csv2 のいずれかとして定義できるため、これはあまり問題ではないと思います。

答え1

もっとシェル中心のソリューション (awk?) があるかもしれませんが、問題がここまで複雑になると、私は通常これに頼ります。以下は、すべてをメモリに読み込み、行をキーとしてハッシュに収集し、その値が対応するタイトルになるperlperl スクリプトです。csv2

次に をループしcsv1、タイトルを取り出し、 の各タイトルについてcsv2、タイトル内の各単語が出現する回数をカウントします。 を超える場合desired、 からの一致するタイトルとその「ソース」行が印刷されますcsv1

#!/usr/bin/env perl

my @csv2 = ();
open CSV2, "<csv2" or die;
@csv2=<CSV2>;
close CSV2;

my %csv2hash = ();
for (@csv2) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
  $csv2hash{$_} = $title;
}

open CSV1, "<csv1" or die;
while (<CSV1>) {
  chomp;
  my ($title) = $_ =~ /^.+?,\s*([^,]+?),/; #/ match the title 
  my @titlewords = split /\s+/, $title;    #/ get words
  my $desired = 3;
  my $matched = 0;
  foreach my $csv2 (keys %csv2hash) {
    my $count = 0;
    my $value = $csv2hash{$csv2};
    foreach my $word (@titlewords) {
      ++$count if $value =~ /\b$word\b/i;
      last if $count >= $desired;
    }
    if ($count >= $desired) {
      print "$csv2\n";
      ++$matched;
    }
  }
  print "$_\n" if $matched;
}
close CSV1;

関連情報