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

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

ただいまの
回答率

88.33%

txtを一行ずつ分かち書き, 品詞抽出したいが、入力のまま出力される

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,430

tono_1812

score 9

txtを一行ずつ分かち書きしてtxtに保存。
また、txtを一行ずつ"形容詞"、"名詞"、"動詞"を抽出、保存したいと考えています。

import MeCab

tagger = MeCab.Tagger("-Owakati")

f = open("lines.txt","r")
read_text = f.readline() # readline()で一行ずつ読み込み。

for text in read_text: # readline()だけだと一行しか読み込まれなかったため、for文で繰り返す。
    for output in tagger.parse(text): # 分かち書きを行う。
        with open("wakati_lines.txt", mode="a") as write_file: # wでは、上書きされるため、aモードで追記する。
            write_file.write(output)

上記のスクリプトで分かち書きされると考えていますが、入力がそのまま出力されます。
@hayataka2049さんの回答により原因の判明と分かち書きが出来ました。

import MeCab
import codecs
import ast

tagger = MeCab.Tagger() # mecab標準辞書でオブジェクト生成

f = open('lines.txt', 'r')
reader = f.read()
reader = reader.replace("\n", "|")
f.close()

node = tagger.parseToNode(reader)
result = [reader]

while node:
    word = node.surface
    #品詞を取得
    pos = node.feature.split(",")[0]
    if pos in ["名詞"]:
        result.append(node.surface)
    elif pos in["動詞","形容詞"]:
        result.append(node.feature.split(",")[6])
    #次の単語に進める
    node = node.next
    print(result, file=codecs.open("haiku_wakati.txt", "w"))

期待する出力:
['今年', '梅雨', '遅い']
['寝る', '時間']
この形式にする理由は、random.chices()でランダムに要素を抽出し、翻訳生成用データセットを作るためです。
例えば、俳句の場合、
夏 + 梅雨 + 鯉 -(翻訳)-> 鯉 こく 梅雨 傘立 あふれ(分かち書き)
また、[]や''はreplce()で簡単に除去できます。
そして、今後、txt列の品詞抽出を行いたい方がいらっしゃった場合にこの質問は役に立つと考えています。

実際の出力:
['今年の梅雨は遅い。|寝る時間よ。|・・・しなさい。|私に任せなさい。', '今年', '梅雨', '遅い', '|', '寝る', '時間', '|', ・・・ '|', '私', '任せる', 'なさる']

最初に'文章', 次に'品詞','品詞'が出力されます。
read()が全てを読み込むのは分かるのですが、pop()で出来ず、最初の要素をどうやって取り除くか。品詞部分が、\n、\tでは、区切られない問題に対処する必要があると考えています。

参考サイト:
MeCabの形態素解析で使うTagger、Nodeオブジェクトのプロパティとメソッド
https://takaxtech.com/2018/11/03/article271/
Pythonでファイルの読み込み、書き込み
https://note.nkmk.me/python-file-io-open-with/

今年の梅雨は遅い。
寝る時間よ。
もう手遅れだ。
嘘を付くのは悪いことだ。
そろそろ寝る時間だぞ。
右側通行をしなさい。
私に任せなさい。

環境:Ubuntu 18.04

何卒、ご教授よろしくお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

修正点1
read_text = f.readline()

for text in read_text:

read_textには「f.readline()で得られた一行分の文字列」が入ります。なので、そもそもやっていることが間違っています。f.readlines()なら正しいですし、そもそも単にfと書いてもいいのですが。
(あと、ファイルはopenしたらcloseしましょう)

修正点2

上の点を修正すると、上のコードの場合結果のファイル(あ、追記モードでこれまでやっていたなら一度削除してください)には、

今年 の 梅雨 は 遅い 。 
寝る 時間 よ 。 
もう 手遅れ だ 。 
嘘 を 付く の は 悪い こと だ 。 
そろそろ 寝る 時間 だ ぞ 。 
右側 通行 を し なさい 。 
私 に 任せ なさい 。 

のようなスペースで区切られた文字列が入ります。

オプションを-Owakatiとした場合、

>>> import MeCab
>>> tagger = MeCab.Tagger("-Owakati")
>>> tagger.parse("昨日はたいへんだった")  # 空白で区切られた文字列である
'昨日 は たいへん だっ た \n'


という挙動になるのですが、実は内側のforは文字列から1文字ずつ取り出して1文字ずつ書くという動作をしているだけなので、純粋に無駄です。

以上を勘案して、スペース区切りでいいのであればこんな感じ。

import MeCab

tagger = MeCab.Tagger("-Owakati")

f = open("lines.txt","r")
read_text = f.readlines()
f.close()  # withを使うべきかどうか、またreadlinesが効率的かという議論もあるのですが、ひとまず

for text in read_text:
    result = tagger.parse(text)
    with open("hinshi_lines.txt", mode="a") as write_file:
        write_file.writelines(result)


質問文のコードはひどく非効率的なことをしています。

修正点3

今年の梅雨は遅い。-> ['今年', '梅雨', '遅い']

結果はpythonのlistを期待しているということでしょうか? その場合は、ファイル出力時のフォーマットをどうするのかというところから検討するべきだと思います。また、人間が簡単に読めなくても良いのであればpickleなどを使うという手もあります(そういうことがしたいようにも思えます)。

修正点4

下のコードの場合。whileループ内でファイルにresultを書き込むのはおかしいです。インデントを一段階上げて、外側のforのブロックに属するようにするのがたぶんただしいのでしょう。

修正点5

修正点3とも関連しますが、上の修正を施して実行すると、

今年梅雨遅い寝る時間手遅れ嘘付くの悪いこと寝る時間右側通行するなさる私任せるなさるのようにすべての単語が連続して出てくるので、出力フォーマットについてはよく考えて、適切な書き出し処理を行いましょう。

修正点6

コメントの半分くらいが本当の処理内容と違っているので、混乱しないように直す。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/07/20 05:05

    ast.literal_evalなのですが、
    今年の梅雨は遅い。
    ^
    SyntaxError: invalid character in identifier
    読み込みでそのまま使えません。また、最後のresultで使うと全てを一つのリストにしてしまいます。

    キャンセル

  • 2019/07/20 15:17

    ['今年', '梅雨', '遅い']のような文字列をリストに復元するのに使えるという意味ですが、たぶん使うタイミングが違うのではないでしょうか。

    キャンセル

  • 2019/07/25 19:20 編集

    自身のスキル不足でベストアンサーをなかなか出せずに申し訳ありません。

    キャンセル

+1

これで取得できると思います。for text in read_text:をやると重複するのでこれが一番無難なやり方な気がします。

f = open('lines.txt', 'r')
reader = f.readline()
result = []

node = mecab.parseToNode(reader)


while node:
    #単語を取得
    word = node.surface
    #品詞を取得
    pos = node.feature.split(",")[0]
    if pos in ["名詞"]:
        result.append(node.surface)
    elif pos in["動詞","形容詞"]:
        result.append(node.feature.split(",")[6])
    #次の単語に進める
    node = node.next
print(result)  

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/07/19 01:50 編集

    ご回答頂きありがとうございます!
    一行分ではありますが、期待する処理が出ました!
    うまくforを使ってみます!
    もしよろしければ、保存するアイデアなどがございましたら、教えて頂けると幸いです。

    キャンセル

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

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

関連した質問

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