2015年12月21日月曜日

[C#]パターンマッチングの実装

ネット上であまり見かけないので投稿。

非線形データを1次元のデータ配列にした後、データ同士がどれくらい一致しているかを判定するプログラムです。
いわゆるパターンマッチング手法といっていいと思われます。
画像処理で用いる場合はデータを分割して各要素の偏差値を求め、
その偏差値のデータ列を下記プログラムに入れるとデータがどれくらい一致しているか計算できるようになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
 class Program
 {
  static void Main(string[] args)
  {
   var collection = new Collection()
   {
    new Model(new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, 4),
    new Model(new double[] { 2, 2, 4, 4, 5, 6, 7, 8, 9, 11, 11, 12, 13, 14, 15, 10 }, 4),
    new Model(new double[] { 1, 12, 3, 14, 5, 26, 57, 18, 9, 40, 11, 22, 13, 14, 15, 11 }, 4),
    new Model(new double[] { 4, 12, 3, 4, 35, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, 4),
    new Model(new double[] { 5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13, 11 }, 4),
    new Model(new double[] { 2, 33, 3, 45, 5, 16, 7, 8, 19, 10, 11, 125, 13, 14, 15, 16 }, 4),
   };

   var ret = collection.Filter(2);

   var testData = new Model(new double[] { 1, 12, 3, 4, 25, 6, 7, 8, 49, 10, 11, 12, 43, 14, 15, 16 }, 4);

   Console.WriteLine("Matching={0}", collection.Matching(testData));

   Console.ReadKey();

  }
 }


 public class Collection : List<Model>
 {
  public Collection Filter(double sigma)
  {
   var result = new Collection();
   var length = this[0].Patterns.Length;
   var list = new List<Model>();
   list.AddRange(this);

   for (var stage = 0; stage < length; stage++)
   {
    var data = list.Select(_ => _.Patterns[stage]).ToArray();
    var ave = data.Average();
    var sig = data.Sigma() * sigma;
    list = list.Where(_ => sig >= Math.Abs(_.Patterns[stage] - ave)).ToList();
   }

   result.AddRange(list);
   return result;
  }

  public double Matching(Model model)
  {
   return this.Select(_ => _.Matching(model)).ToArray().Max();
  }
 }


 public class Model
 {
  public double[] Data { get; }
  public double[] Patterns { get; }
  public int Count => Patterns.Length;

  /// <summary>
  /// 
  /// </summary>
  /// <param name="data"></param>
  public Model(double[] data, int split_count)
  {
   Data = data;
   var len = data.Length;
   var spt = (len / split_count);
   Patterns = new double[split_count];
   for (var idx = 0; idx < split_count; idx++)
   {
    Patterns[idx] = data.ToList().GetRange(idx * spt, spt).ToArray().Sigma();
   }
  }

  /// <summary>
  /// コサイン類似度
  /// </summary>
  /// <param name="model"></param>
  /// <returns></returns>
  public double Matching(Model model)
  {
   var sum = 0.0;
   var v1 = 0.0;
   var v2 = 0.0;
   for (var stage = 0; stage < Count; stage++)
   {
    var val1 = Patterns[stage];
    var val2 = model.Patterns[stage];
    sum += val1 * val2;
    v1 += Math.Pow(val1, 2);
    v2 += Math.Pow(val2, 2);
   }
   v1 = Math.Sqrt(v1);
   v2 = Math.Sqrt(v2);


   var result = sum / (v1 * v2);

   return result;
  }
 }

 public static class DoubleExtensions
 {
  public static double Sigma(this double[] self)
  {
   var len = self.Length;
   var ave = self.Average();
   var bun = 0.0;
   foreach (var dat in self)
   {
    bun += Math.Pow(dat - ave, 2);
   }
   bun = bun / len;
   return Math.Sqrt(bun);
  }
 }
}

0 件のコメント:

Androider