電子書籍の厳選無料作品が豊富!

Pythonのjanomeを使ってるんですけど、この表示結果を降順にするプログラムを知りたいです。
import urllib.request

from janome.analyzer import Analyzer
from janome.charfilter import *
from janome.tokenizer import Tokenizer


class MainTextCharFilter(CharFilter):

def __init__(self, start, end):
self.start = start
self.end = end

def apply(self, text):
return text.split(self.start)[1].split(self.end)[0]

# 夏目漱石
# 吾輩は猫である
url = 'http://www.aozora.gr.jp/cards/000148/files/789_1 …

html = ''

with urllib.request.urlopen(url) as response:
html = response.read().decode('shift_jis')

char_filters = [UnicodeNormalizeCharFilter(),
MainTextCharFilter('<div class="main_text">', '<div class="bibliographical_information">'),
RegexReplaceCharFilter('<rp>\(.*?\)</rp>', ''),
RegexReplaceCharFilter('<.*?>', '')]

tokenizer = Tokenizer()

analyzer = Analyzer(char_filters, tokenizer)

meishi=0
josi=0
kigou=0
doushi=0
jodousi=0
fukusi=0
settousi=0
keiyousi=0
renntaisi=0
setuzokusi=0
kandousi=0
fira=0
for token in analyzer.analyze(html):
if token.part_of_speech.split(',')[0] == "名詞":
meishi=meishi+1
elif token.part_of_speech.split(',')[0] =="助詞":
josi =josi+1
elif token.part_of_speech.split(',')[0] =="記号":
kigou=kigou+1
elif token.part_of_speech.split(',')[0] =="動詞":
doushi=doushi+1
elif token.part_of_speech.split(',')[0] =="助動詞":
jodousi=jodousi+1
elif token.part_of_speech.split(',')[0] =="副詞":
fukusi=fukusi+1
elif token.part_of_speech.split(',')[0] =="接頭詞":
settousi=settousi+1
elif token.part_of_speech.split(',')[0] =="形容詞":
keiyousi=keiyousi+1
elif token.part_of_speech.split(',')[0] =="連体詞":
renntaisi=renntaisi+1
elif token.part_of_speech.split(',')[0] =="接続詞":
setuzokusi=setuzokusi+1
elif token.part_of_speech.split(',')[0] =="感動詞":
kandousi=kandousi+1
elif token.part_of_speech.split(',')[0] =="フィラー":
fira=fira+1

print("名詞:"+str(meishi))
print("助詞:"+str(josi))
print("記号:"+str(kigou))
print("動詞:"+str(doushi))
print("助動詞:"+str(jodousi))
print("副詞:"+str(fukusi))
print("接頭詞:"+str(settousi))
print("形容詞:"+str(keiyousi))
print("連体詞:"+str(renntaisi))
print("接続詞:"+str(setuzokusi))
print("感動詞:"+str(kandousi))
print("フィラー:"+str(fira))


実行結果

名詞:58432
助詞:61080
記号:25630
動詞:28901
助動詞:19816
副詞:6576
接頭詞:1519
形容詞:3599
連体詞:2145
接続詞:1398
感動詞:740
フィラー:226

と表示されます

質問者からの補足コメント

  • elif token.part_of_speech.split(',')[0] =="フィラー":
    fira=fira+1
    この文の後に 
    a=["名詞"+str(meishi), "助詞"+str(joshi)・・・str(fira)]
    list.sort(a.reserve=Ture)
    print(a)
    を入れたのですが、降順になりませんでした。
    試しに"名詞"+str()を消して、[meishi、joshi・・・fira]にしたら、降順になりましたが、名詞とか助詞とか漢字を入れたいです

      補足日時:2021/01/17 16:05

A 回答 (6件)

ちなみに、おまけ。


Pythonの辞書型を使えば、もうちょっとシンプルに書ける事は書けるんだけど、ちょっと「特殊なお約束」がいくつか出てくるので、「考え方」としては連想リストと共通してても、煩わしいトコがあったりします。
No.5のコードの部分は辞書型を使えばこうなるでしょう。参考までに。

# ここでは、「空の辞書型」にどうやってキーと値を入れていくのか、そういう例を示す
dict = {}

for token in analyzer.analyze(html):
 key = token.part_of_speech.split(',')[0]
 # Pythonの辞書型は内部に存在しないキーに対してはKeyErrorと言う
 # エラーを返す。
 # これはハッシュテーブルとしてはかなり特異な動作なんで煩わしい。
 # 従って例外機構でトラップを仕掛けてKeyErrorを投げてきた時に
 # そいつを捕まえて辞書型にkeyを新たに作成して値をセットしてる。
 # なお、これはif〜elseに似てる動きだが、if〜elseじゃ実装出来ない。
 try: dict[key] += 1
 except KeyError:
  dict.setdefault(key, 1)

[print("{0}: {1}".format(item[0], item[1])) for item in sorted(dict.items(), key = lambda x: x[1], reverse = True)]

## ここまで

例外処理:
https://docs.python.org/ja/3/c-api/exceptions.html
    • good
    • 0
この回答へのお礼

ありがとうございます!無事できました

お礼日時:2021/01/21 21:38

あれ?



> また回答者の方はそもそもがおかしいとおっしゃっていると思いますが、

「そもそもがおかしい」って何だろ。これの事かな?No.1の

> そもそもデータのまとめ方が

のデータのまとめ方?まとめ方が分からない?連想リストが作れなかった、って事かしらん。

ちょっと一応確認しておきたいんですが、これって元になったプログラム、とかどっかからコピペしてきたモンなんですか?
いや、全然怒ってるわけじゃなくって。もしそうだとしたら

「良い勉強してるな」

って思っただけ、です。

クッソマジメに専門学校やら大学とかで一生懸命教科書読んでプログラミング学んできた層だと、

「基礎が出来てないのに背伸びするな」

的な説教かますでしょうが、逆ですね。良い勉強法だと思いますよ。他人が作ったコードをパクってきて改造する(笑)。NEC PC-6001とかNEC PC-8001とか、パソコン黎明期のある程度金持ってた家のガキどもで、幸いにもそれらの機械を買ってもらえたラッキーなヤツらは、そうやってプログラミングを覚えたそうです。
ワタシャさすがにそういう金持ち層ではなかったんで具体的にはそういう子供時代は過ごしてませんが、当時、パソコン雑誌に紹介されているBASICのプログラムリストを全部手打ちして、見様見真似で改造する。それでBASICとか覚えていった人たちがかつては山ほどいたわけです。
というわけで「パクって改造」。由緒正しい勉強法ですね。今でもそれを推奨してる人達は山ほどいます。

ただまぁ、改造する際に、着目点とか、ちょっとしたコツがあるんですよ。
まず原版のコードのこの辺に注目します。

for token in analyzer.analyze(html):
 if token.part_of_speech.split(',')[0] == "名詞":
  meishi=meishi+1
 elif token.part_of_speech.split(',')[0] =="助詞":
  josi =josi+1
 elif token.part_of_speech.split(',')[0] =="記号":
  kigou=kigou+1
 elif token.part_of_speech.split(',')[0] =="動詞":
  doushi=doushi+1
 elif token.part_of_speech.split(',')[0] =="助動詞":
  jodousi=jodousi+1
 elif token.part_of_speech.split(',')[0] =="副詞":
  fukusi=fukusi+1
 elif token.part_of_speech.split(',')[0] =="接頭詞":
  settousi=settousi+1
 elif token.part_of_speech.split(',')[0] =="形容詞":
  keiyousi=keiyousi+1
 elif token.part_of_speech.split(',')[0] =="連体詞":
  renntaisi=renntaisi+1
 elif token.part_of_speech.split(',')[0] =="接続詞":
  setuzokusi=setuzokusi+1
 elif token.part_of_speech.split(',')[0] =="感動詞":
  kandousi=kandousi+1
 elif token.part_of_speech.split(',')[0] =="フィラー":
  fira=fira+1

まぁ、ハッキリ言えばクッソ無駄なコードです。これもmeishiだjosiだ、って変数をバラバラにしちゃった弊害なんですけどね。
ただ、このコードで着目する構造が一箇所ある。
例えばこれ、ですね。

if token.part_of_speech.split(',')[0] == "名詞":

ここではtoken.part_of_speech.split(',')[0]が返す返り値と文字列の等価性が判定されている。多分この辺コピペしてきたんだろう、とは思うんですが。
問題はどうしてこれが成り立ってるのか、着目すべき部分はそこです。
そして改造する際の「考え方」は以下のようになる。
この部分は、中身が何にせよ、右辺が文字列な以上、左辺も文字列じゃないといけない。分かりますか?データ型が一致してないと、そもそも等価判定は意味を成さない。
そして、右辺が"名詞"、"助詞"、・・・等の具体的な文字列である以上、左辺の返り値も当然、"名詞"、"助詞"、・・・って言う同じ具体的な文字列じゃないとならない。そこに絶対的な信用があるわけですよ。
決して左辺は、例えば"SEX"とか"日本の女の子とSEXしたい人はコチラ♥->"なんて無意味な文字列を返すわけがない。そもそもjanomeはそういうツールではない。
そうすると、最初に連想リスト

a = [["名詞", 0], ["助詞", 0], ["記号", 0], ["動詞", 0], ["助動詞", 0], ["副詞", 0], ["接頭詞", 0], ["形容詞", 0], ["連体詞", 0], ["接続詞", 0], ["感動詞", 0], ["フィラー", 0]]

を定義して、各要素の第0要素、つまりタグをチェックして、token.part_of_speech.split(',')[0]の返り値と一致する場合は第1要素に加算、つまり+1していく、って事をやれば自然と連想リストaはデータを蓄えていけるわけです。なんも別にmeishiやらjosiやらバラバラに変数を持っておく必要がない。それもこれも、token.part_of_speech.split(',')[0]の返り値が「特定の文字列である」と言う保証が成り立つから出来るわけです。
つまり、件の長ったらしい場所を基本的にはこう書き換える。

a = [["名詞", 0], ["助詞", 0], ["記号", 0], ["動詞", 0], ["助動詞", 0], ["副詞", 0], ["接頭詞", 0], ["形容詞", 0], ["連体詞", 0], ["接続詞", 0], ["感動詞", 0], ["フィラー", 0]]

for token in analyzer.analyze(html):
 for elm in a:
  if token.part_of_speech.split(',')[0] == elm[0]:
   elm[1] += 1

そうすれば、40行近い部分をたった6行程度で置き換えられるのです。
まず、

for token in analyzer.analyze(html):

はanalyzer.analyze(html)の結果からデータ、要するにトークン(字句: token)を一個一個引っ張りだしてくる。ここに品詞情報がまとめられてるわけですね。
そして内側のループで、各トークン毎に、

for elm in a:

としてる。連想リストから要素を引っこ抜いてきてるわけです(ここがelm)。["名詞", 0]とか ["助詞", 0]とかですね。これをリストの先頭から順繰りに引っ張り出す。
そしてそれらのタグ(第0要素)、とトークンにある「品詞」の文字列、つまりtoken.part_of_speech.split(',')[0]が等しいかどうか検査する。同じ場合は第1要素に1を足す。そうじゃなかったら何もしない、と。そういうコードを書けば良いので、

if token.part_of_speech.split(',')[0] == elm[0]:
 elm[1] += 1

で終了、と。それだけでanalyzerが走査したデータは分類されて連想リストに蓄積されていきます。言われてみれば簡単でしょ?
ただし、注意点、と言うかtipsが1つ。
外側の

for token in analyzer.analyze(html):

ってのは1個1個tokenを持ってくるわけです。与えられる情報はいきなり複数にはならない。当然一度に一つ、ですね。まずはそこを押さえる。
ここで例えばtokenが持ってる情報がいきなり名詞だったとしましょう。それはaの先頭にあるからいきなりビンゴです。aの第0要素は["名詞", 1]になる。
めでたしめでたし・・・なんだけど、内側のfor elm in a:は依然と連想リストaのケツに向かって走査します。今与えられたtokenは既に名詞だって分かってるし、「一度に与えられるtokenは一個」により以降の走査は無駄になるんですよ。
必要なのは「内側のループは、品詞が確定したと同時に抜け出した方が効率的だ」と言う考え方で、そのための実装法です。無駄な走査を排除さえすれば効率が良くなる。当たり前ですね。
こういう場合に使うのが、breakです。

for token in analyzer.analyze(html):
 for elm in a:
  if token.part_of_speech.split(',')[0] == elm[0]:
   elm[1] += 1
   break

これでタグと品詞名が一致すれば、データを記録して内側のループを脱出、次のtokenを即座に引っこ抜いてくれます。
    • good
    • 0

> 私が今したいのは、表示結果を降順にする。

そのために、printで表示しているところを改善、変更する必要があると考えています。

O.K.。うん。分かった。
でもその考え方が間違ってるんですよ(笑)。

ええとね、つまりこういう事です。

> 表示結果を降順にする。

これが間違ってる。「表示結果を」降順にする必要はない。
必要なのは「データを」降順にする事です。
データを降順に出来れば表示結果は表示するだけ、なんで自然と降順になってる。
違いが分かりますか?
根本的に、「表示をなんとかしよう」ってのが間違ってる、って言ってるわけです。
なんとかするのはデータの方だ、って事です。
ここを考え違いすればよくない。

もう一度いいます。

データさえなんとかすれば自然と表示はなんとかなるんだ。

と。

ひょっとしてC言語やった事あるのかもしれませんが、あの言語を最初に勉強するとマズい最大の理由の一つは、

「表示で全てなんとかしよう」

とする辺りなんですね。それじゃあダメなんですよ。
プログラム対象ってのは極端な話、データです。データ設計に始まり、データ弄りで終わる。あらゆるプログラムは基本的には

「データをどう加工/フィルタリングするのか」

に終始します。表示は「結果を見せる」だけなんでそこまでこだわる必要はない、のです。
C言語なんかの場合、そもそも表示機能がクソみてぇなしょぼい貧弱な機能しかない事もあり、プログラミング初心者は

「どうやって表示するか」

だけに腐心します。初心者がやってるこたぁ、如何にprintfに思い通りの表示をさせるか、に終始するんですよ。全然プログラミングの本懐とカンケーない。
もし、Cの経験がちょっとでもあるのならPythonを使う以上その辺は忘れて下さい。そして念仏のように次の真言を唱える。

「表示は大事じゃないんだ」

と。
そもそも、インタプリタで開発する以上、その辺は如何ようにも後で弄くれますから。

我々が今手にしたデータは、結果次の通りです。

a = [["名詞", 58432], ["助詞", 61080], ["記号", 25630], ["動詞", 28901], ["助動詞", 19816], ["副詞", 6576], ["接頭詞", 1519], ["形容詞", 3599], ["連体詞", 2145], ["接続詞", 1398], ["感動詞", 740], ["フィラー", 226]]

そもそもこれを端末上に表示する場合、別にこれだけで事足ります。

>>> print(a)
[['名詞', 58432], ['助詞', 61080], ['記号', 25630], ['動詞', 28901], ['助動詞', 19816], ['副詞', 6576], ['接頭詞', 1519], ['形容詞', 3599], ['連体詞', 2145], ['接続詞', 1398], ['感動詞', 740], ['フィラー', 226]]
>>>

C言語なんかは「配列を印字する機能さえない」んで、こういう風にリストをそのまま印字する事さえ出来ない。だからC言語初心者はfor文とprintfと格闘する必要が出てくるんですが、Pythonだとそんな必要はない。インタプリタ上で、この場合、aって打つだけでも中身は見れますからね。
この場合、連想リストaの第1要素を利用して降順にソートするなら

sorted(a, key = itemgetter(1), reverse = True)

だけで充分です(要”"from operator import itemgetter")。

>>> sorted(a, key = itemgetter(1), reverse = True)
[['助詞', 61080], ['名詞', 58432], ['動詞', 28901], ['記号', 25630], ['助動詞', 19816], ['副詞', 6576], ['形容詞', 3599], ['連体詞', 2145], ['接頭詞', 1519], ['接続詞', 1398], ['感動詞', 740], ['フィラー', 226]]
>>>

降順に並んでますね。printするならこれだけで良い。

>>> print(sorted(a, key = itemgetter(1), reverse = True))
[['助詞', 61080], ['名詞', 58432], ['動詞', 28901], ['記号', 25630], ['助動詞', 19816], ['副詞', 6576], ['形容詞', 3599], ['連体詞', 2145], ['接頭詞', 1519], ['接続詞', 1398], ['感動詞', 740], ['フィラー', 226]]
>>>

これで必要充分なんですよ。
問題は。
これじゃあカッコ悪い、とか(笑)。もうちょっと見やすくしたいな、ってだけの話でしょ?
要するに「プログラミング」と言うトピックで考えるとあまり本質的じゃない「おまけの」部分なんです。「余力があればなんとかしたい」ってだけの部分ですよね。すでに降順にソーティングする事自体は終わってるわけですから。
そういう「キレイにした出力」を整形出力、と言います。英語ではformatted printとか呼んでて、C言語のprintfのfはこのformattedの意、ですね。言い換えるとC言語には整形出力しかない、って事でもあるんですが。

Python3.xの整形出力の基本は、Python2.x時代と違い、C言語のprintf的な「書式指定」とは随分離れたカタチがデフォルトになりました。一々書式指定しなくても任意の要素を埋め込んでくれる。
例えば。

print("{0} {1}!".format("Fuck", "you"))

とすれば

Fuck you!

と印字してくれます。文字列のメソッドにformatと言うのがあって、これは可変長引数なんですが、0番目、1番目、・・・の要素を{0}、{1}、・・・に埋め込んでくれます。非常に便利なんですね。

print("{0}は{1}なり。".format("朕", "国家"))

>>> print("{0}は{1}なり。".format("朕", "国家"))
朕は国家なり。
>>> print("{0}なんかに付き合ってあげる{1}、{2}くらいしかいないんだからね!".format("あんた", "物好き", "私"))
あんたなんかに付き合ってあげる物好き、私くらいしかいないんだからね!
>>>

初見じゃビビるかもしれないでしょうが、まぁ、色々と試してみて下さい。見た目に反してかなり「使える」機能です。

さて、そうすると。
今やりたい整形出力は

<品詞名>: <その総数>

って事ですね。これだけで、

"{0}: {1}".format(何とやら)

ってカタチがprintの引数に突っ込まれる、ってのが分かっています。
今、もう一回見ますが、aと言うデータは

a = [["名詞", 58432], ["助詞", 61080], ["記号", 25630], ["動詞", 28901], ["助動詞", 19816], ["副詞", 6576], ["接頭詞", 1519], ["形容詞", 3599], ["連体詞", 2145], ["接続詞", 1398], ["感動詞", 740], ["フィラー", 226]]

なんで、基本的にはリストの要素を取ってきては、その第0要素が{0}に、第1要素が{1}に突っ込まれれば、例えば

名詞: 58432

と出力されるのが分かります。要するに、aを舐めていけば良いのです。あー、ちなみに、「リストを舐める」と言うのはリストのアタマから最後まで順繰りになんかやっていく、って言う古い言い回しですね。要はforループにすれば良い、って事なんですが。
Pythonの場合だと、材料がリスト相手なら、リスト内包表記を使った方が早い、です。

Pythonリスト内包表記の使い方:
https://note.nkmk.me/python-list-comprehension/

ハッキリいいますが、Pythonでリスト内包表記はmustです。Python始めたばっかりなら尚更の事、リスト内包表記に慣れる事。forループ使う前に「まずはリスト内包表記で書けないか?」考えるようにしましょう。80%くらいはリスト内包表記で解決する事が多いです。
つまり、

[print("{0}: {1}".format(item[0], item[1])) for item in sorted(a, key = itemgetter(1), reverse = True)]

ってのはリスト内包表記で整形出力せよ、って言ってるわけです。

for item

のitemってのはソート済みリストから順繰りに要素を抜いてきてる、って事です。例えば最初のブツはリスト['助詞', 61080]になるのが分かるでしょう。
従って、item[0]は'助詞'、item[1]は61080となって、そのまま{0}と{1}に埋め込まれて、結果その部分は、

助詞: 61080

として出力される、と。
簡単ですね。
    • good
    • 0

> よろしければ、補足の質問にもお答えいただきたいです。



うーん、何をやろうとしてんのかな。
そもそも、データさえ並び替えれれば出力なんざどーでも良いのです(笑)。いや、マジで。
整形出力したいんでしょ?だったら、下手にデータを「文字列にする」とか余計な事をしない方がいい。じゃないと、UTF-8(文字コード)の順に並び替えられてしまって、数値の大小は無視されます。
正しくは・・・多分こういう事がしてぇんじゃねぇかな。
aが完成した連想リストとして、

from operator import itemgetter

したあと、

[print("{0}: {1}".format(item[0], item[1])) for item in sorted(a, key = itemgetter(1), reverse = True)]

って事じゃねぇかな。
    • good
    • 0
この回答へのお礼

お答えしていただきありがとうございます。大変申し訳ないのですが、あまり理解できませんでした。私が今したいのは、表示結果を降順にする。そのために、printで表示しているところを改善、変更する必要があると考えています。
また回答者の方はそもそもがおかしいとおっしゃっていると思いますが、根本的に変える場合だと、どのような手順で、どこをどのように改善したらいいのか、細かく教えていただきたいです。自分の力不足で申し訳ございません。

お礼日時:2021/01/17 17:25

> リストを使って、変数宣言をする方法ってあるんですか?



例えば、ですね。
変数宣言、って意味だと

ls = []

になっちゃうんだけど、こういう手があります。

ls = [["名詞", 0], ["助詞", 0], ["記号", 0], ["動詞", 0], ["助動詞", 0], ["副詞", 0], ["接頭詞", 0], ["形容詞", 0], ["連体詞", 0], ["接続詞", 0], ["感動詞", 0], ["フィラー", 0]]

こういうカタチで二次元リストにして、要素の第0要素をタグとして利用する。そうすれば、要素の第1要素をキーとしてソーティングをぶちかます事が可能ですね(ちなみに、こういうデータ構築法を「連想リスト」と呼んだりします)。

もっと直球的な方法はPythonの辞書型を使う手がありますね。多分それが好きな人の方が多いでしょう。

【Python入門】dictionary(辞書)の使い方。基本と応用:
https://www.sejuku.net/blog/24122

実は根本的な発想は辞書型も連想リストも同じなんですが、「検索」って意味では辞書型の方が高速な動作が可能です(コンピュータ・プログラミングの専門用語では「ハッシュテーブル」と呼びます)。
リストは線形操作、って言うと難しいんですが、要は「リストの先頭から順繰りに手繰っていく」ので、リストがデカいケースだとリストのケツに到達するのに時間がかかるんですね。
一方、ハッシュテーブルはハッシュ法と言うテクニックを用いて、どの要素にアクセスするにせよ時間がかからない、と言った特徴があります。ただ、順不同なんで、「どういう順番でデータが置かれるのか」ってのが分からん。直接ソーティング対象にするにはちと向かないデータ構造です(だからソートする前に一工夫必要になるでしょう)。
ただまぁ、12要素程度だったらリストでも充分かな、とか思いますが。
どっちも実装してみて確かめて見るのが一番でしょう。
    • good
    • 0
この回答へのお礼

ありがとうございます!是非試させていただきます。よろしければ、補足の質問にもお答えいただきたいです。

お礼日時:2021/01/17 16:23

> この表示結果を降順にするプログラムを知りたいです。



表示結果がどうの、ってのは最終的な問題で、そもそもデータのまとめ方が間違っています。

meishi=0
josi=0
kigou=0
doushi=0
jodousi=0
fukusi=0
settousi=0
keiyousi=0
renntaisi=0
setuzokusi=0
kandousi=0
fira=0

この辺が全然ダメ、です。
最低でもリストを使って、「並べ替えれる」状態にすべきですね。

[Python入門]リストの基本 (1/4):
https://www.atmarkit.co.jp/ait/articles/1905/31/ …

リスト等でデータを1つにまとめたら、あとはソートを使って並べ替えれば良い。

ソート HOW TO:
https://docs.python.org/ja/3/howto/sorting.html

そこまで終わればあとは出力はどーにでもなります。
    • good
    • 0
この回答へのお礼

リストを使って、変数宣言をする方法ってあるんですか?

お礼日時:2021/01/17 15:57

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!