- 投稿日:2019-12-11T23:50:29+09:00
Python,Numpyで非線形座標変換をする時のBilinear補間関数
目的
画像処理における座標変換では,変換後の画像の(i,j)のindexに該当する元画像の座標を逆変換を用いて求めて,Bilinear等の手法で補間するのが一般的です。
CやC++等では仕方なしに2重ループを回していたのですがNumpyとPythonではforループは極力使いたくないと思っていた所,効率の良さそうなコードをGithubのとあるページで発見したので自分なりに変えてみました。
言うまでも無いことですが,Affineだったり射影変換の場合はそれ専用のOpenCVのライブラリを使ったほうが早いです。
コードの流れ
1. 変換により参照される元画像の座標も求める
変換前の(i,j)の座標indexを記録した配列を作ります。
arangeを用いて配列を作成し,tileやrepeatを用いて重ね合わせればあっという間にできますね。以下の例では画像中心cx,cyから差し引いて,正規化した後に球面極座標に変換するために円周率をかけてたりしますが同じことです。
xline = (np.arange(wid)-cx)*2*pi/wid yline = -(np.arange(hei)-cy)*pi/hei Xsline = np.tile(xline,hei) Ysline = yline.repeat(wid,axis=0)あとは,これらの配列に対して様々な非線形変換を当てはめればOKです。
正弦余弦や対数など簡単な関数ならnumpyに用意があるので配列にそのまま渡せば全て一様に処理してくれてfor文を書くよりも断然早いです。2. 求めた座標を基に変換後の画像をBilinear補間で求める
先程求めた座標に対応するピクセル値を補間でもってきて最後に画像の形にReshapeしておしまいです。
以下のコードでは,ピクセルindexの最大値,最小値を超えた場合はループさせていますが,一般的には0などの値でうめる(Padding)するのが良いでしょう。
def _bilinear_interpolation_loop(frame, screen_coord): ''' Calculate input: frame and its subpixel coordinate [x,y] ''' frame_height,frame_width,frame_channel =frame.shape #uf = np.mod(screen_coord[0],1) * frame_width # long - width #vf = np.mod(screen_coord[1],1) * frame_height # lat - height x0 = np.floor(screen_coord[0]).astype(int) # coord of pixel to bottom left y0 = np.floor(screen_coord[1]).astype(int) uf = screen_coord[0] # long - width vf = screen_coord[1] # lat - height x2 = np.add(x0, np.ones(uf.shape).astype(int)) # coords of pixel to top right y2 = np.add(y0, np.ones(vf.shape).astype(int)) # Assume Loop x2 = np.where(x2 > frame_width-1, 0, x2) y2 = np.where(y2 > frame_height-1, 0, y2) base_y0 = np.multiply(y0, frame_width) base_y2 = np.multiply(y2, frame_width) A_idx = np.add(base_y0, x0) B_idx = np.add(base_y2, x0) C_idx = np.add(base_y0, x2) D_idx = np.add(base_y2, x2) flat_img = np.reshape(frame, [-1, frame_channel]) #print(flat_img.shape) A = np.take(flat_img, A_idx, axis=0) B = np.take(flat_img, B_idx, axis=0) C = np.take(flat_img, C_idx, axis=0) D = np.take(flat_img, D_idx, axis=0) wa = np.multiply(x2 - uf, y2 - vf) wb = np.multiply(x2 - uf, vf - y0) wc = np.multiply(uf - x0, y2 - vf) wd = np.multiply(uf - x0, vf - y0) #print(wa,wb,wc,wd) # interpolate AA = np.multiply(A.astype(np.float32), np.array([wa, wa, wa]).T) BB = np.multiply(B.astype(np.float32), np.array([wb, wb, wb]).T) CC = np.multiply(C.astype(np.float32), np.array([wc, wc, wc]).T) DD = np.multiply(D.astype(np.float32), np.array([wd, wd, wd]).T) out = np.reshape(np.round(AA + BB + CC + DD).astype(np.uint8), [frame_height, frame_width, 3]) return out実行例
以下のブログにて正距円筒図法の画像を元に全天球カメラを仮想的に回転させた際の画像を作成していました。
- 投稿日:2019-12-11T23:30:59+09:00
PyTorch入門(1)自動微分とか
Pytorch入門
ChainerがPyTorchへの移行を発表したということで、国内でもTensorFlowからPyTorchへの流れが強まるのではと思い、チュートリアルをこなしてみる。
インストール
公式 を参照。
チュートリアル
公式の通りに試してみる。
1.PyTorchとは
書き方はかなりNumpy-like。
$ x = torch.empty(5, 3) $ print(x) tensor([[2.4835e+27, 2.5428e+30, 1.0877e-19], [1.5163e+23, 2.2012e+12, 3.7899e+22], [5.2480e+05, 1.0175e+31, 9.7056e+24], [1.6283e+32, 3.7913e+22, 3.9653e+28], [1.0876e-19, 6.2027e+26, 2.3685e+21]])メソッドに_(アンダーバー)を接尾することで破壊的な処理となる。
$ x = torch.tensor([5.5, 3]) $ y = torch.tensor([2.5, 5]) $ y.add_(x) $ print(y) tensor([8., 8.])CPU-GPU間のテンソルのやり取りが非常に簡単。
if torch.cuda.is_available(): device = torch.device("cuda") y = torch.ones_like(x, device=device) x = x.to(device) z = x + y print(z) print(z.to("cpu", torch.double)) # ``.to`` can also change dtype together!
- 投稿日:2019-12-11T23:30:30+09:00
公式ドキュメントをちゃんと読まない私がDjangoの静的ファイルについて理解するまで
アドベントカレンダー11日目の記事です。
はじめに
週末・深夜Pythonistaです。仕事でデータまとめるときにpandas,numpy,scipyあたりをちょこちょこする程度の人です。
趣味でDjangoを使うにあたって静的ファイルでよく躓いたので、『わからないところがわからない人』向けに、その躓きポイントと解決法をまとめられたらなと思います。
間違っている点があればご指摘いただけるとありがたいです。
ぶっちゃけると公式ドキュメントがとてつもなく親切なので、
しっかり読んで、理解して、その通り手を動かせば必ず理解しているはずの内容なのです。Q1:そもそも静的ファイルis何?
静的ファイル?
動かないファイルって何だよ・・・?A1: Webの仕組みを知りましょう。
公式ドキュメント読みましょう
ものすごく乱暴に言うとCSS,JS等です。
Djangoが生成する動的なコンテンツに対して、サーバーから何も加工を加えずクライアント側に提供するファイルのことを指します。Webサーバの仕組み:MDNリファレンス
静的ファイルという言い方自体はDjango独自の言い回し(?)のようですが、ウェブアプリケーションの仕組みがわかっていれば難なく理解できるものだと思います。
これがわからないあなた(過去の自分)はWebの仕組みを勉強しましょう。
静的ファイル (画像、JavaScript、CSS など) を管理する¶
ウェブサイトではふつう、画像や JavaScript、CSS などの追加のファイルを配信する必要があります。Django では、こうしたファイルのことを「静的ファイル (static files)」と呼んでいます。静的ファイルの管理を簡単にするために、Django は django.contrib.staticfiles を提供しています。
(Django公式ドキュメント)ちゃんと公式ドキュメントに書いてありますね。。。
Q2:結局静的ファイルはどこに置くのが正解?
Django公式ドキュメント付属のチュートリアル・DjangoGirlsTutorial(有名なDjangoのチュートリアル)では、staticディレクトリをアプリ内に掘るように紹介されています。
polls ディレクトリの中に、 static ディレクトリを作成します。Django はそこから静的ファイルを探します。
(Django公式ドキュメント)
上記のpollsはアプリ名なので、アプリ内にディレクトリを作成するように指示しています。
静的ファイルはプロジェクトのどこに置けばいいの?
Djangoは、ビルトインの "admin" アプリにより、静的ファイルをどこで探せばいいのかわかっています。私たちがやることは、blog アプリのための静的ファイルを追加することだけです。そのために、blogアプリの中に static というフォルダを作ります
djangogirls
├── blog
│ ├── migrations
│ ├── static
│ └── templates
└── mysite
(DjangoGirlsTutorial)こちらも同様にアプリ内にディレクトリを作成しています。
一方で、ベストプラクティスではプロジェクト直下にstaticフォルダを置くことを推奨しています。
A2: どっちでもいい
アプリ内にstaticディレクトリを作成する場合も、以下のような構成にして名前空間を利用することがほとんどだからです。(※名前空間を使わない場合のまずい場合を番外編でご紹介します)
強いて言えば、散らばっていると個人的に見にくいので、ベストプラクティスを踏襲することをお勧めします。
併用は避けましょう。(アプリ名) ┣static └ (アプリ名) ┣css ┣jsQ3:本番環境css当たらない問題
Djangoアプリをデプロイしようとする者の心を折るものそれが静的ファイルだと信じています。。。
A3:Webサーバーの設定の見直しをしましょう
本番環境で静的ファイルの提供を行うのはWebサーバー側が担うことが多いと思います。
静的ファイルが一か所にまとめられているか?正しく配信の設定がなされているかを確認しましょう。Django側で気を付けるべきは1か所に静的ファイルをまとめられているかどうかに尽きます。
静的ファイルを1か所にまとめるコマンドはpython manage.py collectstatic
です。このコマンドを叩くと開発環境下でdjango.contrib.staticfilesから見えている静的ファイルをsettings.pyで設定した
STATIC_ROOT
に集められます。しかもすでに同名のファイルがある場合はタイムスタンプを確認して新しいものを保存してくれるスグレモノ!
これを行うことで、Nginxなどの設定が楽になります。このcollectstaticコマンド周りの公式ドキュメントは日本語化が進んでおらず英語のままなのも、公式ドキュメント斜め読み勢を陥れる一端となっているのかもしれません。。。
番外編:静的ファイルの衝突
アプリ内にstaticディレクトリを作成する場合、静的ファイルの名前の衝突に注意が必要です。
Djangoでは名前が同じ静的ファイルが存在する場合、最初に検索に引っかかった方のファイルのみ提供されます。以下のように同じ名前の静的ファイルが存在する場合、Django側ではどちらの静的ファイルを提供すべきかがわかりません。
この場合saticfilesが見つけた最初のcssを当てることになります。project_root/app1/static/main.cssh1{ color: blue; }project_root/app2/static/main.cssh1{ color: red; }なので、それぞれのテンプレートで以下のように
main.css
を呼ぼうとしても...project_root/app1/templates/app1.html{% load static %} <link rel="stylesheet" href="{% static "main.css" %}"> {% static "main.css" %} <!-- デバッグ用 --> <h1>App1.html<h1>project_root/app2/templates/app2.html{% load static %} <link rel="stylesheet" href="{% static "main.css" %}"> {% static "main.css" %} <!-- デバッグ用 --> <h1>App2.html<h1>このように片方のCSSしか当たりません。
App1はテキスト青色、App2はテキスト赤色にしたかったのに両方青色になってしまいます
こんな時はfindstaticコマンドを叩きましょう。
このコマンドを使うことで、Djangoの検索順がわかります。$ python manage.py findstatic main.css Found 'main.css' here: C:\Users\Cuz\Desktop\Projects\static_files_sample\app1\static\main.css C:\Users\Cuz\Desktop\Projects\static_files_sample\app2\static\main.cssこの時上のapp1のcssが当たり、app2のcssが当たっていないことがわかると思います。
これもまた公式ドキュメントに書いてあります
まとめ
公式ドキュメント読みましょう!読もう!いや読むんだ!
これに尽きますね。。。
改めて読むととても細かいところまで書いているので、きちんと隅から隅まで丁寧に読むことをお勧めします。(メディアファイル関連やデプロイまでは時間が足りませんでした。。。来年こそ計画的に書くぞっ!)
明日は@skd_nwさんの『開発で使ったライブラリの話とか』です!
よろしくお願いいたします!
- 投稿日:2019-12-11T23:19:48+09:00
Cisco IOS-XEのACLをRESTCONFで操作する(Python編)
はじめに
モデル駆動型プログラマビリティで使用されるトランスポートプロトコル(NETCONF、RESTCONF、gRPC)の内、RESTCONFを使ってCisco IOS-XEのACL設定を行った時のメモです。
操作ツールとしては、Postman、Python、Ansible等がありますが、今回はPythonを使い、名前付き拡張ACLに対してCRUD(作成、取得、更新、削除)を行いました。1. RESTCONF概要
RESTCONFは、RESTFulインターフェースを提供するHTTP(S)ベースのプロトコルです。データ形式として、XMLに加えJSON形式もサポートしています(NETCONFはSSHベースでXML形式のみ)。
RESTCONF/NETCONFのCRUD操作
RESTCONF NETCONF GET <get>
,<get-config>
POST <edit-config>
(operation="create")PUT <edit-config>
(operation="create/replace")PATCH <edit-config>
(operation="merge")DELETE <edit-config>
(operation="delete")IOSはもともとREST APIもサポートしていますが、REST APIがベンダー固有の実装なのに対し、RESTCONFはRFC8040で標準化されたプロトコルであり、別物になります。
RESTCONFを含むモデル駆動型プログラマビリティの概要は、DevNet Learning Labsの以下セッションで分かり易くまとめられています。
モデル駆動型プログラマビリティの紹介2. 用意した環境
こちらの記事と同様、Cisco dCloud環境を利用させて頂きました。
対象機器は「CSR1000v with IOS XE 16.08」、クライアントのPythonバージョンは「3.6.8」を使用しました。3. ACL作成
まず、ACL設定が何も無い状態から、ACL名
TEST
を1行追加します。3-1. Pythonコード
URL
https://[IPアドレス]:[ポート番号]/restconf/data/Cisco-IOS-XE-native:native/ip/access-list
に対し、PUT
メソッドでリクエストを行っています。Body内のデータはJSON形式で定義しています。
ちなみにPOST
メソッドでは作成できませんでした。create_acl.py#!/usr/bin/python import requests import json # disable warnings from SSL/TLS certificates requests.packages.urllib3.disable_warnings() # credentials for CSR1000v HOST = '[IPアドレス]' PORT = 443 # 環境によって異なる USER = '[ユーザ名]' PASS = '[パスワード]' def main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # RESTCONF doby for new ACL body_data = { "Cisco-IOS-XE-native:access-list": { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST", "access-list-seq-rule": [ { "sequence": "30", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.4.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] } } # this statement performs a PUT on the specified url response = requests.put(url, auth=(USER, PASS), headers=headers, data=json.dumps(body_data), verify=False) # print the json that is returned print(response) if __name__ == '__main__': main()3-2. 実行結果
Status Code 204 (No Content)が返ってきました。
$ python create_acl.py <Response [204]>Configを見ると、想定通りのACLが作成されていました。
csr1#sh run | begin TEST ip access-list extended TEST permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.2554. ACL取得
次に、作成したACLをRESTCONFで情報取得してみます。
4-1. Pythonコード(1) IP設定取得
main()
関数以外は3.と同様のため、これ以降は抜粋して記載します。
まず、URLhttps://[IPアドレス]:[ポート番号]/restconf/data/Cisco-IOS-XE-native:native/ip
に対し、IP関連設定の情報をGET
メソッドで取得してみました。get_ip_info.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # this statement performs a GET on the specified url response = requests.get(url, auth=(USER, PASS), headers=headers, verify=False) # print the json that is returned print(response.text)4-2. 実行結果(1) IP設定取得
途中の
"access-list": { "Cisco-IOS-XE-acl:extended": [ ~ ] }
で、設定した内容を確認できました。
ちなみに、ACL設定前はこの"access-list"
キーを含めて作成されていませんでした。そのため、3.の作成時、下の階層の"Cisco-IOS-XE-acl:extended": [ ~ ]
をいきなりPUTしてもエラーになりました。$ python get_ip_info.py { "Cisco-IOS-XE-native:ip": { "domain": { "name": "demo.dcloud.cisco.com" }, "forward-protocol": { "protocol": "nd" }, "route": { "ip-route-interface-forwarding-list": [ { "prefix": "0.0.0.0", "mask": "0.0.0.0", "fwd-list": [ { "fwd": "GigabitEthernet1", "interface-next-hop": [ { "ip-address": "198.18.128.1" } ] } ] } ] }, "ssh": { "rsa": { "keypair-name": "ssh-key" }, "version": 2 }, "access-list": { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST", "access-list-seq-rule": [ { "sequence": "30", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.4.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] }, "Cisco-IOS-XE-http:http": { "authentication": { "local": [null] }, "server": true, "secure-server": true } } }4-3. Pythonコード(2) 拡張ACL設定取得
こちらは、より深い階層のURL
https://[IPアドレス]:[ポート番号]/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended
で、拡張ACLのみをピンポイントで取得する例です。get_acl.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # this statement performs a GET on the specified url response = requests.get(url, auth=(USER, PASS), headers=headers, verify=False) # print the json that is returned print(response.text)4-4. 実行結果(2) 拡張ACL設定取得
$ python get_acl.py { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST", "access-list-seq-rule": [ { "sequence": "30", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.4.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] }5. ACLマージ
既存ACLに対し、エントリ(ACE)追加と、別のACLを追加する例をご紹介します。
5-1. Pythonコード(1) エントリ追加
既存のACL名
TEST
の2行目に、エントリを追加してみます。先ほどのシーケンス番号30
に対し、今回は50
を指定しています。ちなみにシーケンス番号を指定しない場合、エラーで設定できませんでした。
マージ用のメソッドであるPATCH
を使い、既存設定はそのままで、Body内の内容を追加している形です。merge_ace.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # RESTCONF doby for new ACL body_data = { "Cisco-IOS-XE-native:access-list": { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST", "access-list-seq-rule": [ { "sequence": "50", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.5.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] } } # this statement performs a PUT on the specified url response = requests.patch(url, auth=(USER, PASS), headers=headers, data=json.dumps(body_data), verify=False) # print the json that is returned print(response)5-2. 実行結果(1) エントリ追加
Status Code 204 (No Content)が返ってきました。
$ python merge_ace.py <Response [204]>Configを見ると、想定通り2行目に追加されていました。
csr1#sh run | begin TEST ip access-list extended TEST permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255 permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.2555-3. Pythonコード(2) 別のACL追加
今度は、別のACL名
TEST2
を1行追加してみます。merge_another_acl.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # RESTCONF doby for new ACL body_data = { "Cisco-IOS-XE-native:access-list": { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST2", "access-list-seq-rule": [ { "sequence": "70", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.7.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] } } # this statement performs a PUT on the specified url response = requests.patch(url, auth=(USER, PASS), headers=headers, data=json.dumps(body_data), verify=False) # print the json that is returned print(response)5-4. 実行結果(2) 別のACL追加
$ python merge_another_acl.py <Response [204]>ACL名
TEST2
が追加されている事が分かります。csr1#sh run | begin TEST ip access-list extended TEST permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255 permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255 ip access-list extended TEST2 permit ip 192.168.7.0 0.0.0.255 192.168.100.0 0.0.0.2556. ACLリプレイス
今度は、既存ACLを上書きし、別のACL名
TEST3
を作成する例です。通常のACL作業では基本的に実施しない(意図せずやってしまうと大事故になる)ケースだと思います。ただ、ACL設定以外では、パラメーターシートを元に「~~の設定は〇〇であるべき」のように宣言型で定義できるので、既存設定削除などの煩わしさ無しで設定できるメリットはあると思います。6-1. Pythonコード
メソッドは、新規作成時と同様
PUT
を用います。replace_acl.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # RESTCONF doby for new ACL body_data = { "Cisco-IOS-XE-native:access-list": { "Cisco-IOS-XE-acl:extended": [ { "name": "TEST3", "access-list-seq-rule": [ { "sequence": "90", "ace-rule": { "action": "permit", "protocol": "ip", "ipv4-address": "192.168.9.0", "mask": "0.0.0.255", "dest-ipv4-address": "192.168.100.0", "dest-mask": "0.0.0.255" } } ] } ] } } # this statement performs a PUT on the specified url response = requests.put(url, auth=(USER, PASS), headers=headers, data=json.dumps(body_data), verify=False) # print the json that is returned print(response)6-2. 出力結果
$ python replace_acl.py <Response [204]>見事に上書されました。
csr1#sh run | begin TEST ip access-list extended TEST3 permit ip 192.168.9.0 0.0.0.255 192.168.100.0 0.0.0.255ちなみに、以下記事を書いた方によると、NX-OSの場合、
PUT
メソッドがRFC8040の仕様通りではなく、PATCH
メソッドと同様マージになるケースもあるようです。
Exploring IOS-XE and NX-OS based RESTCONF Implementations with YANG and Openconfig7. ACL削除
最後に、作成したACLの削除を行ってみます。
事前に「3.ACL作成」と「5.ACLマージ」を実行し、TEST3
が上書きされ、TEST
とTEST2
が設定された状態にしておきます。csr1#sh run | begin TEST ip access-list extended TEST permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255 permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255 ip access-list extended TEST2 permit ip 192.168.7.0 0.0.0.255 192.168.100.0 0.0.0.2557-1. Pythonコード
URL
https://[IPアドレス]:[ポート番号]/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended=TEST2
に対し、DELETE
メソッドでリクエストを行います。
URLの末尾に/extended=TEST2
を指定することで、TEST2
をACL単位で削除できます。
(エントリ単位での削除方法はちょっと分かりませんでした。。)delete_acl.pydef main(): # url string to issue request url = "https://{h}:{p}/restconf/data/Cisco-IOS-XE-native:native/ip/access-list/extended=TEST2".format(h=HOST, p=PORT) # RESTCONF media types for REST API headers headers = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} # this statement performs a DELETE on the specified url response = requests.delete(url, auth=(USER, PASS), headers=headers, verify=False) # print the json that is returned print(response)7-2. 実行結果
$ python delete_acl.py <Response [204]>
TEST2
が削除されました。csr1#sh run | begin TEST ip access-list extended TEST permit ip 192.168.4.0 0.0.0.255 192.168.100.0 0.0.0.255 permit ip 192.168.5.0 0.0.0.255 192.168.100.0 0.0.0.255最後に
まだインターネット上にサンプルコードが少なかったので、お試しでやってみました。以前の記事で、要件資料と複雑なJinja2テンプレートを組み合わせてACL Configの生成を行いましたが、RESTCONFであれば、要件資料のようなパラメーター情報をJSON形式に変換すれば設定ができるため、自動化と相性が良いと思います。
今回はPythonの例でしたが、Ansibleのrestconf_get
とrestconf_config
モジュールを使った例や、設定項目毎のモデル(URL)の確認・作成方法もまとめてみたいと思います。
- 投稿日:2019-12-11T23:10:50+09:00
Power BI で表現可能なR, Pythonの地理情報可視化
本記事は@yugoes1021王子による Power BI Advent Calendar 2019に参加しています。
もうネタが無いので
Power BIで地理関連の記事を各種書いてきましたが、単に地図上に可視化するという観点では、もうあまりネタが無いので、どうしても重箱の隅をつつくような記事になりがちです。(とはいえ、Web版とかEmbeddedとかを触るリソース、時間がない orz)
なので、残る可能性としてはPower BI Desktop上でRやPythonを使った地図だろうということに落ち着きました。
R, Pythonでの拡張は各所で紹介されているので、ここではオフィシャルのリンクだけにしておきます。
前と同じUberのオープンデータを使っての評価をしています。サンフランシスコのタクシープローブデータです。R を使用した Power BI ビジュアルの作成
Power BI Desktop で Python スクリプトを実行するR
Rの方がバリエーションがあります。Power BIではPythonより歴史があります。
若干躓きやすいのが、PowerBIが使うRのバージョン、インストール場所です。
以下のオプションページで設定できますので、自分の使いたい、いつも使っているRインタープリタを指定しましょう。
そうすることで、libraryのインストールの手間が省けます。
ただし、同じインタープリタでも環境をユーザーフォルダに保存する場合もあるので、その場合はグローバルなインタープリタ環境でインストールする必要があるでしょう。library(maps)
古めのライブラリです。基本的には各種の白地図を表示して、その上にデータを表示するものです。
(コード中のggmapはバウンディングボックスを得る便利関数のためだけに使います)
with関数でポイントを重畳できます。library(maps) library(ggmap) sbbox <- make_bbox(lon = dataset$longitude, lat = dataset$latitude, f = 0) map('usa', col = "grey", fill = TRUE, bg = "white", border = 0, xlim = c(sbbox[1], sbbox[3]), ylim = c(sbbox[2], sbbox[4])) with(dataset, points(longitude, latitude, pch = 1, col = 'blue', cex = .2))library(sf)
空間データを適切に扱うためのライブラリです。一度sf形式のデータフレームに変換する必要があります。
なんとデータフレームを直接plotできます。library(sf) library(sp) dfsf <- dataset %>% st_as_sf(coords = c('longitude', 'latitude'), crs = 4236) plot(dfsf, col = "blue", pch = 21)library(tmap)
比較的容易にいろいろな主題図を描くことができるライブラリです。
通常のplotモードとLeafletビューアが立ち上がるviewモードを切り替えられ、便利です。
ですが、以下をみてわかるように、viewモードではベースマップが貼り付けられません。残念。library(tmap) library(dplyr) library(sf) library(sp) dfsf <- dataset %>% st_as_sf(coords = c('longitude', 'latitude'), crs = 4236) tmap_mode("plot") map <- tm_shape(dfsf, name = "uber") + tm_symbols(shape = 21, col = "blue", size = 0.05) + tm_basemap("Stamen.Watercolor") maplibrary(ggplot2)
ggplotにマップを描く機能が統合されています。おそらく、一般的なデータ処理を行う人は、通常はこれを使うのが一番しっくりくるのではないでしょうか?
library(ggplot2) library(mapproj) library(ggmap) sbbox <- make_bbox(lon = dataset$longitude, lat = dataset$latitude, f = 0) usmap <- map_data("state") ggplot() + geom_polygon(data = usmap, aes(x = long, y = lat, group = group), fill = "grey", alpha = 0.5) + geom_point(data = dataset, aes(x = longitude, y = latitude)) + theme_void() + coord_map(xlim = c(sbbox[1], sbbox[3]), ylim = c(sbbox[2], sbbox[4]))library(ggmap)
やはり背景地図をもっと細かいものが欲しいとなると、これです。
どうもGoogle Maps APIの制限がきつくなったせいか、API Keyの登録が必要です。
また、便利な登録用関数がある以下の開発版を入手するようにしましょう。自分のR環境で以下の方法で最新版をインストールすると、register_googleというキー設定が可能な関数が入ってきますので、アップグレードしておきます。
devtools::install_github("dkahle/ggmap")library(ggplot2) library(mapproj) library(ggmap) register_google(key = "YOUR_API_KEY") sbbox <- make_bbox(lon = dataset$longitude, lat = dataset$latitude, f = 0) map <- get_stamenmap(bbox = sbbox, zoom = 13, maptype = "toner-lite") ggmap(map) + geom_point(aes(x = longitude, y = latitude), color = "blue" ,data = dataset, alpha = .5)Python
Folium, ShapelyなどPythonには本格的な地図系可視化ライブラリや、非常に扱いやすいgeo pandasなど地理データ処理ライブラリがそろっているのですが、Power BI上で試そうとしたところ、なかなか動いてくれませんでした。
同じくFoliumを動かそうという人がいたのですが、以下のように、そもそも現状のPower BIでは限られたライブラリしか動かないらしく、素直にあきらめることにしました。。。Help to implement Python Script - Microsoft Power BI Community
The following Python packages (non-Intel MKL) are currently supported for use in your Power BI reports. Reference: Python packages and versions
- Matplotlib
- numpy
- pandas
- scikit-learn
- scipy
- seaborn
- statsmodels
Pythonもインタープリタを以下で設定します。Anacondaになると思いますが、たとえ新しいライブラリをインストールしても、Power BIでは使えないので、ご了承ください。
Matplotlib
その中でもMatplotlibにはmpl_toolkits:basemapというライブラリが存在するようです。
Matplotlib標準ではなく、インストールする必要があります。現在はpipインストールはサポートしておらずcondaなどを使います。conda install -c anaconda basemap
でインストールするとAnaconda環境で使えるようになりました。
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap m = Basemap(llcrnrlon=BBox[0],llcrnrlat=BBox[2],urcrnrlon=BBox[1],urcrnrlat=BBox[3]) m.drawcoastlines() x, y = m(dataset.longitude, dataset.latitude) m.plot(x, y, 'o') plt.show()が、そもそもMatplotlib以外のライブラリだめなので、PowerBIでは動きませんでした。orz
恒例の性能評価
以前と同じデータを使っているので、標準ライブラリとの比較をしてみましょう。
クエリエディタであらかじめレコード数を絞れるようにして試しました。
Pythonはお茶を濁して単に2次元グラフを表示しています。1,000レコード
標準マップ含めて、問題なく表示されます。あくまで表示された数の話ですが。
10,000レコード
標準マップはすべての点が表示されない旨のメッセージが出ます。
他も見た感じ、大きな欠落は無い模様です。スピードについてもどれもそれほど変わりません。100,000レコード
ArcGISはかけ始めました。標準マップはどうもランダムサンプリングをするようになっており、見た目の範囲はそれほど変わっていません。
他のライブラリは、PowerBIで動いているとは知らず、全部表示できているようです。(ほんと?)
tmapとggmapが少し遅いかなという以外は、それほど変わりません。1分も待たされることはありません。1,000,000レコード
ここまでくると、Rビジュアルについても、データの間引きが行われているようです。
また、Uberデータの中に、ラスベガスまで行っている車があるので、全体を表示するのにggmapは時間がかかります(地図の拡大率の調整が必要)まとめ
このような単純なマップではわざわざRのコードを使って可視化する意味は薄いのですが、特殊な描画や演算が必要な場合は、Rでしっかりとライブラリ化したものを埋め込んで使うようにすれば、出番もあるのではと思いました。
- 投稿日:2019-12-11T22:56:21+09:00
Pythonでアルゴリズム(ABC 146 C 二分探索
今日からatcoderをpythonで解いていきます。
https://atcoder.jp/contests/abc146/tasks/abc146_c
この問題は二分探索の最も典型的な問題です。
二分探索、理論はシンプルですが実装すると境界でWAしたりしなかったりしてしまいますよね。
かといって最後の2つに絞れたら2つとも試してみて答える、なんて泥臭いこともしたくないですww今回は、左側から詰めていく二分探索(ある数を超えないぎりぎりの数を探すタイプの二分探索です)
l = 0 r = 1000000001 a, b, x = tuple(map(int, input().split(" "))) while l < r - 1: m = l + (r - l) // 2 p = m * a + b * len(str(m)) if p > x: r = m else: l = m print(l) #l = left, m = middle, r = right境界でバグらせないための今回のポイントは
if p > x: r = m else: l = mの部分です。
なんでここがポイントなの!?それは、l + (r - l) // 2
ここを見てください。
書き換えると、(l + r) // 2
の部分ですね。(桁数がオーバーしないように。PYTHONなら不要という説をよく聞きます)この値って、//で割られているのl+rが奇数のときは10.5とか1000.5とか、floatだと端数が出てきててこれを切り捨てています。
つまり、mはキモチ左によっているこれを読んで「いい加減すぎる!ふざけるな!!」と怒る人がいるかもしれません。
そういう人たちには声を大にして言いたい。
すみませんでした超えちゃいけないから恐る恐る左から寄ってくる感じにしたらいいんじゃないかな
くらいの発送です。ではでは
- 投稿日:2019-12-11T22:55:02+09:00
2Dデータ(DXF)に適当な高さ情報を加えて3Dデータ(STL)を出力
open3dとdxfgrabberを使えばよい.
dxf2stl.pyimport open3d as o3d import dxfgrabber # DXFを読み込み dxf = dxfgrabber.readfile("test.dxf") # 実線の円だけ選択 c_cirs = [e for e in dxf.entities if e.dxftype == 'CIRCLE' and e.linetype == 'CONTINUOUS'] mesh = None H = 50.0 # 円筒の高さ Z = 0.0 # 円筒を並べるZ値 for c in c_cirs: m = o3d.geometry.TriangleMesh.create_cylinder(radius=c.radius, height=H) m.translate([c.center[0], c.center[1], Z]) if mesh is None: mesh = m else: mesh += m mesh.compute_vertex_normals() # STLで出力 o3d.io.write_triangle_mesh("sample.stl", mesh)
- 投稿日:2019-12-11T22:55:02+09:00
2Dデータ(DXF)に適当な高さ情報を与えて3Dデータ(STL)を出力
open3dとdxfgrabberを使えばよい.
dxf2stl.pyimport open3d as o3d import dxfgrabber # DXFを読み込み dxf = dxfgrabber.readfile("test.dxf") # 実線の円だけ選択 c_cirs = [e for e in dxf.entities if e.dxftype == 'CIRCLE' and e.linetype == 'CONTINUOUS'] # 立体化 mesh = None H = 50.0 # 円筒の高さ Z = 0.0 # 円筒を並べるZ値 for c in c_cirs: m = o3d.geometry.TriangleMesh.create_cylinder(radius=c.radius, height=H) m.translate([c.center[0], c.center[1], Z]) if mesh is None: mesh = m else: mesh += m mesh.compute_vertex_normals() # STLで出力 o3d.io.write_triangle_mesh("sample.stl", mesh)色とかレイヤー毎に高さ決めたDXF描いて,自動で立体化したい.
- 投稿日:2019-12-11T22:31:32+09:00
弱い紐帯の強さとコミュニティ
この記事は静岡アドベントカレンダー13日目の記事です。
はじめに
朝ツイッター見てたら私の知っている静岡中部のIT勉強会というエントリーがドンブラコと流れてきて、うっかり拾ってしまい、つい埋めねばならない気がしたので、今回書くことにしました。ちなみに言語戦争の歴史は私の駄スライドなので、その当時の事はそっちを見てください。
私自身はShizuoka.pyの主催をやっていますが、最近は頻度高くは開催していません。ガチのPythonistaが東京に流れちゃったりとか、忙しくなって参加しにくくなったりとか色々あって参加者が減ってしまった、、、かといって初心者対応は静岡読書会で疲れちゃったしなーっていうのが主な理由です。
最近の静岡のPython事情については20日目に載るようなので今回は特にここでは触れません。
弱い紐帯の強さとプログラミングコミュニティ
弱い紐帯の強さという言葉をご存知でしょうか?要するに社会的なつながりが強いほど、保持している情報は類似してしまい、ある程度弱いつながりからのほうが新しい情報やアイデアがもたらされるという一見逆説的な理論です。
プログラミングコミュニティはまさに弱い紐帯を構成しているわけで、プログラミングという弱い共通項で多様な背景の人間が情報交換することで得られるものは大きいと思うし、皆さん恩恵を感じていると思います。そういう意味で、静岡に色々なコミュニティが形成されて多様なエコシステムが出来上がっている現状は望ましいことだと思います。
Mishima.sykという謎コミュニティ
静岡で開催されているコミュニティのなかでも特に秘密のヴェールに包まれている感があるのがMishima.sykでしょう。Mishima.sykは主にライフサイエンス業界の人が集まってPythonやRやJavascriptとか機械学習や深層学習の話をしています。静岡には製薬企業の研究所があったり、遺伝研があるので、そっち系の研究者がかなり集まっている関係でそういうコミュニティが形成されました。
資料や内容などもできるだけオープンにしているので、もし機械学習とかプログラミングをベースにしていて、バイオインフォマティクスとかケモインフォマティクスにも興味があるんだよねーという方がいましたら、気軽に参加してください。色々勉強になったり新しい発見があったりすると思うし、弱い紐帯の強さというものも実感できると思います。
静岡でバイオ・ケミストリ関連の学部に通っていて、Dryにも興味のある学生さんとか、バイオ系のDryの仕事をしてみたいなーというIT系の方も参加するとコネクションができていいことあるかもしれません。
Shizuoka.pyどうなってんの?
海外のPyConに遊びに行くのが忙しくてさぼってます。来年には開催しようと思っています。
- 投稿日:2019-12-11T22:23:25+09:00
Cifar10用DCGANモデルを晒すwith keras
概要
- 時間がないので,GANの仕組みなどはとりあえず割愛
- generatorとdiscriminatorの設定を書く
- 味噌はgenerator,discriminatorともに活性化関数にLeakyReLuを使う
- 学習プロセス全体のソースコードはgithubにアップロード予定です(準備中です申し訳ありません)
モデルパラメータ達
Generator
generatordef _build_generator(self) -> Model: start_pix_x = 4 start_pix_y = 4 kernel_ini = RandomNormal(mean=0.0, stddev=0.02) inputs = Input(shape=self.noise_shape) x = Dense( units=256*start_pix_x*start_pix_y, kernel_initializer=kernel_ini, bias_initializer='zeros')(inputs) x = LeakyReLU(alpha=0.2)(x) x = Reshape((start_pix_x, start_pix_y, 256))(x) x = Conv2DTranspose( filters=128, kernel_size=4, strides=2, padding='same', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) # x = BatchNormalization(axis=3)(x) x = Conv2DTranspose( filters=128, kernel_size=4, strides=2, padding='same', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) # x = BatchNormalization(axis=3)(x) x = Conv2DTranspose( filters=128, kernel_size=4, strides=2, padding='same', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) x = Conv2D( filters=3, kernel_size=3, padding='same', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) y = Activation('tanh')(x) model = Model(inputs, y) if self.verbose: model.summary() return modelDiscriminator
discriminatordef _build_discriminator(self) -> Model: kernel_ini = RandomNormal(mean=0.0, stddev=0.02) inputs = Input(shape=self.shape) x = GaussianNoise(stddev=0.05)(inputs) # prevent d from overfitting. x = Conv2D( filters=64, kernel_size=3, padding='SAME', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) # x = Dropout(0.5)(x) x = Conv2D( filters=128, kernel_size=3, strides=2, padding='SAME', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) # x = Dropout(0.5)(x) # x = BatchNormalization(axis=3)(x) x = Conv2D( filters=128, kernel_size=3, strides=2, padding='SAME', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) # x = Dropout(0.5)(x) # x = BatchNormalization(axis=3)(x) x = Conv2D( filters=256, kernel_size=3, strides=2, padding='SAME', kernel_initializer=kernel_ini, bias_initializer='zeros')(x) x = LeakyReLU(alpha=0.2)(x) x = Flatten()(x) features = Dropout(0.4)(x) validity = Dense(1, activation='sigmoid')(features) model4d = Model(inputs, validity) model4g = Model(inputs, validity) if self.verbose: model4d.summary() return model4d, model4g出力結果
出力結果の行はクラスに対応しています.
DCGANはただ画像を生成するだけですが,元の画像で構築した学習モデルに生成画像を入力して予測されたラベルによってラベリングして,予測クラスごとに画像を出力させています.
generatorにLeakyReLUをいれることで,物体の対象がよりしっかりと生成できてる感がある感じがします.
結論
突貫でやったので,詳しく後日ちゃんと書きたいと思います.
- 投稿日:2019-12-11T22:15:50+09:00
DiscordAPIでラグナロクマスターズのチーム管理ツールを作りました
この記事はDiscord Advent Calendar 2019の11日目の記事です。
ごきげんよう?
DiscrodAPIを使ってラグナロクマスターズのチーム管理ツールを作成した @bboobbaa です。当記事では、ツールのコンセプトや構成、運用について、サービス開発について大切だと感じたことをご紹介します!
ラグナロクマスターズとは
ラグナロクオンラインが2002年に公開されてから17年経つ2019年、スマホ対応のラグナロクマスターズ(以下ラグマス)がリリースされました!
昔は2Dだったキャラクターも3Dとなり、「ラグナロクをを知るものたちよ、今こそここに来たれ!」というゲームです。
まだ遊んだことがない方はラグマスで遊びましょう?♀️ゲームコミュニケーションはDiscord時代
さてさて近年、ゲームのコミュニケーションやコミュニティ運用は、Discord全盛と言っても大袈裟ではないですよね。
ラグマスでも多くのギルドがDiscordを利用しています。
こういった現状を踏まえ、Discordを使ったラグマスギルドをお手伝いするためのツールを作りました!「カプラのラグマスギルド管理」公開
サービス「カプラのラグマスギルド管理」のコンセプトはギルドマスター支援としました。
ラグマスのギルドマスターを助けるサービスを目指しています。
なぜギルドマスターを助けるの
実は、ラグマスのギルドマスターはとても多忙なのです!
ラグマスのギルドマスター(以下ギルマス)は多くの仕事を抱えています。
一例を挙げると以下のようなものです。
- 攻城戦メンバー出席管理
- 攻城戦パーティー調整
- メンバー勧誘やギルドPRなどの情報発信
これらは通常Discordや、グーグルスプレッドシート、Twitterなどで行われています。
ギルマスは単にゲームを遊ぶだけでなく、これら複数のツールを駆使しながらギルドメンバーが楽しく遊べる環境づくりに貢献しています。実はギルマスを続けるのってすごく大変じゃないですか??
なんとかギルマスを助けなくちゃという気持ちでサービスを開発しました。Discordのイベント出欠機能をリリース
2019年9月、「カプラのラグマスギルド管理」をリリースしました。
ラグマスのギルド管理支援サービスを公開しました? https://t.co/pOhW4no9qS
— わかなだょ〜@ラグマスリネレボ (@wakanadayo_game) September 4, 2019
ギルマスのメンバーステータスやGvG出欠をDiscordボットを使って応援します!今後も随時アップデート予定です!#ラグマス #ラグマスギルド以下のような機能を備えています。
- Discordボットからの対話コマンド機能
- Discordからのメンバーインポート
- アカウント登録機能
- イベント出欠機能
こんなかんじで、リーダーが管理画面からDiscordの部屋と連携してイベントを立てると、メンバーがDiscordから返事をするとGvGの出欠が取れたりします?https://t.co/XPClL1BJNq pic.twitter.com/puBpkGC4s6
— わかなだょ〜@ラグマスリネレボ (@wakanadayo_game) September 4, 2019とにかく公開したかったので最低限の機能という感じで公開しました。
サービスの売りとしては、ギルドメンバーが使っているDiscordからボット対話ができるのでスプレッドシートより学習コストが少ない点、アカウント機能があるためスプレッドシートの公開運用よりもセキュアであるところです。
構成
ユーザーは2箇所からサービスにアクセスすることができます。
Discordからはボット経由で、ウェブからはブラウザで「カプラのラグマスギルド管理」にアクセスできる仕組みです。
Discordのボット側はdiscord.py、サーバーサイドはLaravelからMySQLをみています。
LaravelからDiscordを叩きたい場合は、RestCordを使用しました。フロントはBootstrapです。
開発の感触
Discordボット開発では、discord.pyがすごくいいです。
discord.pyでなんでもできるので不自由しないはずです。他にはLaravelの権限でGateが便利でした。権限追加がとてもやりやすいです。
リリース後の運用
リリース後は運用フェーズになります。
幸運にも初期からいくつかのラグマスのギルドにご利用いただけました。初期からユーザーに使っていただいたことが、サービスが進化するきっかけとなりました。
リリース直後は問題山積
リリース日を急いだこともあり、リリース後は問題山積みでした。
- バグ報告
- ユーザーからの要望
- 仕様変更部分の修正
リリース後すぐに「仕様をぼんやりしたたままリリース日のラインだけを引いた開発」、を進めたツケを払うことになりました。
テストできなかった箇所やリリース直前の仕様変更部分でのバグ対応や、ユーザーさんからもっとこうして欲しいという意見を次々といただき対応に追われました。
一時期は1日1件ペースで問い合わせをいただくこともありました。
サービスを公開していると、アプリケーションやインフラだけでなくUIもテキストなどの説明文も全てがユーザーのUXに関係してきます。十分な対応がない箇所は全てが脆弱性になる可能性があります。しかし全ては身から出た錆であり、ギルマスとラグマスユーザーのためには対応しなければなりません。
ユーザーのおかげでサービスがどんどんよくなる
「カプラのラグマスギルド管理」ではユーザーさんから要望をいただき新機能がどんどん追加されています。
最近では、ボットからDiscordのリアクションAPIを利用してPOSTできたりと少しずつバージョンアップを重ねています。サービスが良くなってくると Twitter でご紹介いただけるようになった気がしています。
わかなさんがまた更新してくれました
— あにすま@ラグマス神々の黄昏 (@anisuma_tawapri) November 17, 2019
カプラのラグマスギルド管理https://t.co/smIIeO06G2
これまでスポット管理には特化していた為、定期イベント管理は少し苦手でしたが、リセット機能を使うことで、イベント設定の使い回しが出来るようになりました。
どんどん使いやすくなってますね#ラグマス https://t.co/vTSl4P2gtn食器洗い乾燥機と言われました?♀️
4) イベントの登録
— くーろん@MAFiA (@Cloner_RO) December 9, 2019
管理画面でこんな感じに入力
5) Discordで出欠確認
「イベント」コマンドでbotが登録イベントを発言。スタンプを押すだけ
6) リマインドは「確認」コマンド
まずは出欠確認の部分だけだけど、便利すぎて食器洗い乾燥機を思い出したよ #ラグマス pic.twitter.com/LW5ZSInJVZ正直な話、リリース時を振り返ると機能が少なく、わかりにくい箇所や使いにくいところばかりでした。
そんな不十分なツールでも使ってくださったユーザーのみなさんに本当に感謝しかないです。この感謝を胸に、今後もギルマスを支援できるツールを目指し真剣に向き合っていきます。
最後はサービス開発について
結論からいえば、サービス開発で大事なことは2つあると思いました。
- まずはリリースする
- 問い合わせ窓口を用意する
まずリリースする
サービス開発では、「まずリリースしろ」とよく言われます。
これは本当にその通りだと感じました。リリースするからことによって自分の考えが足りなかった箇所や思い込みに気づかされます。
サービスをリリースするだけで、多くのことを学ぶことができるからです。問い合わせ窓口を用意する
あとはユーザーさんから連絡をもらうための問い合わせ窓口を設けることが大事です。窓口はTwitterでもなんでも大丈夫です。ユーザーさんから意見をいただければ励みになり、開発が進みます。
ユーザーさんが開発の後押しをしてくれるのです。
Discordボットいいですよ
この記事を読んで、Discordボットサービス開発に興味を持った方がいたら、ぜひリリースしてください。
ただし問い合わせ窓口は必ず用意しましょう。P.S.
Discordはすごいサービスです。Qiitaをご覧のみなさんにはSlackやChatworkが有名かもしれませんが、Discordいいですよ。APIや権限周りは綺麗で使いやすいと感じました。Discordボットを作ったことがない方がいたらぜひ試して欲しいと思います!
商標
© Gravity Co., Ltd. & Lee MyoungJin(studio DTDS). All rights reserved.
© GungHo Online Entertainment, Inc. All Rights Reserved.
- 投稿日:2019-12-11T22:06:46+09:00
DXFをpythonで扱う
dxfgrabberを使うとpythonでDXFを読めるらしい
pip install dxfgrabber
フリーの2DCAD jw_cadで適当なDXFファイルを作っておいて,読んでみる.
dxf.pyimport dxfgrabber dxf = dxfgrabber.readfile("test.dxf") # ヘッダ # print(dxf.header) # 図形要素 # print(vars(dxf.entities)) cirs = [e for e in dxf.entities if e.dxftype == 'CIRCLE'] lines = [e for e in dxf.entities if e.dxftype == 'LINE'] # 円 print(vars(cirs[0])) # {'dxftype': 'CIRCLE', 'handle': None, 'owner': None, 'paperspace': None, 'layer': '_0-0_', 'linetype': 'CONTINUOUS', 'thickness': 0.0, 'extrusion': (0.0, 0.0, 1.0), 'ltscale': 1.0, 'line_weight': 0, 'invisible': 0, 'color': 7, 'true_color': None, 'transparency': None, 'shadow_mode': None, 'layout_tab_name': None, 'center': (94.41901840490794, 356.85030674846627), 'radius': 16.151818630112004} # centerが円中心のx, y # radiusが円半径 # linetypeが線種 # 直線 print(vars(lines[0])) #{'dxftype': 'LINE', 'handle': None, 'owner': None, 'paperspace': None, 'layer': '_0-0_', 'linetype': 'CONTINUOUS', 'thickness': 0.0, 'extrusion': None, 'ltscale': 1.0, 'line_weight': 0, 'invisible': 0, 'color': 7, 'true_color': None, 'transparency': None, 'shadow_mode': None, 'layout_tab_name': None, 'start': (386.4472392638037, 316.28711656441715), 'end': (386.4472392638037, 522.9865030674847)} # startが始点x,y,endが終点x,y # linetypeが線種参考
https://qiita.com/ackermanrf128/items/d9275a7d077c1dff3ec7
- 投稿日:2019-12-11T22:06:46+09:00
DXFをpythonで読む
dxfgrabberを使うとpythonでDXFを読めるらしい
pip install dxfgrabber
フリーの2DCAD jw_cadで適当なDXFファイルを作っておいて,読んでみる.
dxf.pyimport dxfgrabber dxf = dxfgrabber.readfile("test.dxf") # ヘッダ print(dxf.header) #{'$ACADVER': 'AC1009', '$DWGCODEPAGE': 'ANSI_1252', '$INSBASE': (0.0, 0.0, 0.0), '$EXTMIN': (0.0, 0.0), '$EXTMAX': (841.0, 594.0), '$LIMMIN': (0.0, 0.0), '$LIMMAX': (841.0, 594.0), '$LTSCALE': 1.0} # 図形要素 # print(vars(dxf.entities)) cirs = [e for e in dxf.entities if e.dxftype == 'CIRCLE'] lines = [e for e in dxf.entities if e.dxftype == 'LINE'] # 円 print(vars(cirs[0])) # {'dxftype': 'CIRCLE', 'handle': None, 'owner': None, 'paperspace': None, 'layer': '_0-0_', 'linetype': 'CONTINUOUS', 'thickness': 0.0, 'extrusion': (0.0, 0.0, 1.0), 'ltscale': 1.0, 'line_weight': 0, 'invisible': 0, 'color': 7, 'true_color': None, 'transparency': None, 'shadow_mode': None, 'layout_tab_name': None, 'center': (94.41901840490794, 356.85030674846627), 'radius': 16.151818630112004} # centerが円中心のx, y # radiusが円半径 # linetypeが線種 # 直線 print(vars(lines[0])) #{'dxftype': 'LINE', 'handle': None, 'owner': None, 'paperspace': None, 'layer': '_0-0_', 'linetype': 'CONTINUOUS', 'thickness': 0.0, 'extrusion': None, 'ltscale': 1.0, 'line_weight': 0, 'invisible': 0, 'color': 7, 'true_color': None, 'transparency': None, 'shadow_mode': None, 'layout_tab_name': None, 'start': (386.4472392638037, 316.28711656441715), 'end': (386.4472392638037, 522.9865030674847)} # startが始点x,y,endが終点x,y # linetypeが線種 # 線種一覧(jw_cadの) print(vars(dxf.linetypes)) #{'_table_entries': {'CONTINUOUS': <dxfgrabber.linetypes.Linetype object at 0x000002B43A5796A0>, 'DASHED1': <dxfgrabber.linetypes.Linetype object at 0x000002B43A5796D8>, 'DASHED2': <dxfgrabber.linetypes.Linetype object at 0x000002B43A579710>, 'DASHED3': <dxfgrabber.linetypes.Linetype object at 0x000002B43A579748>, 'CENTER1': <dxfgrabber.linetypes.Linetype object at ...参考
https://qiita.com/ackermanrf128/items/d9275a7d077c1dff3ec7
- 投稿日:2019-12-11T21:58:59+09:00
お店を自由に追加してみよう!CreateView編
今回はCreateView編です。
今までTemplateViewを用いてListView、DetailViewを作成してまいりました。
その1-TemplateViewを使ってみよう!IndexView編
その2-TemplateViewをつかってみよう!Detailview編
お店の一覧、お店の詳細ページを表示ができるようになっている状態の続きと致します。自分で好きなデータを追加できるって掲示板みたいで楽しいですよね!!
(見慣れない技術の解説)forms.pyでフォームの定義をおこなう
ここであらたなフォームという技術が現れました。
このforms機能はお仕事ではかなり使われることが多いので、慣れておくことを絶対にお勧めしています。
これは何なのかというと、pythonでこのform部分を作ってしまい、モデルと連動してhtmlの<form></form>を作成することになります。
最終的にwebサイトのフォーム部分を表示させることが目的となっていますので、怖がらずで大丈夫ですよ!編集するファイルはこちら
mysite │ db.sqlite3 │ manage.py ├─mysite │ │ settings.py │ │ urls.py └─test_app │ admin.py │ apps.py │ models.py │ tests.py │ urls.py(ここをルーティング設定のために編集) │ views.py(ここをCreateView設定のために編集) │ forms.py(ここにお店追加の投稿フォームclassを作成) ├─migrations └─templates └─test_app └─list.html(ここにお店追加のリンクを表示) detail.html form.html(お店追加の投稿フォームを表示)ルーティング設定
このURLでお店の作成ページにリンクできるように設定します。
http://localhost:8000/createtest_appのurls.pyにルーティングを設定します。
urls.pyfrom django.urls import path from . import views app_name = 'test_app' urlpatterns = [ path('index/',views.IndexView.as_view(),name='index'), path('<int:pk>/',views.DetailView.as_view(),name='detail'), path('create/',views.CreateView.as_view(),name='create'),#(ここを追加) ]解説:
app_name
とは、どのアプリ名に対してのルーティングファイルであるか指定する必要があります。ページ上のリンク(htmlで書かれたページのaタグで書かれたリンク)で<a href="{% url 'test_app:create' %}">
と記述するため、test_app
とアプリ名を設定する必要があります。
また、path('create/',views.CreateView.as_view(),name='create')
ではcreate/のURLでviews.pyのCreateViewを呼び出しています。as_view()はテンプレートビューをビューとして扱うため使用されています。このas_view()を付けることによりビュー定義の記述がぐっと楽になります。テンプレートビュー限定のメリットですね。CreateViewをviews.pyに設定
views.pyfrom django.views import generic from .models import Shop from .forms import ShopCreateForm #ここを追加 # Create your views here. class IndexView(generic.ListView): model = Shop template_name = 'test_app/list.html' class DetailView(generic.DetailView): model=Shop template_name = 'test_app/detail.html' class CreateView(generic.CreateView):#ここ以降を追加 model = Shop form_class = ShopCreateForm template_name = "test_app/form.html" success_url = "/index"解説:
form_class
にforms.pyで投稿フォーム用に今回作成するフォームクラスであるShopCreateForm
を指定しています。
ちゃんとimport ShopCreateForm
を忘れずにお願いします。views.pyとforms.pyを作成し、行ったり来たりするため、慣れるまで時間がかかりますが頑張りましょう!焦らず無理せず。
きっと慣れてさらに先に進めます!お店投稿フォームをforms.pyに作成
forms.pyが作成されていない場合、新たに作成する場合があります。作成する場所はtest_appの直下です。views.pyやmodels.pyと同じレベルに作成します。
forms.pyはこのようになります。forms.pyfrom django import forms from .models import Shop class ShopCreateForm(forms.ModelForm): class Meta: model = Shop fields = ("name","tell_num","address")解説:forms.ModelFormとは、フォームを作成するためのdjangoの機能となっています。
モデルで定義されているフィールドをそのまま使います。
そのなかで、fields
にてフォームで表示させるフィールドを選び、絞ることができます。お店のデータを投稿したい場合、すでにShopのフィールドは作成されています。実際にmodels.pyの内容はこちらで、
name
やtell_num
、address
、created_at
などフィールドが作成されています。models.pyfrom django.db import models # Create your models here. class Shop(models.Model): #各フィールドの定義 name = models.CharField('shopname',max_length=30) tell_num = models.CharField('tell_number',max_length=13) address = models.CharField('address',max_length=30) created_at = models.DateTimeField(auto_now_add=True) #admin画面の表示内容 def __str__(self): return self.nameこのShopクラスのフィールドをそのままforms.pyで使うことを意味しているのです。
ModelFormを用いると大変楽にフォームページを作成できるのです!投稿ページのform.htmlを作成
mysite/test_app/templates/test_app/form.html<!DOCTYPE> <html> <head> </head> <body> <p>お店の作成</p> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">save</button> </form> </body> </html>解説:
<form method="post">
でフォームタグを作成しています。
{% csrf_token %}
はフォームデータに改ざん防止の特殊な何かのデータを添えて送信するもので、必須です。
これを入れないと、djangoがエラーとなるため、必ず入れましょう。
{{ form.as_p }}
はforms.pyで作成したShopCreateFormをviews.pyのform_class
で指定していたため、html側ではform
と記述するだけでフォームが呼び出せます。form
と書く、と刷り込ませて良いです!
<button type="submit">save</button>
ではデータ送信ボタンを作成しています。」「お店の投稿」リンクを一覧ページに作成
test_app/templates/test_app/list.html<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p>一覧の表示</p> <p><a href="{% url 'test_app:create' %}">お店の追加</a></p><!-- この行を追加 --> <table> <tr> </tr> {% for shop in shop_list %} <tr> <td>{{ shop.name }}</td> <td><a href="{% url 'test_app:detail' shop.pk %}">詳細</a></td> </tr> {% endfor %} </table> </body> </html>解説:お店の追加ページへリンクするためにリンクを書いています。パスは
test_app
というアプリ名のcreate
を指定しています。formの表示の確認を行う
「お店の追加フォーム画面」で団子屋という名前のお店を追加してみます。
お疲れさまでした✨
最後に
データ追加のための設定は必ずと言っていいほど重要なdjangoスキルとなるでしょう。CreateViewを用いてスピーディに作成ができることでかなりDjangoに慣れているといえると思います。
3つくらい自分でアプリを作成してみれば、データの追加、詳細、一覧が含まれるような、お店掲示板、日記などのアプリケーションが作成できるようになっていると思います。わからないところや質問など受け付けておりますので、一緒に頑張ってゆきましょう!
- 投稿日:2019-12-11T21:51:17+09:00
AaaSからZaaSまで「as a Service」を探したら色々なサービスが見えた話
はじめに
近年IT界隈では、IaaS(Infrastructure as a Service)やPaaS(Platform as a Service)などの~~ as a Serviceという言葉をよく聞くと思います。
ここでは、それらをまとめて、 [A-Z]aaS と呼びたいと思います。
FirebaseなどのBaaS(Backend as a Service)やAWS LambdaなどのFaaS(Function as a Service)など色々な[A-Z]aaSを聞く機会が増えてきたんじゃないでしょうか。
今回は色々な[A-Z]aaSを探してみました。結果としては 910個 もの[A-Z]aaSを見つけることができました。(探した結果を全て、後半に表示してあります。)[A-Z]aaSの探し方
最初の二文字を固定して、グーグル検索のサジェストに表示されるものを収集することにします。
英字が26文字なので、26*26=676パターンあります。
(注意) グーグル検索のサジェストに出てきただけで、実際にそういうサービスがあるとは限りません。個人的に面白かった5選
ZaaS (Zangyou as a Service)
サービス残業のことです。まさかの日本語ですね。
(参考)https://monobook.org/wiki/ZaaS_(Zangyo_as_a_Service)FaaS (Failure as a Service)
障害をシミュレートするサービスだそうです。
(参考)https://www.apriorit.com/dev-blog/567-failure-as-a-service
https://dev.classmethod.jp/testing/reinvent2018-gremlin/SaaS (Service as a Service)
サービスとしてのサービスという名の通り、サービスを顧客にあったように、紹介するみたいなイメージでしょうか。
(参考)https://www.ft.com/content/f88bc87a-0e4b-11e2-8b92-00144feabdc0CaaS (Crime as a Service)
他のサービスを攻撃するために作られたツールをサービスとして提供するというもののようです。危険ですね。。。
(参考)https://www.entrepreneur.com/article/298727探す方法
(検討1) Google Suggest APIを使う
Google Suggest APIというものがあり、ある単語を打った時のグーグル検索のサジェストを取得することができます。(参考)https://so-zou.jp/web-app/tech/web-api/google/suggest/
しかし、[A-Z]aaSをサジェストから探すときには、普通にサジェストを得るのではなく、まず検索バーに「as a Service」を打っておき、その後に調べたい先頭の文字を打つ必要があります。そのためGoogle Suggest APIではどういったURLのページにリクエストを送ればいいのか分かりませんでした。。。これが可能であれば、収集のための作業量が10分の1になっていたと思います。(検討2) python OCR を使う
サジェストされた結果を手で打っていくことだけは避けたかったため、OCRで画像情報を文字列に変換していくという方法にします。
chromeの検索バーにひたすらAa as a ServiceからZz as a Serviceまでを打ち、サジェストされている画面をスクリーンショットで取っていきます。(スクリーンショットを取ること676回)以下のプログラムでOCRを使って、画像から文字情報を抜き出します。
そのあとに、[A-Z]aaSの形式に当てはまる形のものを、抜き出します。
以下のコードで、画像から[A-Z]aaSを抜き出すことができました。main.pyfrom PIL import Image import glob import pyocr import pyocr.builders tools = pyocr.get_available_tools() tool = tools[0] langs = tool.get_available_languages() lang = langs[0] # a ~ zで回す for i in range(97, 97+26): image_files = glob.glob("images/%c/Screen*"%chr(i)) words = [] for image_file in image_files: im = Image.open(image_file) croped_image = im.crop((300, 250, 1000, 730)) #基準 # OCRで画像から文字を一行ずつ読み取る line_and_word_boxes = tool.image_to_string( croped_image, lang=lang, builder=pyocr.builders.LineBoxBuilder() ) #得た文字列から一列ごとに読み取る for line_words in line_and_word_boxes: if line_words.content.endswith('as a service') and line_words.content.startswith(chr(i)): word = line_words.content.capitalize() word = word.replace('service', 'Service') print(word) words.append(word) #改行コードで連結して、ファイルに書き込む words = '\n'.join(list(set(words))) with open("result/"+chr(i)+".txt", 'w') as f: print(words, file=f)A-Zまで
以下に取得した[A-Z]aasを全て掲載します。今回、調べることができたのは、910個と、とてつもない量です。スクロールするのもめんどくさいです。探したのは、グーグル検索のサジェストから得た結果だけなので、実際にはもっとあると思います。
AaaS
Apple as a Service
Auto as a Service
Aas as a Service
About platform as a Service
Availability as a Service
Agriculture as a Service
Application as a Service
Advisory as a Service
Api management as a Service
Aws platform as a Service
Agency as a Service
Aws kafka as a Service
Air conditioning as a Service
Agreement as a Service
Audit as a Service
Amazon kubernetes as a Service
Active directory as a Service
About rap as a Service
All as a Service
Azure app as a Service
Admin as a Service
As a Service
Afschepen as a Service
Advertising as a Service
Access management as a Service
Axa insurance as a Service
App as a Service
Android as a Service
Access as a Service
Azure platform as a Service
Airbnb as a Service
Aws blockchain as a Service
Adobe as a Service
Accounting as a Service
Akka as a Service
Azure desktop as a Service
Air as a Service
Agreement software as a Service
Aws infrastructure as a Service
Authentication as a Service
Appliance as a Service
About software as a Service
Amazon container as a Service
Adesso as a Service
Agent as a Service
Aem content as a Service
Airport as a Service
Alert as a Service
Apache as a Service
Aviation as a Service
Azure database as a Service
Amazon as a Service
Ansible as a Service
Automation as a Service
Amazon blockchain as a Service
Aem as a Service
About infrastructure as a Service
Azure infrastructure as a Service
Alexa as a Service
Ad as a Service
Avaya as a Service
Api as a Service
Avatar as a Service
Ai as a Service
And security as a Service
Av as a Service
Ad self Service plus as a Service
Assurance as a ServiceBaaS
Branch as a Service
Book as a Service
Banking as a Service
Bi as a Service
Bginfo unable to launch as a Service
Backup as a Service
Bike as a Service
Bbva banking as a Service
Bicycle as a Service
Bss as a Service
Business process as a Service
Breakfast as a Service
Brand as a Service
Bd as a Service
Bginfo unable to relaunch as a Service
Big data as a Service
Battery as a Service
Bginfo as a Service
Billing as a Service
Bcp as a Service
Broker as a Service
Business as a Service
Bim as a Service
Building as a Service
Brain as a Service
Blockchain as a Service
Backend as a Service
Business model as a Service
Bem as a Service
Bgp as a Service
Business model software as a ServiceCaaS
Cloud as a Service
Cfo as a Service
Classification as a Service
Customer experience as a Service
Cctv as a Service
Cyber threat intelligence as a Service
Cyber as a Service
Cyber range as a Service
Cat as a Service
Css as a Service
Car as a Service
Cybercrime as a Service
Chef as a Service
Customer Service as a Service
Cyber security as a Service
Client as a Service
Cnae software as a Service
Crime as a Service
Cdn as a Service
Credit as a Service
Community as a Service
Cdw device as a Service
Cntlm as a Service
Cto as a Service
Cloud computing as a Service
Ccm as a Service
Cts as a Service
Check-in as a Service
Channel as a Service
Certificate as a Service
Capital as a Service
Cdm as a Service
Chat as a Service
Crafting as a Service
Ckan as a ServiceDaaS
DDos as a Service
Data as a Service
Dxc insurance as a Service
Data center as a Service
Drupal as a Service
Docker as a Service
Dhs workplace as a Service
Db as a Service
Django as a Service
Duo as a Service
Dpi as a Service
Dxc storage as a Service
Ddi as a Service
Dynamics as a Service
Devops as a Service
Db2 as a Service
Dxc digital insurance as a Service
Django platform as a Service
Database as a Service
Dpo as a Service
Dmp as a Service
Dmarc as a Service
Dsp as a Service
Data science as a Service
Dxc desktop as a Service
Docker swarm as a Service
Device as a Service
Dhcp as a Service
Dsi as a Service
Dba as a Service
Dxc backup as a Service
Dxc platform as a Service
Dr as a Service
Development as a Service
Dns as a Service
Docker start as a Service
Desktop as a Service
Drone as a Service
Dns server as a Service
Dc as a Service
Dropbox as a Service
Dxc device as a Service
Docker compose as a Service
Dpd sorry as a Service
Delivery as a Service
Domain as a Service
Dnssec as a Service
Dms as a Service
Dbaas database as a Service
Dmz as a Service
Drm as a Service
Driver as a ServiceEaaS
Exchange as a Service
Etcd as a Service
Economy as a Service
Epc as a Service
Erp as a Service
Ea as a Service
Example of platform as a Service
Elasticsearch as a Service
Endpoint as a Service
Example of infrastructure as a Service
Email as a Service
Emotion as a Service
Education as a Service
Energy as a Service
Ejemplos de software as a Service
Ey cyber as a Service
Edi as a Service
Esb as a Service
Ejemplos de infrastructure as a Service
Ejemplos de platform as a Service
Ecommerce as a Service
Engineering as a Service
Examples of data as a Service
Experience as a Service
Examples of platform as a Service
Ethernet as a Service
Ec2 as a Service
Escrow as a Service
Elastic as a Service
Ey cybersecurity as a Service
Ehr as a Service
Etl as a Service
Equipment as a Service
Eshop as a Service
Editing as a Service
Elk as a Service
Edge as a Service
Ejemplo software as a Service
Environment as a Service
Efficiency as a Service
Ecosystem as a Service
Espace as a Service
Email encryption as a Service
Examples of software as a Service
Employee as a Service
Eugene wei status as a Service
Ehr software as a Service
Eit as a Service
Exe as a Service
Ecm as a Service
Eu mobility as a ServiceFaaS
Function as a Service
Fitness as a Service
Fleet as a Service
Fit as a Service
Fax as a Service
Failure as a Service
Ffmpeg as a Service
Fabric as a Service
Food as a Service
File upload as a Service
Fw as a Service
Ff14 crafting as a Service
Fn function as a Service
Fido as a Service
Flow as a Service
Fda software as a Service
Fpga as a Service
Fhir as a Service
Factory as a Service
Ftp as a Service
Finance as a Service
Ftp server as a Service
Fte as a Service
Fm as a Service
Farming as a Service
Flask as a Service
Full as a Service
Food delivery as a Service
Flags as a Service
Fbaas functional blockchain as a Service
Feature as a Service
Ffxiv crafting as a Service
File transfer as a Service
Fees as a Service
Federation as a Service
Feedback as a Service
Fx as a Service
Feature flags as a ServiceGaaS
Gpedit logon as a Service
Gpo as a Service
Gep kafka as a Service
Gitea as a Service
Gpu as a Service
Git as a Service
Gateway as a Service
Gunicorn as a Service
Gbm as a Service
Gym as a Service
Gitlab as a Service
Gcp desktop as a Service
Gps tracking as a Service
Gpo logon as a Service
Gmail as a Service
Government as a Service
Google docs is an example of software as a Service
Github as a Service
Gis as a Service
Gcp platform as a Service
Global mobility as a Service
Gcp database as a Service
Gcp blockchain as a Service
Graphics as a Service
Graphic design as a Service
Gitlab runner as a Service
Gcp infrastructure as a Service
Games as a Service
Gas as a Service
Graphql as a Service
Gpl software as a Service
Goods as a ServiceHaaS
Hbase as a Service
Hfs as a Service
Healing as a Service
Hfs procurement as a Service
Hp device as a Service
Hardware as a Service
History of as a Service
Hdfs as a Service
Hw as a Service
Hilti as a Service
Hp printing as a Service
Healthcare as a Service
Highway as a Service
Hpe as a Service
Html to pdf as a Service
Hpe everything as a Service
Hpc as a Service
Hsm as a Service
Hr as a Service
Hadoop as a Service
Hana as a Service
Headset as a Service
Heating as a Service
Hvac as a Service
Hp as a Service
Health as a Service
Hci as a Service
Httpd as a Service
Hbr platform as a Service
Human as a Service
Hp printer as a Service
Hacking as a ServiceIaaS
Iis as a Service
Ibm blockchain as a Service
Ibm backup as a Service
Iphone as a Service
Irc as a Service
Iaas as a Service
Immobilier as a Service
Infrastructure as a Service
Irrigation as a Service
Ico as a Service
Icinga as a Service
Ifix as a Service
Ifrs 16 software as a Service
Internet as a Service
Is a platform as a Service
Ix as a Service
Iot platform as a Service
It security as a Service
Image as a Service
Ixia testing as a Service
In platform as a Service
Iot as a Service
Iaas infrastructure as a Service
Ivanti as a Service
Ict as a Service
Ivr as a Service
Id as a Service
Iam as a Service
Identity as a Service
Iis server as a Service
Ips as a Service
Ibm platform as a Service
Ipad as a Service
Ira as a Service
Ixia lab as a Service
Iwsva as a Service
Ibm deep learning as a Service
Ios as a Service
Iis express as a Service
Ipsec as a Service
Ims as a Service
Industry as a Service
Iga as a Service
Ibm infrastructure as a Service
Ibm mainframe as a Service
Ibm software as a ServiceJaaS
Job as a Service
Jde as a Service
Journalism as a Service
Jboss eap 7 as a Service
Judge as a Service
Jboss as a Service
Jira as a Service
Jmeter as a Service
Jenkins as a Service
Jdownloader as a Service
Jupyterhub as a Service
Jq as a Service
Javascript as a Service
Justice as a Service
Jboss run as a Service
Jwt as a Service
Json as a Service
Jd retail as a Service
Jfrog as a Service
Js as a Service
Java class as a Service
Jewelry as a Service
Jupyter as a Service
Jupyter notebook as a Service
Java as a Service
Jvm as a ServiceKaas
Kubectl as a Service
Kubernetes function as a Service
Kubernetes as a Service
Kpi as a Service
Kms as a Service
Knowledge base as a Service
Keyboard as a Service
Kitchen as a Service
Key management as a Service
Kpmg as a Service
Kvh connectivity as a Service
Knowledge as a Service
Kyc as a Service
Kpmg mobility as a Service
Key as a Service
Kafka as a Service
Kvm as a ServiceLaaS
Leadership as a Service
Lab as a Service
Laundry as a Service
Light as a Service
Linux start as a Service
Learning as a Service
Lync as a Service
Lenovo as a Service
Lan as a Service
Library as a Service
Laser cutting as a Service
Legal as a Service
Logistics as a Service
Log on as a Service
Laptop as a Service
Linux as a Service
Luxury as a Service
Location as a Service
Life as a Service
List of as a Service
Login as a ServiceMaaS
Mvno as a Service
Ml platform as a Service
Ms windows as a Service
Mdm as a Service
Mobility as a Service
Mro as a Service
Machine learning as a Service
Mdx as a Service
Ml as a Service
Ms project as a Service
Mpls as a Service
Msp and as a Service
Management as a Service
Music as a Service
Mlaas machine learning as a Service
Machine as a Service
Microsoft as a Service
Marketing as a Service
Mind as a Service
Middleware as a Service
Mfa as a Service
Mysq|i start as a Service
Mdr as a Service
Mssql as a Service
Matt broker as a Service
Mrp as a Service
Model as a Service
Mft as a Service
Maas mobility as a Service
Mysql as a Service
Matt as a Service
Microsoft desktop as a Service
Mvp as a Service
Mq as a Service
Man truck as a Service
Mongodb as a Service
Marketplace as a ServiceNaaS
Ngrok as a Service
Nike as a Service
Nip as a Service
Nvidia gaming as a Service
Nps as a Service
Nlu as a Service
Ng serve as a Service
Npm start as a Service
Ntrights logon as a Service
Npm as a Service
No as a Service
Nfv as a Service
Netflix as a Service
Notification as a Service
Nms as a Service
Notebook as a Service
Ntp as a Service
Nzbget as a Service
Nfs as a Service
Nslookup as a Service
Nature as a Service
Nat as a Service
Nfaas named function as a Service
Nginx as a Service
Nexus as a Service
Network as a Service
Nzta mobility as a Service
Nmap as a ServiceOaaS
Openshift as a Service
Oecd software as a Service
Omnichannel as a Service
Openvpn as a Service
Odoo as a Service
Owncloud as a Service
Omada controller as a Service
Office as a Service
Otp as a Service
Oauth2 as a Service
Oss as a Service
Oauth as a Service
Ocr as a Service
Otrs as a Service
Owin as a Service
Outlook as a Service
Operations as a Service
Organization as a Service
Ot as a Service
Orchestration as a Service
Object storage as a Service
Oracle cloud database as a Service
Outsourcing as a Service
Oms as a Service
Oem mobility as a Service
Ott as a Service
Operating system as a Service
Object as a Service
Output as a Service
Oracle cloud as a Service
On software as a Service
Oxidized as a Service
Of infrastructure as a ServicePaaS
Pipeline as a Service
Pm2 as a Service
Ptp as a Service
Police as a Service
Platform as a Service
Pharma as a Service
Pwc insights as a Service
Pump as a Service
Pbx as a Service
Python function as a Service
Philips as a Service
Pef as a Service
Phone as a Service
Pmo as a Service
Portal as a Service
Performance as a Service
Pizza as a Service
Pension as a Service
Perks as a Service
Python as a Service
Pwc dpo as a Service
Puppet as a Service
Proxy as a Service
Pci as a Service
Push as a Service
Ppt on software as a Service
Pwc software as a Service
Pos as a Service
Ppm as a Service
Product as a Service
Porsche as a Service
Ps4 as a Service
Post as a Service
Paas platform as a Service
Power as a Service
Plex as a Service
Payroll as a Service
Pure as a Service
Pigeon as a Service
Procurement as a Service
Pc as a Service
Python program as a Service
Pdf as a Service
Pgbouncer as a Service
Pms as a Service
Payment as a Service
Pivotal function as a Service
Psexec as a Service
Parking as a Service
Ping as a Service
Python script as a Service
Push notifications as a Service
Pwc mobility as a ServiceQaaS
Quality as a Service
Qbittorrent run as a Service
Qradar as a Service
Qr code as a Service
Qover insurance as a Service
Qic mobility as a Service
Qitc as a Service
Qa automation as a Service
Qa as a Service
Qa testing as a Service
Quality management as a Service
Qvartz mobility as a Service
Qsync as a Service
Qlik web connectors as a Service
Qlik as a Service
Qbittorrent as a Service
Qkd as a Service
Quant as a ServiceRaaS
Rds as a Service
Room as a Service
Retail as a Service
Rolls royce as a Service
Rsyslog as a Service
Rabbit as a Service
Rsync as a Service
Ransomware as a Service
Rtb bidder as a Service
Rng as a Service
Run exe as a Service
Rh as a Service
Run plex as a Service
Rpa software as a Service
Rclone as a Service
Run onedrive as a Service
Rhapsody as a Service
Rss as a Service
Radio as a Service
Run dropbox as a Service
Radius as a Service
Run unifi as a Service
Rdp as a Service
Run unifi controller as a Service
Rtorrent as a Service
Ras as a Service
Rpa as a Service
Risk as a Service
Rap as a Service
Roaming as a Service
Rfp for soc as a Service
Rfp as a Service
Rfp software as a Service
Rslinx running as a Service
Rbac as a Service
Rfid as a Service
Right to log on as a Service
Robotics as a Service
Run powershell script as a Service
Rabbitmg as a Service
Rsa as a Service
Res as a Service
Rem as a ServiceSaaS
Sdr as a Service
Software as a Service
Snail mail as a Service
Synology as a Service
Snow as a Service
Swarm as a Service
Sleep as a Service
Sccm as a Service
Science as a Service
Slack as a Service
Strategy as a Service
Script powershell as a Service
Spark as a Service
Swift as a Service
Sql database as a Service
Symfony factory as a Service
Storage as a Service
Spring boot as a Service
Sftp as a Service
Skills as a Service
Syslog as a Service
Sql server as a Service
Svn as a Service
Switch as a Service
Sdn as a Service
Support as a Service
Saas as a Service
Service desk as a Service
Ship as a Service
Swagger as a Service
Symfony controller as a Service
Supply chain as a Service
Streaming as a Service
Scheduling software as a Service
Symfony repository as a Service
Smtp as a Service
Smtp relay as a Service
Sd-wan as a Service
Shop as a Service
Scanning as a Service
Subscription as a Service
Sap as a Service
Shell as a Service
Site as a Service
Sam as a Service
Sms gateway as a Service
Spring as a Service
Space as a Service
Skype as a Service
Slice as a Service
Smart home as a Service
Sip as a Service
Smartphone as a Service
School as a Service
Sdk as a Service
Shadow gaming as a Service
Signalr as a Service
Sbe as a Service
Surface as a Service
Sv as a Service
Sms as a Service
Status as a Service
Secret as a Service
Snowflake as a Service
Sales as a Service
Saas software as a Service
Snackbar as a Service
Sprint as a Service
Security as a Service
Siem as a Service
System as a ServiceTaaS
Task scheduler as a Service
Tape as a Service
Testing as a Service
Team as a Service
Types of cloud as a Service
Train as a Service
Tftpd32 is running as a Service
Tftpd as a Service
Tyco as a Service
Tts as a Service
Teamviewer as a Service
Thm as a Service
Training as a Service
Tcpdump as a Service
Tunnel as a Service
Tivo as a Service
Tech as a Service
Transportation as a Service
Tftpd64 as a Service
Tms as a Service
Truck as a Service
Translation as a Service
Tyre as a Service
Two factor authentication as a Service
Tftp as a Service
Tftp server as a Service
Time as a Service
Table as a Service
Tightvnc as a Service
Tfs as a Service
Types of as a Service
Turn as a Service
Telephony as a Service
Tool as a Service
Tftpd32 as a Service
Tax as a Service
Tv as a ServiceUaaS
Uwp as a Service
Uav as a Service
Uma as a Service
Umbraco as a Service
User profile as a Service
Unifi controller run as a Service
Uipath as a Service
Uber as a Service
Ux research as a Service
Upload as a Service
Uptime as a Service
Ux as a Service
Uitp mobility as a Service
Unifi as a Service
Ui components as a Service
Ups as a Service
Ultravnc as a Service
Uwsgj as a Service
Uat as a Service
Unified communications as a Service
Url shortener as a Service
User as a Service
Update as a Service
Ultrasound as a Service
Ubuntu run as a Service
Unifi controller as a Service
Ubuntu start mongodb as a Service
Uipath orchestrator as a Service
Uipath rpa as a Service
Ui as a Service
Uipath robot as a Service
Utility as a Service
Ubuntu as a Service
Uber mobility as a ServiceVaaS
Vdi desktop as a Service
Vault encryption as a Service
Vulnerability management as a Service
Volvo as a Service
Vsts agent as a Service
Voice as a Service
Vision as a Service
Vehicle as a Service
Vault as a Service
Vnc as a Service
Ve as a Service
Vmware infrastructure as a Service
Vdi vs desktop as a Service
Vpp as a Service
Voicemail as a Service
Vb.net run program as a Service
Vod as a Service
Vdi as a Service
Vastgoed as a Service
Vnf as a Service
Vb6 as a Service
Vtiger as a Service
Video streaming as a Service
Vmware as a Service
Vbox as a Service
Vmware desktop as a Service
Value as a Service
Vpn as a Service
Vic as a Service
Vad ar en as a Service
Vr as a Service
Volvo car as a Service
Video editing as a Service
Virtualization as a Service
Vcenter as a Service
Voip as a Service
Vpc as a Service
Vmware workstation as a Service
Volte as a Service
Vbs as a Service
Vulnerability assessment as a Service
Vbscript as a Service
Vncserver as a Service
Vulnerability scanning as a Service
Video as a Service
Vmware kubernetes as a Service
Vb.net as a Service
Value chain as a ServiceWaaS
Website as a Service
Word as a Service
Web security as a Service
Wfm as a Service
Wpf as a Service
Web scraping as a Service
Workstation as a Service
Wgl energy as a Service
Web as a Service
Wp curve Service as a Service
Wef as a Service
Water as a Service
Wsl as a Service
What is mobility as a Service
What is platform as a Service
What is infrastructure as a Service
Warehouse as a Service
Wsus as a Service
Wifi as a Service
Washing machine as a Service
Wmi as a Service
Workforce as a Service
Waiter as a Service
Windows update as a Service
What is marketing as a Service
What is software as a Service
What is security as a Service
Wrapper as a Service
Windows as a Service
Windows python as a Service
Warmte as a Service
Workplace as a Service
Watch as a Service
Windows install as a ServiceXaaS
Xaas everything as a Service
Xampp install apache as a Service
Xenmobile as a Service
Xaas anything as a Service
Xwiki as a Service
Xampp as a Service
Xbox games as a Service
Xendesktop as a Service
Xbox as a Service
Xinet as a Service
Xenapp as a Service
Xaas as a Service
Xmpp as a Service
Xcode as a Service
Xtaas telemarketing as a ServiceYaaS
Youtube as a Service
ZaaS
Zangyou as a Service
Zscaler as a Service
Zookeeper as a Service
Zabbix as a Service
Zf mobility as a Service
Zscaler security as a Service
Zdnet windows as a Service
Zabbix agent as a Service
Zeppelin as a Service
Zscaler firewall as a Service
Zero carbon as a Service
Zap as a Service終わりに
とてつもない量のサービスですね。今まで普通にあったサービスも、ゴロがいいからみたいな理由で、as a Serviceと名づけられてるのかなと思いました。
これからは何かをしたいと思う時、自分で準備するよりもサービスを利用した方がいいということでしょうか。
以下の2年ほど前の記事にもまとまっていたので紹介しておきます。
https://boxil.jp/mag/a3600/
- 投稿日:2019-12-11T21:19:18+09:00
PolyglotをRaspberry Piで動かして英文の形態素解析を行う
Advent calendarのネタづくりを行うときに英文の形態素解析を行う必要が生まれたのですが少し引っかかったので残しておきます。
Polygotとは何か
日本語の領域だとMeCabが形態素解析ツールとしてよく取り上げられていますが、英文の形態素解析となるとそんなに事例が多くありません。
そんな英文の形態素解析を始めとして言語特定、言語検出などの自然言語解析の機能を多く持ったライブラリが、Polygotです。言語サポート範囲
公式ドキュメントに記載されてる通りになりますが機能ごとによって対応言語に差があります。
機能名 対応言語数 説明 トークン化 165言語 文字列を自然言語処理を行う際に扱う文章の最小単位に分割します 言語検出 196言語 解析対象の文字列の言語を特定します 固有表現抽出 40言語 解析対象の文字列から固有表現を抽出します Polygotでは、場所、組織、人の3つのタイプを抽出することができます 品詞タグづけ 16言語 解析対象の文字列の各トークンに対して、品詞タグを付与します 感情分析 136言語 ネガティブ、ニュートラル、ポジティブの3つの種別を取得できる 分散表現 137言語 単語をd次元のベクトル空間にマッピングします 形態素解析 135言語 解析対象の文字列から意味を持つ最小単位に分割します 翻字 69言語 入力された文字列を指定した言語の文字列に変換する 上記表から見てもわかるように多くの言語をサポートしています。
Install
実際にPolygotを動くようにセットアップしていきましょう。
Polygotをインストールする
$ sudo pip3 install -U polyglotpolyglot自体は、上記コマンドを実行するだけでインストールすることができます。
しかし、実際にpolyglotで言語解析を行うには解析対象の言語の辞書を取得する必要があります。
辞書取得時にICUがインストール済みでないとエラーを吐きます。なので、ダウンロード前に下記コマンドを実行して必要なライブラリを取得します。$ sudo apt-get -y install libicu-dev $ sudo pip3 install -U pyicu $ sudo pip3 install -U morfessorその他にpycld2がモデルのダウンロードに必要となります。
通常のLinux環境であれば$ sudo pip install pycld2
を叩くだけでインストールすることが可能です。ですが、Raspberry Pi上で上記コマンドを実行すると下記のようなエラーが表示されていまいます。arm-linux-gnueabihf-gcc: error: unrecognized command line option ‘-m64’ error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1 ---------------------------------------- ERROR: Failed building wheel for pycld2上記エラーはARMアーキテクチャ向けのコンパイラーに-m64オプションが用意されていないことでコンパイルに失敗しているため発生しています。
このままでは、pycld2をインストールすることができないため、Raspberry Pi上でPolyglotを動かすことが出来ません。さぁ困った....pycld2をRaspberry Piにインストールする
そのままでは、インストールできないためpycld2の
setup.py
に指定されている-m64コンパイルオプションを取り除いた上でsetup.pyを実行する必要があります。
下記のリポジトリからgit clone
した上で、setup.py
を弄ります。
aboSamoor/pycld2 - Github$ git clone https://github.com/aboSamoor/pycld2.git $ cd pycld2/git cloneしたpycld2のディレクトリに移動し、直下に配置されているsetup.pyのLine 78に記述されているコンパイルオプションの配列から-m64を削除してから保存します。
変更前language="c++", # TODO: -m64 may break 32 bit builds extra_compile_args=["-w", "-O2", "-m64", "-fPIC"],変更後language="c++", # TODO: -m64 may break 32 bit builds extra_compile_args=["-w", "-O2", "-fPIC"],変更後、下記コマンドを実行します。
$ sudo pip3 install hogehoge/pycld2/Successfully built pycld2 Installing collected packages: pycld2 Successfully installed pycld2-0.42実行後、Successfullyが表示されればインストール成功です。
モデルのダウンロードを行う
下記コマンドを実行するモデルのダウンロードができます。
今回は英文の形態素解析を行うので、英語のモデルをダウンロードします。$ polyglot download morph2.en [polyglot_data] Downloading package morph2.en to [polyglot_data] /home/pi/polyglot_data...実際に形態素解析を行う
あとは下記のようなサンプルコードを実行するだけです。
morph.pyfrom polyglot.text import Text sample_text = "One Hamburger and a Medium Coffee please." tokens = Text(sample_text) print(tokens.morphemes)実際に上記スクリプトを実行すると下記のような形で結果を取得することが可能です。
$ python3 morph.py ['One', ' ', 'Ham', 'burg', 'er and a Medium Coffee p', 'lease', '.']おわりに
今回はとあるプログラムを作るためにPolyglotを初めて活用しました。言語判定などできるので、TwitterAPIと絡めて日本語であればMeCab側で処理して、それ以外をPolyglotに任せるなども出来るかと思います。中々、英文の自然言語処理を業務で使うことは無いと思いますが1つの引き出しとして備忘録的に残しておきます。
- 投稿日:2019-12-11T21:17:05+09:00
pyenv が homebrew でインストールした tcl-tk と動かない。
遭遇した問題
Macでpyenvを使ってインストールしたpythonから、tkinter を使おうとしたところ、次のエラーがでた。
Traceback (most recent call last): File "./annotate.py", line 3, in <module> import tkinter File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/tkinter/__init__.py", line 36, in <module> import _tkinter # If this fails your Python may not be configured for Tk ModuleNotFoundError: No module named '_tkinter'環境は、
- MacOS Catalina 10.15.1
- pyenv 1.2.15
ググって見つかった(そして解決しなかった)方法
pyenv で python を再インストールする
pyenv でpythonをインストールしている場合、一旦 uninstall して install し直すと解決するらしい。
ググって見つけたのは以下のページ:
具体的には、↓な感じ。
$ pyenv versions system * 3.7.5 # 現在使われている python が 3.7.5 だったので $ pyenv uninstall 3.7.5 # python 3.7.5 をアンインストールして $ brew install tcl-tk # homebrew で tcl-tk をインストールして $ export LDFLAGS="-L/usr/local/opt/tcl-tk/lib" # tcl-tk の開発に必要な $ export CPPFLAGS="-I/usr/local/opt/tcl-tk/include" # 徹底を行ってから $ pyenv install 3.7.5 # 改めて python 3.7.5 をインストールするしかし、再度 tkinter を実行してみると・・・
$ python -m tkinter DEPRECATION WARNING: The system version of Tk is deprecated and may be removed in a future release. Please don't rely on it. Set TK_SILENCE_DEPRECATION=1 to suppress this warning. Traceback (most recent call last): File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/runpy.py", line 85, in _run_code exec(code, run_globals) File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/tkinter/__main__.py", line 7, in <module> main() File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/tkinter/__init__.py", line 3988, in _test root = Tk() File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/tkinter/__init__.py", line 2025, in __init__ self._loadtk() File "/Users/???/.pyenv/versions/3.7.4/lib/python3.7/tkinter/__init__.py", line 2040, in _loadtk % (_tkinter.TK_VERSION, tk_version)) RuntimeError: tk.h version (8.6) doesn't match libtk.a version (8.5)と別のエラーが出てしまう。
この場合、header は version 8.6 を使ってコンパイルされるが、実行時に参照されるライブラリが version 8.5 担っているということらしい。さらにググるも、日本語の解は見つからず。
解決策
英語でググったところ、次のページが見つかった。
これですね。
つまり、/usr/local/Cellar/pyenv/VERSION/plugins/python-build/bin/python-build を編集して、次のパッチを当てる(と言っても、銭湯が "!" となっている1行書き換える)。
diff -c python-build.orig python-build *** python-build.orig 2019-12-10 17:47:04.000000000 +0900 --- python-build.new 2019-12-11 11:53:17.000000000 +0900 *************** *** 772,778 **** export CC=clang fi ${!PACKAGE_CONFIGURE:-./configure} --prefix="${!PACKAGE_PREFIX_PATH:-$PREFIX_PATH}" \ ! $CONFIGURE_OPTS ${!PACKAGE_CONFIGURE_OPTS} "${!PACKAGE_CONFIGURE_OPTS_ARRAY}" || return 1 ) >&4 2>&1 { "$MAKE" $MAKE_OPTS ${!PACKAGE_MAKE_OPTS} "${!PACKAGE_MAKE_OPTS_ARRAY}" --- 772,778 ---- export CC=clang fi ${!PACKAGE_CONFIGURE:-./configure} --prefix="${!PACKAGE_PREFIX_PATH:-$PREFIX_PATH}" \ ! $CONFIGURE_OPTS --with-tcltk-includes='-I/usr/local/opt/tcl-tk/include' --with-tcltk-libs='-L/usr/local/opt/tcl-tk/lib -ltcl8.6 -ltk8.6 ' ${!PACKAGE_CONFIGURE_OPTS} "${!PACKAGE_CONFIGURE_OPTS_ARRAY}" || return 1 ) >&4 2>&1 { "$MAKE" $MAKE_OPTS ${!PACKAGE_MAKE_OPTS} "${!PACKAGE_MAKE_OPTS_ARRAY}"それで、pyenv で python を再インストールすれば良い。
$ pyenv uninstall 3.7.5 $ pyenv install 3.7.5 $ python -m tkinterうまく行った。
- 投稿日:2019-12-11T21:11:04+09:00
Anaconda on Windows Terminal
Anaconda on Windows Terminal
前回の「ROS on Windows Terminal」に続いて、今回は「Anaconda on Windows Terminal」です。
setting.json
Windows Terminalを管理者として実行し、+からsettingを選びます。
そうすると、setting.jsonが開くので、{ "acrylicOpacity": 1.0, "closeOnExit": true, "colorScheme": "Campbell", "commandline": "cmd.exe /k C:\\Users\\username\\Anaconda3\\Scripts\\activate.bat", "cursorColor": "#FFFFFF", "cursorShape": "bar", "fontFace": "consolas", "fontSize": 14, "guid": "{xxx}", "historySize": 9001, "name": "Anaconda", "padding": "0, 0, 0, 0", "snapOnInput": true, "startingDirectory": "%HOME%", "tabTitle": "Anaconda", "useAcrylic": true, "icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png" }あとは少々変更
- ユーザ名(username)を自分の物に
- Anacondaのディレクトリの場所が正しいのか確認
- "startingDirectory"の変更
より詳しい説明
画像が多くわかりやすいので、英語ですがもしわからなければこちらを参照してください。
結果
help me
複数の仮想環境のターミナルを入れることはできないのか...?
- 投稿日:2019-12-11T21:09:00+09:00
Slackに飛んでくるブックマークをDoc2VecとPCAで可視化してみた
アドベントカレンダーの11日目の記事です
これは
みんな(4人)のブックマークの分散表現を獲得して可視化したもの
ブックマークするとIFTTTが拾ってslackに吐くようにしているのでそこから処理する
前提条件
参考文献/利用したもの
- 【転職会議】クチコミをword2vecで自然言語処理して会社を分類してみる
- 【word2vec】会社のクチコミを自然言語処理した結果を可視化してみる
- pythonによる日本語前処理備忘録
- slackのチャットログをお手軽バックアップ
- gensim
- slack-dump
- slack api
できるもの
やる前の予想
- Rくん
- ガジェットやセキュリティ系が多い
- ブクマ数79
- Yさん
- 4人の中で一番範囲が広い
- 実はこのユーザだけみんなにシェアする目的で取捨選択したものを投稿している
- ブクマ数864
- Mくん
- Webと機械学習など
- ブクマ数240
- S(自分)
- Webと機械学習とガジェットに加えて「今年はサンマが不漁」みたいなのまで投げてしまう
- ブクマ数896
結果
なんとなくそうなった気がする
準備
はてなブックマークをIFTTTにSlackへ投稿させる仕組み
手順
下図の4コマ目と5コマ目の間にRSS Feedを受け取るためのURLを入力する
今回ははてなブックマークなので
http://b.hatena.ne.jp/<username>/rss
となるこんな感じ
理由
はてな内でユーザをお気に入りすればいい?
それでも悪くはない(むしろ両方してもいい)が、コミュニティ内だとこんな感じで気軽にコメントしあえる
Slackコマンドの/feedをつかえばいい?
投稿文をカスタマイズできるので今回のように遊びに使える、またSlackコマンドを使うとめちゃくちゃスペースを取るので困る
Slackから投稿メッセージを取得
簡単にできそうなのはこの2種類
- SlackのAPI
- Go製のツール(今回はこっち)
どちらにせよトークンが必要なのでここから取得
$ wget https://github.com/PyYoshi/slack-dump/releases/download/v1.1.3/slack-dump-v1.1.3-linux-386.tar.gz $ tar -zxvf slack-dump-v1.1.3-linux-386.tar.gz $ linux-386/slack-dump -t=<token> <channel>DMとかもいっしょに取ってきて邪魔なので 別のところに移す
pythonimport zipfile, os os.mkdir('dumps') with zipfile.ZipFile('./zipfize_name') as z: for n in z.namelist(): if 'channel_name' in n: z.extract(n, './dumps')ファイルを開いて中身を取得する、日付ごとになっているので全部を1つにする
pythonimport json, glob posts = [] files = glob.glob('./dumps/channel/{}/*.json'.format(dirname)) for file in files: with open(file) as f: posts += json.loads(f.read())Messageを取り出して記事タイトルとユーザ名を紐づける(この辺はIFTTTでの設定による)
pythonuser_post_dic = { 'Y': [], 'S': [], 'M': [], 'R': [], } for p in posts: if "username" not in p or p["username"] != "IFTTT": continue for a in p["attachments"]: # 雑回避 try: user_post_dic[a["text"]].append(a["title"]) except: pass users = user_post_dic.keys() print([[u, len(user_post_dic[u])] for u in users])出力[['Y', 864], ['S', 896], ['M', 240], ['R', 79]]本編
前処理
クレンジングとわかち書き
投稿されるメッセージはこんな感じになっていてサイトのタイトルやURLは不要なので削除する
ブラウザのテキストエリアでNeovimを使う <http://Developers.IO|Developers.IO> フロントエンドエンジニアのためのセキュリティ対策 / #frontkansai 2019 - Speaker Deck matplotlibで日本語 モダンJavaScript再入門 / Re-introduction to Modern JavaScript - Speaker Deck
re
を使う時のお作法がよくわからなかったのでゴリ押し
加えて、MeCabでの分かち書きもおこなう、環境にはsudachipyなども入っているが、手に馴染んでいるものをつかう、速いしpythonimport MeCab, re m = MeCab.Tagger("-Owakati") _tag = re.compile(r'<.*?>') _url = re.compile(r'(http|https)://([-\w]+\.)+[-\w]+(/[-\w./?%&=]*)?') _title = re.compile(r'( - ).*$') _par = re.compile(r'\(.*?\)') _sla = re.compile(r'/.*$') _qt = re.compile(r'"') _sep = re.compile(r'\|.*$') _twi = re.compile(r'(.*)on Twitter: ') _lab = re.compile(r'(.*) ⇒ \(') _last_par = re.compile(r'\)$') def clean_text(text): text = text.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)})) text = re.sub(_lab, '', text) text = re.sub(_tag, '', text) text = re.sub(_url, '', text) text = re.sub(_title, '', text) text = re.sub(_sla, '', text) text = re.sub(_qt, '', text) text = re.sub(_sep, '', text) text = re.sub(_twi, '', text) text = re.sub(_par, '', text) text = re.sub(_last_par, '', text) return text p_all = [] m_all = [] for u in users: user_post_dic[u] = list(map(clean_text, p_dic[u])) m_all += [m.parse(p).split('\n')[0] for p in p_dic[u]] p_all += [u + '**' + p for p in user_post_dic[u]]
p_all
で各要素の頭にユーザ名を付けたのは前処理によってテキストが消滅してしまい、listのindexがずれてしますため、苦し紛れで紐づけている
(ちなみにURLを記事タイトルとしてブクマしている場合など)一応はきれいになった
ブラウザのテキストエリアでNeovimを使う フロントエンドエンジニアのためのセキュリティ対策 matplotlibで日本語 モダンJavaScript再入門Doc2Vec
m_all
が分散表現を獲得する時の材料となる文章本体
p_all
は呼び方にすぎないパラメータは熱心には検討していない
pythonfrom gensim import models # 参考記事: http://qiita.com/okappy/items/32a7ba7eddf8203c9fa1 class LabeledListSentence(object): def __init__(self, words_list, labels): self.words_list = words_list self.labels = labels def __iter__(self): for i, words in enumerate(self.words_list): yield models.doc2vec.TaggedDocument(words, ['%s' % self.labels[i]]) sentences = LabeledListSentence(m_all, p_all) model = models.Doc2Vec( alpha=0.025, min_count=5, vector_size=100, epoch=20, workers=4 ) # 持っている文から語彙を構築 model.build_vocab(sentences) model.train( sentences, total_examples=len(m_all), epochs=model.epochs ) # 順番が変わってしまうことがあるので再呼び出し tags = model.docvecs.offset2doctagPCAと描画
PCAのライブラリを利用するのは初めてで、あんなに手順を踏んで学んだのに2行で使えてすごい
pythonfrom sklearn.decomposition import PCA import matplotlib.pyplot as plt import japanize_matplotlib vecs = [model.docvecs[p] for p in tags] draw_scatter_plot(vecs, ls) # 紐付けを解く tag_users = [p.split('**')[0] for p in tags] tag_docs = [p.split('**')[1] for p in tags] # 4色で同じ程度の色感を見つけるのは難しかった cols = ["#0072c2", "#Fc6993", "#ffaa1c", "#8bd276" ] # 無理に1行で書いた clusters = [cols[0] if u == tag_users[0] else cols[1] if u == tag_users[1] else cols[2] if u == tag_users[2] else cols[3] for u in lab_users] # 平面なので2次元 pca = PCA(n_components=2) coords = pca.fit_transform(vecs) fig, ax = plt.subplots(figsize=(16, 12)) x = [v[0] for v in coords] y = [v[1] for v in coords] # 凡例をつけるためにこのループをする for i, u in enumerate(set(tag_users)): x_of_u = [v for i, v in enumerate(x) if tag_users[i] == u] y_of_u = [v for i, v in enumerate(y) if tag_users[i] == u] ax.scatter( x_of_u, y_of_u, label=u, c=cols[i], s=30, alpha=1, linewidth=0.2, edgecolors='#777777' ) plt.legend( loc='upper right', fontsize=20, prop={'size':18,} ) plt.show()できたもの(再掲)
やる前の予想
- Rくん
- ガジェットやセキュリティ系が多い
- ブクマ数79
- Yさん
- 4人の中で一番範囲が広い
- 実はこのユーザだけみんなにシェアする目的で取捨選択したものを投稿している
- ブクマ数864
- Mくん
- Webと機械学習など
- ブクマ数240
- S(自分)
- Webと機械学習とガジェットに加えて「今年はサンマが不漁」みたいなのまで投げてしまう
- ブクマ数896
結果
なんとなくそうなった気がする
おわり
もう少しデータがあつまればユーザの推論とか回してレコメンドとかしたいですね
遅れてすみませんでした(12/11/21:00)
- 投稿日:2019-12-11T20:47:00+09:00
Pythonで定義元や定義先を参照できるjedi-vimのショートカットコマンド
jedi-vimは、VimにおけるPythonの入力補完ツール。
入力補完だけでなく、定義先や定義元へのジャンプもできたりする。
JavaのEclipseなどのIDEツールで使える機能みたいなやつ。コマンド忘れがちなので、備忘録として書いておきます。
項目 キー 説明 g:jedi#completions_command <Ctrl-Space>
補完開始 g:jedi#goto_command <leader>
dDefinition(またはAssignment)に移動 g:jedi#goto_assignments_command <leader>
gAssignmentに移動 g:jedi#documentation_command <K>
pydoc表示 g:jedi#rename_command <leader>
r変数リネーム g:jedi#usages_command <leader>
n使用箇所表示 :Pyimport :Pyimport モジュールのオープン
- 投稿日:2019-12-11T19:21:20+09:00
NMF(非負値行列因子分解)の初期値問題
この記事は古川研究室 Advent_calendar 11日目の記事です。
本記事は古川研究室の学生が学習の一環として書いたものです。内容が曖昧であったり表現が多少異なったりする場合があります。はじめに
前回の記事はNMFとは何なのか?などNMFを初めて勉強する方への記事でした。本記事ではsklearnのNMFライブラリーを実装し初期値による誤差の違いを見ていきます。
NMFの初期値問題
NMFでは学習を始める前に$W,H$の初期値を設定する必要があります。下の図は学習1回目の初期値をランダムにした場合です。$W,H$にランダムな値を当てはめて学習1回目の推定値$\hat{Y}$を計算します。その後元データ$Y$との誤差を計算し、$||Y-\hat{Y}||$が小さくなるように$W,H$を更新していきます。このランダム初期値の場合、学習後の推定値$\hat{Y}$がランダム値によって異なります。つまりランダム値により毎回違う推定値$\hat{Y}$が出てきてしまうという問題があります。NMFの初期値問題を解決する方法は多々あるのですが、一定の推定値$\hat{Y}$を得るためにsklearnのNMFライブラリーではランダム初期化以外にnndsvd,nndsvda,nndsvdarを選択でき、これらの手法で初期化すると一定の推定値を求めることが出来ます。nndsvdについては参考論文[1]こちらをご覧ください。
sklearn.decomposition.NMF
sklearnのNMFでは初期化方法に5種類選択できます。
今回はカスタム初期値以外の4種類(nndsvd,nndsvda,nndsvdar,random)の手法を比較します。
誤差はフロベニウスを用いています。比較結果が以下になります。randomは10回の平均誤差を表示しています。100回学習するとランダム初期値よりも他の手法の誤差が小さくなっています。またnndsvd,nndsvdarは学習初期段階での誤差がランダムより小さいことが分かります。よってこのデータに関しては初期値にnndsvdを用いた方がよさそうです。
Python code
from sklearn.decomposition import NMF import matplotlib.pyplot as plt import numpy as np np.random.seed(1) X = np.random.rand(100, 10) x_plot=np.arange(1,11,1) time=100 x_plot_t = np.arange(1, time+1, 1) loss_t = np.ones(time) loss_t1 = np.empty((time,100)) loss_t2 = np.empty(time) loss_t3 = np.empty(time) for j in range(time): model_t = NMF(n_components= 10, init='nndsvd', random_state=1, max_iter=j+1, beta_loss=2,solver='cd')# ,l1_ratio=1,alpha=0.7) Wt = model_t.fit_transform(X) Ht = model_t.components_ loss_t[j] = model_t.reconstruction_err_ model_t2 = NMF(n_components=10, init='nndsvda', random_state=1, max_iter=j + 1, beta_loss=2,solver='cd' )#,l1_ratio=1,alpha=0.7) Wt2 = model_t2.fit_transform(X) Ht2 = model_t2.components_ loss_t2[j] = model_t2.reconstruction_err_ model_t3 = NMF(n_components=10, init='nndsvdar', random_state=1, max_iter=j + 1, beta_loss=2,solver='cd')# ,l1_ratio=1,alpha=0.7) Wt3 = model_t3.fit_transform(X) Ht3 = model_t3.components_ loss_t3[j] = model_t3.reconstruction_err_ for j in range(100): for r in range(10): model_t1 = NMF(n_components=10, init='random', random_state=r, max_iter=j+1, beta_loss=2,solver='cd')#, l1_ratio=1, alpha=0.7) Wt1 = model_t1.fit_transform(X) Ht1 = model_t1.components_ loss_t1[j,r] = model_t1.reconstruction_err_ loss_t1 = np.sum(loss_t1, axis=1) * 0.1 plt.plot(x_plot_t,loss_t,label="nndsvd",color='b') plt.plot(x_plot_t, loss_t1,color='red',label="random") plt.plot(x_plot_t, loss_t2,label="nndsvda",color='orange') plt.plot(x_plot_t, loss_t3,label="nndsvdar",color='g') plt.xlabel("epoch") plt.ylabel("error") plt.legend() plt.show()参考文献
[1] http://scgroup.hpclab.ceid.upatras.gr/faculty/stratis/Papers/HPCLAB020107.pdf
[2] https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.NMF.html
- 投稿日:2019-12-11T19:15:08+09:00
pandasの時系列プロットの時間軸を、matplotlibでフォーマットし直す
df.plot()後にset_major_formatter()すると、xtickの年や月の表記が壊れる問題に悩まされていたが、解決したのでメモ。
前提
pandas:0.24.2
matplotlib:3.1.0データの準備
import pandas as pd import numpy as np N = 100 x = np.random.rand(N) y = x**2 df = pd.DataFrame( index=pd.date_range('2020-01-01', periods=N, freq='D'), data=dict(x=x, y=y) ) df.head()問題ないケース
# pandasのプロット機能を使用 df.plot()
DataFrameのindexがDatetimeIndexの場合、自動的にtickをフォーマットしてくれる。
このフォーマットでOKなら、それでよし。問題になるケース
import matplotlib.dates as mdates # pandasのプロット機能を使用 ax = df.plot() # フォーマットし直す ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d'))%Yに対する表示がおかしい。0051年て何・・・。
下記サイトによれば、pandasとmatplotlibのdatetimeユーティリティは互換性がないことが原因らしい。
https://code-examples.net/ja/q/2a2a615解決策
import matplotlib.dates as mdates # x_compatオプションを渡す。これでtickの自動調整が抑制される。 ax = df.plot(x_compat=True) ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d'))%Yが正しく2020年になった!
※この例だと「pandasのフォーマットのほうがよくない?」となってしまいますが、そこはご容赦を・・x_compatはx_compatibilityの略?
公式ドキュメントによれば、x_compatはtickの自動調整を抑制するパラメータとのこと。
https://pandas.pydata.org/pandas-docs/stable/user_guide/visualization.html#suppressing-tick-resolution-adjustmentもっとスマートなやり方がありましたら、ご教授いただければ幸いです。
- 投稿日:2019-12-11T19:03:07+09:00
Webサービス(CtoC)集客・マネタイズ成功させたければこれを実装しろ
はじめまして。加藤です。
本業はインフラエンジニアですが、技術書典で技術系同人誌を頒布したりWebサービスを個人開発したりしています。さて、突然ですが、あなたはWebサービスを個人開発して公開していますか。
そして「せっかく開発したのに誰も利用してくれない」「マネタイズ(収益化)したいが上手くいかない」といった悩みを抱えていないでしょうか。
PythonやRuby、PHPなどの言語やそれらのフレームワークを使ってWebサービスを個人開発し、Firebaseなどで公開する人たちは年々増えており、彼らと話す機会も多いので、そういった声をよく聞きます。
私はそれを聞くたびに心の中で「そりゃ当たり前だ!あんなクソサービス誰も使わないわ。」と叫んでいます。
脳内妄想をこじらせた技術オタクがゴミを生む
私を含めて、Webサービスを個人開発している人たちの大多数がしている会話は「○○の言語と△△のフレームワークを使って開発しました~~」とか「Firebaseを使って~~」「フロントエンドは□□で~~」みたいにどんな技術を用いて開発したかを語るものばかりです。
「お前ら、マネタイズがしたいんだよね?マーケティングの話とかしないの?」
「素晴らしいサービス(自称)だから黙っていれば利用者が増えてスケールすると思ってない?」
「口ではマネタイズに困っているとか言いながら、そのための行動も勉強もしてないじゃん。」と感じるようになって、私は自然と彼らと距離を置くようになりました。
自戒を込めて言うと、世の中には業務改善に役立った素晴らしいサービス(この伝票開発アプリとか)やマシュマロ(質問箱)のように多く人が利用したサービスもありますが、大多数は技術者が「面白いサービスを思いついた。こりゃ流行るぞ!」とか「この人たちはきっとこれに悩んでいるから、こういったサービスなら課題解決できるに違いない」と自分の脳内妄想を実現させた結果生みだしたゴミなので過疎って終了するわけです。
「金稼ぎ」や「欲求充足」ができる仕組みを実装しろ
では、どうしたら利用者が増えるサービス開発ができて、その結果マネタイズが成功するのか。
これを研究してきた私がたどり着いたのが、利用者が「金稼ぎ」と「欲求充足」ができる機能を実装したサービスを作れでした。
その証拠にYoutuberが雨後の筍のように増えていますし、ニコ生主等が続々とVTuberに転生してYouTubeやMirrativ等で配信していますし、小説家になろうでは書籍化を目指して多くの作家が小説公開していて、絵師や同人漫画家がTwitterやPixivでイラスト・マンガを投稿して、いまだ多くのブロガーが無料ブログやWordPressで記事を投稿しています。
みんな本音を言えば「(広告収入・投げ銭・書籍化・マンガ化等で)大金を稼ぎたい」し「承認欲求を満たしたい」から、それらのサービスを使ってせっせと活動しているんです。
※好きでやっている勢もいるでしょうが、なぜ1円にもならないのにPVや再生数が伸びなくてもクソリプやアンチコメが来ても作品・動画の投稿が続けられるかといったら、ここに集約されます。そして、それらの実装のために参考になるWebサービスが無料小説投稿サイトです。
詳細は後述しますが、収益還元の仕組みや作家・ユーザー相互の交流機能により「金稼ぎ」と「欲求充足」がどちらもできるからですね。
あなたが手っ取り早く集客やマネタイズに成功するWebサービスが作りたいのなら「大金がほしい」「注目されたい、ちやほやされたい、モテたい」といった人間が普遍的に持つ欲を(法律に抵触しない範囲で)利用者が満たせるものを開発してはいかがでしょうか。
※あなた自身が欲望に忠実になり闇落ちすると、悪質アフィリエイターやSEOを悪用したWELQ中の人のような存在になるので、健全な精神状態と倫理観は維持しましょう。
金稼ぎとは
ここでは投稿した作品に対する収益還元や作家への投げ銭が得られるサービスを紹介します。
これ以外にも、自分の小説やイラストを販売できるBOOTHのようなサービス、Youtubeのスーパーチャット・メンバーシップやFANBOXのようなクリエイター支援や投げ銭のサービスでもお金が稼げますので、サービス設計の参考にしてはいかがでしょうか。
Webサービス運営者は単発の課金であれ月額課金であれ自分の金もうけ(いかに無課金勢に課金させるか)第一で会議でも金や数字(KPIなど)の話しかしない人種なくせに、やれ「カスタマーエクスペリエンス」だとか「カスタマーサクセス」だとかぬかしおるのであほかと思います。
「利用者に収益を還元する」とか「生み出した作品の対価が得られる仕組みを提供する」とか、利用者にメリットがある仕組みの構築(+その結果としてのマネタイズ)ができてはじめてカスタマーについて語る資格があるのではないでしょうか。
広告収益の分配
作品のPVや閲覧人数などに応じて広告収益が分配される仕組みです。
Peing(質問箱)開発者のSeseriさんが作った小説投稿サイト「scraiv」がこの仕組みを導入しています。
他にもアルファポリス、カクヨム、ノベルバなどが同じ仕組みを導入していますね。広告収入の分配といっても、作家の作品内にある広告リンクがGoogleアドセンスのようにクリックされたり、アフィリエイトのように商品が購入されたりすることで分配されるわけではありません。
それぞれのサービスで広告収益分配の基準は異なりますが、純粋に作品が読まれるほど収益が増える仕組みとなっています。
予約投稿を課金で先読み
読者が課金することで、作家が予約投稿した最新話を公開日前に読める仕組みを導入し、課金額の40%を作家に還元しています。
※最新話を無料で読みたいときは公開日まで待てばOK。
これを導入しているのは待ラノだけですね。
利用者による投げ銭
Youtubeのスーパーチャットのように、作者に対して読者が投げ銭を行える仕組みです。
投げ銭機能はノベルアッププラスやマグネットマクロリンク、カクヨムなどが実装しています。
コンテストの開催
優秀な作品は賞金がもらえたり書籍化したりといった特典があるWebコンテストをサイト内で開催し、作家がそれに応募できるようにする仕組みです。
カクヨムやアルファポリス、エブリスタなどがWebコンテストを定期的に開催しています。
欲求充足とは
ここからはマズローの欲求5段階説を用いて説明します。
詳しく知りたい方はfelletさんの下記記事をご覧ください。
マズローの欲求5段階説を図付きで解説!各段階に合わせたサービスも紹介そして「金稼ぎ」で説明した仕組みは小説投稿サイトでいう作家向けのものですが、欲求充足に関しては作家と読者双方の欲求(承認欲求とか作家・読者と繋がりたい欲求とかコミュニティに所属したい欲求など)を満たす仕組みとなっています。
そうしないと、金と欲を満たしたい作家しかおらず読者がいないという地獄のようなサービス(どれだけ作品投稿しようがPVもブックマークも感想も増えない)と化してしまうからです。
社会的欲求を満たす
集団への帰属や愛情を求める欲求です。
コミュニティへの所属や人とのつながりなどが得られることで満たされます。これを満たす機能として、ノベルデイズのコラボノベル(他のユーザーと共同で作品を作る)機能やエブリスタのコミュニティ機能などが挙げられます。
また、ハーメルンには「捜索掲示板」という機能があり、自分が探している作品について質問すると他の利用者が返信で教えてくれるので、そこで作品について盛り上がることができます。
承認欲求を満たす
他人から尊敬されたい、認められたい、賞賛されたいという欲求です。
具体的には作品を読まれたい、読者に応援されたいなどです。マグネットマクロリンクでは読者が作品を読んだり、作家が創作活動をすることによって自動的に「磁界」というポイントが貯まります。
作家は自分が獲得した磁界(ポイント)と読者からプレゼントされた磁界(ポイント)を消費することで、サイト内で自分の作品を宣伝することができます。ノベルアッププラスでは、ログインするともらえるポイントを作家にプレゼントしたり、感想コメントや感想スタンプを送るなどで作家・作品を応援する仕組みがあります。
また、豊富なランキングやピックアップがあり、自分の作品が露出する機会が多いことも重要です。
例えばノベルアッププラスには新着作品・注目作品・読者のオススメ作品などのピックアップがありますし、ハーメルンには総合・短編・オリジナル(作品)・二次創作・R18など複数のランキングがあります。カクヨムでは特集(カクヨム公式レビュアーが選んだ作品を紹介する記事)や各ジャンルの作品のピックアップが公開されており、多くの作品に日があたりやすくなっています。
自己実現欲求を満たす
自分の可能性を追求し、技術や能力の向上に努めて作家としての自分の理想像を実現したいという欲求です。
ツギクルには「人工知能を用いた文章解析システム」があり、AIが客観的に作品の分析をしてくれます。
また、ノベルアッププラスでは小説を投稿する、小説を読む、感想を書き込むなどにより自分のアカウントのレベルや称号がアップしていきます。
一番の悪手は「運営が多数の利用者を置き去りにして全力で金儲けしだすこと」
今回ご紹介した方法が正解ではありません。
多くの利用者を獲得する方法が他にもありますし、そこを研究していただけたらと思います。
ただし、「絶対にWebサービス運営者がやってはならない悪手」が存在します。
それは一部の利用者のみ優遇するや運営の商品・広告を推しすぎることです。
一部の利用者のみ優遇するとは、小説投稿サイトで例えると「PVが稼げる作家の作品バナーをトップページの目立つ場所に掲載したり、書籍化のバックアップや(公式ブログ等で)販促支援を行う」「運営を通じてお仕事がもらえる」「PVが稼げる作家との打ち合わせと称して高級なご飯を奢る」などの運営が不自然なまでに特定利用者を推していたり癒着している状況を指します。
※実際に小説投稿サイト運営がこれらを行っていると言うわけではありません。運営の商品・広告を推しすぎるとは、小説投稿サイトで例えると「トップページなどが書籍の広告などで溢れかえっていて利用者が使いづらい」状況です。
※アルファポリスがこの傾向にあります。利用者が報われる仕組み、飽きさせない仕組みを作れ
報われる仕組みとは
また、悪手というよりは過疎らないためにやっておいた方がベターなのが「利用者が注目される機会をなるべく平等に与える」「利用者の努力が報われる」サービス設計をする事です。
小説投稿サイトで例えると、既存のランキング(PVやブックマークに基づくもの)だけでなく、活動歴が浅い作家のみのランキングや細かくジャンルわけしたランキングを用意したり、活動歴が長い作家の作品やニッチなジャンルでPVは高くないものの濃いファンがついている作品等を運営がピックアップして紹介するなどです。
ぶっちゃけると、小説投稿サイトもYoutubeもはてなブログも、何らかのコンテンツを投稿するプラットフォームのランキングっていうのは
ランキング上位勢がランキングにのることによってさらにPVやブクマを稼いでランキングにのり続けているのが現状です
そしてそんな状況で並み居る古参上位勢を押しのけてランキング上位に食い込めるのは「強力な個性や才能の持ち主」か「元々大多数のファンを抱えている人間(芸能人や他の小説サイトで固定ファンを抱えた作家など)」か「箱推しされている事務所に所属したり動画再生数や配信の同時接続人数が稼げる人とコラボする(VTuberのにじさんじやホロライブなど)」くらいです。
※お金や互助会の力で何とかしようとする人もいますが、それは多くのサービスで利用規約違反になるので割愛。
きっと、商業出版を果たしたつよつよ作家や自称有識者の読者は「ランキング出来ない人間の努力が足りないからだ。才能がないからだ。」と言うでしょう。
登録者10万人越えのYoutuber・VTuberもオンラインサロンを開いちゃうようなはてなブロガーも「そうだそうだ」と賛同するでしょう。※その格差があるからこそアフィリエイトの情報商材やサロン、コンサル、事務所(○○ネットワークとか)に金を払う人が出てくるわけです。
それは利用者目線で言えば正しい事なのかもしれませんが、Webサービスの運営側としては、努力(作品のコンセプトを工夫する、毎日投稿する、コメント返しするなど)を続けてもランキング入りできず離脱する作家が増えて過疎化するリスクのケアを考えなければいけません。
あなたのWebサービスが世界でオンリーワンのものでないのなら、作家たちは競合他社の類似サービスに移っていくからです。
飽きさせない仕組みとは
そして、代わり映えしない作品(所謂なろう系の異世界転生ものとか)や作家で埋め尽くされたランキングに飽きて離脱する読者もケアしないと、作家と同様に競合他社の類似サービスに移っていかれます。
これへのケアも報われる仕組みと同じく、既存のものとは違うランキングの創設やピックアップで作品を掘り起こす仕組みが必要となります。
- 投稿日:2019-12-11T19:01:34+09:00
xvideosの動画をダウンロード その1
はじめに
今回はAVをダウンロードします。
pornhubは複雑だったのでxvideosの動画をダウンロードしようと思います。
ちょっと解析
まずは動画のURLをゲットすべく解析します。
$ wget "https://www.xvideos.com/video52179795/_~_~_" -O a.html ~~略~~ $ cat a.html|grep "http.*mp4" <div id="html5video_base" style="display: none;"><div style="width: 100%; text-align: center;"><a href="https://cdn77-vid.xvideos-cdn.com/e6ieLBaypmMyelQJTIAYPQ==,1576064969/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4?ui=MTEyLjY4LjE3OS45NS0vdmlkZW81MjE3OTc5NS9ffl9-Xw=="><img src="https://img-l3.xvideos-cdn.com/videos/thumbslll/ea/e9/2b/eae92b8f042cd0aad9e2900f90c43252/eae92b8f042cd0aad9e2900f90c43252.15.jpg" style="max-width:100%; height:auto;"></a></div><div style='width: 99%; text-align: center; font-size: 28px; border: 2px #333 solid; padding: 10px; display: block; background: #888;'><div style='font-weight: bold; padding: 3px; border: 1px #000 solid; background: #CCC;'><a href="https://cdn77-vid.xvideos-cdn.com/VVSwC8m7ljMHGpbbmk-DZg==,1576064969/videos/3gp/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4?ui=MTEyLjY4LjE3OS45NS0vdmlkZW81MjE3OTc5NS9ffl9-Xw==">View Low Qual</a></div><div style='font-weight: bold; padding: 3px; border: 1px #000 solid; margin-top: 10px; background: #CCC;'><a href="https://cdn77-vid.xvideos-cdn.com/e6ieLBaypmMyelQJTIAYPQ==,1576064969/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4?ui=MTEyLjY4LjE3OS45NS0vdmlkZW81MjE3OTc5NS9ffl9-Xw==">View High Qual</a></div></div><br /><script> html5player.setVideoUrlLow('https://cdn77-vid.xvideos-cdn.com/VVSwC8m7ljMHGpbbmk-DZg==,1576064969/videos/3gp/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4?ui=MTEyLjY4LjE3OS45NS0vdmlkZW81MjE3OTc5NS9ffl9-Xw=='); html5player.setVideoUrlHigh('https://cdn77-vid.xvideos-cdn.com/e6ieLBaypmMyelQJTIAYPQ==,1576064969/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4?ui=MTEyLjY4LjE3OS45NS0vdmlkZW81MjE3OTc5NS9ffl9-Xw==');それっぽいのが出てきました。
Pythonで抽出
URLがわかればこっちのもんです。
とりあえずURLを抽出するところまでPythonでやってみます。
htmlの入手にはrequestsを使いました。
xvideos.pyimport sys import requests import re try: url=sys.argv[1] body=requests.get(url) except: exit() for url in re.findall("http[^\"\']+\.mp4[^\"\']+",body.text): print(url)$ python3 xvideos.py "https://www.xvideos.com/video52179795/_~_~_" https://cdn77-vid.xvideos-cdn.com/M3DPGG1uE8PVTE-r3Za1Kg==,1576065831/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4 https://cdn77-vid.xvideos-cdn.com/wi_Y89u_DuPPNvDFB7v_GQ==,1576065831/videos/3gp/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4 https://cdn77-vid.xvideos-cdn.com/M3DPGG1uE8PVTE-r3Za1Kg==,1576065831/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4 https://cdn77-vid.xvideos-cdn.com/wi_Y89u_DuPPNvDFB7v_GQ==,1576065831/videos/3gp/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4 https://cdn77-vid.xvideos-cdn.com/M3DPGG1uE8PVTE-r3Za1Kg==,1576065831/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4ちゃんと出てきました。
動画をGETする
URLを取り出せたので、後は動画をGETするだけです。
URLが複数でできましたが、全部同じような感じだったので一番目のやつを使うことにします。
動画のGETもrequestsを使います。
動画なのでstreamモードという小技を使います。よくわかりませんが、イテレーターを返すみたいな感じで、動画の取得が早くなるらしいです。
xvideos.pyimport sys import requests import re try: url=sys.argv[1] body=requests.get(url) except: exit() url=re.search("http[^\"\']+\.mp4[^\"\']+",body.text) if not url: exit() url=url.group() dname="videos/" fname=re.search("[^/\.]+\.mp4",url).group() print("directory :",dname,"filename :",fname) with open(dname+fname,"wb") as f: for chunk in requests.get(url,stream=True): f.write(chunk)いよいよ、動画を取得します。。。
$ mkdir videos $ python3 xvideos.py https://www.xvideos.com/video52179795/_~_~_ directory : videos/ filename : xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4 getting https://cdn77-vid.xvideos-cdn.com/HL9sfQdQCckONKcxgNV0SQ==,1576066626/videos/mp4/e/a/e/xvideos.com_eae92b8f042cd0aad9e2900f90c43252.mp4結果は、お察しください。
まとめる
使いやすいようにこの関数をまとめます。
まとめてる最中にところどころ改良しました。
xvideos.pydef getvideo(url,dname="videos/",fname=""): try: body=requests.get(url) except: return False url=re.search("http[^\"\']+\.mp4[^\"\']+",body.text) if not url: return False url=url.group() if not fname: fname=re.search("[a-zA-Z0-9]+\.mp4",url).group() if dname[len(dname)-1]!='/': dname+='/' try: with open(dname+fname,"wb") as f: for chunk in requests.get(url,stream=True): f.write(chunk) except: return False return True多分ちゃんと使えます。
終わりに
次回はもう少し高機能にしようと思います。
では、よい性生活を。
- 投稿日:2019-12-11T18:57:59+09:00
LINE WORKSボットをAmazon Lexで作る
LINEWORKS Advent Calendar 2019 11日目の記事です。
チャットボットを簡単に作れるサービスは数多く存在してますが、今回は前から気になってた「Amazon Lex」を使ったLINE WORKSボット開発をしてみました。
Amazon Lex とは
「Amazon Lex」は、会話型インターフェイスを提供するAWSサービスです。LexにはAlexaで使われている技術と同じものが使われているそう。
自然言語処理だけでなく音声認識も内包されてます。https://aws.amazon.com/jp/lex/
似たようなサービスにDialogFlowとかAzure Bot ServiceとかIBM Watson Assistant
今回はAmazon Lexで対話フローを作成し、LINE WORKSのトーク上で会話できるボットを作りました。
!!!注意!!!
2019/12/11現在、Amazon Lexは米国英語のみ対応しており、日本語および東京リージョンでの利用は対応してません。
今回についても英語で対話するチャットボットとなります。(日本語対応いつになるか...)
また、使用するリージョンはオレゴンとしました。構成
- AWS Lambdaを使ったサーバーレス構成とする。
- LINE WORKSからのコールバックをAPI Gateway経由で受け取る。そこからAmazon Lexと連携して対話処理をする。
- LINE WORKSのパラメータはSystems Managerパラメータストアで管理する。
- LINE WORKSのアクセストークンは定期的に実行されるLambdaにより更新される。
開発環境
- 言語: Python 3.7.1
- デプロイ: LambdaのデプロイにはServerless Frameworkを使う
実装
1. Amazon LexでBotを作成
今回は、以下の公式のチュートリアルに沿ってサンプルを使ったBotを作成しました。
ボットの例: BookTrip - Amazon Lex https://docs.aws.amazon.com/ja_jp/lex/latest/dg/ex-book-trip.html
簡単に説明すると、車やホテルを予約するチャットボットです。
サンプルから「BookTrip」を選んで、作成しました。このような画面で、インテントの設定や、対話フローについて設定をします。
他のチャットボット作成サービスを使ったことがある人ならすぐ使える印象。初心者はなかなか一から設定するのは大変そうだなと感じました。
2. LINE WORKS Developer Consoleから各種キー作成 & ボット作成
LINE WORKS Developer Consoleへログインし、キーの作成や今回のボットを作成します。
詳しくはこちらの過去記事を参照ください。
LINE WORKS トークBot をPythonで実装してみる 〜前編: API認証〜 https://qiita.com/mmclsntr/items/1d0f520f1df5dffea24b
3. LINE WORKSボットアプリサーバー作成
Lambdaで構成し、ランタイムをPython3.7で実装しました。
Lambda関数は以下の2つ
- LINE WORKS アクセストークン定期更新
- CloudWatch Event スケジュールイベントで定期実行 (半日に一回)
- LINE WORKS チャットボット
- API Gateway経由でLINE WORKSからのメッセージを取得し、Amazon Lex Botと連携。
以下、サンプルコード
lambda_function.pyimport json import jwt import requests import urllib import boto3 import os from datetime import datetime from base64 import b64encode, b64decode import hashlib import hmac from requests.structures import CaseInsensitiveDict ssm = boto3.client('ssm') lex = boto3.client('lex-runtime') #################################### # Systems Manager パラメータストア # #################################### def get_parameter(key): """ SSMパラメータストアからパラメータ取得 """ response = ssm.get_parameters( Names=[ key ], WithDecryption=True ) parameters = response["Parameters"] if len(parameters) > 0: return response['Parameters'][0]["Value"] else: return "" def put_parameter(key, value): """ SSMパラメータストアへパラメータを格納 """ response = ssm.put_parameter( Name=key, Value=value, Type='SecureString', Overwrite=True ) ############## # Amazon Lex # ############## def post_text_to_lex(text, user_id, bot_name, bot_alias): """ Amazon Lexへテキストを送信 & 返答取得 """ response = lex.post_text( botName=bot_name, botAlias=bot_alias, userId=user_id, inputText=text ) return response["message"] ################## # LINE WORKS API # ################## def get_jwt(server_list_id, server_list_privatekey): """ LINE WORKS アクセストークンのためのJWT取得 """ current_time = datetime.now().timestamp() iss = server_list_id iat = current_time exp = current_time + (60 * 60) # 1時間 secret = server_list_privatekey jwstoken = jwt.encode( { "iss": iss, "iat": iat, "exp": exp }, secret, algorithm="RS256") return jwstoken.decode('utf-8') def get_server_token(api_id, jwttoken): """ LINE WORKS アクセストークン取得 """ url = 'https://authapi.worksmobile.com/b/{}/server/token'.format(api_id) headers = { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' } params = { "grant_type" : urllib.parse.quote("urn:ietf:params:oauth:grant-type:jwt-bearer"), "assertion" : jwttoken } form_data = params r = requests.post(url=url, data=form_data, headers=headers) body = json.loads(r.text) access_token = body["access_token"] return access_token def validate_request(body, signature, api_id): """ LINE WORKS リクエスト検証 """ # API IDを秘密鍵に利用 secretKey = api_id.encode() payload = body.encode() # HMAC-SHA256 アルゴリズムでエンコード encoded_body = hmac.new(secretKey, payload, hashlib.sha256).digest() # BASE64 エンコード encoded_b64_body = b64encode(encoded_body).decode() # 比較 return encoded_b64_body == signature def send_message(content, api_id, botno, consumer_key, access_token, account_id): """ LINE WORKS メッセージ送信 """ url = 'https://apis.worksmobile.com/{}/message/sendMessage/v2'.format(api_id) headers = { 'Content-Type' : 'application/json;charset=UTF-8', 'consumerKey' : consumer_key, 'Authorization' : "Bearer " + access_token } params = { "botNo" : int(botno), "accountId" : account_id, "content" : content } form_data = json.dumps(params) r = requests.post(url=url, data=form_data, headers=headers) if r.status_code == 200: return True return False ###################### # Lambda関数ハンドラ # ###################### def update_token_handler(event, context): """ LINE WORKS アクセストークン定期更新 Lambdaハンドラー関数 """ # SSMパラメータストアからLINE WORKSのパラメータを取得 api_id = get_parameter("lw_api_id") server_list_id = get_parameter("lw_server_list_id") server_list_privatekey = get_parameter("lw_server_list_private_key").replace("\\n", "\n") # JWT取得 jwttoken = get_jwt(server_list_id, server_list_privatekey) # Server token取得 access_token = get_server_token(api_id, jwttoken) # Access Tokenをパラメータストアに設定 put_parameter("lw_access_token", access_token) return def chat_with_lex_handler(event, content): """ LINE WORKS チャットボット Lambdaハンドラー関数 """ botno = os.environ.get("BOTNO") lex_bot_name = os.environ.get("LEX_BOT_NAME") lex_bot_alias = os.environ.get("LEX_BOT_ALIAS") # SSMパラメータストアからLINE WORKSのパラメータを取得 api_id = get_parameter("lw_api_id") consumer_key = get_parameter("lw_server_api_consumer_key") access_token = get_parameter("lw_access_token") event = CaseInsensitiveDict(event) headers = event["headers"] body = event["body"] # リクエスト検証 if not validate_request(body, headers.get("x-works-signature"), api_id): # 不正なリクエスト return # Jsonへパース request = json.loads(body) # 送信ユーザー取得 account_id = request["source"]["accountId"] res_content = { "type" : "text", "text" : "Only text" } # 受信したメッセージの中身を確認 request_type = request["type"] ## Message if request_type == "message": content = request["content"] content_type = content["type"] ## Text if content_type == "text": text = content["text"] # Amazon Lexと連携 reply_txt = post_text_to_lex(text, account_id.replace("@", "a"), lex_bot_name, lex_bot_alias) res_content = { "type" : "text", "text" : reply_txt } # 送信 rst = send_message(res_content, api_id, botno, consumer_key, access_token, account_id) res_body = { "code": 200, "message": "OK" } response = { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": json.dumps(res_body) } return responseこちらの過去記事もご参照ください。
LINE WORKS トークBot をPythonで実装してみる 〜後編: チャットボット実装〜 https://qiita.com/mmclsntr/items/28ba6baaf23124a53663ソースコード
https://github.com/mmclsntr/lineworks-bot-with-amazon-lex
動かしてみる
以下の感じで、LINE WORKSのトーク上で、Lexで作成したチャットボットと (英語で) 会話できます。
まとめ
Amazon Lexで作成したチャットボットもLINE WORKSで問題なく動かせることができました。
簡単な問い合わせであれば楽に実現できるかなと思います。ぜひ日本語対応していただけると。。あとはLexで対話の設定をいろいろチューニングして遊んでみようと思います。
- 投稿日:2019-12-11T18:09:35+09:00
Pythonで好きな場所のファイルをimportする方法
条件
Pythonのバージョンによって?できるものとできないものがあるようです。
いくつか例をあげたので、もし失敗したら他の方法を試してみてください。フォルダの構成
├─python | main.py | | ├─code | | |mnist.py | | ├─dataset | | |activation_function.pyやりたいことリスト
- main.py内でmnist.pyをimportしたい!!
- mnist.py内でactivation_function.pyをimportしたい!!
- mnist.py内でmain.pyをimportしたい!!
やりたいこと1
main.py内でmnist.pyをimportしたい!!
方法1
main.pyimport sys sys.path.append("code") import mnist方法2
「Pythonの自作モジュールをimportしたいならsys.pathを設定しよう」を参考にさせていただきました。
自分の環境では動作しませんでした。環境によっては動作するかもしれません。main.pyimport sys sys.path.append("python/code") import mnistやりたいこと2
mnist.py内でactivation_function.pyをimportしたい!!
方法1
ゼロから作るDeepLearningを参考にしました。
自分の環境では動作しませんでした。環境によっては動作するかもしれません。mnist.pyimport sys, os sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定 import dataset.activation_function方法2
mnist.pyimport sys sys.path.append("") # sys.path.append("./")でも可能 # sys.path.append(".")でも可能 import dataset.activation_function方法3
mnist.pyimport sys sys.path.append("dataset") import activation_function #ちょっと綺麗になるやりたいこと3
mnist.py内でmain.pyをimportしたい!!
これはわかりませんでした!!可能であるのならば教えてください!!
まず下のファイルから上のファイルをimportするのはフォルダーの構造として駄目な気がするので、フォルダー構成を見直せ!ということなのかもしれません。
ちなみに以下のことを試してみましたが自分の環境では、動作しませんでした。mnist.pyimport sys sys.path.append("../") import mainおわり
紹介した多くの方法がPython2系では動かないと思います。
Python3を使っておきましょう
いくつか動かないのはおそらく親フォルダーの扱いの差だと思うのですが、これはOS(MacとWindows)の差なのかな?あとで検証するかもです。タイトルの「ファイル」の部分が適切では無い気がしますので、それに置き換わる何かがあればコメントで教えてくださいm(_ _)m
- 投稿日:2019-12-11T17:31:43+09:00
英文PDFをまとめて日本語化
PDFをまとめて翻訳したい
PDFを翻訳したい場合、Google翻訳の標準機能がまず思い当たりますが、ファイルサイズの上限があって不便です。
前回の記事ではGoogle Translate APIを叩いて英文を日本語化しました。読みたい文章がPDFの場合にはGoogleドキュメントやAdobe Acrobatを使ってテキストを取り出すことが考えられますが、工程数が多い難点があります。ここもPDF Minerというライブラリを使うことでPythonにやらせることができそうです。
以下の記事を大変参考にさせていただきました。
【PDFMiner】PDFからテキストの抽出作成したスクリプトは以下にあります。
https://github.com/KanikaniYou/translate_pdfあるフォルダ内にある全てのPDFについて、抽出・翻訳・テキストファイルとして出力します。
PDFMinerでの取り出し後に要らない文字列まで取ってしまうので("......"と同じ記号が続く文字列など。目次とかにあるよね。)、抽出後のテキストファイルを中間ファイルとしてまとめておき、人の手で要らない部分を取ってやることができるようにしています。
翻訳作業の全体の流れとしては
0. クイックスタート | Google Cloud Translation API Documentation | Google Cloud Platformを参考にGoogle Translate APIを取得
1. PDFからテキストを取り出してテキストファイルとして保存する(pdf_to_txt.py)
2. テキストを整形して一行の新しいテキストファイルとして保存する(let_translatable.py)
3. 英文を日本語化してテキストファイルとして保存する(translate_en_jp.py)となります。先述の通り、1.のあとで人手でテキストファイルを見てやることで、PDF Minerで抽出したテキストをチェックし、Google Translate APIを無駄に叩かなくて済むように必要箇所だけ取り出すということができます。
環境
Python3系の入ってるLinuxならいけると思います。手元の環境は Cloud9でUbuntu 18.04です。
pip install pdfminer.sixちなみにPDF Minerはとても有用なのですが、日本語などを取り出したい時に文字化けが起こりやすいようです。今回は英語を取り出すのでそこまで頻繁に問題は起きないと思います。
PDF Minerでの日本語取り出しに関する既知の不具合: Still have issues with CID Characters #39
git clone https://github.com/KanikaniYou/translate_pdf cd translate_pdfファイル構成です。(説明上、翻訳したいPDF10個をすでに置いてあります。)
. ├── eng_txt ├── eng_txt_split ├── jpn_txt ├── let_translatable.py ├── pdf_source │ ├── report_1.pdf │ ├── report_10.pdf │ ├── report_2.pdf │ ├── report_3.pdf │ ├── report_4.pdf │ ├── report_5.pdf │ ├── report_6.pdf │ ├── report_7.pdf │ ├── report_8.pdf │ └── report_9.pdf ├── pdf_to_txt.py └── translate_en_jp.py1.テキストの取り出し
pdf_to_txt.pyimport sys from pdfminer.converter import PDFPageAggregator from pdfminer.layout import LAParams, LTContainer, LTTextBox from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager from pdfminer.pdfpage import PDFPage import os import re def find_textboxes_recursively(layout_obj): if isinstance(layout_obj, LTTextBox): return [layout_obj] if isinstance(layout_obj, LTContainer): boxes = [] for child in layout_obj: boxes.extend(find_textboxes_recursively(child)) return boxes return[] def pdf_read_controller(filepath): try: text_in_pdf = "" with open(filepath, 'rb') as f: for page in PDFPage.get_pages(f): try: interpreter.process_page(page) layout = device.get_result() boxes = find_textboxes_recursively(layout) boxes.sort(key=lambda b:(-b.y1, b.x0)) text_in_page = "" for box in boxes: text_in_box = "" text_in_box += box.get_text().strip().strip(" ") text_in_box.rstrip("\n") text_in_box = re.sub(r' ', " ", text_in_box) text_in_page += text_in_box text_in_pdf += text_in_page except Exception as e: print(e) return(text_in_pdf) except Exception as e: print(e) print("error: " + filepath) return("no-text") def make_txtfile(folder_path,file_name,text='error'): if text != "no-text": with open(folder_path+"/"+file_name, mode='w') as f: f.write(text) laparams = LAParams(detect_vertical=True) resource_manager = PDFResourceManager() device = PDFPageAggregator(resource_manager, laparams=laparams) interpreter = PDFPageInterpreter(resource_manager, device) if __name__ == '__main__': for file_name in os.listdir("pdf_source"): if file_name.endswith(".pdf"): print(file_name) text_in_page = pdf_read_controller("pdf_source/" + file_name) make_txtfile("eng_txt_split",file_name.rstrip("pdf")+"txt",text_in_page)フォルダ「pdf_source」下のpdfを全部読んでテキストファイルを作って出力します。
$ python pdf_to_txt.py report_3.pdf report_7.pdf report_2.pdf report_1.pdf unpack requires a buffer of 10 bytes unpack requires a buffer of 8 bytes report_5.pdf report_9.pdf report_8.pdf unpack requires a buffer of 6 bytes unpack requires a buffer of 6 bytes unpack requires a buffer of 4 bytes report_4.pdf report_6.pdf report_10.pdfいくつかエラーが出ていますが、無視してテキストファイルを作成します。PDFはややこしいですね。
似たようなエラー:struct.error: unpack requires a string argument of length 161-2.テキストの目視チェック(しなくてもOK)
例えばテキストの一部にこういった箇所が含まれていました。Google Translate APIを無駄に叩きたくないので要らないところは削除してやりましょう。
2.テキストの整形
let_translatable.pyimport os if __name__ == '__main__': for file_name in os.listdir("eng_txt_split"): if file_name.endswith(".txt"): print(file_name) text = "" with open("eng_txt_split/"+file_name) as f: l = f.readlines() for line in l: text += str(line).rstrip('\n') path_w = "eng_txt/" + file_name with open(path_w, mode='w') as f: f.write(text)PDF Minerで出てくるテキストは改行だらけで、このままGoogle翻訳に入れると上手く翻訳してくれそうにありません。そこで、改行を無くしたテキストファイルを新しく作成し、フォルダ eng_txt_split に出力してやります。
$ python let_translatable.py report_4.txt report_10.txt report_2.txt report_6.txt report_9.txt report_5.txt report_8.txt report_7.txt report_3.txt report_1.txt3.英語→日本語に翻訳!
できたテキストをいよいよ翻訳します。中身は前記事を参考にしていただければと思います。
translate_en_jp.pyimport requests import json import os import re import time API_key = '<ここにAPIキーを入れてください>' def post_text(text): url_items = 'https://www.googleapis.com/language/translate/v2' item_data = { 'target': 'ja', 'source': 'en', 'q':text } response = requests.post('https://www.googleapis.com/language/translate/v2?key={}'.format(API_key), data=item_data) return response.text def jsonConversion(jsonStr): data = json.loads(jsonStr) return data["data"]["translations"][0]["translatedText"] def split_text(text): sen_list = text.split('.') to_google_sen = "" from_google = "" for index, sen in enumerate(sen_list[:-1]): to_google_sen += sen + '. ' if len(to_google_sen)>1000: from_google += jsonConversion(post_text(to_google_sen)) +'\n' time.sleep(1) to_google_sen = "" if index == len(sen_list)-2: from_google += jsonConversion(post_text(to_google_sen)) time.sleep(1) return from_google if __name__ == '__main__': for file_name in os.listdir("eng_txt"): print("source: " + file_name) with open("eng_txt/"+file_name) as f: s = f.read() new_text = split_text(s) path_w = "jpn_txt/" + file_name with open(path_w, mode='w') as f: f.write(new_text)$ python translate_en_jp.py source: report_4.txt source: report_10.txt source: report_2.txt source: report_6.txt source: report_9.txt source: report_5.txt source: report_8.txt source: report_7.txt source: report_3.txt source: report_1.txt長いテキストだとちょっと時間がかかります。
成果物
jpn_txtフォルダ内に翻訳後のテキストファイルが入ります。
これで英文PDFに悩まされずに済みそうですね!
もっとも、これで出力されるテキストはレイアウトなんて概念はなくなってますし、ページ間などでは上手く翻訳されないこともあると思います。本来はその辺の処理もできればいいんでしょうけど、なかなか難しそうです。あくまでたくさんあるPDFについて日本語で目を通したいという際に使って頂ければと思います。
- 投稿日:2019-12-11T16:43:25+09:00
manylinux wheelのビルドにcircleciを使わせていただいている話
manylinux wheelとは
manylinuxという「大体すべてのLinuxディストリで動く」wheelがあります。
wheelというのはコンパイル済みのネイティブライブラリを含むPythonパッケージです。
有名どころではtensorflowがこのmanylinux wheelを配布していたりします。
このmanylinux wheelのビルドにcircleciが最適だったので紹介します。なぜ manylinux wheel の作成に circleciが適しているか
- CIサービス内でArtifact(私の場合wheel)の保存ができる
ため私はcircleciでmanylinux wheelのビルドを行っています。
具体的には https://github.com/ecell/ecell4_base/blob/master/.circleci/config.yml のように行っています。
yml中では
- store_artifacts: path: /root/circle/wheelhouseがそれを行っている箇所になります。
私が知る限りtravisci 内 ではこれを行うことができずcircleciを使わせていただいている次第です。manylinuxのwheelは「大体すべてのLinuxで動く」というものなので
この環境中での動作テストだけでは不十分で他の複数のディストリでの動作も見たかったりします。docker: - image: quay.io/pypa/manylinux2010_x86_64はCentOS6です。(なぜそんな古いバージョンなのかはここでは割愛します。)
なのでとりあえずパッケージを置いておく場所がほしかったりします。
そこでこのArtifactの保存の機能が助かるんですね。おわりに
昨日 【大阪】CircleCI ユーザーコミュニティミートアップ#2 でCircleCI Japanコミュニティの皆様にお世話になり本記事を書きたいと思うに至りました。
コミュニティって重要ですね。以上。
- 投稿日:2019-12-11T16:09:24+09:00
Python(Flask)のプロジェクトで途中からLinterを導入しようとした話
話題
- 参画しているプロジェクトのバックエンド(Flask)のコードがLinterやformatterが導入されておらず苦労した(なお継続中)話
希望
- Githookでlint&formatしたくね??????
問題
- プロジェクト初期に書かれたコードがpep8ガン無視でlint導入したらエラー出まくる
- 一気にautofixかけたいけど、差分が出過ぎてレビューができない。
- 全量リファクタもしたいが工数もないし、テストコードも十分じゃなさそう
妥協
- 新規作成するコードはLint&formatかける。
- 既存コードは一旦そのまま
- 既存コードのテストコードを見直し、追加実装していつでもリファクタできるようにしておく
- hookでLintはかける。formatはにんげんが判断してかける(苦肉の策)。
- 既存は
git commit --no-verify
で無理矢理コミットする開発方法
新規の場合
- コーディングする
- コミット時にLintが走る
- formaterをかける
black hogehoge.py
- 手で直さなきゃならないとこはエラーがなくなるまで修正してコミット(ファイル指定でlint
flake8 hogehoge.py
)- pushしてプルリクレビューへ
既存コードに手に入れる場合
- コーディングする
- コミット時にLintが走る
- エラーがあれば、
black hogehoge.py --diff
で追記部分のエラーを確認して手で直す。(ファイル指定でlintflake8 hogehoge.py
)- コミットするときは
git commit --no-verify
でhookを飛ばす- pushしてプルリクレビューへ
Linterの設定
linterはflake8を利用する。
1.pip install flake8
2. 設定ファイルを作成(プロジェクトルートに.flake8
を作る)[flake8] ignore = E203, E266, E501, W503, F403, F401 max-line-length = 79 max-complexity = 18 select = B,C,E,F,W,T4,B9※flake8の設定は適宜変えること
- 正直現代でmax-line-length<80は少なすぎ
- igonoreはblackとの関係でE203,W503,W504は必須。それ以外は適宜
- ここ見て
formatterの設定
formatterはblackを使う
1.pip install black
2. 設定ファイルを作成(プロジェクトルートにpyproject.toml
を作る)pyproject.toml[tool.black] line-length = 79 include = '\.pyi?$' exclude = ''' /( \.git ) '''flake8と合わせること
- たまにformaterかけたのにエラーになるときあってびっくりする
githookの設定
hookの設定にはpre-commit使います。
1.pip install pre-commit
2. 設定ファイルの作成(プロジェクトルートに.pre-commit-config.yaml
を設置)repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: - id: flake8まとめ
- ツールの導入期間なこともあっていびつで手作業が含まれてしまってる。ちょっとずつ変えていく
- linterやformaterは最初に入れる。ルールも最低限のもの(pep8)入れるべき
- 書きづらいなと思った段階でルールを変えていく
- テストコードがちゃんとしてれば既存いじっても基本は問題ない
- 投稿日:2019-12-11T16:00:42+09:00
Worker Threadデザインパターンでスマートメーター計測ロガー
はじめに
かなり前からスマートメーターの電力情報を取得することができていますが、UDP通信であることや微弱な電波であるせいか長期間の安定的な運用が難しいとの指摘もあります。
長期間運用できる安定性と、様々な情報ソースに対応する拡張性をもった計測ロガーについて考えてみました。考え方としては、
- Worker Thread デザインパターンによって、機能ごとの処理を互いに分離する
- これにより、機能ごとの再起動など柔軟に対処でき、安定性が高まる
- また、さまざまな機能を持ったクライアント、ワーカーを追加でき、機張性が高まる
というものです。
注) このプログラムは現状 WiSun モジュール RL7023 Stick-D/DSS(デュアルスタック)にしか対応していません。
ただ、デバイスドライバ部を少し修正すれば対応できると思います。Worker Thread デザインパターンによる実装
やりたいことは、
- スマートメータのデータを取得する
- データをファイルに保存する
の2つなので、前者をクライアントスレッド、後者をワーカースレッド、保存するデータをリクエストとして捉えれば、Worker Thread デザインパターンで設計できそうです。Pythonのスレッドは並列処理によるパフォーマンス改善は望めないそうですが、今回の場合処理のほとんどがIOなので、機能を整理してわかりやすくすることと、拡張性、安定性の向上が目的となります。
なお、Arduinoからのセンサ情報を記録したいということが開発のきっかけとなっているので、クライアントとしてシリアルポートからのデータを読み取るクラスも実装しています。
今のところ、クライアントもワーカーも管理上区別する必要がないので、共通の Worker クラスを定義し、これを継承してスマートメーター通信とファイル保存のクラスを定義することにしました。
Workerクラスで定義されているのは、管理スレッドから停止の命令をうけつけ、stop イベントをセットする stop() 関数だけです。スレッドループでは stop イベントがセットされていない限りループを繰り返し、stop イベントがセットされていたら、ループを抜けて終了処理を行い、スレッドを終了する流れになります。stop() 関数はメインスレッドから呼び出されるので、join() を実行してWorkerのスレッドが終了するまでここで待機します。worker.pyimport threading class Worker ( threading.Thread ): """Woker はスレッドを停止するためのイベントを設定するための抽象クラス。 """ def __init__( self ): super().__init__() self.stopEvent = threading.Event() def stop ( self ): """ストップイベントをセットする。 """ self.stopEvent.set() # スレッドが終了するまで待機 self.join() def run(self): """ストップイベントがセットされていない限り、繰り返し実行する。 この関数はオーバーライドする。 """ while not self.stopEvent.is_set(): # do something pass # ここに終了処理を書くWorker を継承した BrouteReader、FileRecorder、SerialReader クラスについての解説はここでは省略します。機能の概要は後述しますが、詳細はソースコードのコメントを参照してください。
メインの処理は2部構成で、
- 動作する Worker を定義する設定部
- 設定に従ってオブジェクトを作成しスレッドを開始する実行部
から成り立っています。
設定部 keiconf.py は以下のようなコードになります。
keiconf.pyimport queue from keilib.recorder import FileRecorder from keilib.broute import BrouteReader # オブジェクト(スレッド)間で通信を行うための Queue record_que = queue.Queue(50) # 動作させるワーカオブジェクトの構成 worker_def = [ { 'class': FileRecorder, # FileRecorderオブジェクトを作成 'args': { # 引数 'fname_base': 'mydatafile', # 記録ファイルの名前に使われる文字列 'record_que': record_que # 記録するデータをやり取りする Queue } }, { 'class': BrouteReader, # BrouteReaderオブジェクトを作成 'args': { # 引数 'port': '/dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_xxxxxxxx-if00-port0', # WiSUNドングルのシリアルポート 'baudrate': 115200, # WiSUNドングルのボーレート 'broute_id': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # BルートID(電力会社に申請) 'broute_pwd': 'xxxxxxxxxxxx', # Bルートパスワード(電力会社に申請) 'requests':[ # 取得するプロパティ値の定義 { 'epc':['D3','D7','E1'], 'cycle': 3600 }, # 積算電力量用 係数(D3),有効桁数(D7),単位(E1),3600秒ごと { 'epc':['E7'], 'cycle': 10 }, # 瞬時電力(E7),10秒ごと { 'epc':['E0','E3'], 'cycle': 300 }, # 積算電力量(E0,E3),300秒ごと ], 'record_que': record_que # 記録するデータをやり取りする Queue } }, ]ここでは、worker_defリストに FileRecorderとBrouteReaderの2つのクラスがコンストラクタへの引数の構成とともに定義されています。 実行部ではここに定義されているクラスのインスタンスを作成し管理します。作成されたオブジェクトはそれぞれ別のスレッドで動作を開始しますが、スレッド間でデータを受け渡すためのチャンネルとしてqueue.Queueオブジェクトを利用しています。record_queは2つのオブジェクト間でファイルに保存するデータを受け渡すために共有されています。BrouteReaderはスマートメーターから情報を得ると体裁を整えて record_queに投入(put)します。一方FileRecorderはrecoed_queに流れてきたデータを取得(get)してファイルに保存します。Queue オブジェクトはスレッドセーフであるため、排他制御を意識せずに使用できます。
実行部 key.py の核心部は以下のコードです。
kei.py# Launch each worker instance(各ワーカーインスタンスの起動) for wdef in worker_def: wdef['instance'] = wdef['class']( **wdef['args'] ) # インスタンスの作成 wdef['instance'].start() # スレッドの開始 # Check if the threads have stopped at intervals, and restart them if stopped. # 一定間隔でスレッドが停止したかどうかを確認し、停止していた場合は再起動 while True: for wdef in worker_def: if not wdef['instance'].isAlive(): wdef['instance'] = wdef['class']( ** wdef['args']) wdef['instance'].start() time.sleep(10)このようにメインスレッド内で、ワーカースレッドが不慮のエラーでストールした場合、再起動する仕組みを入れておくことによって長期間の安定動作が可能になります。
このほか kei.py にはログファイルの処理や、終了処理のためのシグナルハンドラの設定などが含まれています。詳細はソースコードを確認してください。ソースコード https://github.com/kjmat/keilog
特徴
Raspberry Pi + Python3 で動作
- Raspbian Lite (デスクトップが入ってない。ヘッドレスで運用)
- SDカード 4G以上(データ量に応じて。16Gもあれば十分)
- ネットワーク接続(時刻同期のためとかいろいろ)
- python3, python3-serial, python3-requests
- (最新ディストリビューションだと serial だけ別途インストール)
- ラズパイでなくても大丈夫だと思いますが、前提としてつけっぱなしになります。
スマートメーターBルートデータ取得機能
- WiSun モジュール RL7023 Stick-D/DSS(デュアルスタック)に対応
- (シングルスタックの RL7023 Stick-D/IPS を使うには修正が必要。持ってないので未実装です)
- 取得したいスマートメーターのプロパティと取得間隔を定義できる。
対応プロパティ = D3,D7,E0,E1,E3,E7,E8,(EA,EB)
- D3: 係数「積算電力量計測値」を実使用量に換算する係数
- E7: 積算電力量計測値の有効桁数
- E1: 単位「積算電力量計測値」の単位 (乗率)
- E0: 積算電力量 計測値 (正方向計測値)
- E3: 積算電力量 計測値 (逆方向計測値)
- E7: 瞬時電力計測値(逆潮流のときは負の値)
- E8: 瞬時電流計測値(T/R相別の電流)
- EA: 定時 積算電力量 計測値 (正方向計測値)
- EB: 定時 積算電力量 計測値 (逆方向計測値)
- 状態遷移による振る舞いの管理 → 接続が切れても自動的に再接続
シリアルポートからのデータ取得機能
- USBに接続した Arduino などの周辺機器から入力されたデータも記録可能
- Arduinoはセンサのライブラリや作例が豊富で使いやすいし消費電力も少ない
- XbeeやTWELiteDIPなどで無線化すれば、離れた場所のセンサも記録できる
- TWELiteDIPにセンサを直結した場合、電池で数年持つセンサノードが作れる
リモートの Http サーバーにデータを POST する機能
- 遠隔地に置いたラズパイのデータを(そこそこ)リアルタイムに取得するため
- Raspi + Soracom(SIM) + AK020(3Gモデム) で安定動作
マルチスレッド(シンプルなフレームワーク)
- 不慮のエラーによる停止からの回復、長期連続運用が可能
- 機能の追加が容易
起動方法
Raspbian Lite にあらかじめ pyserial をインストールしておきます。
$ sudo apt install python3-serial適当な作業ディレクトリを作成し、以下のようにファイルを配置し、
keiconf.py
には構成を定義しておきます。workdir/ |-- keilib/ | |-- __init__.py | |-- broute.py | +-- .... | |-- keiconf.py +-- kei.pyプログラムの実行は次のように行います。
$ python3 kei.py
実行すると
workdir/
ディレクトリ内に、計測データを保存するファイルが2つと、プログラムの実行時の情報を出力するログファイルkei.log
が作成されます。ログについては、$ DEBUG=0 python3 kei.pyのように起動すると、ログレベル = DEBUG となりログの出力先が標準出力になります。ログレベルは USR1 シグナルを受け取ると INFO <-> DEBUG で反転します。
プログラムの終了については、HUP, INT, TERM シグナルによって各オブジェクトにストップイベントを送っています。ストップイベントを受けとったオブジェクトのスレッドはリソースを開放してから終了します。出力ファイルの形式
2つのファイルが作られます。
1. [YYYYMMDD]-[mydatafile].txt 2. sum[YYYYMMDD]-[mydatafile].txt1.は1行につき1件のデータが記録されており、行のフォーマットは以下の形をとります。
[YYYY/MM/DD hh:mm:ss],[UnitID],[SensorID],[Value],[DataID]<改行>なお BrouteReader の場合、出力するデータは以下の通りです。
[UnitID] = BR(固定) [SensorID] = スマートメーターのプロパティコード(EPC): E7, E0 等 [Value] = 測定値(数値) [DataID] = x(固定)2.は1行に各センサーの10分ごとの平均値が記録されます。
[YYYY/MM/DD hh:m0],[UnitID],[SensorID],[AverageValue]<改行>日付のフォーマットに秒がない点に注意です。
参考