2012-01-13 1 views
5

私はデータベースに多くのベンダーを持っていますが、それらはすべてデータの面で異なります。以前のデータに基づくデータ検証ルールを作成したいと思います。実際のデータに基づいて自動的にパターンを作成するにはどうすればよいですか?

例:

A: XZ-4, XZ-23, XZ-217 
B: 1276, 1899, 22711 
C: 12-4, 12-75, 12 

目標:ベンダーBのためのユーザ入力文字列「XZ-217」の場合、アルゴリズムは、以前のデータを比較し、言う必要があります。この文字列はB以前のデータをベンダーに類似していないです。

このような比較を達成するための良い方法やツールがありますか? Answerにはいくつかの汎用的なアルゴリズムやPerlモジュールがあります。

編集: 「類似性」は定義するのが難しいですが、私は同意します。しかし、私は以前の約100のサンプルを分析し、新しいデータと分析の結果を比較することができたアルゴリズムに注目したいと思います。類似性は、長さ、文字/数字の使用、文字列作成パターン、類似の開始/終了/中間、いくつかの区切り文字を含むことがあります。

私はそれが簡単な作業ではないと感じますが、非常に広い用途。だから私は、すでにいくつかのヒントがあることを願っています。

+3

これは実際には曖昧です。「似ている」のようなものを定義しようとする。あなたが正確な規則を与えない限り、コンピュータは「あ、それは十分に近い」と言うことはできません。たとえば、「X文字以上が共通している」、「同じY文字で始まっています」、「真ん中に同じ記号があります(ダッシュなど)」などがあります。 – FakeRainBrigand

+1

追加の制約をいくつか課すことができない限り、これはかなり難しいでしょう。パターン学習アルゴリズムが 'qr /.*/'を使うことを決める方法を考えてみましょう。 –

答えて

0

Tie::StringApproxHashモジュールがあった場合は、ここに請求書が収まるでしょう。

ファジィロジック機能String::ApproxとハッシュインターフェイスTie::RegexpHashを組み合わせたものを探していると思います。

前者がより重要です。後者はコーディングの軽い仕事をするでしょう。

1

はここに私の実装とテストケースの上にループです。基本的には、関数に良い値のリストを与え、正規表現を構築しようとします。

出力:

A: (?^:\w{2,2}(?:\-){1}\d{1,3}) 
B: (?^:\d{4,5}) 
C: (?^:\d{2,2}(?:\-)?\d{0,2}) 

コード:

#!/usr/bin/env perl 

use strict; 
use warnings; 

use List::MoreUtils qw'uniq each_arrayref'; 

my %examples = (
    A => [qw/ XZ-4 XZ-23 XZ-217 /], 
    B => [qw/ 1276 1899 22711 /], 
    C => [qw/ 12-4 12-75 12 /], 
); 

foreach my $example (sort keys %examples) { 
    print "$example: ", gen_regex(@{ $examples{$example} }) || "Generate failed!", "\n"; 
} 

sub gen_regex { 
    my @cases = @_; 

    my %exploded; 

    # ex. $case may be XZ-217 
    foreach my $case (@cases) { 
    my @parts = 
     grep { defined and length } 
     split(/(\d+|\w+)/, $case); 

    # @parts are (XZ, -, 217) 

    foreach (@parts) { 
     if (/\d/) { 
     # 217 becomes ['\d' => 3] 
     push @{ $exploded{$case} }, ['\d' => length]; 

     } elsif (/\w/) { 
     #XZ becomes ['\w' => 2] 
     push @{ $exploded{$case} }, ['\w' => length]; 

     } else { 
     # - becomes ['lit' => '-'] 
     push @{ $exploded{$case} }, ['lit' => $_ ]; 

     } 
    } 
    } 

    my $pattern = ''; 

    # iterate over nth element (part) of each case 
    my $ea = each_arrayref(values %exploded); 
    while (my @parts = $ea->()) { 

    # remove undefined (i.e. optional) parts 
    my @def_parts = grep { defined } @parts; 

    # check that all (defined) parts are the same type 
    my @part_types = uniq map {$_->[0]} @def_parts; 
    if (@part_types > 1) { 
     warn "Parts not aligned\n"; 
     return; 
    } 
    my $type = $part_types[0]; #same so make scalar 

    # were there optional parts? 
    my $required = (@parts == @def_parts); 

    # keep the values of each part 
    # these are either a repitition or lit strings 
    my @values = sort uniq map { $_->[1] } @def_parts; 

    # these are for non-literal quantifiers 
    my $min = $required ? $values[0] : 0; 
    my $max = $values[-1]; 

    # write the specific pattern for each type 
    if ($type eq '\d') { 
     $pattern .= '\d' . "{$min,$max}"; 

    } elsif ($type eq '\w') { 
     $pattern .= '\w' . "{$min,$max}"; 

    } elsif ($type eq 'lit') { 
     # quote special characters, - becomes \- 
     my @uniq = map { quotemeta } uniq @values; 
     # join with alternations, surround by non-capture grouup, add quantifier 
     $pattern .= '(?:' . join('|', @uniq) . ')' . ($required ? '{1}' : '?'); 
    } 
    } 


    # build the qr regex from pattern 
    my $regex = qr/$pattern/; 
    # test that all original patterns match (@fail should be empty) 
    my @fail = grep { $_ !~ $regex } @cases; 

    if (@fail) { 
    warn "Some cases fail for generated pattern $regex: (@fail)\n"; 
    return ''; 
    } else { 
    return $regex; 
    } 
} 

パターンを発見する作業を簡略化するために、オプションパーツは、端部に来るかもしれないが、何の必須部品は任意のものの後に来ないことができます。これはおそらく克服することができますが、それは難しいかもしれません。

1

ジョエルと私は同じようなアイデアを思いついた。以下のコードは、3種類のゾーンを区別しています。

  1. 1以上の非単語文字
  2. 英数字クラスタ

のクラスタには、文字列の入力にマッチする正規表現のプロファイルを作成します。さらに、既存のプロファイルを拡張するためのロジックも含まれています。最後に、タスクサブには、これがどのように大きなアプリケーションに統合されるかを示す擬似ロジックが含まれています。

use strict; 
use warnings; 
use List::Util qw<max min>; 

sub compile_search_expr { 
    shift; 
    @_ = @{ shift() } if @_ == 1; 
    my $str 
     = join('|' 
       , map { join('' 
          , grep { defined; } 
          map { 
           $_ eq 'P' ? quotemeta; 
           : $_ eq 'W' ? "\\w{$_->[1],$_->[2]}" 
           : $_ eq 'D' ? "\\d{$_->[1],$_->[2]}" 
           :    undef 
           ; 
          } @$_ 
         ) 
       } @_ == 1 ? @{ shift } : @_ 
     ); 
    return qr/^(?:$str)$/; 
} 

sub merge_profiles { 
    shift; 
    my ($profile_list, $new_profile) = @_; 
    my $found = 0; 
    PROFILE: 
    for my $profile (@$profile_list) { 
     my $profile_length = @$profile; 

     # it's not the same profile. 
     next PROFILE unless $profile_length == @$new_profile; 
     my @merged; 
     for (my $i = 0; $i < $profile_length; $i++) { 
      my $old = $profile->[$i]; 
      my $new = $new_profile->[$i]; 
      next PROFILE unless $old->[0] eq $new->[0]; 
      push(@merged 
       , [ $old->[0] 
        , min($old->[1], $new->[1]) 
        , max($old->[2], $new->[2]) 
        ]); 
     } 
     @$profile = @merged; 
     $found = 1; 
     last PROFILE; 
    } 
    push @$profile_list, $new_profile unless $found; 
    return; 
} 

sub compute_info_profile { 
    shift; 
    my @profile_chunks 
     = map { 
       /\W/ ? [ P => $_ ] 
      : /\D/ ? [ W => length, length ] 
      :  [ D => length, length ] 
     } 
     grep { length; } split /(\W+)/, shift 
     ; 
} 

# Psuedo-Perl 
sub process_input_task { 
    my ($application, $input) = @_; 

    my $patterns = $application->get_patterns_for_current_customer; 
    my $regex = $application->compile_search_expr($patterns); 

    if ($input =~ /$regex/) {} 
    elsif ($application->approve_divergeance($input)) { 
     $application->merge_profiles($patterns, compute_info_profile($input)); 
    } 
    else { 
     $application->escalate( 
      Incident->new(issue => INVALID_FORMAT 
         , input => $input 
         , customer => $customer 
         )); 
    } 

    return $application->process_approved_input($input); 
} 
関連する問題