- 投稿日:2020-05-18T23:43:37+09:00
初学者向け Python 練習問題 #1 [基本的なデータ型・if 文]
この問題集は、社内の企画職やマーケティング職の方向けに作成しました。
毎回10問程度出題しています。分からない問題があるときには、「必要な知識」でググると出てくるはずです。上手にググることは、上達の一番のコツだと思います。最初は難しいですが、粘り強く勉強していきましょう!!
目標
- 四則演算ができるようになる
- 異なるデータ型の扱いに慣れる
- if文で簡単な条件分岐ができるようになる
レベルについて
★
とてもよく使います。できるようにしましょう。★★
少し複雑な処理をするときに使います。★★★
よく勉強できていますね。Python初心者を卒業できそうです。★★★★
トリッキーですが Python ではよく使います。覚えておきましょう。★★★★★
おぉ。業務でもPython利用できそうですね!さすがです。
出題内容
Python チュートリアル
3. An Informal Introduction to Python
https://docs.python.org/ja/3/tutorial/introduction.html
4.4. More Control Flow Tools
https://docs.python.org/ja/3/tutorial/controlflow.html
- 基本的なデータ型
- int
- float
- string
- bool
- list # 後で詳しく扱います
- tuple # 後で詳しく扱います
- dict # 後で詳しく扱います
- set # 後で詳しく扱います
- if 文
問題1
レベル ★
必要な知識 数値の演算/インタープリタでの実行対話型インタープリターで 1 + 2 3 - 4 2 * 5 8 / 2 を実行したときの結果を予測し、実際に入力して確かめてください。問題2
レベル ★
必要な知識 数値の演算/インタープリタでの実行対話型インタープリタで、7 を 3 で割った商とあまりを求めてください。 ただし、商とあまりは整数とします。問題3
レベル ★
必要な知識 数値の演算/コマンドラインからの実行/変数への格納/数値の出力以下の問題は otsukai.py ファイルを作成しコマンドラインから実行してください。 あるサイトの累計ユーザー数は50,000人です。 今月は3,000人がサイトを訪れました。 この月のユーザー全体の人数に対する訪問人数の割合はどれくらいですか? 来月もこのスクリプトを使いたいので、数値は変数に格納しましょう。
問題4
レベル ★
必要な知識 文字列/文字列の結合/文字列の出力以下の問題は aisatsu.py ファイルを作成しコマンドラインから実行してください。 あなたはWebサイトを作成しています。 name にはユーザーの名前が、message には挨拶が入力されています。 sentence 変数に、挨拶と名前を結合した文章を格納し、出力してください。sample.pyname = 'taro' message = 'こんにちは' # 以下で sentence に 'こんにちは taro' となるように格納してください。 # 格納できたら sentence を出力してください問題5
レベル ★
必要な知識 文字列の繰り返し以下の問題は cry.py ファイルを作成しコマンドラインから実行してください。 「く」 を50文字、「そ」 を 25 文字、「ぉ」 を 10 文字連結して出力してください。 (出力例) くくくく....くくくくくそそ.....そそそぉぉぉぉぉぉぉぉぉぉ
問題6
レベル ★
必要な知識 真偽値以下の出力結果を予測しコマンドラインで実行して確かめてください。 2 < 2 2 <= 2 3 > 1 0 == 0 0 != 0 1 is 2 3 is not -3 '3' is 3 2020 > 2019 and 2019 > 2018 2020 > 2019 or 2021 < 2020 True or False or True and False問題7
レベル ★
必要な知識 配列へのアクセスこれ以降の問題は適切な名前のファイルを作成し、コマンドラインから実行してください。 ユーザーの名前と年齢のデータが配列に格納されています。 この配列の中から「tadokoroさん」のデータを出力してください。sample.pydata = ['kobayashi 23', 'tanaka 53', 'tadokoro 24'] # data のなかから tadokoro さんのデータを出力してください
問題8
レベル ★
必要な知識 if 文あなたは信号機をつくっています。 signal が 'red' なら 'stop'を 'yellow' なら 'caution!' を、 'blue' なら 'GOGO!' と出力してください。sample.pysignal = 'red' # これは 'yellow' になるかもしれないし 'blue' になるかもしれません # 以下でif文を利用して条件分岐してください。問題9
レベル ★★
必要な知識 文字列の操作/文字列へのスライス/文字列の長さあなたは動画のサムネイルの下に説明文を追加しようと考えています。 しかし、文章が長すぎると収まらないので 20 文字より大きいの場合は19文字で切って、 '...' を付け加える処理をしようと考えています。 20文字以下の場合はそのまま出力してください。sample.pysentence = 'これは、サンプルのセンテンスです。20文字以上の場合には長すぎて収まらないのでいい感じに収めていただけると助かります。' # 以下でsentence の長さによって出力を分けてください問題10
レベル ★★★
必要な知識 配列の長さ/配列へのアクセスrank リストには、今年の販売業績が高い順に名前が入っています。 以下のデータを抽出してください。 (1) もっとも業績の良い3人 (2) 業績の良さが奇数番目の人 (3) もっとも業績の悪い3人 (4) 真ん中の業績の人 (ただし総人数が偶数nの時は、n/2 番目の人と n/2 - 1 番目の人も出力してください)sample.pyrank = ['tanaka', 'sasaki', 'satou', 'simizu', 'koizumi', 'yoshioka', 'tamaru', 'kiyomiya'] # 以下で (1) - (4) を出力してください。 # また、(4) については、rank リストから一人減らしても 、正しく出力されているかを確認してください。さていかがだったでしょうか。
実力をつけるにはアウトプットが重要です。できなくてもあきらめずに取り組んでいきましょう!
次回は今回までの知識をベースに for 文と while 文について扱っていきます。
- 投稿日:2020-05-18T23:41:20+09:00
gzip 圧縮されたテキストファイルを書き出す
こんにちは。
Python で gzip 圧縮されたテキストファイルを書き出してみました。中身は CSV データです。"Using csv.DictWriter to output an in-memory gzipped csv file?" (Stack Overflow) を参考にしました。$ ./write_csv_gzfile.py temp.csv.gz $ gzip -dc temp.csv.gz a,b 1,2 3,4write_csv_gzfile.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- import io, csv, gzip, sys from pathlib import Path buffer = io.BytesIO() with gzip.GzipFile(fileobj=buffer, mode='wb') as compressed: with io.TextIOWrapper(compressed, encoding='utf-8', newline='\n') as wrapper: writer = csv.DictWriter(wrapper, ["a", "b"], lineterminator='\n') writer.writeheader() writer.writerows([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]) p = Path(sys.argv[1]) p.write_bytes(buffer.getvalue())
- 投稿日:2020-05-18T23:41:00+09:00
クラスDiscordに課題管理Botを導入した話
こんばんは。りーぜんとです。
題名の通りです。経緯↓
Teams絶対課題見逃すから課題管理用discordBOTつくる
— りーぜんと (@50m_regent) May 13, 2020流行りのオンライン授業が弊学でも始まりました。草ですね。
もはやオンライン授業の代名詞となっているZoomではなくMicrosoftのTeamsというものを使って授業が行われています。このTeams、学生側は課題が絶望的に管理しづらいです。(個人の意見)
じゃあクラスのDiscordに管理用Bot作って入れれば良いじゃん。(名案)
他の高校生たちがクラスDiscordを運用してるかは知りませんが、うちはあるので活用するまでです。
要は、この記事ではPythonを使ってdiscordのbotを作る方法を説明します。
作成したコードはGitHubに載っているので参考にしてみてください。目次
Discord上でBotアプリを作る
新しいBotをDiscordに登録します。Discord Developer Portalにアクセスします。
右上のNew Applicationをクリックして好きな名前(Botの名前になります)で新しいアプリケーションを作成しましょう。
次に左のタブのBotからAdd Botします。
Usernameの下にTokenというところがあるのでCopyしてどこかに保存しておきましょう。
今度はOAuth2タブに移動します。Scopesの中のbotにチェックマークを入れて、Bot PermissionのSend messagesとManage messagesにチェックを入れます。
出てきたリンクを開いてBotを自分が参加しているDiscordサーバーに追加することができます。
Discordを開いて確認してみましょう。とりあえず接続してみる
今回はPythonを使ってコードを書きます。discord.pyを使うので各自使えるようにしておいてください。
とりあえずメッセージに反応するようにします。
main.pyimport discord TOKEN = '***' client = discord.Client() @client.event async def on_message(message): if message.content == 'hello': await message.channel.send('hi') client.run(TOKEN)TOKENはDiscord上でBotアプリを作るでコピーした文字列に置き換えてください。
かわいいですね。
client.run()でBotを実行してます。
on_message()は何かメッセージが送信されたときに実行されます。送られてきた内容がhelloだったときにhiと返すだけのBotです。
次はよくある!pのようなコマンドを実装してみます。コマンドを追加してみる
コマンドの数だけif文はセンスがないのでコマンドリストを作っておいてそこからコマンドを読むようにしましょう。
main.pyimport discord assignment_list = [] client = discord.Client() async def kadaihelp(message): string = 'コマンドリスト\n' for command in COMMANDS: string += '------------------------\n' string += '{}: {}\n'.format('!' + command, COMMANDS[command]['description']) string += ' 使い方: {}\n'.format(COMMANDS[command]['use']) string += ' 省略形: {}\n'.format(COMMANDS[command]['alias']) string += '------------------------' await message.channel.send(string) async def newkadai(message): msg = message.content.split(' ') try: title, deadline, memo = msg[1:] assignment_list.append({ 'title': title, 'deadline': deadline, 'memo': memo }) await message.channel.send('課題を追加しました!') except: await message.channel.send('入力形式が間違っています。') async def deletekadai(message): msg = message.content.split(' ') for i in range(len(assignment_list)): if assignment_list[i]['title'] == msg[1]: assignment_list.pop(i) await message.channel.send('課題を削除しました') async def kadailist(message): string = '課題一覧\n' for i, assignment in enumerate(assignment_list): string += '------------------------\n' string += '{}. {}\n'.format(i + 1, assignment['title']) string += '締切: {}\n'.format(assignment['deadline']) string += '備考: {}\n'.format(assignment['memo']) string += '------------------------\n' string += '現在、{}個の課題が出されています。'.format(len(assignment_list)) await message.channel.send(string) async def close(message): await client.close() TOKEN = '***' COMMANDS = { 'kadaihelp': { 'description': 'このリストを表示します。', 'use': '!kadaihelp', 'alias': '!kh', 'func': kadaihelp }, 'newkadai': { 'description': '新しい課題を追加します。', 'use': '!newkadai \{タイトル\} \{締切\} \{備考\}', 'alias': '!nk', 'func': newkadai }, 'deletekadai': { 'description': '課題削除', 'use': '!deletekadai \{課題名\}', 'alias': '!dk', 'func': deletekadai }, 'kadailist': { 'description': '登録されている課題一覧を表示します。', 'use': '!kadailist', 'alias': '!kl', 'func': kadailist }, 'exit': { 'description': 'Botを終了します。', 'use': '!exit', 'alias': '!ex', 'func': close } } @client.event async def on_ready(): print('KadaiShosu起動') @client.event async def on_message(message): msg = message.content.split(' ') if message.author.bot: return for command in COMMANDS: if msg[0] in ['!' + command, COMMANDS[command]['alias']]: await COMMANDS[command]['func'](message) client.run(TOKEN)とりあえず5つコマンドを追加してみました。
COMMANDSの中に辞書形式でコマンドリストを作成しました。on_message()の中で全てのコマンドに対して呼び出されたかどうかを判別して実際にその動作を行う関数を実行します。それぞれのコマンドに省略形としてaliasを用意しました。もっと良い書き方があるかもしれません。是非いろいろ試してみてください。
ここまででとりあえず課題管理Botとしては機能します。しかし、このままではプログラムを閉じたときに課題がリセットされてしまいます。
最後に変数を保存、読み込む部分だけ書いてみます。main.pyimport pickle async def close(message): pickle.dump(assignment_list, open('assignments.pkl', 'wb')) await message.channel.send('Bye^^') await client.close() def load_assignments(): global assignment_list assignment_list = pickle.load(open('assignments.pkl', 'rb')) load_assignments()新しくpickleというモジュールを使います。このモジュールは変数の保存を実現してくれます。
close()は書き換えて、他は書き足してください。
!exitでBotを終了したときにassignments_listが保存されて、起動時に読み込まれます。まとめ
今回はここまでとします。他にもいろいろな機能があると嬉しいと思うので、自分で実装してみてください。
よければTwitterフォローしてください。じゃあね。
- 投稿日:2020-05-18T23:25:56+09:00
オフラインで Python 環境構築
インターネットに接続していない端末に Python パッケージをインストールしたいときのやり方メモ。
インストールしたいパッケージの一覧は
requirements.txt
にまとまっているものとする。Windows 10 (x86_64) + Anaconda3 環境で検証したが、他のプラットフォームでも同様にできる気がする。
オンライン端末でパッケージをダウンロードする
$ pip download --dest=src -r requirements.txtこのオンライン端末はオフライン端末 (インストール先) とプラットフォームが同じものを使う。
pip download
には--platform
というオプションがあるので、これをうまく指定すると異なるプラットフォームでもできるのかもしれないが未検証。
参考: pip download — pip documentation
src
ディレクトリを見ると、パッケージが wheel や tarball の形式でダウンロードできていることがわかる。オフライン端末でパッケージをインストールする
オンライン端末でダウンロードしたパッケージファイルをオフライン端末にコピーし、
src
ディレクトリ以下に置いてあるものとする。$ pip install --no-index --find-links=src -r requirements.txtこれで
requirements.txt
をもとにパッケージのインストールを試みるが、PyPI (インターネット) からではなくsrc
ディレクトリにあるパッケージを探しに行くようになる。
- 投稿日:2020-05-18T23:07:08+09:00
【Python】【BFS】AtCoder Beginner Contest 168-Dを解く
問題文 .. (Double Dots)
あるところに、洞窟があります。
洞窟にはN個の部屋とM本の通路があり、部屋には1からNの、通路には1からMの番号がついています。
通路iは部屋Aiと部屋Biを双方向につないでいます。どの2部屋間も、通路をいくつか通って行き来できます。
部屋1は洞窟の入り口がある特別な部屋です。
洞窟の中は薄暗いので、部屋1以外の各部屋に1つずつ道しるべを設けることにしました。
各部屋の道しるべは、その部屋と通路で直接つながっている部屋の1つを指すように置きます。
洞窟の中は危険なので、部屋1以外のどの部屋についても以下の条件を満たすことが目標です。
その部屋から出発し、「いまいる部屋にある道しるべを見て、それが指す部屋に移動する」ことを繰り返すと、
部屋1に最小の移動回数でたどり着く。
目標を達成できる道しるべの配置が存在するか判定し、存在するならばそのような配置を1つ出力してください。解答
from collections import deque mi = lambda:list(map(int,input().split())) mix = lambda x:list(mi() for _ in range(x)) ########## def main(n,m,l): #グラフの作成 g = creategraph(n,l) #独立した部屋がある場合はNG for i in range(1,len(g)): if len(g[i]) == 0: print("No") return #BFSで道標を作成 navi = bfs(n,g) #部屋1を除く部屋の道標が更新されていなければNG if min(navi[2:]) == 0: print("No") else: print("Yes") for i in navi[2:]: print(i) #BFS幅優先探索 def bfs(n,g): navi = [0]*(n+1) navi[1] = -1 q = deque() q.append(1) while len(q) > 0: #キューから現在地を取り出す cp = q.popleft() #現在地の隣接点リストを取得 np = g[cp] #隣接点が未探索ならキューに入れる+道標更新 for i in np: if navi[i] == 0: navi[i] = cp q.append(i) return navi #各Nodeの隣接点を保持するリストを生成する def creategraph(n,l): g = {i:[] for i in range(n+1)} for a,b in l: g[a].append(b) g[b].append(a) return g def resolve(): n,m = mi() l = mix(m) main(n,m,l) if __name__ == "__main__": resolve()
- 投稿日:2020-05-18T23:07:08+09:00
【Python】【BFS】AtCoder Beginner Contest 168-D [.. Double Dots]
※競技プログラミングででてくるアルゴリズムの実装方法をまとめる自分用備忘録です。
問題文 (.. Double Dots)
あるところに、洞窟があります。
洞窟にはN個の部屋とM本の通路があり、部屋には1からNの、通路には1からMの番号がついています。
通路iは部屋Aiと部屋Biを双方向につないでいます。どの2部屋間も、通路をいくつか通って行き来できます。
部屋1は洞窟の入り口がある特別な部屋です。
洞窟の中は薄暗いので、部屋1以外の各部屋に1つずつ道しるべを設けることにしました。
各部屋の道しるべは、その部屋と通路で直接つながっている部屋の1つを指すように置きます。
洞窟の中は危険なので、部屋1以外のどの部屋についても以下の条件を満たすことが目標です。
その部屋から出発し、「いまいる部屋にある道しるべを見て、それが指す部屋に移動する」ことを繰り返すと、
部屋1に最小の移動回数でたどり着く。
目標を達成できる道しるべの配置が存在するか判定し、存在するならばそのような配置を1つ出力してください。解答
from collections import deque mi = lambda:list(map(int,input().split())) mix = lambda x:list(mi() for _ in range(x)) ########## def main(n,m,l): #グラフの作成 g = creategraph(n,l) #独立した部屋がある場合はNG for i in range(1,len(g)): if len(g[i]) == 0: print("No") return #BFSで道標を作成 navi = bfs(n,g) #部屋1を除く部屋の道標が更新されていなければNG if min(navi[2:]) == 0: print("No") else: print("Yes") for i in navi[2:]: print(i) #BFS幅優先探索 def bfs(n,g): navi = [0]*(n+1) navi[1] = -1 q = deque() q.append(1) while len(q) > 0: #キューから現在地を取り出す cp = q.popleft() #現在地の隣接点リストを取得 np = g[cp] #隣接点が未探索ならキューに入れる+道標更新 for i in np: if navi[i] == 0: navi[i] = cp q.append(i) return navi #各Nodeの隣接点を保持するリストを生成する def creategraph(n,l): g = {i:[] for i in range(n+1)} for a,b in l: g[a].append(b) g[b].append(a) return g def resolve(): n,m = mi() l = mix(m) main(n,m,l) if __name__ == "__main__": resolve()
- 投稿日:2020-05-18T23:05:14+09:00
Python入門 開発環境を整えよう
以下のサイトの手順で行いました。
https://prog-8.com/docs/python-env③pyenvのインストールでコマンドを実行する際に
~/.bash_profile に権限(ロック)がかかっていたためコマンドを認識してくれませんでした。以下を実行してからコマンドを実行したところ正しくインストールできました。1.ルートフォルダを表示する
command + shift + G
2.隠しファイルを表示する
command + shift + .(コマンド + シフト + ピリオド)
3.bash_profileをクリックし、「情報を見る」からロックを解除
- 投稿日:2020-05-18T22:18:28+09:00
深さ優先探索(DFS)と幅優先探索(BFS)をpythonで実装する
やったこと
AtCoder Typical Contest 001 の A - 深さ優先探索 をpythonで実装してみた。
この問題は、深さ優先探索(DFS: Depth First Search)でも幅優先探索(BFS: Breadth First Search)でも解くことができるため、両方実装した。
また、深さ優先探索については、再帰関数を用いるパターンとスタック(LIFO)を用いるパターンでそれぞれ実装した。DFS
再帰関数を用いるケース
# A 深さ優先探索 再帰関数 import sys sys.setrecursionlimit(10**7) #再帰関数の呼び出し制限 def dfs(v_x, v_y, seen_list, c_list): if v_x < 0 or v_y < 0 or v_x >= w or v_y >= h: return if seen_list[v_y][v_x]: return if c_list[v_y][v_x] == "#": return seen_list[v_y][v_x] = True dfs(v_x+1,v_y,seen_list,c_list) dfs(v_x-1,v_y,seen_list,c_list) dfs(v_x,v_y+1,seen_list,c_list) dfs(v_x,v_y-1,seen_list,c_list) h, w = map(int, input().split()) seen_list = [[False] * w for i in range(h)] c_list = [] s = [] g = [] for i in range(h): _tmp = list(input()) if "s" in _tmp: s = [_tmp.index("s"),i] if "g" in _tmp: g = [_tmp.index("g"),i] c_list.append(_tmp) dfs(s[0],s[1],seen_list,c_list) if seen_list[g[1]][g[0]]: print("Yes") else: print("No")スタックを用いるケース
# A 深さ優先探索 スタック(LIFO) from collections import deque def dfs(stack, seen_list, c_list): while len(stack)>0: v_x, v_y = stack.pop() if v_x < 0 or v_y < 0 or v_x >= w or v_y >= h: continue if seen_list[v_y][v_x]: continue if c_list[v_y][v_x] == "#": continue seen_list[v_y][v_x] = True stack.append([v_x+1,v_y]) stack.append([v_x-1,v_y]) stack.append([v_x,v_y+1]) stack.append([v_x,v_y-1]) h, w = map(int, input().split()) seen_list = [[False] * w for i in range(h)] c_list = [] s = [] g = [] stack = deque() for i in range(h): _tmp = list(input()) if "s" in _tmp: s = [_tmp.index("s"),i] if "g" in _tmp: g = [_tmp.index("g"),i] c_list.append(_tmp) stack.append(s) dfs(stack,seen_list,c_list) if seen_list[g[1]][g[0]]: print("Yes") else: print("No")BFS
# A 深さ優先探索 幅優先探索で解く キュー(FIFO) from collections import deque def bfs(que, seen_list, c_list): while len(que)>0: v_x, v_y = que.pop() if v_x < 0 or v_y < 0 or v_x >= w or v_y >= h: continue if seen_list[v_y][v_x]: continue if c_list[v_y][v_x] == "#": continue seen_list[v_y][v_x] = True que.appendleft([v_x+1,v_y]) que.appendleft([v_x-1,v_y]) que.appendleft([v_x,v_y+1]) que.appendleft([v_x,v_y-1]) h, w = map(int, input().split()) seen_list = [[False] * w for i in range(h)] c_list = [] s = [] g = [] que = deque() for i in range(h): _tmp = list(input()) if "s" in _tmp: s = [_tmp.index("s"),i] if "g" in _tmp: g = [_tmp.index("g"),i] c_list.append(_tmp) que.append(s) bfs(que,seen_list,c_list) if seen_list[g[1]][g[0]]: print("Yes") else: print("No")
- 投稿日:2020-05-18T22:17:03+09:00
Docker内でMySQLにPythonで接続する
DocekrもMySQLも初心者用のメモ
github url: https://github.com/kenjiSpecial/docker_mysql_python_beginner
docker-compose.yml
docker-compose.ymlversion: "3" services: mysql_db: container_name: "mysql_db" image: mysql:5.7 command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci volumes: - db_volume:/var/lib/mysql environment: # Set up mysql database name and password MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: employees MYSQL_USER: user MYSQL_PASSWORD: password networks: - app-tier python3: restart: always build: ./python container_name: "python3" working_dir: "/root/" tty: true depends_on: - mysql_db networks: - app-tier volumes: - ./python:/root - pycache_volume:/root/.cache networks: app-tier: driver: bridge volumes: db_volume: pycache_volume:DockerfileにPythonのコンテナ情報を入力する
DockerfileFROM python:3.7 # ADD . /root COPY . /opt WORKDIR /opt RUN pip install -r requirements.txtmain.py
main.pyimport mysql.connector as mysql user_name = "user" password = "password" host = "mysql_db" # docker-composeで定義したMySQLのサービス名 database_name = "employees" conn = mysql.connect( host="mysql_db", user="user", passwd="password", port=3306, database="employees" ) conn.ping(reconnect=True) print(conn.is_connected())
- 投稿日:2020-05-18T22:13:35+09:00
ファイルを保存時に自動でテストを実行する
テスト駆動開発において、「ファイルを更新」 -> 「コンソールでテストコマンドを実行」 というプロセスが面倒でした。
そこで、このプロセスを自動化する方法を記載します。動作イメージ
環境
- Ubuntu 18.04
準備
sudo apt install inotify-toolsコード
以下のスクリプトを作成し、プロジェクトのルートに配置します。
autorun.sh#!/usr/bin/env bash TEST_RUNNER="pytest -s tests" # 実行したいテストコマンドを指定 TARGETS="./src ./tests" # 監視したディレクトリを指定 while inotifywait -r -e modify -e create -e delete $TARGETS; do $TEST_RUNNER done実行権限も付与。
chmod +x ./autorun.sh
実行
./autorun.sh参考
- 投稿日:2020-05-18T22:13:04+09:00
ゼロから始めるLeetCode Day29「46. Permutations」
概要
海外ではエンジニアの面接においてコーディングテストというものが行われるらしく、多くの場合、特定の関数やクラスをお題に沿って実装するという物がメインである。
その対策としてLeetCodeなるサイトで対策を行うようだ。
早い話が本場でも行われているようなコーディングテストに耐えうるようなアルゴリズム力を鍛えるサイト。
せっかくだし人並みのアルゴリズム力くらいは持っておいた方がいいだろうということで不定期に問題を解いてその時に考えたやり方をメモ的に書いていこうかと思います。
前回
ゼロから始めるLeetCode Day28「198. House Robber」基本的にeasyのacceptanceが高い順から解いていこうかと思います。
Twitterやってます。
問題
46. Permutations
難易度はMedium。
Top 100 Liked Questionsからの抜粋です。異なる数字を集めたリストが与えられるので、全ての順列を返しなさい、という問題です。
タイトルのPermutationは順列という意味らしいですよ。Example:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]これが全ての順列を返している例ですね。
解法
class Solution: def permute(self, nums: List[int]) -> List[List[int]]: return list(itertools.permutations(nums)) # Runtime: 36 ms, faster than 87.99% of Python3 online submissions for Permutations. # Memory Usage: 14.1 MB, less than 5.36% of Python3 online submissions for Permutations.はい。
あっけないですね。Python様の前ではLeetCodeのMediumも赤子同然です。これにて終了です、対戦ありがとうございました。
ごめんなさい、流石にこれだとあまりにもひどいので他に解法を考えました。
class Solution: def permute(self, nums: List[int]) -> List[List[int]]: ans = [] self.dfs(nums, [], ans) return ans def dfs(self, nums, emp, ans): if not nums: ans.append(emp) for i in range(len(nums)): self.dfs(nums[:i]+nums[i+1:], emp+[nums[i]], ans) # Runtime: 32 ms, faster than 96.70% of Python3 online submissions for Permutations. # Memory Usage: 13.9 MB, less than 5.36% of Python3 online submissions for Permutations.
dfs
を別個に考えて実装しました。
numsの要素数が尽きるまで再起関数で回し続けるというものです。スライスを使えば実装できます。
念のために書いておきますが、nums[:i]の場合は最初の要素からi番目までを取得し、nums[i+1:]の場合はi+1番目から最後の要素まで取得します。
そして、要素が無くなったときにempを元々用意していたリストの
ans
に追加することで二次元配列にしています。こちらの方が若干速くなりました。
良さげな解答があれば追記します。
- 投稿日:2020-05-18T21:38:13+09:00
繰り返しテキストデータ生成ツール「rpttxt」を作りました
rpttxtとは?
こちら (https://github.com/nendo-code/rpttxt) で配布しております。
例えばこんな感じのデータを作りたいとします。
output.txt{ "id": 1, "name": "John", "age": 21 } { "id": 2, "name": "Paul", "age": 35 } { "id": 3, "name": "Tom", "age": 64 }このデータを作る場合、あなたならどうしますか?
私なら、適当に手作業で済ませるのであればこんな感じにすると思います。1.
まず1項目分作る。step1.txt{ "id": 1, "name": "John", "age": 21 }2.
必要な項目数分コピペする。step2.txt{ "id": 1, "name": "John", "age": 21 } { "id": 1, "name": "John", "age": 21 } { "id": 1, "name": "John", "age": 21 }3.
各項目の値を狙いの値に書き換える。step3.txt{ "id": 1, "name": "John", "age": 21 } { "id": 2, "name": "Paul", "age": 35 } { "id": 3, "name": "Tom", "age": 64 }まあこんな感じで手作業してもいいですが、件数が多いとダルいしミスも発生しやすいです。
データはデータ、テンプレートはテンプレートで分けて書けるといいなあ、と私は思いました。
なので、テンプレートを書き
template.txt{ "id": {No}, "name": "{Name}", "age": {Age} }データをcsvで書いて(またはExcel等でcsvでエクスポートして)
data.csv{No},{Name},{Age} 1,John,21 2,Paul,35 3,Tom,64それらを食わせると下記のような出力が得られるプログラムを作成しました。
それがrpttxtです。output.txt{ "id": 1, "name": "John", "age": 21 } { "id": 2, "name": "Paul", "age": 35 } { "id": 3, "name": "Tom", "age": 64 }なんで作ったの?
世の中に似たツールはありそうな気はしましたが、求めている操作感のものがパッと見つからなかったので、車輪の再発明しました。
あと、「UNIXという考え方―その設計思想と哲学」という本を読んで素晴らしいなあと思ったので、それっぽいシンプルなインターフェースのコマンドラインツールにしました。(例)コマンドラインでの実行
$ python3 rpttxt.py template.txt data.csv > output.txtなにで作ったの?
python3で書いて、Windows用のバイナリはPyInstallerで作りました。
PyInstallerは楽チンでとてもいいツールです。
あと、Windowsだとコマンドライン立ち上げるのがダルいので一発で変換かけるバッチファイルもオマケで付けました。
- 投稿日:2020-05-18T21:28:59+09:00
(覚書)matplodlibで3D 散布図を作る
3D 散布図のイメージを作りたかったので、
google colaboratoryでmatplotlibを使い画像ファイル作りました。すべての点にラベルつけてます。
#matplotlibで日本語を使えるようにする !pip install japanize-matplotlib from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot from numpy.random import rand from pylab import figure from google.colab import files import pandas as pd import japanize_matplotlib #散布図の各点のラベル名と3次元座標 #点が多い場合はcsvとか読みこんだ方がいいかも df = pd.DataFrame({'ラード説': [20, 30, 20], '鶏油説': [10, 15, 15], '香味油説': [5, -10, 10], 'ウェイパー説': [40, 50, 25], '味の素説': [15, -30, -15], '中華鍋説': [0, 0, 50], '鍋のあおり説': [0, 5, 30], '硬めご飯説': [-20, 20, 40], 'タイ米説': [-15, -25, 45], '卵かけご飯説': [-35, -15, 45], '酒説': [10, -20, -30], 'マヨネーズ説':[-5, 20, -10], 'チャーシュー説': [40, 10, -15], '水島流チャーハン説':[10, -50, -50] }) #画像サイズと解像度 fig = figure(figsize=(10, 10), dpi=100) ax = fig.add_subplot(111, projection='3d') #各点を描画していく for i in range(df.shape[1]): ax.scatter(df.iloc[0,i],df.iloc[1,i],df.iloc[2,i]) ax.text(df.iloc[0,i],df.iloc[1,i],df.iloc[2,i], '%s' % (df.columns[i]), size=15) #軸ラベル ax.set_xlabel('美味しい - 微妙') ax.set_ylabel('素材の味 - 調味料の味') ax.set_zlabel('しっとり - パラパラ') #軸の長さ ax.set_xlim(-55, 55) ax.set_ylim(-55, 55) ax.set_zlim(-55, 55) #出力するpngファイル名 pyplot.savefig( '炒飯のコツ-3D-散布図.png' ) pyplot.show() #pngファイルのDL files.download('炒飯のコツ-3D-散布図.png')※ちなみにこの図は私が書いてる炒飯のブログで使ったものです。
- 投稿日:2020-05-18T21:24:23+09:00
Python)スクレイピング内容をローカルPCに保存
プログラミング初心者がスクレイピングを学んでいます。とりあえずスクレイピングした内容をローカルファイルにダウンロードできるようになったので忘れないうちに備忘録としてメモ。
#ウェブサイトから記事とURLをスクレイピングして結果をローカルPCにダウンロードするためのサンプル #必要なモジュールのインポート from bs4 import BeautifulSoup import requests import pandas as pd # データフレームを作成 columns = ["記事タイトル", "URL"] df = pd.DataFrame(columns = columns) # requestsでウェブサイトから内容を取得してBeautifulSoupで内容を取捨選択など加工 res = requests.get("https:~~スクレイピングしたいウェブサイトのURL~~") soup = BeautifulSoup(res.content, 'html.parser') # BeautifulSoupの初期化 tags = soup.find_all("XXXXX", {"class": "YYYYYY"}) # XとYはウェブサイトに応じて変わる # 記事名とURLをデータフレームに追加 for tag in tags: article = tag.a.string url = tag.a.get("href") se = pd.Series([article, url], columns) df = df.append(se, columns) #「to_csv」を使ってコードが保存されているのと同じフォルダにcsvファイルを保存 df.to_csv("./news.csv") print("終了")
- 投稿日:2020-05-18T21:08:52+09:00
gRPCでElixirとPython間通信をする
1.はじめに
Elixir~Python間を、gRPCを使って異言語間通信を試してみます。
今回の記事では、
- gRPCサーバ:Elixir
- gRPCクライアント:Python3
としています。
実行環境
ハード Raspberry Pi 3B+ OS Raspbian Buster, Ubuntu Server 20.04LTS Python 3.7.3 Elixir 1.7.4 (compiled with Erlang/OTP 21) ※いづれもaptパッケージでインストールできるもの
2.Elixir側
最初に、Elixir側の準備をします。
ここでは、elixir-grpcのライブラリに含まれているサンプル「examples/helloworld」を使ってみます。
クライアントからnameに名前を代入してリクエストすると、サーバからは"Hello "という文字列が帰ってきます。
※詳しい手順は別記事にまとめてますので、こちらをご覧下さい。
コマンドライン$ pwd ..../elixir/ #クローンします $ git clone https://github.com/elixir-grpc/grpc.git #サンプルディレクトリに移動して $ cd ./grpc/examples/helloworld/ #依存関係の処理、コンパイルします。 $ mix do deps.get, compile2.Python側
次はPython側でクライアント・サーバを作る手順を示します。
(1)grpcツールのインストール
$ sudo apt install python3-grpcio python3-grpc-tools -y(2)インターフェースの生成
protoファイルを解析して、Python向けのインターフェースを生成するツールを準備します。
コマンドライン$ pwd ..../elixir/grpc/examples/helloworld #Pythonのスクリプトを保存するフォルダを作成 $ mkdir python $ cd python #elixirのほうで定義している、protoファイルをコピー python $ cp ../priv/protos/helloworld.proto ./ #ツールのファイルを生成 python $ touch codegen.py python $ chmod 755 codegen.pyPython向けのインターフェースを生成するツールのソースコードです。
今回はhelloworld.proto
を対象にしてるので、コード中では__NAME="helloworld"
としています。codegen.py#!/usr/bin/env /usr/bin/python3 # -*- coding: utf-8 -*- """ protoファイルのコンパイル 以下の2つのファイルが生成されます。 ・*_pb2.py : シリアライズのインターフェース ・*_pb2_grpc.py : gRPCのインターフェース """ from grpc.tools import protoc #protoのファイル名(の名前部分) __NAME="helloworld" #Python向けのインターフェースを生成 protoc.main( ( '', '-I.', '--python_out=.', '--grpc_python_out=.', ('{}.proto'.format(__NAME)), ) )Python向けのインターフェースを生成します。
コマンドラインpython $ ./codegen.py python $ ls codegen.py helloworld_pb2_grpc.py helloworld_pb2.py helloworld.proto
*_pb2.py
と、*_pb2_grpc.py
の二つのファイルが生成されました。(3)Python側クライアントの作成
クライアントのスクリプトを作成
コマンドライン#ファイルを生成 python $ touch client.py python $ chmod 755 client.pyclient.py#!/usr/bin/env /usr/bin/python3 # -*- coding: utf-8 -*- """ gRPCクライアント by myasu 2020 """ import sys import grpc # 先ほどprotoから生成したインターフェースをインポート import helloworld_pb2 import helloworld_pb2_grpc def grpc_client(request): """gRPCクライアントの通信処理 Parameters ---------- request : string gRPCサーバに送るメッセージ """ # Elixir側のgRPCサーバのアドレスとポートを指定して接続 with grpc.insecure_channel('localhost:50051') as channel: stub = helloworld_pb2_grpc.GreeterStub(channel) # Elixir側のgRPCサーバにrequestする response = stub.SayHello(helloworld_pb2.HelloRequest(name=request)) # responseの内容を確認 print(' Response:', response.message) if __name__ == '__main__': """メイン処理 """ # 引数の読み込み args = sys.argv # 引数の長さをチェック if len(args) == 2: # 実行 grpc_client(args[1]) else: # エラー print('Arguments are too short')Python→Elixir→Python間の通信テスト
最初にサーバ・Elixir側を起動します。
ターミナル1・ElixirのgRPCサーバ側$ mix grpc.server 10:58:01.872 [warn] cowlib should be >= 2.9.0, it's 2.8.1 now. See grpc's README for details 10:58:01.977 [info] Running Helloworld.Endpoint with Cowboy using http://0.0.0.0:50051 (・・・ここから先はクライアントの要求があったときに表示・・・) 22:58:04.739 [info] Handled by Helloworld.Greeter.Server.say_hello 22:58:04.744 [info] Response :ok in 4ms 22:58:09.237 [info] Handled by Helloworld.Greeter.Server.say_hello 22:58:09.237 [info] Response :ok in 15μs 22:58:13.552 [info] Handled by Helloworld.Greeter.Server.say_hello 22:58:13.552 [info] Response :ok in 15μs [Ctrl-\]で停止次にクライアント・Python側を実行します。
スクリプトの引数には、任意のメッセージが指定できます。ターミナル2・PythonのgRPCクライアント側python $ ./client.py chika Response: Hello chika python $ ./client.py you Response: Hello you python $ ./client.py ruby Response: Hello ruby python $ ./client.py CYaRon! Response: Hello CYaRon!メッセージの先頭に、サーバ側が"Hello "を付けて返してきます。
このような感じで、異種言語間の通信が出来ました。4.参考資料
- 投稿日:2020-05-18T20:25:01+09:00
pythonで書くガチャ-基本的データ構造における実装-
内容
ガチャのデータ設計(基本的構造1、基本的構造2)に対応した実装を行います。
こちらのソースコードを改良
設定情報一覧
gacha_lottery
id gacha_group item_type times rarity omake_times omake_rarity cost A_normal_1 A 0 1 0 0 0 10 A_normal_11 A 0 10 0 1 3 100 B_fighter_2 B 0 2 0 0 0 30 A_omake_2_11 A 0 9 2 2 3 200 A_omake_fighter_6 A 2 5 0 1 3 100 ガチャを実行する際に使用するIDという位置づけになりますので、具体的に分かりやすい文字列のIDにします
gacha_items
id gacha_group weight item_id 1 A 3 5101 2 A 9 4201 3 A 9 4301 4 A 9 4301 5 A 20 3201 6 A 20 3301 7 A 20 3401 8 A 40 2201 9 A 40 2301 10 A 40 2401 11 B 15 4201 12 B 30 3201 13 B 55 2201 items
id rarity item_name item_type HP 5101 5 UR_勇者 1 1200 4201 4 SSR_戦士 2 1000 4301 4 SSR_魔法使い 3 800 4401 4 SSR_神官 4 800 3201 3 SR_戦士 2 600 3301 3 SR_魔法使い 3 500 3401 3 SR_神官 4 500 2201 2 R_戦士 2 400 2301 2 R_魔法使い 3 300 2401 2 R_神官 4 300 3199 3 SR_勇者 1 600 rarity
id rarity_name 5 UR 4 SSR 3 SR 2 R 1 N 改修の観点
機能要件
基本的構造2のデータ構造で実現できるガチャの仕組みとして、以下の要件を満たすようにします。
- ガチャ対象アイテムをグループ(母集団)として複数設定
- ガチャ実行時に属性の絞り込み(item_type)
- ガチャ実行時に通常抽選のレアリティ以上絞り込み(rarity)
- ガチャ実行時に通常抽選の回数指定(times)
- ガチャ実行時におまけのレアリティ以上絞り込み(omake_rarity)
- ガチャ実行時におまけの抽選回数指定(omake_times)
データ構造の変更点
前回(Pythonで書くガチャ-データ設計-)の実装におけるデータ構造と大きく異なる点としてガチャ情報とアイテム情報が分離されていることがあります。そのため、情報の紐付けが必要となります。
実装
gacha.pyimport random def gacha(lots, times: int=1) -> list: return random.choices(tuple(lots), weights=lots.values(), k=times) def get_rarity_name(rarity: int) -> str: rarity_names = {5: "UR", 4: "SSR", 3: "SR", 2: "R", 1: "N"} return rarity_names[rarity] # ガチャのIDと設定の辞書 def get_gacha_lottery_info(gacha_lottery_id: str) -> dict: gacha_lottery = { "A_normal_1": {"gacha_group": "A", "item_type": 0, "times": 1, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":10}, "A_normal_11": {"gacha_group": "A", "item_type": 0, "times": 10, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100}, "B_fighter_2": {"gacha_group": "B", "item_type": 0, "times": 2, "rarity": 0, "omake_times": 0, "omake_rarity": 0, "cost":30}, "A_omake_2_11": {"gacha_group": "A", "item_type": 0, "times": 9, "rarity": 2, "omake_times": 2, "omake_rarity": 3, "cost":200}, "A_omake_fighter_6": {"gacha_group": "A", "item_type": 2, "times": 5, "rarity": 0, "omake_times": 1, "omake_rarity": 3, "cost":100} } return gacha_lottery[gacha_lottery_id] def set_gacha(items: dict, gacha_items: dict): # ガチャの設定に必要なアイテム情報をガチャアイテム情報に含める dic_gacha_items = {} for gacha_item_id, info in gacha_items.items(): info["item_info"] = items[info["item_id"]] dic_gacha_items[gacha_item_id] = info # 抽選対象リストを抽出 def get_lots(lottery_info: dict): lots = {} omake_lots = {} for id, info in dic_gacha_items.items(): if lottery_info["gacha_group"] != info["gacha_group"]: continue if lottery_info["item_type"] and lottery_info["item_type"] != info["item_info"]["item_type"]: continue if not(lottery_info["rarity"]) or lottery_info["rarity"] <= info["item_info"]["rarity"]: lots[id] = info["weight"] if lottery_info["omake_times"]: if not(lottery_info["omake_rarity"]) or lottery_info["omake_rarity"] <= info["item_info"]["rarity"]: omake_lots[id] = info["weight"] return lots, omake_lots # ガチャ実行 def exec(gacha_lottery_id: str) -> list: lottery_info = get_gacha_lottery_info(gacha_lottery_id) lots, omake_lots =get_lots(lottery_info) ids = gacha(lots, lottery_info["times"]) if len(omake_lots) > 0: ids.extend(gacha(omake_lots, lottery_info["omake_times"])) return ids return exec def main(): # アイテム情報 items = { 5101: {"rarity": 5, "item_name": "UR_勇者", "item_type": 1, "hp": 1200}, 4201: {"rarity": 4, "item_name": "SSR_戦士", "item_type": 2, "hp": 1000}, 4301: {"rarity": 4, "item_name": "SSR_魔法使い", "item_type": 3, "hp": 800}, 4401: {"rarity": 4, "item_name": "SSR_神官", "item_type": 4, "hp": 800}, 3201: {"rarity": 3, "item_name": "SR_戦士", "item_type": 2, "hp": 600}, 3301: {"rarity": 3, "item_name": "SR_魔法使い", "item_type": 3, "hp": 500}, 3401: {"rarity": 3, "item_name": "SR_神官", "item_type": 4, "hp": 500}, 2201: {"rarity": 2, "item_name": "R_戦士", "item_type": 2, "hp": 400}, 2301: {"rarity": 2, "item_name": "R_魔法使い", "item_type": 3, "hp": 300}, 2401: {"rarity": 2, "item_name": "R_神官", "item_type": 4, "hp": 300}, 3199: {"rarity": 3, "item_name": "SR_勇者", "item_type": 1, "hp": 600} } # ガチャアイテム情報 gacha_items = { 1: {"gacha_group": "A", "weight": 3, "item_id": 5101}, 2: {"gacha_group": "A", "weight": 9, "item_id": 4201}, 3: {"gacha_group": "A", "weight": 9, "item_id": 4301}, 4: {"gacha_group": "A", "weight": 9, "item_id": 4401}, 5: {"gacha_group": "A", "weight": 20, "item_id": 3201}, 6: {"gacha_group": "A", "weight": 20, "item_id": 3301}, 7: {"gacha_group": "A", "weight": 20, "item_id": 3401}, 8: {"gacha_group": "A", "weight": 40, "item_id": 2201}, 9: {"gacha_group": "A", "weight": 40, "item_id": 2301}, 10: {"gacha_group": "A", "weight": 40, "item_id": 2401}, 11: {"gacha_group": "B", "weight": 15, "item_id": 4201}, 12: {"gacha_group": "B", "weight": 30, "item_id": 3201}, 13: {"gacha_group": "B", "weight": 55, "item_id": 2201} } # 実施するガチャのタプル gacha_lottery_ids = ( "A_normal_1","A_normal_11","B_fighter_2","A_omake_2_11","A_omake_fighter_6" ) #アイテム等をセット func_gacha = set_gacha(items, gacha_items) # gacha_lottery_idの設定にてガチャを実行 for gacha_lottery_id in gacha_lottery_ids: print("==%s==" % gacha_lottery_id) ids = func_gacha(gacha_lottery_id) for id in ids: item_info = items[gacha_items[id]["item_id"]] print("ID:%d, %s, %s" % (id, get_rarity_name(item_info["rarity"]), item_info["item_name"])) if __name__ == '__main__': main()実行結果
gacha_lotteryのID分ガチャを実行して、挙動を確認します
==A_normal_1== ID:10, R, R_神官 ==A_normal_11== ID:8, R, R_戦士 ID:10, R, R_神官 ID:10, R, R_神官 ID:6, SR, SR_魔法使い ID:5, SR, SR_戦士 ID:8, R, R_戦士 ID:5, SR, SR_戦士 ID:8, R, R_戦士 ID:10, R, R_神官 ID:5, SR, SR_戦士 ID:7, SR, SR_神官 ==B_fighter_2== ID:13, R, R_戦士 ID:13, R, R_戦士 ==A_omake_2_11== ID:7, SR, SR_神官 ID:10, R, R_神官 ID:10, R, R_神官 ID:6, SR, SR_魔法使い ID:8, R, R_戦士 ID:7, SR, SR_神官 ID:9, R, R_魔法使い ID:9, R, R_魔法使い ID:6, SR, SR_魔法使い ID:4, SSR, SSR_神官 ID:1, UR, UR_勇者 ==A_omake_fighter_6== ID:8, R, R_戦士 ID:5, SR, SR_戦士 ID:5, SR, SR_戦士 ID:8, R, R_戦士 ID:5, SR, SR_戦士 ID:2, SSR, SSR_戦士考察
機能要件は、満たすことができまました。
実際の運用に耐えうる仕組みとしては、不十分な箇所がありますが、
まずは、この構成が基本形となります。
今後は、運用上必要な機能要件を満たすために、新しい情報の追加、情報の再配置といったデータ構造の変更を行い、それに合わせて実装を改修していくことになります。今回の実装において、改めて技術的に難しいことは特段導入していません。
端的に言えば、データ構造を再検討し実装を改修しただけです。
実際の開発現場に置いてサービス設計者には、企画者の原案(機能要件)を表現するために、文章の構成を見直しストーリーを書き直す脚本家のような能力が求められます。(大事なことなんですがイマイチ理解されていない現実があります)追記
情報量(マスタ)が多くなり、またユーザの状態も考慮したガチャを行うため、次回以降はDBを用いた実装を行います
- 投稿日:2020-05-18T20:11:17+09:00
【python】PEP8に準拠したdocstringの書き方
PEP8規約を守らないと犬に吠えられる!?
pythonには,スタイルガイド,コーディング規約としてPEP8というものがあります.
チームでの場合は,PEP8規約を満たすのかコードをチェックすることにより,共通の書き方に揃い,より可読性が高くなると同時に開発がしやすくなります.
PEP8をチェックするのはgithubなどにpushするとき,またはpull requestを作るときが考えれられます.そういった段階でCIツールを利用して,テストをおこない,規約に反する箇所を知らせてくれます.
そういったツールの一つとして,reviewdogというものがあります.
PEP8規約に反する行動をとると,この犬に吠えられるのです..
私は,docstringに関するエラーH404,H405というもので犬に吠えられ続けて,頑張ってコードを直して犬に許してもらった話です.
エラーコードH4xxの内容
H4xx 系のエラーコードはdocstringに関するものになります.
docstringとは,関数やクラスメソッドを作成したときにその関数についての記述を行う場所です.
ちなみにこの良さそうに見えるdocstring, これだと犬に吠えられます!!(PEP8に完全には準拠していない)def docstring_sample(hoge1: str): """ ここがdocstringです. 引数がある場合はここで説明したりします.(何個かテンプレートみたいなのがあるのでそれはそれで調べてみてください) 今回はnumpyスタイルで Parameters ---------- hoge1 : str ここに入力された文字列を出力する""" print(hoge1)他にもいろいろなことに関するエラーコードがありこちらに素晴らしくまとまっています!
では,4xxの内容を見ていきましょう!
- [H401] Docstrings should not start with a space.
- [H403] Multi line docstrings should end on a new line.
- [H404] Multi line docstrings should start without a leading new line.
- [H405] Multi line docstrings should start with a one line summary followed
英語が苦手な僕は,H404とH405の理解に苦しみました...
では訳します(意訳です)
- [H401] Docstringsの記述はスペースから始めてはいけません.
- [H403] 複数行のdocstringを書く場合は,最後の行で改行してから閉じなさい.
- [H404] 複数行のdocstringを書く場合は,最初に1行空けてから書きなさい.
- [H405] 複数行のdocstringを書く場合は,1行その関数に関するまとめを書いてから書きはじめなさい.
ということです.
これを読んだときの僕は,
「なるほど,ということは先程のサンプルでは,最後に改行がない所,最初に一行空けてないところがだめだったんだ,書き直そう」
ということは,こんな感じかdef docstring_sample(hoge1: str): """ 引数hoge1を出力する関数.(最初まとめを書けって言われたから) ここがdocstringです. 引数がある場合はここで説明したりします.(何個かテンプレートみたいなのがあるのでそれはそれで調べてみてください) 今回はnumpyスタイルで Parameters ---------- hoge1 : str ここに入力された文字列を出力する """ print(hoge1)ところが,これでは犬に吠えられます.
エラーコードはH405です.
なぜだ..っというので数時間溶かしました.
(僕のところでは犬に吠えられる限りmasterにマージできないルールなのです...)対処法
数時間していろいろやってみた結果,やっと犬に認められました!!
それはH405はdocstringの一番最初に書く!
つまり,一行改行するまえにサマリを書く-> 一行改行するということです.
ようやく,PEP8規約に準拠したdocstringができました!
def docstring_sample(hoge1: str): """引数hoge1を出力する関数. ここがdocstringです. 引数がある場合はここで説明したりします.(何個かテンプレートみたいなのがあるのでそれはそれで調べてみてください) 今回はnumpyスタイルで Parameters ---------- hoge1 : str ここに入力された文字列を出力する """ print(hoge1)ちなみに一行しかdocstringを書かない場合は文末の
"""
は改行しなくていいです.def docstring_sample(hoge1:str): """引数hoge1を出力する関数""" print(hoge1)まとめ
PEP8の中には,流石にこれは無視してよくね?っていうのが何個かあると思います.(1行の文字数制限とか)
だけど,そこで自分を律して律儀に書くと,それはそれで達成感ありますよ!
ぜひやってみてください!下に参考文献を載せておきます!
numpyスタイルは僕も参考にしていつも使わさせていただいています!すごくわかりやすくてありがとうございます!参考文献
- 投稿日:2020-05-18T20:09:36+09:00
pythonとphpの違いまとめ(主要項目の対比表)
pythonとphpの違いまとめ(主要項目の対比表)
pythonとphpのクラスやメソッドなど主要項目の違いまとめ。
どっちがどっちかわからくなるの防止。
項目 Python PHP 関数 def 関数名(): function 関数名(): 処理の終わり 改行 ; 変数 変数名 $変数名 クラス定義 class クラス名: class クラス名{} コンストラクタ def __init__(self,): アクセス権 function __construct(){} インスタンス クラス名() new クラス名() プロパティ *1 インスタンス.プロパティ名 $プロパティ名 プロパティの呼び出し インスタンス名.プロパティ名 インスタンス->プロパティ名 インスタンス自身 self $this 自身のプロパティ呼び出し self.プロパティ名 $this->プロパティ名 メソッド def メソッド名(self,) アクセス権 function メソッド名() メソッド呼び出し インスタンス.メソッド名() インスタンス->メソッド名() 継承 class クラス名(親クラス名): class クラス名 extends 親クラス名{} ファイル読込み import モジュール名 require_once(' ') クラス読込み from モジュール名 import クラス名 require_once(' ') 親クラスのメソッド呼び出し super().メソッド名() parent::メソッド名() クラスメソッド @classmethod アクセス権 static function メソッド名(){} クラスメソッドの呼び出し クラス名.メソッド名() クラス名::メソッド名() クラスプロパティ なし(?) アクセス権 static $プロパティ名 クラスプロパティの呼び出し なし(?) クラス名::$プロパティ名 出力 print() echo/print 配列 [] array() for文 for 変数 in 配列など: for($i=初期値: 条件式: ステップ){} 配列からひとつずつ抜き出す for 変数 in 配列: foreach($変数名 as 配列) if文 if 条件式: if(条件式){} else if elif elseif switch文 なし switch(){case 条件: 処理; break;} and and &&/and or or パイプ2本/or インクリメント演算子 なし ++ デクリメント演算子 なし -- 整数型に変換 int() intval() 文字列型に変換 str() strval() 小数点に型変換 float() floatval() 3桁区切り '{:,d}'.format(数値) number_format() *1:pythonではインスタンス変数と呼ぶ
表にして眺めてみると違いや、規則性がわかりやすい。
- 投稿日:2020-05-18T20:09:36+09:00
PythonとPHPの違いまとめ(主要項目の対比表)
PythonとPHPの違いまとめ(主要項目の対比表)
pythonとphpのクラスやメソッドなど主要項目の違いまとめ。
どっちがどっちかわからくなるの防止。
項目 Python PHP 関数 def 関数名(): function 関数名(){} 処理の終わり 改行 ; コメントアウト # // or /* */ 変数 変数名 $変数名 クラス定義 class クラス名: class クラス名{} コンストラクタ def __init__(self): アクセス権 function __construct(){} インスタンス クラス名() new クラス名() プロパティ *1 インスタンス.プロパティ名 $プロパティ名 プロパティの呼び出し インスタンス名.プロパティ名 インスタンス->プロパティ名 インスタンス自身 self $this 自身のプロパティ呼び出し self.プロパティ名 $this->プロパティ名 メソッド def メソッド名(self) アクセス権 function メソッド名() メソッド呼び出し インスタンス.メソッド名() インスタンス->メソッド名() 継承 class クラス名(親クラス名): class クラス名 extends 親クラス名{} ファイル読込み import モジュール名 require_once(' ') クラス読込み from モジュール名 import クラス名 require_once(' ') 親クラスのメソッド呼び出し super().メソッド名() parent::メソッド名() クラスメソッド @classmethod
アクセス権 static function メソッド名(){} クラスメソッドの呼び出し クラス名.メソッド名() クラス名::メソッド名() クラスプロパティ (メソッド定義と同列に)
プロパティ名アクセス権 static $プロパティ名 クラスプロパティの呼び出し クラス名.プロパティ名 クラス名::$プロパティ名 出力 print() echo/print 配列 [] array() キーあり配列 {キー名:値} array(キー名=>値) キーあり配列呼び名 辞書型 連想配列 配列の要素数 len(配列) count(配列) 変数展開*2 f'{変数}' "${変数}" for文 for 変数 in range(始値, 終値, ステップ)
※ 終値は含まないfor($変数名=初期値: 条件式: ステップ){} 配列からひとつずつ抜き出す for 変数 in 配列: foreach($変数名 as 配列) if文 if 条件式: if(条件式){} else if elif 条件式: elseif (条件式){} switch文 なし switch(){case 条件: 処理; break;} and and &&/and or or パイプ2本/or インクリメント演算子 なし ++ デクリメント演算子 なし -- 整数型に変換 int() intval() 文字列型に変換 str() strval() 小数点に型変換 float() floatval() 3桁区切り '{:,d}'.format(数値)
f'{数値:,d}'number_format() *1. pythonではインスタンス変数と呼ぶ
*2. PHP:シングルクオテーションだと文字列として出力
python:f文字列の場合
表にして眺めてみると違いや、規則性がわかりやすい。
- 投稿日:2020-05-18T18:41:25+09:00
RayによるPython分散並列処理入門
Rayとは
RayはPythonにおける分散並列処理を高速かつシンプルに書けるフレームワークで, 既存のコードを並列化することも容易な設計となっています.
Rayを使うことでmultiprocessingなどに比べ簡単にプロセスレベルの並列処理を記述することができます.本記事はRayチュートリアルの内容をもとにしており,
コードはPython 3.8.2, Ray 0.8.4での動作を確認しています.インストール
ターミナルでpipなどからインストールできます.
$ pip install ray使い方
基本的な用途としては覚える文法は
ray.init
ray.remote
ray.get
の3つのみで, この記事では加えてray.wait
ray.put
も紹介します.Rayによる並列化の基本
実行に3秒かかる関数
func
が二度呼び出され全体の実行に6秒かかる以下のコードについて,func
の実行を並列化することを考えましょう.import time def func(x): time.sleep(3) return x begin_time = time.time() # 開始時刻を記録 res1, res2 = func(1), func(2) # funcを2度呼ぶ print(res1, res2) # 出力: 1 2 end_time = time.time() # 終了時刻を記録 print(end_time - begin_time) # 6秒ぐらいRayを使う場合には 必ず最初に
ray.init
で使用するリソース数の指定などを行いRayのプロセスを起動する必要があります.import ray # ray.init() のように明示的に指定しなかった場合自動的にリソース数が決定されます ray.init(num_cpus=4) # 時間計測をより正確にする都合上Rayの起動を少し待つ time.sleep(1)ある関数を並列で実行させたい場合, その関数をRayの扱える remote関数 にする必要があります.
といってもやり方は簡単で, その関数に@ray.remote
とデコレーターをつけるだけです.
remote関数は(関数名).remote(引数)
として呼び出すとRayの並列ワーカーに送られて実行されます.
.remote(引数)
は終了を待たずに Object ID というものをreturnします.@ray.remote def func(x): time.sleep(3) return x begin_time = time.time() res1, res2 = func.remote(1), func.remote(2) print(res1) # 出力例: ObjectID(45b9....)結果を取得したい場合には, remote関数から返ってきたObject IDを
ray.get
に渡してあげればよいです.
ray.get
はObject IDに対応する結果がすべて取得できるまでブロッキングします.print(ray.get(res1), ray.get(res2)) # 出力: 1 2 # ray.getはリストを受けとることもできる print(ray.get([res1, res2])) # 出力: [1, 2] end_time = time.time() print(end_time - begin_time) # 3秒ぐらい以上のコードを1つのスクリプトとして実行すると3秒程度しかかかっておらず,
func
の実行が並列化されていることがわかります.
基本はこれだけです.依存関係のある並列化
Rayはremote関数間に依存関係があっても, Object IDをそのまま受け渡すことで処理が可能です.
受け渡されたObject IDは実際に実行される際には通常のPythonオブジェクトに復元されて実行されます.
以下の例では,vec
内の4つの各要素に対してfunc1
とfunc2
を順に適用しています. 1要素の処理には2秒かかります.
※これ以降の例では時間計測のためのコードを省略しています@ray.remote def func1(x): time.sleep(1) return x * 2 @ray.remote def func2(x): time.sleep(1) return x + 1 vec = [1, 2, 3, 4] results = [] for x in vec: res = func1.remote(x) # resにはObjectIDが入っている res = func2.remote(res) # ObjectIDをそのまま次のremote関数に渡す results.append(res) # resultsはObjectIDのリスト print(ray.get(results)) # 出力: [3, 5, 7, 9]Rayは依存関係を解析し, 依存先のない
func1
を先に並列実行し,その後func1
の処理の終わった要素についてfunc2
を並列実行します.
逐次では8秒かかるこの処理は並列化により2秒程度で実行されます.また, Rayはネストされた呼び出しにも対応しており,
func2
を次のように書き換えても問題なく動作します.
ネスト呼び出しの条件は, 呼び出したい関数が事前に定義されていることだけです.@ray.remote def func2(x): x = func1.remote(x) # ObjectIDが返される time.sleep(1) return ray.get(x) + 1 # ObjectIDと直接足し算は出来ないため, ray.getしてから計算する print(ray.get([func2.remote(x) for x in vec])) # 出力: [3, 5, 7, 9]私の環境での実測値は2秒より少し遅くなりましたが, 8秒よりは速く並列に実行できています.
Actor
remote関数は実行されたあとそのままreturnしてしまい状態を持つことができません.
状態をもつような処理を, Rayではクラスを@ray.remote
で修飾することにより実現します.
@ray.remote
で修飾されたクラスを Actor と呼びます.例えば, 次のような一度のインクリメントにつき1秒かかるカウンターを考えましょう.
Actorのインスタンスを作る時も, 関数呼び出しのときと同様.remote()
を付けます.@ray.remote class Counter: def __init__(self, init_val, sleep=True): # カウンターをinit_valで初期化 self.count = init_val self.sleep = sleep def increment(self): if self.sleep: time.sleep(1) self.count += 1 return self.count # 初期値0と100のカウンターを作る counter1, counter2 = Counter.remote(0), Counter.remote(100)それぞれのカウンターを3回ずつインクリメントしながら, 各段階での値をresultsに記録していきましょう.
results = [] for _ in range(3): results.append(counter1.increment.remote()) results.append(counter2.increment.remote()) print(ray.get(results)) # 出力: [1, 101, 2, 102, 3, 103]合計6回インクリメントがされていますが, カウンターごとに並列化されているので3秒しかかからずに値を取得することができます.
また, Actorの同一のインスタンスのメソッドを並列に呼び出したいときには, Actorのインスタンスを引数にとるremote関数を定義すればよいです.
例えば次のように, 1秒おきにincrement
を呼び出すincrementer
という関数を0.5秒ずらして実行させてみましょう.
ここではincrement
自体が一瞬で終わるようなCounter
を用意していることに注意してください.@ray.remote def incrementer(counter, id, times): # 1秒おきにtimes回インクリメントを行う for _ in range(times): cnt = counter.increment.remote() print(f'id= {id} : count = {ray.get(cnt)}') time.sleep(1) counter = Counter.remote(0, sleep=False) # 1回のインクリメントが一瞬で終わるカウンター incrementer.remote(counter, id=1, times=5) time.sleep(0.5) # 開始を0.5秒ずらす inc = incrementer.remote(counter, id=2, times=5) ray.wait([inc]) # 次に説明する, 終了を待つ関数実行すると, 次のように
incrementer
がcounter
の値を0.5秒おきに交互に更新している様子がわかります.(0.0秒後) id = 1 : count = 1 (0.5秒後) id = 2 : count = 2 (1.0秒後) id = 1 : count = 3 (1.5秒後) id = 2 : count = 4 (2.0秒後) ......ray.wait
並列実行されているObject IDのリストを
ray.get
に渡すと, そのすべての実行が終了するまで値を取得できません.
ray.wait
を使うと, 並列実行された関数のうち指定した数が終了するまで待機し, その時点で終了したIDとそうでないIDを別々にreturnしてくれます.@ray.remote def sleep(x): # x秒休んでxを返す関数 time.sleep(x) return x ids = [sleep.remote(3), sleep.remote(5), sleep.remote(2)] finished_ids, running_ids = ray.wait(ids, num_returns=2, timeout=None) print(ray.get(finished_ids)) # 出力(3秒経過時点): [3,2] print(ray.get(running_ids)) # 出力(5秒経過時点): [5]ray.put
実は,
remote
関数に渡されたオブジェクトは, そのつど暗黙裏にシリアライズされてRayの共有メモリ上にコピーされます.
そのため, 巨大なオブジェクトをremote
の引数に複数回渡してしまうと余計にコピーするための時間がかかるほか, 共有メモリ上の領域を無駄に消費してしまいます.このような場合には,
ray.put
を用いて事前に一度だけ明示的にコピーを行うことによりこの無駄を回避することができます.
ray.put
はremote
と同様Object IDを返し, これをremote関数に渡してあげればよいです.
一度コピーされたオブジェクトは共有されているので, 並列実行するワーカーはどれもこのオブジェクトを参照することができます.@ray.remote def func4(obj, idx): time.sleep(1) return idx # big_object はサイズの大きなobjectだとする big_object = None big_obj_id = ray.put(big_object) # func.remote()が4回呼ばれるが, いま渡しているのはObjectIDのため再度big_objectのコピーは発生しない results = [func4.remote(big_obj_id, i) for i in range(4)] print(ray.get(results)) # 出力: [0, 1, 2, 3]なお, Rayの
ray.get
によるデシリアライズはpickle.load
に比べて非常に高速であるようです.おわりに
公式ドキュメントにはより詳細な使い方が載っています.
特にExamplesには分散環境でのパラメータサーバーや強化学習などの具体的な用例が載っていて参考になるでしょう.
またRayを基盤とした高レベルなフレームワークも用意されており, 強化学習向けのRLlibやハイパーパラメータチューニング向けのTuneなどがあります.
是非Rayを使って快適な並列処理ライフを手に入れましょう.参考サイト
- 投稿日:2020-05-18T18:19:37+09:00
Deep Learning Specialization (Coursera) 自習記録 (C2W1)
はじめに
Deep Learning Specialization の Course 2, Week 1 (C2W1) の内容です。
(C2W2L01) Train / Dev / Test sets
内容
参考
- 投稿日:2020-05-18T18:18:32+09:00
doc2vecでredmineのチケット(csv)を学習させてみる
やったこと
問い合わせ履歴(redmineのチケット)を機械学習し
検索ワードに対して、類似する過去チケットをリコメンドするPoCを作ってみました。背景
私はコールセンター的な業務をすることがあり、問い合わせの記録をredmineにチケットとして記録してます。
そんな中、最近流行りの機械学習を使った検索で、業務の効率化をできそうかを試してみたく、doc2vecでPoC作ってみました。
結論
結論としては、作りが雑すぎたためか、使い物にならない精度でした。
しかし、前処理とかもう少し頑張れば実運用でも使えるかもしれない可能性は感じました。実装の流れ
大きく3段階あります。
① docker上のubuntuでjupyter-notebookを起動し、機械学習&チケットの検索の環境を用意
② doc2vecでチケット(csv)を機械学習させ、モデルを作成
③ 検索ワードを入力し、類似するチケットをリコメンド環境
- docker (windows10)
- ubuntu:16.04
- jupyter-notebook
- python
- doc2vec
- redmineのチケット(csvファイル)
全体的に、すぐに使える!業務で実践できる!Pythonによる AI・機械学習・深層学習アプリのつくり方を参考にさせて頂いております。
作り方
※環境構築関連は、ありがちだけどPythonでチャットボット作ってみたでもう少し詳しく説明してます
1. dockerを起動し、Dockerfileを
build
する2. イメージを
run
するdocker run -it -p 8888:8888 -v //c/Users/xxx/xxx:/xxx book-mlearn-image
xxx
の部分にパスを入力して、ホストのディレクトリをマウントする3.
issues.csv
(問い合わせ対応のcsv)をマウント済みのディレクトリに格納する4. jupyter-notebookを起動する
jupyter notebook --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.iopub_data_rate_limit=100000000実行すると、
http://127.0.0.1:8888/?token=xxx
にアクセスしてね、と表示されますが、docker上で動いてるのでdocker-machine env
で出てくるipアドレスに置き換えてアクセスしてください。例:
http://192.168.99.100:8888/id=xxx
5. 新規でpython3のファイルを作成する
6. 必要なものをインストールする
以下をコピペし実行します。時間かかります。
!curl -kL https://bootstrap.pypa.io/get-pip.py | python3 !apt-get update -y !apt-get upgrade -y # ?について、十分な空き容量がないとエラーになる場合がある。エラーメッセージでググったら解決法でる。 !apt-get -yV install swig-doc !apt-get -yV install swig-examples !apt-get -yV install swig2.0-doc !apt-get -yV install swig2.0- !apt-get -yV install swig2.0 !apt-get -yV install swig !apt-get install mecab libmecab-dev mecab-ipadic-utf8 -y !git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git /tmp/work/ # ?のclone先をホストOSとの共有ディレクトリにしているとエラーとなる場合がある。 # そのためtmp/workに保存している !mkdir /var/lib/mecab/dic/mecab-ipadic-neologd !apt-get install file -y !/tmp/work/bin/install-mecab-ipadic-neologd -n -p /var/lib/mecab/dic/mecab-ipadic-neologd -y # ?はdockerのVM VirtualMachineのメモリ割り当てが1024MBの場合エラーが出ることがある。1.5GB(1538MB)くらいに増やすと私はエラー出なくなりました。 !pip install mecab-python3 import MeCab import sys import MeCab m = MeCab.Tagger ("-d /var/lib/mecab/dic/mecab-ipadic-neologd") # mecabの稼働確認 print(m.parse ("すもももももももものうち")) # word2vecの準備 !pip install gensim7. チケットを学習しモデルを作成
以下を実行します。
import pandas as pd import zipfile import os.path import urllib.request as req import MeCab from gensim import models from gensim.models.doc2vec import TaggedDocument # ここの/xxx/はissues.csvのパスに修正してください df = pd.read_csv("/xxx/issues.csv", encoding='cp932') list =[] # リストへの格納方法が冗長な感じがしますがお許しください #Mecabの初期化 mecab = MeCab.Tagger() mecab.parse("") for i in range(len(df["No"])): list.append( {"ticket":{ "No":str(df["No"][i]), # No・・・チケットのナンバー(#) }, "content":[ {"title":str(df["題名"][i]), # 題名・・・チケットのタイトル # csvの学習させたい項目を単純に連結させる # 担当者/内容/システム名/機能名/ページ名/説明・・・チケットの各項目名のサンプルです "detail":str(df["担当者"][i]) + str(df["内容"][i]) + str(df["システム名"][i]) + str(df["ページ名"][i]) + str(df["説明"][i])}, ]}, ) def ticket_list(): for ticketlist in list: ticket = ticketlist["ticket"] for content in ticketlist["content"]: yield ticket, content #引数のテキストを分かち書きして配列にする def split_words(text): text = text.replace('\n','') text = text.replace('\r','') text = text.replace('\u3000','') node = mecab.parseToNode(text) wakati_words = [] while node is not None: hinshi = node.feature.split(",")[0] if hinshi in ["名詞"]: wakati_words.append(node.surface) elif hinshi in ["動詞", "形容詞"]: wakati_words.append(node.feature.split(",")[6]) node = node.next return wakati_words #リストをDoc2Vecが読めるTaggedDocument形式にし、配列に追加する documents = [] #チケットリストをループで回す for ticket, content in ticket_list(): #文字列を取得 words = content["detail"] #文字列を分かち書きに wakati_words = split_words(words) #TaggedDocumentの作成 文書=分かち書きにしたチケット詳細 タグ=No:題名 document = TaggedDocument( wakati_words, [ticket["No"] + " : " + content["title"]]) documents.append(document) #TaggedDocumentの配列を使ってDoc2Vecの学習モデルを作成 model = models.Doc2Vec( documents, dm=1, vector_size=300, window=5, min_count=1) #Doc2Vecの学習モデルを保存 model.save('redmine.model') print("モデル作成完了")8. 検索ワードを入れ、類似するチケットを表示させる
以下を実行します。
import urllib.request as req import zipfile import os.path import MeCab from gensim import models #Mecabの初期化 mecab = MeCab.Tagger() mecab.parse("") #保存したDoc2Vec学習モデルを読み込み model = models.Doc2Vec.load('redmine.model') #引数のテキストを分かち書きして配列にする def split_words(text): node = mecab.parseToNode(text) wakati_words = [] while node is not None: hinshi = node.feature.split(",")[0] if hinshi in ["名詞"]: wakati_words.append(node.surface) elif hinshi in ["動詞", "形容詞"]: wakati_words.append(node.feature.split(",")[6]) node = node.next return wakati_words def similar(search_word): words = search_word wakati_words = split_words(words) vector = model.infer_vector(wakati_words) print("--- 「" + search_word + '」に類似するチケットは? ---') print(model.docvecs.most_similar([vector],topn=3)) print("") # similar("ここに検索したい文章を入れる")すると...
--- 「aaaしたいがbbbできない」に類似するチケットは? --- [('1234 : cccしたいができない', 0.9499524831771851), ('2345 : dddする方法を教えて欲しい', 0.9499462246894836), ('3456 : eeeでエラーが発生した', 0.9499049782752991)]と、類似するチケットが表示されます。
ここでいう、
1234
のような数字はチケットのNo、
cccしたいができない
はチケットの題名、
0.9499524831771851
はチケットの詳細と、similar("~")に入れた文章との類似度です。類似度は1に近いほど似てるという意味です。
ここでは類似度が高い上位3件を表示させています。
以上です。
リコメンド精度が低い理由は、いろいろ考えられますが、一番の問題は前処理がテキトウすぎることかなと思ってます。
さすがに雑すぎでしたかね...。ちなみに、redmineのチケットをcsv形式で出力して使っている理由は、DBにアクセスするコードを書くのが面倒だったためです。
また、問い合わせ履歴をいい感じに処理してくれる機械学習として、watson discoveryも少し触ってみたのですが、色々と覚えるのに時間がかかりそうだったためすぐ作れそうなdoc2vecで実装しました。
何か間違いやアドバイスあればコメントください。
- 投稿日:2020-05-18T18:03:50+09:00
Visual Studio CodeでライブラリをインストールしたのにUnable to import
ライブラリをインストールしたのにUnable to import
Djangoでバージョンを合わせたのにvscodeにずっと指摘されたのでその原因と解消方法を書いておきます。
原因
実行環境とVSCodeの仮想環境が異なるからです。
例えば、A,BというPythonの仮想環境があったとしましょう。
Aの方にライブラリをインストールしたとしてもBの方をつかってVSCodeで動いていたらそりゃ指摘されますよねってことです.
解決策
参考
https://stackoverflow.com/questions/48270385/vs-code-error-when-importing-django-module
- 投稿日:2020-05-18T17:57:54+09:00
【Python】Excelと同じ切片0の近似式の算出方法【scikit-learn】メモ
概要
- Excelには、切片固定で近似式を算出する方法がある
- numpy.polyfitでは、切片固定ができない。
- scikit-learnのLinerRegression,make_pipeline,PolyFeaturesで実現する
Excelの場合
Pythonの場合
pandas+scikit-learnfrom sklearn.linear_model import Ridge,LinearRegression from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline # Excelと同じデータを作成 x=np.array([0,3,12.5,18.7,25,20,16,12.8,10.24,8.192]) y=np.array([0,15,46.6,60.3,74.3,59.44,47.552,46,36.8,29.44]) # DEGREE(次数) degree = 3 # LinearRegression # make_pipelineでPolynomialFeaturesとLineaRegressionをがっちゃんこ model = make_pipeline(PolynomialFeatures(degree,include_bias=False),LinearRegression(fit_intercept=False)) model.fit(x.reshape(-1,1),y) y_model=model.predict(x.reshape(-1,1)) #データフレームの確認 df = pd.DataFrame({'y_model.predict':y_model,},index=x) df.sort_index().plot(kind ='line',figsize=(10.,5.)) # 係数 model.steps[1][1].coef_array([ 5.06817229e+00, -1.71343566e-01, 3.49200227e-03])まとめ
- numpyのpolyfitではできなかったけど、scikit-learnでできてよかった。
- Excelの代替手段としてPython使ってるときに困ったのでメモ
- 投稿日:2020-05-18T17:07:55+09:00
チャネルファースト・チャネルラストの入れ替え
はじめに
Pytorchを前提に記述されたプログラムをKerasに書き換えている時に、画像データ配列の軸を入れ替える必要があったため、その方法について共有します。
Image Channel Order
Channels First : (N, C, H, W) ← PyTorch
Channels Last : (N, H, W, C) ← KerasN : 画像の枚数
C : チャネル数(色など)
H : 画像のHeight
W : 画像のWidthチャネルファースト(Channels First)
PyTorchなどにおいて、画像データ配列でモデルを学習する際は、Channels First 形式が一般的です。画像の次元の並び方が (Channel, Height, Width)となっています。Channel(Color)の次元が配列の先頭にあることが名前からわかります。
チャネルラスト(Channels Last)
Keras,PIL,OpenCVなどにおいて、画像データ配列を扱う場合は、Channels Last 形式が一般的です。画像の次元の並び方が (Height, Width, Channel)となっています。Channelの次元が配列の最後にあることが名前からわかります。
Channels First → Channels Last
仮の Channels First の画像配列データを作成します。
仮の画像配列データimg = np.arange(100*64*64*3).reshape(-1,3,64,64) img.shape (100, 3, 64, 64)方法1 : np.transpose()
np.transpose()%%timeit img.transpose(0,2,3,1).shape (100, 64, 64, 3) 791 ns ± 92.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)方法2 : np.swapaxes()
np.swapaxes()%%timeit np.swapaxes(img, 1, 3).shape (100, 64, 64, 3) 1.54 µs ± 410 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)方法3 : np.moveaxes()
np.moveaxes()%%timeit np.moveaxes(img, 1, 3).shape (100, 64, 64, 3) 9.29 µs ± 956 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)方法4 : np.rollaxes()
np.rollaxes()%%timeit np.rollaxes(img, 1, 4).shape (100, 64, 64, 3) 2.89 µs ± 358 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)方法5 : np.einsum()
np.einsum()%%timeit np.einsum('ijkl->ilkj', img).shape (100, 64, 64, 3) 1.77 µs ± 210 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)最後に
np.transpose() が最速でした。お好きなのを使用してください。
what-is-the-correct-way-to-change-image-channel-ordering-between-channels-first を参考にしました。
- 投稿日:2020-05-18T17:05:09+09:00
Djangoのversion確認
- 投稿日:2020-05-18T16:22:01+09:00
python bit全探索
方法1
>>> from itertools import product >>> n = 3 >>> [[i for i, j in zip(range(n), mask) if j] for mask in product((0, 1), repeat=n)] [[], [2], [1], [1, 2], [0], [0, 2], [0, 1], [0, 1, 2]] >>> n = 5 >>> [[i for i, j in zip(range(n), mask) if j] for mask in product((0, 1), repeat=n)] [[], [4], [3], [3, 4], [2], [2, 4], [2, 3], [2, 3, 4], [1], [1, 4], [1, 3], [1, 3, 4], [1, 2], [1, 2, 4], [1, 2, 3], [1, 2, 3, 4], [0], [0, 4], [0, 3], [0, 3, 4], [0, 2], [0, 2, 4], [0, 2, 3], [0, 2, 3, 4], [0, 1], [0, 1, 4], [0, 1, 3], [0, 1, 3, 4], [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4]]方法2
>>> n = 3 >>> [[i for i in range(bit) if bit & (1<<i)] for bit in range(1<<n)] [[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]] >>> n = 5 >>> [[i for i in range(bit) if bit & (1<<i)] for bit in range(1<<n)] [[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2], [3], [0, 3], [1, 3], [0, 1, 3], [2, 3], [0, 2, 3], [1, 2, 3], [0, 1, 2, 3], [4], [0, 4], [1, 4], [0, 1, 4], [2, 4], [0, 2, 4], [1, 2, 4], [0, 1, 2, 4], [3, 4], [0, 3, 4], [1, 3, 4], [0, 1, 3, 4], [2, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4], [0, 1, 2, 3, 4]]
- 投稿日:2020-05-18T15:59:11+09:00
[Python3] janomeとmarkovifyを使った文章の自動生成
はじめに
Pure Pythonで書かれた日本語形態素解析エンジンJanomeと、マルコフ連鎖ライブラリmarkovifyを使って、日本語の文章を学習して自動生成します。
基本的には
markovifyで日本語の文章を学習して、マルコフ連鎖により文章生成を行う
をもとにさせていただいています。背景・目的
実はmarkovifyを使用した日本語文章の学習と自動生成は先例が1つならずあるのですが、それらは大抵MeCabを使用したもので、その導入の関係上Windows環境や一部の仮想環境では些か手間がかかります(Herokuとか)。
その点Janomeは導入がWindowsでも容易で、実際に自動生成への利用法を紹介した記事もやはりあるのですが、markovifyとJanomeを併用したものは(ニッチ過ぎて)見当たりませんでした。markovifyを使ったほうがお手軽に生成文の自然さを高められますので、できれば使用したいところです。
そこで今回、両者を併用して文章を生成できるようにしてみたため、メモ代わりに置いておきます。まぁ、併用したくなるような人は、自力で書き換え可能な気はするので、本当にメモ程度ですが……。
準備
- Python (3.8.1)
- Janome (0.3.10)
- markovify (0.8.0)
janomeもmarkovifyも
pip install
で導入可能です。(環境によってはpip3
)コード
まずは全体。textGen部分は参考文献1を半ば以上流用しています。
janomeGen.py#!/usr/bin/env python3 # -*- coding:utf-8 -*- from janome.tokenizer import Tokenizer import MeCab import markovify def split(text): # 改行、スペース、問題を起こす文字の置換 table = str.maketrans({ '\n': '', '\r': '', '(': '(', ')': ')', '[': '[', ']': ']', '"':'”', "'":"’", }) text = text.translate(table) t = Tokenizer() result = t.tokenize(text, wakati=True) # 1形態素ずつ見ていって、間に半角スペース、文末の場合は改行を挿入 splitted_text = "" for i in range(len(result)): splitted_text += result[i] if result[i] != '。' and result[i] != '!' and result[i] != '?': splitted_text += ' ' if result[i] == '。' or result[i] == '!' or result[i] == '?': splitted_text += '\n' return splitted_text def textGen(file): f = open(file, 'r', encoding="utf-8") text = f.read() sentence = None while sentence == None: # 素材によっては空の文章が生成されることがあるので、その対策 # テキストを処理できる形に分割 splitted_text = split(text) # モデルの生成 text_model = markovify.NewlineText(splitted_text, state_size=3) # モデルを基にして文章を生成 sentence = text_model.make_sentence() # 学習データの保存 with open('learned_data.json', 'w') as f: f.write(text_model.to_json()) # データを使いまわす場合 """ with open('learned_data.json') as f: text_model = markovify.NewlineText.from_json(f.read()) """ # 結合された一連の文字列として返す return ''.join(sentence.split())以下、順番に見ていきます。
テキストの下処理
table = str.maketrans({ '\n': '', '\r': '', '(': '(', ')': ')', '[': '[', ']': ']', '"':'”', "'":"’", }) text = text.translate(table)markovifyが読み取れるよう、一部の文字を置換しておきます。改行とスペースはそれぞれ文章の区切りと単語の区切りを示すために使うので一旦削除(英文交じりの日本文などはうまく処理できなくなってしまいますが、今回は無視)。
また、markovifyの動作に悪影響を及ぼす'bad characters'も無害な全角文字に置換しておきます。(markovify v0.7.2からはmarkovify.Textのwell_formedパラメータで、bad charactersを含むセンテンスを無視するかどうかを指定できますが、丸ごと無視してしまうのはもったいないので事前に置換で済ませています)
テキストの分割
t = Tokenizer() result = t.tokenize(text, wakati=True) splitted_text = "" for i in range(len(result)): splitted_text += result[i] if result[i] != '。' and result[i] != '!' and result[i] != '?': splitted_text += ' ' if result[i] == '。' or result[i] == '!' or result[i] == '?': splitted_text += '\n'やっていること自体は参考記事1とほぼほぼ同じなのでそちらを参照していただいたほうが正確です。
JanomeのTokenizerでこのようにtokenizeすると、形態素で分けたリストとして返してくれます。
「私はリンゴを一つ食べる。」なら['私', 'は', 'リンゴ', 'を', '一つ', '食べる', '。']
という感じ。形態素の本体のみ欲しい人にはMeCabよりもお手軽で便利。今回はmarkovifyが読めるように形態素を1個ずつ読んで間を半角スペースで分け、文末に来たら改行で区切ります(英文と形を揃える感じ)。参考記事では句点でのみ切っていましたが、今回は!と?でも切るようにしました。読点をどう区切るかは好みによりますが、ここでは1つの単語として分けました。英語同様の形にする場合、if文のところを
if i+1 < len(result): if result[i] != '。' and result[i] != '!' and result[i] != '?' and result[i+1] != '、': splitted_text += ' ' if result[i] == '。' or result[i] == '!' or result[i] == '?': splitted_text += '\n' else: if result[i] != '。' and result[i] != '!' and result[i] != '?': splitted_text += ' ' if result[i] == '。' or result[i] == '!' or result[i] == '?': splitted_text += '\n'とか何とか書き換えるとうまくいくはずです。たぶん。
文章の生成
def textGen(file): f = open(file, 'r', encoding="utf-8") text = f.read() sentence = None while sentence == None: # 素材によってはNoneが返ることがあるので、その対策 # テキストを処理できる形に分割 splitted_text = split(text) # モデルの生成 text_model = markovify.NewlineText(splitted_text, state_size=3) # モデルを基にして文章を生成 sentence = text_model.make_sentence() # 学習データの保存 with open('learned_data.json', 'w') as f: f.write(text_model.to_json()) # 結合された一連の文字列として返す return ''.join(sentence.split())今回は青空文庫ではないものからの生成のために書いていたので、そのための処理は省いて単純に読み込んでいます。markovifyの割と標準的な手順なので、それ以外はおおよそ参考文献1に準じています。
また、state_sizeや素材文の分量の関係で時々Noneが返ってしまうことがある(markovifyのIssue#96, Issue#22)ので、ここでは安易にNoneじゃないものを返すまで回しておきました。ある程度の文章量があれば無限ループにはならないと思います。
なお、make_sentenceのキーワード引数triesで試行回数を指定しておくことでもある程度対応可能です。(下のコード)
# テキストを処理できる形に分割 splitted_text = split(text) # モデルの生成 text_model = markovify.NewlineText(splitted_text, state_size=3) # モデルを基にして文章を生成 sentence = text_model.make_sentence(tries=100)生成結果
テスト用に、青空文庫の坊ちゃんからdelruby.exeを用いてルビを削除し、不要な部分を除いたものを基に生成してみました。
- 一人不足ですがと考えてみると世の中はみんなこの生徒のようなものだ、虫の好かない奴が親切で、しかも上品だが、貧乏士族のけちん坊と来ちゃ仕方がないから、大きな声を出すもんだ。
- 溌墨の具合も至極よろしい、試してご覧なさいと、おれよりも下等だが、日本人はみな口から先へ免職になったら、よさそうな下宿を教えてくれるかも知れないから、為替で十円あげる。
- それも花の都の電車が通ってる所なら、野だは狼狽の気味で、はたで見ているときに来るかい」「そのマドンナさんが不たしかなのが、飛び起きると同時に忘れたようにうらなり君が眼に付く、途中をあるいていても眼がくらむ。
- 野だは時々山嵐に話しかけるが、山嵐の云う通りにした。
- 男はあっと小声に云ったが、やがて帰って来て言葉が出ないから、出すんだ。
- おれが戸を開けて中に居るんだ」「僕の前任者がやられたんだ。
- 歴史も教頭と同説だと云ってやった。
- 門口へ立ったなり中学校を教えろと云ったらあなたがおうちを持って教場へ出たら、山嵐は君それを引き込めるのかと驚ろいた。
- うらなり君に別れて、うちを出る時から、こんなに答えるんだろう。
- そうしておいて喧嘩を吹き懸ける男だ。
おおよそ目的は達せられているようです。
あとがき
JanomeもMeCabも日本語形態素解析をしてくれるという意味では同様の機能を持つので、細かい書き換えのみで実装できました。Bot作成時などに活用できそうです。
参考
- 投稿日:2020-05-18T15:22:20+09:00
Windows10 Python 3.8.3でOpenCVを使うためのメモ。
Windows10 Python 3.8.3でOpenCVを使うためのメモ。
pip install opencv-python を打つ。
Installing collected packages: numpy, opencv-python
Successfully installed numpy-1.18.4 opencv-python-4.2.0.34入ったみたいです。
あらかじめ書いておいたPCカメラから撮影するスクリプト(省略)を起動して確認します。
python C:\python\cvtest.py
はい、テレワークで子供たちのおもちゃになったデッサン人形君が撮影できました。
大丈夫みたいです。以上。
- 投稿日:2020-05-18T14:27:47+09:00
Windows10でPython環境構築メモ
Windows10でpython環境構築メモ
テレワーク等、諸事情にてパソコンを買い替えたのでWindows10にPython開発環境を作ったメモ。
古いパソコンで環境構築した際にメモっていなかったので。URL:https://www.python.org/downloads/
Looking for a specific release?で、Python 3.8.3を選択。
Windows x86-64 executable installer をダウンロードしてウィザードでインストール。
注)"Add Python 3.x to PATH"をチェックして環境変数に登録する。”Disable path length limit ”とか言うで、クリックしてあげる。
インストールが完了したか確認する。
CMD起動して、pythonと打つ。
大丈夫そう。おや、時間が。。まいっか。
後で設定を変えましょう。