How to make datas our friends

「エンジニアは発信していくことが責務である」という言葉に感化されて始めた勉強したことを書き留めていく備忘録的なやつ。

JupyterNotebookでboto3を使えるようにした話

概要

ローカルのJupyterNotebookからS3の操作をしたかったのでAWS SDKのboto3を入れたという話。

boto3に関してはこちらを参照: AWS SDK for Python | AWS

ちなみにJupyterNotebookでと書いているが、普通にPythonのboto3をローカルに落として使えるようにしただけです笑

やったこと

boto3のinstall

pip install boto3

で、インストール可能ですが、僕はanacondaでインストールしました。

conda install -c anaconda boto3=1.4.5

boto3のバージョン情報を見る場合はこれで見れます。

conda info boto3

クレデンシャル情報の登録

インストールが完了したら、aws cliを使ってクレデンシャル情報を登録。

aws configure

と叩くと情報を入力できます。

AWS Access Key ID : ACCESS_KEY_ID
AWS Secret Access Key : SECRET_ACCESS_KEY
Default region name : ap-northeast-1
Default output format : json

今回、リージョンは普段使っている ap-northeast-1 にしました。

多分大丈夫だと思いますが ACCESS_KEY_ID / SECRET_ACCESS_KEY に何を入れればいいのかわかんない人はこちらを参照してみてください: AWS アカウントのアクセスキー管理 - アマゾン ウェブ サービス

これで無事boto3がJupyterNotebookで使えるようになりました。

めでたし、めでたし。

LINE API x Google App Script で bot を作成した話

f:id:minion024:20170717181521p:plain

あらすじ

友達とLINEしてるときに、グループにBotを追加したら面白そう!便利そう!夢がある!と思いたちBotの作成にチャレンジしました。

LINEが Messaging API というものを提供しているので、今回はこれを使用します。
business.line.me

サーバーを用意するのが面倒だったので、GASで出来ないかちょっと調べたらGASで実装している先人が結構いたので、僕もGASを使用して実装することにしました。

作成手順

まずは、LINE BUSINESS CENTERからアカウントの作成を行います。
アカウントの作成は手順通りにやれば5分くらいで終わりました。
ちなみに今回は Developer Trial でやります。

ちなみに料金はこんな感じ。(2017/07/14時点)
f:id:minion024:20170716023401p:plain

アカウントの作成が完了したらアカウントリストからBot用のビジネスアカウントを作成します。

ビジネスアカウントの作成が完了したらLINE@ MANAGERから作成したビジネスアカウントの設定が変更できるようになります。

APIを有効にするため、FVに以下のような画面が表示されているので Enable API のボタンを押下。
f:id:minion024:20170716024407p:plain

APIを有効にするといくつか使用できない機能があるっぽいです。
f:id:minion024:20170716024719p:plain

問題ないのでそのまま進めると Setting 画面が表示されます。
全て使用したいので全て許可しました。笑
f:id:minion024:20170716025028p:plain:w300

BOTの名前が思いつかなったのでとえりあえず「さちこ」にしました。
よろしくね、さちこちゃん。
f:id:minion024:20170716030903p:plain:w300

つづいてGASを使用してサーバーをたてます。
GoogleDriveにアクセスしてGoogle App Scriptを新規作成します。
「その他」にGASがなければ「+アプリを追加」で選択可能になります。
f:id:minion024:20170716032157p:plain

Script内に下記を記載して適当なタイトルを付けて保存。

function doGet() {
  return ContentService.createTextOutput('さちこです')
}

メニューの「公開」から→「ウェブアプリケーションとして導入」を選択。
権限周りの設定を行い、更新すると公開用のURLが作成されるので、コピーしてどこかに保存しておきましょう。

f:id:minion024:20170717173759p:plain:w300

このURLにアクセスすると「さちこです」と健気に表示されるはず。
これでサーバーの用意は完了したので、先程のURLをLINE側で設定します。

LINE Developersにアクセスして、Basic information から Webhook URL の設定を変更できるので先程のURLを設定。

これで、Botが返信できるようになったはずです。

とりあえず、メッセージを送信するとオウム返しで "「◯◯」ですか?" と返信するコードを書いてみます。

var CHANNEL_ACCESS_TOKEN = 'LINE DevelopersからコピーしてきたChannel Access Token';

function doPost(e) {
  var reply_token= JSON.parse(e.postData.contents).events[0].replyToken;
  if (typeof reply_token === 'undefined') {
    return;
  }
  var user_message = JSON.parse(e.postData.contents).events[0].message.text;
  var url = 'https://api.line.me/v2/bot/message/reply';
  
  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': reply_token,
      'messages': [{
        'type': 'text',
        'text': '「' + user_message + '」ですか?',
      }],
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);*/
}

コードを書いたら、再度メニュー「公開」からデータの更新をかけます。
このとき、「プロジェクトバージョン」を「新規作成」にしないと、編集が反映されないので注意です。

ここまで、やればBotがオウム返しで返信してくれるようになっているはず。
友達追加で作成したBotを追加して、メッセージを送信します。

オウム返しでかえってくるはず。

f:id:minion024:20170717174231j:plain:w300

もし、返ってこないときはGASの公開範囲周りをいじってみてください。全員アクセスできる権限になっていないとレスポンスが返ってこないです。

とりあえずBotの土台が出来上がったので、後は好きなように改造するだけです。
初めてBotを作りましたが、意外と簡単にできました〜。

暇なときに改造して、賢いBotにしていこうと思います。笑

f:id:minion024:20170717175412j:plain:w300

ShellScriptでMySQLの実行結果を変数にぶち込む

概要

ShellScriptをかきかきしていたときに、MySQLの実行結果(今回は数字)をShellの変数に代入して使用したいタイミングがあり、どうやって実現したのか備忘録。

▼こうゆうことをやりたい。

# MySQLを実行して
mysql -uroot -e "SELECT SUM(hoge) FROM fuga.table;"
# この変数に実行結果を入れたい!!
RESULT = MySQL実行結果

解決方法

# MySQLを実行
MYSQLRES=`mysql -uroot -e "SELECT CONCAT('RESULT=', SUM(hoge)) AS 'id0=0' FROM fuga.table;"`
# evalで変数に代入
eval `echo $MYSQLRES`

1)まずはMySQLの実行結果を変数 MYSQLRES に代入

MYSQLRES=`mysql -uroot -e "SELECT CONCAT('RESULT=', SUM(hoge)) AS 'id0=0' FROM fuga.table;"`

このときCONCATで"RESULT=代入したい数字"にするのが味噌。
このクエリの実行結果はこんな感じになっているはず。(数字は適当)

id0=0
RESULT=10

2)クエリの実行結果をevalで実行する

eval `echo $MYSQLRES`

先程のクエリの実行結果を代入した変数 MYSQLRESecho して eval でまとめて実行すると変数 id0RESULT にそれぞれ数字が代入されます。

id0 はぶっちゃけなんでもいいです、エイリアスで実行不能な文字列をいれると eval したときにエラーになるので、実行できればなんでもいいです笑

これでスクリプト内で変数として自由に利用できます。

> echo $RESULT
> 10

こんな方法もある

RESULT=`mysql -uroot -e "SELECT SUM(hoge) FROM fuga.table;" -B | tail -n 1`

普通にこっちの方がスマート。
ちなみにこれは会社の仲間が考えたやつ。

負けた感がすごい。

かっこいいシートを作ろう!Googleスプレッドシートの便利技 - 初級編 -

普段、仕事で死ぬほどスプレッドシートを使用している024minonです。
今回は、初心者でもすぐに実践できそうなかっけぇぇぇシートを作成するための機能・関数をいくつかご紹介させていただこうと思います。

その1:プルダウンリストを埋め込んでシートかっけぇぇ感じにする

プルダウンリストってこれのことです↓
f:id:minion024:20170520195253p:plain:w300

さっそくプルダウンを作ってみる

まず下画像のように、事前にシート内にリスト内に格納する要素を用意します(画像内だとD列がそれにあたる)。
f:id:minion024:20170520195641p:plain

プルダウンリストを作成したいセル(画像内A2)で右クリック→データの入力規則を押す。モーダルが表示されるので、「条件」の項目を「リストを範囲で指定」に設定し赤枠の部分をクリック。
f:id:minion024:20170520200234p:plain:w450

「データ」と書かれたモーダルが表示されるのでリスト内に格納するセルを指定します(今回はD2:D7と指定しました)。
あとは「OK」ボタンを押して「保存」を選択すればプルダウンリストの完成です。

ちなみにモーダル内の「条件」を「リストを直接指定」に設定し、セルを参照しない形(直接要素を記載)でプルダウンリストを作成することも可能です。
その場合、項目を「,」区切りで記入すればOKです。
f:id:minion024:20170520200841p:plain

その2:タブに色をつけて個性を主張する

シート内のタブに色をつけてcolorfulな感じにしよう(●´ϖ`●)
f:id:minion024:20170520201653p:plain

さっそく色をつけてみる

色を変更したいタブの上で右クリック→「色を変更」→お好みの色を選択
f:id:minion024:20170520201942p:plain

ちなみに僕はメインで見るタブがすぐわかるように色をつけたりします。

その3:見られたくないものに蓋をする

例えばこのようなシートがあるとして(下画像)、計算に使用しているセルをかっこ悪いから表示させたくない!あるいは、見る人を混乱させてしまう可能性があるのでシート上に表示する情報を絞りたい!ってときありますよね?笑
f:id:minion024:20170520203248p:plain

そんなときは隠蔽しよう

非表示にしたい列を選択→選択した列の一番上(アルファベットが書いてある部分)で右クリック→「列○ - ☓を非表示」→消える!
再表示したい場合は上に「◀|▶」こんな感じのやつがあると思うのでクリックすればもとに戻ります。
f:id:minion024:20170520203727p:plain:w400

シート自体も隠蔽しちゃおう

編集中なので見られたくない!ってときはシートごと非表示にしてしまいましょう。
非表示にしたいタブの上で右クリック→「シートを非表示」を選択→消える!
f:id:minion024:20170520204248p:plain

もとに戻したいときは、メニューの「表示」→「非表示のシート」→表示したいシートを選択→表示される!
f:id:minion024:20170520204551p:plain

必要以上に非表示にすると可読性が落ちるのでほどほどに(´・ω・`)

その4:他のシートからデータを参照してスマートに同期させる

同じ(URLの)シート内からセルを参照できるのはご存知だと思いますが、スプレッドシートはとてもスマートなのでIMPORTRANGE関数を使用して(別URLの)他シートのセルを参照することもできます。

今回は、先程作成したプルダウンリストのセルを別シートで読み込んでみます。
f:id:minion024:20170520205829p:plain:w250

常に最新データのコピペ作業とはもうおさらばだ

早速データを挿入したいセルに関数を入力してみましょう。
IMPORTRANGE関数はこんな感じで使用します↓

=IMPORTRANGE("対象シートの固有識別ID","タブ名!範囲")

固有識別IDはURL内に記載されています(下URLの”ここの部分”ってところ)。

https://docs.google.com/spreadsheets/d/"ここの部分"/edit#gid=数字がごちゃごちゃ

今回はプルダウンリストのA1:A2の部分を持ってきたいのでこんな感じ↓
(ちなみに対象のタブ名は「O」です)

=IMPORTRANGE("対象シートの固有識別ID","O!A1:A2")

エラーが表示されるので、セルをクリックして「アクセスを許可」ボタンを押しシートの参照を許可してください。
それでもエラーが出る場合、参照先のシートが他からアクセスできないように設定されている可能性があるので公開範囲を調整してみてください。

無事表示されました(´・ω・`)
f:id:minion024:20170520211100p:plain

ちなみに参照元のプルダウンを鈴木に変更すると、今回作成したシート側も鈴木に変わります。素晴らしい。
※ただし参照元のデータが変更されると、シートのデータも変わってしまうので注意も必要。コピペとは別のものと思ったほうがよい。

その5:条件に一致するセルに色をつけて見て見てアピール

注目させたいセルに色をつけることってあると思うのですが、条件に一致するセルに自動で色付けすることも可能です。
今回は下記画像のように"日付"が今日なのに"ステータス"が未着手の項目に色をつけてみたいと思います。
f:id:minion024:20170520213151p:plain

さっそくおせっかいオートメーションを作成しよう

対象のセルで右クリック→「条件付き書式
f:id:minion024:20170520213354p:plain:w200

セルの書式設定の条件」を「カスタム関数」に変更するとカスタム関数を記入できるようになります。
f:id:minion024:20170520213730p:plain:w200

さっそく中に条件を記載していきしょう。

今回は列Bの「締め切り」が"今日"で列Cの「ステータス」が"未着手"という条件なので下記のように記載します。

=AND(B2:B6=TODAY(), C2:C6="未着手")

まず、対象の列が今日かどうか確認をしています。
> セルの範囲=TODAY()

その次に対象の列が指定した文字と完全一致するか確認しています。
> セルの範囲="文字"

そして条件を複数指定しているのでANDで接続します
> AND(条件1, 条件2)

条件を入力したら保存してください。
これで条件に一致するセルに色が自動でついたはずです。

これを利用すれば特定の曜日に色を付けたり、売上が○を下回った場合に赤い色を付けてアラートを出す等も可能です。

おわりに

今回は普段業務で使っているスプレッドシート芸を他の人にも発信していこうと思いこの記事を書きました。
まだまだ紹介していない便利技がいっぱいあるので中級者編&上級者編も今後書いていこうと思っています。

Python/Shellでtxtファイルの行数をカウントする

背景

言語処理100本ノック 2015を今やっているのでその備忘録的なやつ。

やりたいこと

Pythonで以下URLのhightemp.txtを読み込み行数をカウントする。
同様にUnixコマンドでも行数の確認を行い、その際wcコマンドを使用する。

http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt

結果

Pythonで行数カウント

> import pandas as pd
> data = pd.read_table('http://www.cl.ecei.tohoku.ac.jp/nlp100/data/hightemp.txt', header=None)

> len(data)
> 24

Unixコマンドで行数カウント

> wc -l /Path/To/hightemp.txt
> 24

解説/考察

今回はPythonでtxtファイルを読み込むためにpandasを使用しました。
CSVファイルを読み込む場合はread_csvを、txtの場合はread_tableを使用します。

import pandas as pd

# CSVを読み込み
pd.read_csv('/Path/To/Csv.csv')

# Textを読み込み
pd.read_table('/Path/To/Text.txt')

オプションで区切り文字の指定が可能ですが、デフォルトでread_csv=","/read_table="Tab"になっているので指定しなくても問題ないです。

ちなみにheaderオプションを指定しないと勝手に先頭行をヘッダーとしてデータフレームが作成されます。
今回はhightemp.txt内にヘッダーにあたる行が存在しないので、headerオプションを使用して先頭行がヘッダーにならないようにしています。

> data = pd.read_table('/Path/To/Text.txt', header=None)

こんな感じにデータが読み込まれました。
ヘッダーは数字になってますね。

f:id:minion024:20170520192455p:plain

ちなみにjupyter notebookを使用しています。

あとはlen()を使用して行数をカウントできます。

> len(data)
> 24

.indexを使用して行数を確認することも可能です。

> data.index
> RangeIndex(start=0, stop=24, step=1)

今回はUnixコマンドのwcを使用して行数を確認するよう指示されているのでUnixコマンドでも同様のことをやりました。
wc -l /Path/To/Text.txtで行数確認ができるので一瞬で終わりましたw

> wc -l /Path/To/hightemp.txt
> 24

Pythonで文章をTypoglycemia化する

背景

言語処理100本ノック 2015を今やっているのでその備忘録的なやつ。

やりたいこと

スペースで区切られた単語列に対して、各単語の先頭と末尾の文字は残しそれ以外の文字の順序をランダムに並び替えるプログラムを作成する。
ただし、長さが4以下の単語は並び替えないこと。

適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え、その実行結果を確認する。

結果

単語の文字列をランダムに並び替える関数と、それを使用して文章をTypoglycemia化する関数の2つを用意しました。
なにかしらの関数で同じことができるかなと思ったのですが、すぐに見つからなかった&面白そうなので一から関数を用意して挑戦してみました。

▼ 単語の文字列をランダムに並び替える関数

import random
import re

def radomizeWord(word):
    # 変数を用意
    charList = []
    newList = []
    result = ''

    # 単語を分割してリスト化
    for i in range(0, len(word)):
        charList.insert(i, word[i])

    cnt = len(charList)-1
    
    # 末尾の文字を新リストの末端に挿入し旧リストから削除
    newList.insert(cnt, charList[cnt])
    charList.pop()
    # 先頭の文字を新リストの先頭に挿入し旧リストから削除
    newList.insert(0, charList[0])
    charList.pop(0)

    # 先頭と末尾の単語以外をランダムに抽出して新リストのii番目に挿入
    for ii in range (0, len(charList)):
        num = random.randrange(0, len(charList))
        newList.insert(ii+1, charList[num])
        # 挿入した文字は旧リストから削除
        charList.pop(num)

    for iii in range(0, len(newList)):
        result += newList[iii]

    return result

▼ 上の関数を使用して文章をTypoglycemia化する関数

def generateTypoglycemia(sentense):
    # 文章を単語に分割
    wordsList = re.split('\s', sentense)
    # 変数を用意
    resultList = []
    result = ''
    
    for i in range (0, len(wordsList)) :
        if len(wordsList[i]) <= 4:
            resultList.insert(i, wordsList[i])
        elif len(wordsList[i]) > 4:
            resultList.insert(i, radomizeWord(wordsList[i]))
            
    for ii in range (0, len(resultList)) :
        if ii == 0 :
            result += resultList[ii]
        elif ii != 0 :
            result += (' ' + resultList[ii])
    
    return result


▼ 実行結果

> generateTypoglycemia("I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind.")
> "I c'dnlout blveeie that I cuold alalutcy unrndetsad what I was rainedg : the pheonnamel pwoer of the huamn mdin."

解説/考察

単語の頭と末尾をそのままに間の文字を入れ替えても読めるぞおおお!というのがタイポグリセミアらしいです。
Typoglycemiaとは (タイポグリセミアとは) [単語記事] - ニコニコ大百科

ちょっと長いだけで特別難しいことはやっていないです。

単語を頭とケツの二文字以外ランダムに入れ替える方法ですが、今回は文字をばらして最初の文字をリストの頭に最後の文字をリストのケツに入れてから間の文字をランダムに抽出してリストに順番に入れていく方法で対応しました。

▼単語を文字単位で分割して順番にリストに格納

for i in range(0, 文字列の文字数):
 文字リスト.insert(i, 文字列[i])

▼文字列の頭とケツの文字を新しいリストの頭とケツにINSERTして削除。

「文字列の文字数-1」をやっているのはリストが0から始まるので、len()関数で取得した数字から1引いてあげる必要があるため。

新文字リスト.insert(文字列の文字数-1, 文字リスト[文字列の文字数-1])
文字リスト.pop()
新文字リスト.insert(0, 文字リスト[0])
文字リスト.pop(0)

文字リスト内の残りの文字を、文字リストに収められている文字数分for文を回してランダムに抽出して新文字リストに挿入しています。
挿入が終わったら最後にリストから除外。

for ii in range (0, len(文字リスト)):
 num = random.randrange(0, len(文字リスト))
 新文字リスト.insert(ii+1,文字リスト[num])
 文字リスト.pop(num)

あとは文章を単語に分割して、単語の文字数が4以上の場合この関数を使用して文字を入れ替える、というのをやっています。

心残りは、文章の最後にコンマが来た場合文字として認識してしまう件ですw
たとえば今回の例だと最後の単語が「mind.」であるため四文字以上と判定されランダムに文字を入れ替えてしまうので時間があるときに直したいです。

Pythonで文字列を文字コードに置換し暗号文化する

背景

言語処理100本ノック 2015を今やっているのでその備忘録的なやつ。

やりたいこと

与えられた文字列の各文字を以下の仕様で変換する関数cipherを実装する。

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力

この関数を用い、英語のメッセージを暗号化・復号化する。

結果

▼ 関数cipher

def cipher (sentense):
    # 変数を用意
    sentenseList = []
    result = ''

    for i in range (0, len(sentense)):
        # i番目の文字が小文字の場合
        if sentense[i].islower():
            # リストのi番目にアスキーコード(219-アスキーコード)に対応する文字を挿入
            sentenseList.insert(i, chr(219-ord(sentense[i])))
        # i番目の文字が小文字ではない場合そのままリストに挿入
        else:
            sentenseList.insert(i, sentense[i])
                
    # リストを文字列に戻す
    for ii in range (0, len(sentense)):
        result += sentenseList[ii]
        
    #文字列を返す
    return result

▼ 実行結果

> sentense = "I Am An Engineer."
> txt = cipher(sentense)
> txt
> 'I An Am Emtrmvvi.'
> cipher(txt)
> 'I Am An Engineer.'

解説/考察

リストを作成して文字数(i)回for文を回してリストに文字列のi番目の文字をリストのi番目に挿入しています。

for i in range (0, len(文字列)):
 リスト.insert(i, 文字列[i])

挿入の際にi番目の文字が小文字かどうかをif文で条件分岐させています。

if 文字列[i].islower():
 # i番目が小文字の場合の処理
else:
 # i番目が小文字ではない場合の処理

小文字の場合は、文字をアスキーコードに変換し ”219 - アスキーコード” の計算をおこない得た数字を再度文字に戻してリストに挿入します。
暗号化した文字をもとに戻す場合も同様の処理をおこなえば問題なしです。

リスト.insert(i, chr(219-ord(文字列[i])))

文字を暗号化する一連の流れを確認するとこんな感じ。

> ord('a')
> 97
> 219-97
> 122
> chr(122)
> 'z'
> ord('z')
> 122
> 219-122
> 97
> chr(97)
> 'a'

最後に変換したリスト内の各文字を1つの文字列に戻す作業です。
最初に分解したときと逆のことをやっているだけ。

for ii in range (0, len(文字列)):
       新文字列 += リスト[ii]

ちなみに最初に作成したときは、暗号化と復号化の関数を別々に用意して、関数内で以下のように計算をしていました。

# 暗号化
リスト.insert(i, chr(219-ord(文字列[i])))
# 復号化
リスト.insert(i, chr(219+ord(文字列[i])))

結果、わけわからない文字列が返ってきて失敗しました。
安直な考えで引いたのだから足せばいいやろ!と思ったのですがアホですね。

原因は上でやったように一連の流れを追うとわかります。

> ord('a')
> 97
> 219-97
> 122
> chr(122)
> 'z'
> ord('z')
> 122
> 219+122
> 341
> chr(341)
> 'ŕ'

まあ、わざわざやるまでもないですが。笑