using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace BotSharp.MachineLearning.CRFsuite { public class Ner { // Separator of field values. string separator = " "; Template templates = new Template(); public string GetShape (string token) { string r = ""; foreach (char c in token.ToCharArray()) { if (IsSupperChar(c)) { r += "U"; } else if (IsLowerChar(c)) { r += "L"; } else if (IsDigitChar(c)) { r += "D"; } else if (((IList)new char[]{'.', ','}).Contains(c)) { r += "."; } else if (((IList)new char[]{';', ':', '?', '!'}).Contains(c)) { r += ";"; } else if (((IList)new char[]{'+', '-', '*', '/', '=', '|', '_'}).Contains(c)) { r += "-"; } else if (((IList)new char[]{'(', '{', '[', '<'}).Contains(c)) { r += "("; } else if (((IList)new char[]{')', '}', ']', '>'}).Contains(c)) { r += ")"; } else { r += c; } } return r; } public string Degenerate (string src) { string dst = ""; foreach (char c in src) { if (dst.Trim() == "" || char.Parse(dst.Substring(dst.Length - 1, 1)) != c) { dst += c; } } return dst; } public string GetType (string token) { List T = new List{"AllUpper", "AllDigit", "AllSymbol","AllUpperDigit", "AllUpperSymbol", "AllDigitSymbol", "AllUpperDigitSymbol","InitUpper","AllLetter","AllAlnum"}; HashSet R = new HashSet(T); if (token == null || token.Trim() == "") { return "EMPTY"; } for (int i = 0 ; i < token.Length ; i++) { char c = token[i]; if (IsSupperChar(c)) { if (R.Contains("AllDigit")) { R.Remove("AllDigit"); } if (R.Contains("AllSymbol")) { R.Remove("AllSymbol"); } if (R.Contains("AllDigitSymbol")) { R.Remove("AllDigitSymbol"); } } else if (IsDigitChar(c) || ((IList)new char[]{'.', ','}).Contains(c)) { if (R.Contains("AllUpper")) { R.Remove("AllUpper"); } if (R.Contains("AllSymbol")) { R.Remove("AllSymbol"); } if (R.Contains("AllUpperSymbol")) { R.Remove("AllUpperSymbol"); } if (R.Contains("AllLetter")) { R.Remove("AllLetter"); } } else if (IsLowerChar(c)) { if (R.Contains("AllUpper")) { R.Remove("AllUpper"); } if (R.Contains("AllDigit")) { R.Remove("AllDigit"); } if (R.Contains("AllSymbol")) { R.Remove("AllSymbol"); } if (R.Contains("AllUpperDigit")) { R.Remove("AllUpperDigit"); } if (R.Contains("AllUpperSymbol")) { R.Remove("AllUpperSymbol"); } if (R.Contains("AllDigitSymbol")) { R.Remove("AllDigitSymbol"); } if (R.Contains("AllUpperDigitSymbol")) { R.Remove("AllUpperDigitSymbol"); } } else { if (R.Contains("AllUpper")) { R.Remove("AllUpper"); } if (R.Contains("AllDigit")) { R.Remove("AllDigit"); } if (R.Contains("AllUpperDigit")) { R.Remove("AllUpperDigit"); } if (R.Contains("AllLetter")) { R.Remove("AllLetter"); } if (R.Contains("AllAlnum")) { R.Remove("AllAlnum"); } } if (i == 0 && !IsSupperChar(c)) { if (R.Contains("InitUpper")) { R.Remove("InitUpper"); } } } foreach (string tag in T) { if (R.Contains(tag)) { return tag; } } return "NO"; } public Boolean Get2d (string token) { return token.Length == 2 && IsDigit(token); } public Boolean Get4d (string token) { return token.Length == 4 && IsDigit(token); } // is token digit, alpha or not public Boolean GetDa (string token) { Boolean bd = false; Boolean ba = false; foreach (char c in token) { if (IsDigitChar(c)) { bd = true; } else if (IsAlphaChar(c)) { ba = true; } else { return false; } } return bd && ba; } public Boolean GetDand (string token, char p) { Boolean bd = false; Boolean bdd = false; foreach (char c in token) { if (IsDigitChar(c)) { bd = true; } else if (c == p) { bdd = true; } else { return false; } } return bd && bdd; } public Boolean GetAllOther (string token) { foreach (char c in token) { if (IsDigitChar(c) || IsAlphaChar(c)) { return false; } } return true; } public Boolean GetCapPeriod (string token) { return token.Length == 2 && IsSupperChar(token[0]) && token[1] == '.'; } public Boolean ContainsUpper (string token) { foreach (char c in token) { if (IsSupperChar(c)) { return true; } } return false; } public Boolean ContainsLower (string token) { foreach (char c in token) { if (IsLowerChar(c)) { return true; } } return false; } public Boolean ContainsAlpha (string token) { foreach (char c in token) { if (IsAlphaChar(c)) { return true; } } return false; } public Boolean ContainsDigit (string token) { foreach (char c in token) { if (IsDigitChar(c)) { return true; } } return false; } public Boolean ContainsSymbol (string token) { foreach (char c in token) { if (!IsAlnumChar(c)) { return true; } } return false; } public string B (Boolean v) { return v ? "yes" : "no"; } public void Observation (Dictionary v, string defval = "") { // Lowercased token. v.Add("wl", v["w"].ToString().ToLower()); // Token shape. v.Add("shape", GetShape(v["w"].ToString())); // Token shape degenerated. v.Add("shaped", Degenerate(v["shape"].ToString())); // Token type. v.Add("type", GetType(v["w"].ToString())); // Prefixes (length between one to four). if (v["w"].ToString().Length >= 1) { v.Add("p1", v["w"].ToString().Substring(0, 1)); } else { v.Add("p1", defval); } if (v["w"].ToString().Length >= 2) { v.Add("p2", v["w"].ToString().Substring(0, 2)); } else { v.Add("p2", defval); } if (v["w"].ToString().Length >= 3) { v.Add("p3", v["w"].ToString().Substring(0, 3)); } else { v.Add("p3", defval); } if (v["w"].ToString().Length >= 4) { v.Add("p4", v["w"].ToString().Substring(0, 4)); } else { v.Add("p4", defval); } // Suffixes (length between one to four). string word = v["w"].ToString(); if (v["w"].ToString().Length >= 1) { v.Add("s1", word.Substring(word.Length - 1, 1)); } else { v.Add("s1", defval); } if (v["w"].ToString().Length >= 2) { v.Add("s2", word.Substring(word.Length - 2, 2)); } else { v.Add("s2", defval); } if (v["w"].ToString().Length >= 3) { v.Add("s3", word.Substring(word.Length - 3, 3)); } else { v.Add("s3", defval); } if (v["w"].ToString().Length >= 4) { v.Add("s4", word.Substring(word.Length - 4, 4)); } else { v.Add("s4", defval); } // Two digits v.Add("2d", B(Get2d(v["w"].ToString()))); // Four digits v.Add("4d", B(Get4d(v["w"].ToString()))); // Alphanumeric token. v.Add("d&a", B(GetDa(v["w"].ToString()))); // Digits and '-'. v.Add("d&-", B(GetDand(v["w"].ToString(), '-'))); // Digits and '/'. v.Add("d&/", B(GetDand(v["w"].ToString(), '/'))); // Digits and ','. v.Add("d&,", B(GetDand(v["w"].ToString(), ','))); // Digits and '.'. v.Add("d&.", B(GetDand(v["w"].ToString(), '.'))); // A uppercase letter followed by '.' v.Add("up", B(GetCapPeriod(v["w"].ToString()))); // An initial uppercase letter. v.Add("iu", B(IsSupperChar(v["w"].ToString()[0]))); // All uppercase letters. v.Add("au", B(IsSupper(v["w"].ToString()))); // All lowercase letters. v.Add("al", B(IsLower(v["w"].ToString()))); // All digit letters. v.Add("ad", B(IsDigit(v["w"].ToString()))); // All other (non-alphanumeric) letters. v.Add("ao",B(GetAllOther(v["w"].ToString()))); // Contains a uppercase letter. v.Add("cu", B(ContainsUpper(v["w"].ToString()))); // Contains a lowercase letter. v.Add("cl", B(ContainsLower(v["w"].ToString()))); // Contains a alphabet letter. v.Add("ca", B(ContainsAlpha(v["w"].ToString()))); // Contains a digit. v.Add("cd", B(ContainsUpper(v["w"].ToString()))); // Contains a symbol. v.Add("cs", B(ContainsSymbol(v["w"].ToString()))); } public void DisJunctive(List> X, int t, string field, int begin, int end) { string name = $"{field}[{begin}..{end}]"; for (int offset = begin; offset < end + 1; offset++) { int p = t + offset; if (p < 0 || p >= X.Count) { continue; } List F = (List)X[t]["F"]; F.Add($"{name}={X[p][field]}"); } } public void InitialTemplate (string[] uniFeatures, string[] biFeatures) { foreach (string name in uniFeatures) { for (int i = -2 ; i < 3; i++) { List templateRowFeature = new List(); templateRowFeature.Add(new CRFFeature(name, i)); templates.Features.Add(templateRowFeature); } } foreach (string name in biFeatures) { for (int i = -2 ; i < 2; i++) { List templateRowFeature = new List(); templateRowFeature.Add(new CRFFeature(name, i)); templateRowFeature.Add(new CRFFeature(name, i + 1)); templates.Features.Add(templateRowFeature); } } } public void FeatureExtractor (List> X) { // Append observations. foreach (Dictionary d in X) { Observation(d); } // Apply the feature templates. new Crfutils().ApplyTemplates(X, templates); for (int t = 0; t < X.Count ; t++) { DisJunctive(X, t, "w", -4, -1); DisJunctive(X, t, "w", 1, 4); } if (X != null && X.Count > 0) { ((List)X[0]["F"]).Add("__BOS__"); ((List)X[X.Count - 1]["F"]).Add("__EOS__"); } } public void NerStart(string rawFile, string parsedName, string fields, string[] uniFeatures, string[] biFeatures) { InitialTemplate(uniFeatures, biFeatures); new Crfutils().CRFFileGenerator(FeatureExtractor, fields, rawFile, parsedName, separator); } private Boolean IsSupperChar(char c) { return ((int) c - 'A' >= 0) && ('Z' - (int) c >= 0) ; } private Boolean IsLowerChar(char c) { return ((int) c - 'a' >= 0)&&('z' - (int) c >= 0) ; } private Boolean IsDigitChar(char c) { return ((int) c - '0' >= 0)&&('9' - (int) c >= 0) ; } private Boolean IsAlphaChar (char c) { return IsLowerChar(c) || IsSupperChar(c); } private Boolean IsAlnumChar (char c) { return IsAlphaChar(c) || IsDigitChar(c); } private Boolean IsDigit (String s) { string patternAllDigit = @"^[0-9]+$"; return new Regex(patternAllDigit).IsMatch(s); } private Boolean IsSupper (string s) { string patternAllCaptain = @"^[A-Z]+$"; return new Regex(patternAllCaptain).IsMatch(s); } private Boolean IsLower (string s) { string patternAllCaptain = @"^[a-z]+$"; return new Regex(patternAllCaptain).IsMatch(s); } } public class Template { public List> Features{ get; set; } public Template () { this.Features = new List>(); } } public class CRFFeature { public string Field { get; set; } public int Offset { get; set; } public CRFFeature (string field, int offset) { this.Field = field; this.Offset = offset; } } }