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

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

新規登録して質問してみよう
ただいま回答率
85.49%
UTF-8

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

MacOS(OSX)

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

Q&A

解決済

1回答

1376閲覧

nltkでの日本語WordNetの使い方

退会済みユーザー

退会済みユーザー

総合スコア0

UTF-8

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

MacOS(OSX)

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

0グッド

0クリップ

投稿2018/06/04 22:29

編集2018/06/07 04:23

前提・実現したいこと

以下の参考記事に沿って
シソーラス距離(概念距離)を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

python

1# -*- coding: utf-8 -*- 2#英語WordNetから類似度を算出するモジュール 3import codecs 4 5def makeWordLists(fin1,fin2): 6 """ ファイル名を2つ受け取って単語リストのリストを返す """ 7 fins = [fin1, fin2] 8 wordLists = [[ ], [ ]] 9 for i in [0,1]: 10 f=codecs.open(fins[i], encoding="utf-8") 11 for line in f: 12 wordLists[i].append(line.strip("\r\n").strip("\n")) 13 f.close() 14 return wordLists 15 16def convWords2Synsets(wordList1, wordList2): 17 """ 単語リストを2つ受け取って概念リストのリストを返す """ 18 import jwn_corpusreader 19 from nltk.corpus.reader.wordnet import WordNetCorpusReader 20 jwn = WordNetCorpusReader('nltk_data/corpora/wordnet', 'wnjpn-ok.tab') #英語WordNetと日本語WordNetを指定する 21 synLists = [[ ],[ ]] 22 wordLists = [wordList1, wordList2] 23 for i in [0,1]: 24 for j in range(len(wordLists[i])): 25 synLists[i].append(jwn.synsets(wordLists[i][j])) 26 return synLists 27 28def calcSim(synList1,synList2): 29 """ 概念リストを2つ受け取って類似度の行列を返す """ 30 import numpy as np 31 simMatrix = np.zeros( (len(synList1), len(synList2))) 32 for i in range(len(synList1)): 33 for j in range(len(synList2)): 34 sims = [ ] 35 for syn1 in synList1[i]: 36 for syn2 in synList2[j]: 37 sims.append(syn1.path_similarity(syn2)) 38 simMatrix[i,j] = max(sims) 39 return simMatrix 40 41def writeSim(wordList1, wordList2, simMatrix,fout): 42 """ 単語リストを2つと類似度行列とファイル名を受け取ってファイルに出力する """ 43 f = codecs.open(fout,'w', encoding="utf-8") 44 for i in range(len(wordList1)): 45 for j in range(len(wordList2)): 46 f.write(wordList1[i] + "-" + wordList2[j] +": " + str(simMatrix[i][j])+"\r\n") 47 f.close() 48

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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/04 23:09

編集2018/06/06 12:14
quickquip

総合スコア11038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/06/05 01:26

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

2018/06/05 01:32

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

退会済みユーザー

2018/06/05 12:03

はい、ご回答いただいた内容が理解できておりません。
退会済みユーザー

退会済みユーザー

2018/06/05 12:03

具体的に何をすればいいのかわかりません。
退会済みユーザー

退会済みユーザー

2018/06/06 00:45

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

2018/06/06 01:31

空ファイルでもないんですね……
退会済みユーザー

退会済みユーザー

2018/06/06 03:34

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

退会済みユーザー

2018/06/06 03:35

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

2018/06/06 04:06

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

退会済みユーザー

2018/06/06 04:36

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

2018/06/06 04:40

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

2018/06/06 04:44 編集

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

退会済みユーザー

2018/06/06 04:54

はい、synLists[i].append(jwn.synsets(wordLists[i][j]))でやっています。
退会済みユーザー

退会済みユーザー

2018/06/06 05:15

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

2018/06/06 11:54

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

退会済みユーザー

2018/06/07 04:23

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問