NSLinguisticTaggerを用いると、自然言語のテキストを品詞(名詞、動詞、代名詞)や「個人名」「地名」といった属性で区分(トークンに分解)することができます。日本語の形態素解析も可能です。
使い方は非常にシンプルで、基本的な手順は
- スキームを引数に渡してNSLinguisticTaggerオブジェクトを生成
- 処理対象テキストをセット
- トークン分解開始
の3ステップです。
// スキーム
NSArray *schemes = @[NSLinguisticTagSchemeLexicalClass];
// NSLinguisticTaggerオブジェクトを生成
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:schemes
options:0];
// 処理対象テキスト
NSString *targetText = self.textView.text;
[tagger setString:targetText];
// トークンへの分解開始
[tagger enumerateTagsInRange:NSMakeRange(0, targetText.length)
scheme:NSLinguisticTagSchemeLexicalClass
options:0
usingBlock:
^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
NSString *subStr = [targetText substringWithRange:tokenRange];
NSLog(@"%@ : %@", subStr, tag);
}];
let schemes = [NSLinguisticTagScheme.lexicalClass]
let tagger = NSLinguisticTagger(tagSchemes: schemes, options: 0)
tagger.string = orgStr
let attrStr = NSMutableAttributedString(string: orgStr)
guard let scheme = schemes.first else {fatalError()}
tagger.enumerateTags(in: NSMakeRange(0, orgStr.characters.count), scheme: scheme, options: NSLinguisticTagger.Options(rawValue: 0)) { (tag, tokenRange, sentenceRange, stop) in
guard let tag = tag else {return}
attrStr.addAttribute(.foregroundColor as NSAttributedStringKey, value: color(for: tag), range: tokenRange)
}
textView.attributedText = attrStr
スキームとは、トークン分解をどのような属性に基づいて行うかを指定するもので、上記サンプルで使用しているNSLinguisticTagSchemeLexicalClass
は品詞(「名詞」「動詞」etc)によってテキストを分解するスキームです。
enumerateTagsInRange:scheme:options:usingBlock:
メソッドをコールするとトークン分解が開始され、トークンが見つかるごとに引数 usingBlock に渡されたBloksの処理が実行されます。
##品詞で区分する
名詞、動詞といった品詞でテキストを区分するには、スキームとしてNSLinguisticTagSchemeLexicalClass
を使用します。
enumerateTagsInRange:scheme:options:usingBlock:
メソッドで取得できるタグの種類には、次のようなものがあります。(主なものを抜粋)
- NSLinguisticTagNoun:名詞
- NSLinguisticTagVerb:動詞
- NSLinguisticTagAdjective:形容詞
- NSLinguisticTagPronoun:代名詞
- NSLinguisticTagAdverb:副詞
- NSLinguisticTagConjunction:接続詞
- NSLinguisticTagPreposition:前置詞
- NSLinguisticTagParticle:助詞
- NSLinguisticTagDeterminer:限定詞
たとえば、品詞で区分して色分けする場合、この NSLinguisticTagger で取得できるタグに応じて色を決定するメソッドを実装しておき、
- (UIColor *)colorForAtteributeForLinguisticTag:(NSString *)linguisticTag {
// 名詞
if ([linguisticTag isEqualToString:NSLinguisticTagNoun]) {
return [UIColor redColor];
}
// 動詞
else if ([linguisticTag isEqualToString:NSLinguisticTagVerb]) {
return [UIColor blueColor];
}
// 形容詞
else if ([linguisticTag isEqualToString:NSLinguisticTagAdjective]) {
return [UIColor magentaColor];
}
// 代名詞
else if ([linguisticTag isEqualToString:NSLinguisticTagPronoun]) {
return [UIColor orangeColor];
}
return [UIColor whiteColor];
}
次のようにenumerateTagsInRange:scheme:options:usingBlock:
メソッドのBlocks内でタグに応じた色分けを実施します。
// スキーム
NSArray *schemes = @[NSLinguisticTagSchemeLexicalClass];
// NSLinguisticTaggerオブジェクトを生成
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:schemes
options:0];
// 処理対象テキスト
NSString *targetText = self.textView.text;
[tagger setString:targetText];
// テキストに色をつけるためにNSMutableAttributedStringを生成
NSMutableAttributedString *formatted;
formatted = [[NSMutableAttributedString alloc] initWithString:targetText];
// トークンのタグを取得開始
[tagger enumerateTagsInRange:NSMakeRange(0, targetText.length)
scheme:NSLinguisticTagSchemeLexicalClass
options:0
usingBlock:
^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
// タグに応じた色分け
UIColor *color = [self colorForAtteributeForLinguisticTag:tag];
[formatted addAttribute:NSForegroundColorAttributeName
value:color
range:tokenRange];
}];
// 色分けされたテキストを表示
self.textView.attributedText = formatted;
##地名、個人名、組織名で区分する
スキームとしてNSLinguisticTagSchemeNameType
を使用します。他は品詞による区分の場合と同じ手順でOKです。
##スキーム種別
上で使用した NSLinguisticTagSchemeLexicalClass
, NSLinguisticTagSchemeNameType
以外に、次のようなスキームが用意されています。
- NSLinguisticTagSchemeTokenType:「単語」「区切り文字」「ホワイトスペース」「その他」で分解する
- NSLinguisticTagSchemeLemma:各単語の原形をタグとして取得する
- NSLinguisticTagSchemeLanguage:言語名("ja", "en",...)をタグとして取得する
- NSLinguisticTagSchemeScript:書き文字種別("Jpan", "Latn",...)をタグとして取得する
-
NSLinguisticTagSchemeNameTypeOrLexicalClass:
NSLinguisticTagSchemeLexicalClass
またはNSLinguisticTagSchemeNameType
にある属性で分解する
##言語ごとの対応スキームを確認する
availableTagSchemesForLanguage:
メソッドを使用すると、引数に指定した言語で利用できるスキームの配列(NSArray型)を取得することができます。
NSArray *schemes = [NSLinguisticTagger availableTagSchemesForLanguage:@"ja"];
NSLog(@"schemes:%@", schemes);
上記コードの実行結果は、
schemes:(
TokenType,
Language,
Script
)
となりました。(iPhone 6.1 Simulatorで実行)
つまり、"ja"(日本語)で利用できるスキームはNSLinguisticTagSchemeTokenType
, NSLinguisticTagSchemeLanguage
, NSLinguisticTagSchemeScript
の3種のみであることがわかります。
一方、引数に "en"(英語)を指定しての実行結果は、次のようになりました。
schemes:(
TokenType,
Language,
Script,
Lemma,
LexicalClass,
NameType,
NameTypeOrLexicalClass
)
英語ではすべてのスキームが利用可能であることがわかります。
##日本語の形態素解析を行う
日本語は英語と違い、単語の間がスペースで区切られていません。そこで自然言語処理を行う際のベースとなる処理として、「形態素解析」という、文を形態素(言語で意味を持つ最小単位)に分解する処理が必要となります。
前項でavailableTagSchemesForLanguage:
メソッドを用いて調べたとおり、英語と比較すると日本語の対応スキームは少ないですが、NSLinguisticTagSchemeTokenType
には対応しているので、単語への分解が可能です。すなわち、NSLinguisticTagger
を用いて日本語の形態素解析を行うことができます。
スキームにNSLinguisticTagSchemeTokenType
を指定し、string
プロパティに日本語の文字列を渡すだけ、つまり英語の場合と実装方法は同じなので、詳細な説明は省略します。
##NSStringのカテゴリを利用する
NSLinguisticTagger.h には、NSString のカテゴリも定義されており、NSLinguisticTaggerを使用せず NSStringだけでも同様の処理ができるようになっています。
NSStringに追加されたenumerateLinguisticTagsInRange:scheme:options:orthography:usingBlock:
というメソッドを使用します。使い方はNSLinguisticTagger
とほぼ同様です。
NSString *targetText = self.textView.text;
[targetText enumerateLinguisticTagsInRange:NSMakeRange(0, targetText.length)
scheme:NSLinguisticTagSchemeLexicalClass
options:0
orthography:nil
usingBlock:
^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
// トークン取得時の処理
}];
##サンプルコード
Githubにアップしてあります。
##参考資料