質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • MacOS(OSX)

    1904questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • UTF-8

    109questions

    UTF-8は8ビット符号単位の文字符号化形式及び文字符号化スキームです。データ交換方式、ファイル形式としては、一般的にUTF-8が使われる傾向があります。

nltkでの日本語WordNetの使い方

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 372

tenjin

score 214

 前提・実現したいこと

以下の参考記事に沿って
シソーラス距離(概念距離)をwordnetを使ってpythonで実装しようとしています。
参考記事
なお、参考記事ではpython2.7系で書かれていましたが、実行環境はPython 3.6.3です。

以下前回の質問で、コードと実現したいことはほぼ同じです。
前回の質問

具体的には
「人間とサルの類似度は0.5」というように数値を出したいです。
「人間とサルは0.5だった。」と記事に書いてある様に
0.5という数字をOutput.txtで出力したいです。

参考記事ではTagExamples.txtとWordExamples.txtは文字の羅列でしたが、
現在実行に用いているtxtファイルではTagExamples.txtに「人間」、WordExamples.txtに「サル」とそれぞれ1単語だけ書いています。

 発生している問題・エラーメッセージ

$ python jwn_driver.py

と実行すると以下のエラーが出力されます。

Traceback (most recent call last):
File "jwn_driver.py", line 10, in <module>
simMatrix = sim.calcSim(synLists[0], synLists[1]) #類似度行列を作成
File "/Users/username/Desktop/sim.py", line 38, in calcSim
simMatrix[i,j] = max(sims)
ValueError: max() arg is an empty sequence

参考記事に「入力用のfin1とfin2に取り込むファイルは、\n区切りの単語の羅列だが、取り込むファイルはterapadでutf-8nで書き出す(重要)。」とあったことも影響しているのかと考え、取り込むファイルをutf-8nで書き出そうとしているのですが、
Macでterapad(Windows)の代わりになるようなエディタはありますか。

 該当のソースコード

sim.py

# -*- coding: utf-8 -*-
#英語WordNetから類似度を算出するモジュール
import codecs

def makeWordLists(fin1,fin2):
  """ ファイル名を2つ受け取って単語リストのリストを返す """
  fins = [fin1, fin2]
  wordLists = [[ ], [ ]]
  for i in [0,1]:
    f=codecs.open(fins[i], encoding="utf-8")
    for line in f:
      wordLists[i].append(line.strip("\r\n").strip("\n"))
    f.close()
  return wordLists

def convWords2Synsets(wordList1, wordList2):
  """ 単語リストを2つ受け取って概念リストのリストを返す """
  import jwn_corpusreader
  from nltk.corpus.reader.wordnet import WordNetCorpusReader
  jwn = WordNetCorpusReader('nltk_data/corpora/wordnet', 'wnjpn-ok.tab') #英語WordNetと日本語WordNetを指定する
  synLists = [[ ],[ ]]
  wordLists = [wordList1, wordList2]
  for i in [0,1]:
    for j in range(len(wordLists[i])):
      synLists[i].append(jwn.synsets(wordLists[i][j]))
  return synLists

def calcSim(synList1,synList2):
  """ 概念リストを2つ受け取って類似度の行列を返す """
  import numpy as np
  simMatrix = np.zeros( (len(synList1), len(synList2)))
  for i in range(len(synList1)):
    for j in range(len(synList2)):
      sims = [ ]
      for syn1 in synList1[i]:
        for syn2 in synList2[j]:
            sims.append(syn1.path_similarity(syn2))
      simMatrix[i,j] = max(sims)
  return simMatrix

def writeSim(wordList1, wordList2, simMatrix,fout):
  """ 単語リストを2つと類似度行列とファイル名を受け取ってファイルに出力する """
  f = codecs.open(fout,'w', encoding="utf-8")
  for i in range(len(wordList1)):
    for j in range(len(wordList2)):
      f.write(wordList1[i] + "-" + wordList2[j] +": " + str(simMatrix[i][j])+"\r\n")
  f.close()

jwn_driver.py

fin1 = 'TagExamples.txt' #入力ファイル1
fin2 = 'WordExamples.txt' #入力ファイル2

fout = 'Output.txt' #出力ファイル

import sim

wordLists = sim.makeWordLists(fin1,fin2) #単語リストを作成
synLists = sim.convWords2Synsets(wordLists[0], wordLists[1]) #概念リストを作成
simMatrix = sim.calcSim(synLists[0], synLists[1]) #類似度行列を作成
sim.writeSim(wordLists[0],wordLists[1],simMatrix,fout) #ファイルへの書き込み


jwn_corpusreader.py

# -*- coding: utf-8 -*-
"""
日本語リーダー
""" 
from nltk.corpus.reader.wordnet import WordNetCorpusReader
class JapaneseWordNetCorpusReader(WordNetCorpusReader):
    def __init__(self, root, filename):
        WordNetCorpusReader.__init__(self, root)
        import codecs
        f=codecs.open(filename, encoding="utf-8")
        self._jword2offset = {}
        for line in f:
            _cells = line.strip().split('\t')
            _offset_pos = _cells[0]
            _word = _cells[1]
            if len(_cells)>2: _tag = _cells[2]
            _offset, _pos = _offset_pos.split('-')
            try:
              self._jword2offset[_word].append({'offset': int(_offset), 'pos': _pos})
            except:
              self._jword2offset[_word]=[{'offset': int(_offset), 'pos': _pos}]
    def synsets(self, word):
        if word in self._jword2offset:
            results = [ ]
            for offset in (self._jword2offset[word]):
                results.append(WordNetCorpusReader._synset_from_pos_and_offset(
                self, offset['pos'], offset['offset']
                ))
            return results
        else:
            return None

 試したこと

現在のテキストファイルはSublime Textで入力した後、拡張子を「.txt」に変更しました。

 補足情報(FW/ツールのバージョンなど)

オペレーティングシステム名(os.name):Mac OS
Python Python 3.6.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

utf-8nはつまりBOM(Byte Order Mark)なしのutf-8という意味であって、普通のエディタの(そしてPythonの)utf-8のことです。
意図的にBOM付きを指定しなければ普通は入りません。
そこは気にしないでロジックを直すことに集中すればいいと思います。


UTF-8、UTF-16、UTF-32はUnicodeのコードポイント(ある文字に振られた番号)をバイナリデータに変換する仕様ですが、UTF-16やUTF-32には複数バイト分のデータの並び順にリトルエンディアンとビッグエンディアンの2種類あって、そのことをエンディアンとかバイトオーダーとか呼んでいます。

ということは例えば「UTF-16で書かれたファイル」といってもその表現方法に2種類あることにあります。
「このファイルはどちらのエンディアンで書かれているのか」がわからないと文字情報を読み取れません。そのため、エンディアンを識別するためにファイルの先頭に特別なを置くことができる仕様になっています。
これをBOM(Byte Order Mark)と呼びます。

UTF-8には実質エンディアンは関係ないのですが、UTF-16やUTF-32に倣って「このファイルはUTF-8でエンコードされている」ことを示すを置くこともできます。それも(やっぱりUTF-16やUTF-32に倣って)BOMと呼びます。
したがって、UTF-8には「BOMを付けたファイル」と「BOMを付けていないファイル」の2種類があるわけです。

さて、PythonやSublimeのエンコーディング名、"UTF-8"は「BOMを付けていないファイル」の方を指しています。こちらがデフォルトで、Sublimeでは"UTF-8 with BOM"をわざわざ指定しなければBOMは付きません。

かたやterapadは"utf-8"がBOM付きの方で、"utf-8n"がBOMなしの方なんです。
だからPythonで(UTF-8を指定して)読みこむために、terapadで"utf-8n"を指定しないといけないわけです。
terapadが標準的な名前になっていないというだけの話なんです。

いまは、Sublimeで作成してPythonで読んでいるので、「BOMを気にしないといけない状況ではない」と判断しています。


ちょっと調べてみました。

https://ja.1answer.info/737461636b6f766572666c6f772e636f6d-7a3331343738313532
を見ると、WordNetCorpusReaderのコンストラクタの第2引数に渡すのは Open Multilingual Wordnet (http://compling.hss.ntu.edu.sg/omw/)で提供している、多国語対応Wordnetのファイルを置いたディレクトリの様です

jwn = WordNetCorpusReader('nltk_data/corpora/wordnet', 'wnjpn-ok.tab')


としていますが、これだと(wnjpn-ok.tabが件のディレクトリでないなら)日本語Wordnetのセットアップになっていません。

ですからjwn.synsets()を日本語を引数に呼び出してもシンセットが返ってこないはずです。
すべての結果が空なので、エラーがでている箇所max(sims)simsが空になっているんじゃないでしょうか?


jwn_corpusreader.pyのコード、使われてないからなにかと思いましたが、これは古いNLTK用のソースですか? 動かなかったので捨てたんでしょうか。

新しいNLTKでちゃんと動くように直す

古いNLTKを使って動かす(どんな問題が起きるかはわからない)
の2択になるのではないでしょうか。


ちゃんと調べて、omw は nltk にコーパスとして登録されていることが確認できました。
Python対話環境で以下のコードを試してみてください。

>>> import nltk
>>> nltk.download('wordnet')
(略)
>>> nltk.download('omw')
(略)
>>> from nltk.corpus import wordnet
>>> wordnet.synsets('サル', lang='jpn')
[Synset('simian.n.01'), Synset('monkey.n.01'), Synset('anthropoid.n.01')]
>>> wordnet.synsets('人間', lang='jpn')
[Synset('person.n.01'), Synset('homo.n.02'), Synset('world.n.08'), Synset('person.n.02'), Synset('man.n.03')]
>>> wordnet.synsets('サル', lang='jpn')[1].path_similarity(wordnet.synsets('人間', lang='jpn')[4])  # サルの2番目と人間の5番目の類似度
0.1

langを付けることで日本語からシンセットが引けました。

以上から、convWords2Synsets関数は以下の様になるでしょう。(よくない点は多いですが直さないでおきます)

def convWords2Synsets(wordList1, wordList2):
  """ 単語リストを2つ受け取って概念リストのリストを返す """
  from nltk.corpus import wordnet

  synLists = [[ ],[ ]]
  wordLists = [wordList1, wordList2]
  for i in [0,1]:
    for j in range(len(wordLists[i])):
      synLists[i].append(wordnet.synsets(wordLists[i][j], lang='jpn'))
  return synLists

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/05 10:26

    ご回答いただきましてありがとうございます。
    ロジックを直すとはどういうことでしょうか。
    今回のエラーは文字コードには起因しないということでしょうか。

    キャンセル

  • 2018/06/05 10:32

    文字コードに起因しているようには見えません。ところで、回答の1行目と2行目の意味が分からないってことでしょうか?

    キャンセル

  • 2018/06/05 21:03

    はい、ご回答いただいた内容が理解できておりません。

    キャンセル

  • 2018/06/05 21:03

    具体的に何をすればいいのかわかりません。

    キャンセル

  • 2018/06/06 09:45

    ご丁寧にご説明いただきましてありがとうございます。
    ファイルの文字コードを$ file -mine ファイル名で確認しましたところ、
    TagExamples.txtとWordExamples.txt共にUTF-8 Unicode text, with no line terminatorsでした。

    キャンセル

  • 2018/06/06 10:31

    空ファイルでもないんですね……

    キャンセル

  • 2018/06/06 12:34

    空ファイルとはどういうことでしょうか。
    参考記事では文字の羅列でしたが、現在実行に用いているtxtファイルでは例えばTagExamples.txtに「人間」、WordExamples.txtに「サル」とそれぞれ1単語だけ書いています。
    「人間とサルは0.5だった。」と記事に書いてある様に0.5という数字をOutput.txtで出力したいのですが、

    キャンセル

  • 2018/06/06 12:35

    エラー文をなくすにはどうすればいいでしょうか。

    キャンセル

  • 2018/06/06 13:06

    空ファイルは中身が空なファイルのことですが、fileコマンドの結果がempty fileじゃないのでそうじゃないことはわかります。
    問題はたぶん、コーパスからシンセットが引けてないからだろうな、と思います。WordNetCorpusReader の synsets('サル') synsets('人間') を呼んだ時にシンセットが返ってくるか確認しましたか?

    キャンセル

  • 2018/06/06 13:36

    WordNetCorpusReader の synsets('サル') synsets('人間') を呼んだ時にシンセットが返ってくるかは確認してません。WordNetに関するページ(http://www.nltk.org/howto/wordnet.html)を見ていますが、WordNetCorpusReader の synsets('')を呼ぶ方法とはどれにあたるのでしょうか。お手数おかけしますが、確認させていただきたいので教えていただけますと幸いです。

    キャンセル

  • 2018/06/06 13:40

    synLists[i].append(jwn.synsets(wordLists[i][j])) でやってますよね?

    キャンセル

  • 2018/06/06 13:41 編集

    一番の問題はコードを読まないことと、実行した時の経過を観察していないこと

    キャンセル

  • 2018/06/06 13:54

    はい、synLists[i].append(jwn.synsets(wordLists[i][j]))でやっています。

    キャンセル

  • 2018/06/06 14:15

    コードは読みましたが理解ができず、こちらで質問させていただいた次第です。

    キャンセル

  • 2018/06/06 20:54

    質問タイトルから遠くなったので、「nltkでの日本語WordNetの使い方」などを追加しておくといいと思います。

    キャンセル

  • 2018/06/07 13:23

    ご回答とご説明いただきましてありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • MacOS(OSX)

    1904questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • UTF-8

    109questions

    UTF-8は8ビット符号単位の文字符号化形式及び文字符号化スキームです。データ交換方式、ファイル形式としては、一般的にUTF-8が使われる傾向があります。