読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

代名詞と非自立名詞をフィルタして再計測

日記

形態素解析のサンプルアプリが面白かったので、シャァの演説を見てもらった - 謎言語使いの徒然 の続き。
やってみたら案外よさげになった。

code

import net.reduls.igo.Tagger
import scala.collection.mutable
import scala.io.{Codec, Source}

case class Word(word:String, wordType:String, subType:String)

object Word {
  def apply(word:String, attribute:String):Word = {
    val element = attribute.split(",")
    Word(word, element(0), element(1))
  }


  private def compareType(l:Word, r:Word, types:String):Boolean = l.wordType == r.wordType && l.wordType == types

  private def compareType(l:Word, r:Word, lTypes:String, rType:String):Boolean = l.wordType == lTypes && r.wordType == rType

  def combine(tokens:Iterable[Word]):mutable.MutableList[Word]
   = tokens.foldLeft(mutable.MutableList.newBuilder[Word].result()) { case (lists, token) =>
    if (lists.size == 0) { lists += token }
    else {
      (lists.last, token) match {
        case (l, r) if compareType(l, r, "接頭詞", "名詞") => lists.dropRight(1) += Word(l.word + r.word, "名詞", "固有名詞")
        case (l, r) if compareType(l, r, "名詞")            => lists.dropRight(1) += Word(l.word + r.word, "名詞", "固有名詞")
        case (l, r) if compareType(l, r, "名詞", "動詞")   => lists.dropRight(1) += Word(l.word , "動名詞", "自立") += r
        case (l, r) if compareType(l, r, "動詞", "動詞")   => lists.dropRight(1) += Word(l.word + r.word, "動詞", "自立")
        case (l, r) if compareType(l, r, "動詞", "助動詞") => lists.dropRight(1) += Word(l.word + r.word, "動詞", "自立")
        case o => lists += token
      }
    }
  }

  def splitBySymbol(tokens:List[Word]):List[List[Word]] = {
    val result = mutable.MutableList.newBuilder[List[Word]].result()
    var currentTokens = mutable.MutableList.newBuilder[Word].result()
    val newWord = List("。", "「", "」", "、")
    tokens.foreach { token =>
      if (token.wordType == "記号" && newWord.contains(token.word)) {
        result += currentTokens.toList
        currentTokens = mutable.MutableList.newBuilder[Word].result()
      } else {
        currentTokens += token
      }
    }

    result.filterNot(_.isEmpty).toList
  }
}

class SentenceCalc {

  val wordCount = collection.mutable.Map.empty[Word, Int]
  val pairCount = collection.mutable.Map.empty[(Word, Word), Int]

  def calcSentence(sentence:List[Word]) {
    sentence.foreach(t => {
      countWord(t, t.word)
    })

    makeCombination(sentence).foreach { case (left, right) =>
      val (leftHash, rightHash) = (left.hashCode -> right.hashCode)
      val key = if (leftHash < rightHash) (left -> right) else (right -> left)
      pairCount.get(key) match {
        case Some(v) => pairCount.+=(key -> (v + 1))
        case None => pairCount.+=(key -> 1)
      }
    }
  }

  private def makeCombination(tgt:List[Word]):List[(Word, Word)] = {
    def pairComparator(head:Word, tail:List[Word]):List[(Word, Word)] = {
      tail.size match {
        case 1 => List(head -> tail.head)
        case e => tail.map(v => head -> v) ++ pairComparator(tail.head, tail.tail)
      }
    }

    if (tgt.isEmpty || tgt.size == 1) {
      List.empty[(Word, Word)]
    } else {
      pairComparator(tgt.head, tgt.tail)
    }
  }

  private def countWord(hash:Word, s:String) {
    wordCount.get(hash) match {
      case Some(i) => wordCount += (hash -> (i + 1))
      case None => wordCount += (hash -> 1)
    }
  }
}

object IgoSample extends App {

  import Word._
  import scala.collection.JavaConversions._
  val tagger = new Tagger("ipadic")
  val source = Source.fromFile("sample.txt")(Codec.UTF8)
  val enableName = List("名詞", "動名詞")
  val calc = new SentenceCalc

  source.getLines().foreach(messageLine => {
    val parsedList = combine(tagger.parse(messageLine).map(m => Word(m.surface, m.feature))).toList
    val sentences  = splitBySymbol(parsedList.filter(_.subType != "代名詞"))
    sentences.map(sentence => {
      sentence.filter(w => enableName.contains(w.wordType)).filter(w => w.subType != "非自立")
    }).filterNot(_.isEmpty).foreach(calc.calcSentence)
  })
  source.close()

  println("Result ========================================")
  println("MessageDetect : ")
  calc.wordCount.toList.sortWith({ case ((_, l), (_, r)) => l > r}).foreach(v => println("  %s\t%d".format(v._1, v._2)))
  println("Combinations : ")
  calc.pairCount.toList.sortWith({ case ((_, l), (_, r)) => l > r }).foreach(v => {
    val (left,right) = v._1
    println("  %s = %s\t%d".format(left.word, right.word, v._2))
  })
}
Result ========================================
MessageDetect :
  Word(地球,名詞,一般)  12
  Word(宇宙,名詞,一般)  4
  Word(人,名詞,一般)    3
  Word(ジオン・ダイクン,名詞,一般)       3
  Word(人類,名詞,一般)  3
  Word(議会,名詞,一般)  3
  Word(人間,名詞,一般)  3
  Word(ザビ家,名詞,固有名詞)    3
  Word(ティターンズ,名詞,一般)   3
  Word(地球連邦軍,名詞,固有名詞)        2
  Word(やり方,名詞,一般)        2
  Word(テレビ,名詞,一般)        2
  Word(ジオン公国,名詞,固有名詞)        2
  Word(遺志,名詞,一般)  2
  Word(籠,名詞,一般)    2
  Word(歴史,名詞,一般)  2
  Word(悪,名詞,一般)    2
  Word(能力,名詞,一般)  1
  Word(誤解,名詞,サ変接続)      1
  Word(寄生虫,名詞,一般)        1
  Word(人類同士,名詞,固有名詞)  1
  Word(武力,名詞,一般)  1
  Word(場,名詞,一般)    1
  Word(制圧,動名詞,自立)        1
  Word(御覧,名詞,一般)  1
  Word(魂,名詞,一般)    1
  Word(一つ,動名詞,自立)        1
  Word(ダカール,名詞,固有名詞)   1
  Word(クワトロ・バジーナ大尉,名詞,固有名詞)      1
  Word(欲望,名詞,一般)  1
  Word(力,名詞,一般)    1
  Word(身,名詞,一般)    1
  Word(惑星,名詞,一般)  1
  Word(人類そのもの,名詞,固有名詞)      1
  Word(暴虐,名詞,形容動詞語幹)  1
  Word(現在ティターンズ,名詞,固有名詞)   1
  Word(行為,名詞,サ変接続)      1
  Word(事実,名詞,副詞可能)      1
  Word(集まり,名詞,一般)        1
  Word(ティターズ,名詞,一般)     1
  Word(人々,名詞,一般)   1
  Word(話,名詞,サ変接続)        1
  Word(今,名詞,副詞可能)        1
  Word(戦い,名詞,一般)  1
  Word(勢力,名詞,一般)  1
  Word(方々,名詞,一般)   1
  Word(全て,名詞,副詞可能)      1
  Word(破壊,動名詞,自立)        1
  Word(子,名詞,一般)    1
  Word(間,名詞,一般)    1
  Word(連邦国々民,名詞,固有名詞) 1
  Word(エゥーゴ,名詞,一般)       1
  Word(悪質,名詞,形容動詞語幹)  1
  Word(シャア,名詞,一般)        1
  Word(時,名詞,一般)    1
  Word(手,名詞,一般)    1
  Word(不幸,名詞,形容動詞語幹)  1
  Word(シャア・アズナブル,名詞,一般)     1
  Word(戦闘,名詞,サ変接続)      1
  Word(衰退,動名詞,自立)        1
  Word(前,名詞,副詞可能)        1
  Word(議員,名詞,一般)  1
  Word(汚染,動名詞,自立)        1
  Word(名,名詞,一般)    1
  Word(自分,名詞,一般)  1
  Word(男,名詞,一般)    1
  Word(砂漠,名詞,一般)  1
  Word(欲求,名詞,サ変接続)      1
  Word(自然,名詞,形容動詞語幹)  1
  Word(拡大,動名詞,自立)        1
  Word(自分達,名詞,固有名詞)    1
  Word(生活圏,名詞,固有名詞)    1
  Word(味方,名詞,サ変接続)      1
  Word(水,名詞,一般)    1
  Word(その後,名詞,副詞可能)    1
  Word(ジオン,名詞,一般)        1
  Word(自立,動名詞,自立)        1
  Word(無礼,名詞,一般)  1
  Word(重み,名詞,一般)  1
Combinations :
  地球 = 籠     2
  味方 = 自分達 1
  事実 = 現在ティターンズ        1
  地球 = 惑星   1
  議会 = 破壊   1
  魂 = ティターンズ      1
  地球 = 人     1
  自分達 = ティターンズ  1
  事実 = 地球連邦軍     1
  地球 = 時     1
  欲望 = ザビ家 1
  人間 = 能力   1
  味方 = 議会   1
  地球 = 人類   1
  自立 = 人間   1
  人々 = 魂      1
  議会 = 自分達 1
  やり方 = ザビ家       1
  身 = 力       1
  戦闘 = ティターンズ    1
  悪質 = ザビ家 1
  悪質 = やり方 1
  重み = 人間   1
  力 = 人類そのもの     1
  身 = 人類そのもの     1
  悪 = 全て     1
  地球 = 自然   1
  地球 = 汚染   1
  勢力 = ザビ家 1
  生活圏 = 拡大 1
  誤解 = 力     1
  集まり = ティターンズ  1
  籠 = 自然     1
  男 = 名       1
  自分達 = 議員 1
  武力 = 制圧   1
  名 = シャア・アズナブル        1
  ジオン = 遺志 1
  地球 = 手     1
  地球 = 水     1
  議会 = ティターンズ    1
  議会 = 制圧   1
  クワトロ・バジーナ大尉 = エゥーゴ        1
  前 = 話       1
  自分 = 欲求   1
  地球 = 人間   1
  誤解 = 人類そのもの   1
  勢力 = 歴史   1
  連邦国々民 = テレビ    1
  ジオン公国 = シャア   1
  間 = 人       1
  砂漠 = ダカール        1
  地球連邦軍 = 現在ティターンズ  1
  地球 = 重み   1
  地球 = 人々    1
  ティターンズ = 破壊    1
  味方 = 議員   1
  議会 = 議員   1
  寄生虫 = 地球 1
  歴史 = ザビ家 1
  御覧 = テレビ 1
  水 = 惑星     1
  手 = 人       1
  その後 = 人類同士     1
  ジオン・ダイクン = 遺志        1
  人々 = ティターンズ     1
  やり方 = ティターズ    1
  人類 = 時     1
  味方 = ティターンズ    1
  味方 = 破壊   1
  方々 = テレビ  1
  議会 = 武力   1
  自立 = 宇宙   1
  議員 = 破壊   1
  その後 = 戦い 1
  方々 = 御覧    1
  人類 = 宇宙   1
  集まり = 魂   1
  人類 = 衰退   1
  宇宙 = 人     1
  ティターンズ = 議員    1
  集まり = 地球 1
  ジオン・ダイクン = 子  1
  人間 = 宇宙   1
  暴虐 = 行為   1
  ジオン・ダイクン = ジオン公国  1
  男 = シャア・アズナブル        1
  地球 = ティターンズ    1
  身 = 誤解     1
  自分達 = 破壊 1
  地球 = 魂     1
  集まり = 人々  1
  人類同士 = 戦い       1