読者です 読者をやめる 読者になる 読者になる

PostgreSQLで指定テーブルのみdumpする

よく忘れてしまうpg_dumpで指定テーブルのみdumpする方法のメモです。

この際は PostgreSQL 9.3.9 を利用しました。

指定テーブルのみdump

pg_dump --username=username --table table_name db_name > table_name.sql

指定テーブルのデータのみdump

pg_dump --username=username --data-only --table table_name db_name > table_name.sql

指定テーブルのスキーマのみdump

pg_dump --username=username --schema-only --table table_name db_name > table_name.sql

dumpしたデータを取り込む(SQL実行)

psql --username=username db_name < table_name.sql

PostgreSQLにおけるUNIQUE制約とUNIQUE INDEXの違い?

正直なところ勘違いをしていました。UNIQUEの制限をするためにUNIQUE INDEXをしばしば作成していましたので、この違いを調べました。

PostgreSQL 9.3.9においては、

  • UNIQUE 制約を作ると UNIQUE INDEX も作られるが、
  • UNIQUE INDEX を作っても UNIQUE 制約 は作られないようです。

そのためinformation_schema.table_constraintsテーブルに差が現われます。ただし、エラーメッセージは同じようです。

UNIQUE 制約( CREATE TABLE 時)

  • 作成
CREATE TABLE example1 (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    date DATE DEFAULT CURRENT_DATE NOT NULL,
    UNIQUE(user_id, date)
);

制約確認

SELECT constraint_name, table_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'example1'
  • 結果
constraint_name table_name constraint_type
example1_pkey example1 PRIMARY KEY
example1_user_id_date_key example1 UNIQUE

※NOT NULL制約は省略

INDEX確認

SELECT tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'example1';
  • 結果
tablename indexname indexdef
example1 example1_pkey CREATE UNIQUE INDEX example1_pkey ON example1 USING btree (id)
example1 example1_user_id_date_key CREATE UNIQUE INDEX example1_user_id_date_key ON example1 USING btree (user_id, date)

エラー確認

INSERT INTO example1(user_id, date)
VALUES(1, CURRENT_DATE), (1, CURRENT_DATE);
  • エラーメッセージ
ERROR:  重複キーが一意性制約"example1_user_id_date_key"に違反しています

UNIQUE 制約( CREATE TABLE 後 ALTER TABLE で追加)

  • 作成
CREATE TABLE example2 (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    date DATE DEFAULT CURRENT_DATE NOT NULL
);

ALTER TABLE example2 ADD UNIQUE(user_id, date);

制約確認

SELECT constraint_name, table_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'example2'
  • 結果
constraint_name table_name constraint_type
example2_pkey example2 PRIMARY KEY
example2_user_id_date_key example2 UNIQUE

※NOT NULL制約は省略

INDEX確認

SELECT tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'example2';
  • 結果
tablename indexname indexdef
example2 example2_pkey CREATE UNIQUE INDEX example2_pkey ON example2 USING btree (id)
example2 example2_user_id_date_key CREATE UNIQUE INDEX example2_user_id_date_key ON example2 USING btree (user_id, date)

エラー確認

INSERT INTO example2(user_id, date)
VALUES(1, CURRENT_DATE), (1, CURRENT_DATE);
  • エラーメッセージ
ERROR:  重複キーが一意性制約"example2_user_id_date_key"に違反しています

UNIQUE INDEX

  • 作成
CREATE TABLE example3 (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    date DATE DEFAULT CURRENT_DATE NOT NULL
);

CREATE UNIQUE INDEX example3_user_id_date_key
ON example3(user_id, date);

制約確認

SELECT constraint_name, table_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'public'
AND table_name = 'example3'
  • 結果
constraint_name table_name constraint_type
example3_pkey example3 PRIMARY KEY

※NOT NULL制約は省略

INDEX確認

SELECT tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'example3';
  • 結果
tablename indexname indexdef
example3 example3_pkey CREATE UNIQUE INDEX example3_pkey ON example3 USING btree (id)
example3 example3_user_id_date_key CREATE UNIQUE INDEX example3_user_id_date_key ON example3 USING btree (user_id, date)

エラー確認

INSERT INTO example3(user_id, date)
VALUES(1, CURRENT_DATE), (1, CURRENT_DATE);
  • エラーメッセージ
ERROR:  重複キーが一意性制約"example3_user_id_date_key"に違反しています

Threejs r76で球体&パーティクル

Threejsのr76を用いて球体とパーティクルに挑戦した際のメモです。

こちらのサイトを参考にしました。

手順としてあっているかどうかわかりませんが、前面部分だけパーティクルを描画して欲しかったのでSphereGeometryMeshBasicMaterialにて単色で塗りつぶした上でパーティクルの描画を行っています。

  • 単色(#666699)で塗りつぶした球体をsceneに追加
    var sphere = new THREE.Mesh(
        new THREE.SphereGeometry(200, 100, 100),
        new THREE.MeshBasicMaterial({
            color: 0x666699
        })
    );
    this.scene.add(sphere);
  • その後Pointsを用意してsceneに追加
    this.particles = new THREE.Points(
        sphere.geometry,
        new THREE.PointsMaterial({
            size: 3,
            color: 0xFFFFFF
        })
    );
    this.scene.add(this.particles);

これらのサイトを参考にしました。

画面奥に向かって背面にあたる部分のパーティクルは見えなくなり透けている感はなくなりました。

デモはこちらです。

Python3でPyOCRを用いてPacerのシェア画像から歩数抽出

Pacer(iOS, Android)という歩数管理のアプリではTwitterFacebookに歩数などの記録を画像としてシェアできます。今回はその画像から文字部分を抽出して文字として認識可能なように挑戦したもので、環境は Windows 7, Python 3.5.1 です。

Pacerでシェアした抽出対象の画像

  • 当初種類1に分類している画像を利用していましたが、日付が入っていないので種類2に分類している画像を利用するようにしました。

iPhone SEでキャプチャした画像

種類1 種類2
f:id:nfnoface:20161101140102j:plain:w2501.jpg f:id:nfnoface:20161101140111j:plain:w2502.jpg
f:id:nfnoface:20161102185632j:plain:w2503.jpg f:id:nfnoface:20161102185647j:plain:w2504.jpg

Windowsなので付属のペイントでルーラーグリッド線を有効にして座標を確認

  • 赤枠の部分が抽出対象です。

f:id:nfnoface:20161101141009j:plain f:id:nfnoface:20161101141019j:plain

必要なアプリケーション及びPythonモジュールを用意

Tesseract-OCR

PyOCR, PIL, numpy

  • Pythonから上記OCRを利用するモジュールpyocrを利用しています。
  • 画像操作でPIL(Pillow)が必要になります。
  • 画像を2値化する際numpyを利用しています。
  • インストール時点のバージョンはそれぞれ pyocr (0.4.2), Pillow (3.4.2), numpy (1.11.2) です。

Windows

python -m pip install pyocr
python -m pip install Pillow
python -m pip install numpy

LinuxmacOS

pip install pyocr
pip install Pillow
pip install numpy

コード

  • 当初全体から抽出を試みたものの、どこからどこまでが何の情報かわからないので対象部分を切り抜いてから文字抽出を試みるのが良さそうです。
  • カラー画像だと抽出精度が低くなるようなので2値化しています。
# -*- encoding: utf-8 -*-
from PIL import Image, ImageDraw
import numpy, os, pyocr, pyocr.builders, re, sys

tools = pyocr.get_available_tools()
if len(tools) == 0:
    sys.exit('Not Found OCR tools')

MIN_COLOR = 0
MAX_COLOR = 255

def retrieveStr(image_path):
    result = {}

    # 画像読み込み
    img = Image.open(image_path)

    # 文字を認識しやすいよう閾値200で白か黒の2値に変換
    array = numpy.asarray(img.convert('L'))
    array.flags.writeable = True
    w, h = img.size
    for y in range(h):
        for x in range(w):
            array[y, x] = MAX_COLOR if (array[y, x] > 200) else MIN_COLOR
    img = Image.fromarray(numpy.uint8(array))

    positions = {}

    # 1ピクセル目のRGBで画像種類を判断
    r, g, b = img.crop((0, 0, 1, 1)).convert('RGB').getpixel((0, 0))
    if r == MAX_COLOR:
        # 活動時間
        positions['active time'] = (190, 70, 440, 140)
        # カロリー
        positions['calories'] = (10, 70, 170, 140)
        # Km
        positions['km'] = (450, 70, 630, 140)
        # 歩数
        positions['steps'] = (170, 280, 470, 390)
    else:
        # 活動時間
        positions['active time'] = (220, 410, 420, 460)
        # カロリー
        positions['calories'] = (80, 410, 210, 460)
        # 日付
        positions['date'] = (190, 130, 450, 165)
        # Km
        positions['km'] = (430, 410, 560, 460)
        # 歩数
        positions['steps'] = (220, 220, 440, 300)
        # 日本語辞書を使わないので`年月日`を塗りつぶす
        dr = ImageDraw.Draw(img)
        # [(始点x,始点y),(終点x,終点y)],fill=塗つぶし色
        dr.rectangle([(275, 130), (305, 160)], fill=MAX_COLOR) # 年
        dr.rectangle([(340, 130), (365, 160)], fill=MAX_COLOR) # 月
        dr.rectangle([(405, 130), (430, 160)], fill=MAX_COLOR) # 日

    for label, pos in positions.items():
        # 欲しい部分だけ画像を切り抜く
        im = img.crop(pos)

        # 拡大縮小
        # im.thumbnail((im.size[0] / 3, im.size[1] / 3), Image.ANTIALIAS)
        # im = im.resize((im.size[0] * 3, im.size[1] * 3))

        # 画像表示
        # im.show()

        # 画像から文字抽出
        text = tools[0].image_to_string(
            im,
            builder=pyocr.builders.TextBuilder(tesseract_layout=6)
        )
        # バイト列をUTF-8でエンコードしてUTF-8の文字列にデコード
        text = text.encode('utf_8').decode('utf_8')

        # 時間を取り出して秒に変える
        if label == 'active time':
            at = re.split('[^\d]+', text)[:-1]
            hours = 0  if len(at) == 1 else int(at[0])
            minutes = int(at[len(at) - 1])
            text = (hours * 60 * 60) + (minutes * 60)
        # str -> int
        elif label == 'calories':
            text = int(text)
        # 日付整理
        elif label == 'date':
            text = '-'.join(re.split('[^\d]+', text))
        # str -> float
        elif label == 'km':
            text = float(text)
        # 数字以外の文字を詰める
        elif label == 'steps':
            text = int(''.join(re.split('[^\d]+', text)))

        result[label] = text

    return result

if __name__ == '__main__':
    base_dir = os.path.dirname(__file__)
    if not base_dir:
        base_dir = '.'

    img_dir = base_dir + os.path.sep + 'img' + os.path.sep

    for i in range(4):
        res = retrieveStr(img_dir + str(i + 1) + '.jpg')
        for val in res.items():
            print('{}: {}'.format(*val))
        print()

結果

c:\>python C:\py\ocr\img.py
steps: 17589
active time: 9300
km: 13.9
calories: 640

date: 2016-10-31
steps: 18709
active time: 10140
km: 15.0
calories: 685

steps: 42177
active time: 22080
km: 33.4
calories: 1449

date: 2016-11-01
steps: 11488
active time: 6720
km: 9.0
calories: 390


c:\>

参考

Python3でPycURLを用いてteratail APIのJSONからPythonタグつきの質問を取得

teratailというプログラミングQ&AサイトのAPIからJSON形式で質問取得を試みた際のメモです。Pythonで試みたのでPythonに関連する質問の取得を試みています。環境は Windows 7, Python 3.5.1 です。

必要なPythonモジュールを用意

PycURL

  • PythoncURLライブラリを使うモジュールを利用しています。
    今回はHTTP(S)リクエストする際に用います。
    PHPを使ったことがあれば、PHPのcurl関数のような感じで利用できるイメージです。
  • この際にインストールしたバージョンは pycurl (7.43.0) です。

Windows

python -m pip install pycurl

LinuxmacOS

pip install pycurl

コード

  • BEARER = ''でteratailに登録後取得できるアクセストークンを設定します。
  • 無くても動作しますがteratailの仕様で1時間あたり30回までのアクセスになるそうです。アクセストークンがあれば1時間あたり300回までアクセス可能になるそうです。
# -*- encoding: utf-8 -*-
import io, json, pycurl, sys, urllib.parse

if __name__ != '__main__':
    sys.exit()

# teratail登録後、 https://teratail.com/users/setting/tokens で取得
BEARER = ''

class Teratail:
    API_URL = 'https://teratail.com/api/v1/tags/{}/questions?limit={}&page={}'

    __curl = None
    __tag = ''
    __limit = 20
    __page = 1
    __bearer = None
    __httpCode = 200

    def __init__(self, tag='', limit=20, page=1):
        self.__tag = tag
        self.__limit = limit
        self.__page = page

        # インスタンス生成
        self.__curl = pycurl.Curl()

    def __execute(self):
        json = None
        buf = io.BytesIO()

        headers = {}
        if self.__bearer:
            headers['Authorization'] = 'Bearer {}'.format(self.__bearer)

        self.__curl.reset()
        try:
            # Headerをセット
            headers = ['{}: {}'.format(*v) for v in headers.items()]
            self.__curl.setopt(pycurl.HTTPHEADER, headers)
            # リクエスト先をセット
            self.__curl.setopt(pycurl.URL, self.API_URL.format(self.__tag, self.__limit, self.__page))
            # MethodをGETにセット(デフォルト)
            #self.__curl.setopt(pycurl.CUSTOMREQUEST, 'GET')
            # SSL証明書の有効性を確認しない
            self.__curl.setopt(pycurl.SSL_VERIFYPEER, False)
            # pycurlの結果を変数に格納
            self.__curl.setopt(pycurl.WRITEDATA, buf)
            # 出力
            self.__curl.perform()
            # 取得したjsonデータをUTF-8にデコード
            json = buf.getvalue().decode('UTF-8')
        except (pycurl.error, TypeError):
            pass

        self.__httpCode = self.__curl.getinfo(pycurl.HTTP_CODE)

        buf.close()
        return json

    def setBearer(self, bearer):
        self.__bearer = bearer

    def getHttpCode(self):
        return self.__httpCode

    # http://docs.teratailv1.apiary.io/#introduction
    def getJson(self, tag=None, limit=None, page=None):
        if tag is not None:
            self.__tag = tag
        if limit is not None:
            self.__limit = limit
        if page is not None:
            self.__page = page
        return self.__execute()

tt = Teratail(limit=5)
tt.setBearer(BEARER)

# http://docs.teratailv1.apiary.io/#reference/(tag)/2/0
for tag in ('Python', 'Python 2.7', 'Python 3.x'):
    print(tag)

    jsonData = tt.getJson(tag=urllib.parse.quote_plus(tag))
    if jsonData is None:
        continue

    data = json.loads(jsonData)
    if 'questions' not in data:
        if 'meta' in data and 'message' in data['meta']:
            print('HTTP Status Code: {}'.format(tt.getHttpCode()))
            print('Message: {message}'.format(**data['meta']))
        continue

    print('Data:')
    for row in data['questions']:
        if 'id' not in row:
            continue

        # 質問ページ
        print('https://teratail.com/questions/{id}'.format(**row))
        # 質問のタイトル
        print(row['title'] if 'title' in row else None)
        # 質問を投稿したユーザの名前
        if 'user' in row and 'display_name' in row['user']:
            print(row['user']['display_name'])

        print()

    print()

jQuery: .each()に渡すfunctionの第1引数には連番が渡される

次のようにわざわざ番号を数えるような処理にしてしまうことがありますが…。

サンプルコード

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
$(function(){
    var i = 0;
    $('.element').each(function(){
        console.log(i++);
    });
});
</script>
</head>
<body>
<div>
    <span class="element"></span>
    <span class="element"></span>
    <span class="element"></span>
</div>
</body>
</html>

javascriptのみ抜粋

var i = 0;
$('.element').each(function(){
    console.log(i++);
});

結果

0
1
2

.each()に渡すcallback functionの第1引数には0から順にカウントアップするindexが渡されているので次のような処理で十分です。

サンプルコード

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
$(function(){
    $('.element').each(function(i){
        console.log(i);
    });
});
</script>
</head>
<body>
<div>
    <span class="element"></span>
    <span class="element"></span>
    <span class="element"></span>
</div>
</body>
</html>

javascriptのみ抜粋

$('.element').each(function(i){
    console.log(i);
});

結果

0
1
2

Python 3.5.1でクラスを試す

クラスのサンプル

  • Pythonのクラスは、PHPと異なりメソッドの第1引数に毎回selfと書く必要があったりするようです。
  • メソッドやクラス変数のprotected, privateなどのアクセス権はPythonには無いようです。
  • 参考: Python チュートリアル クラス
# -*- encoding: utf-8 -*-
import inspect, sys

class Example:
    test = None

    # コンストラクタ
    def __init__(self):
        self.test = 'hoge'
        print(sys._getframe().f_code.co_name)
        print()

    # デストラクタ
    def __del__(self):
        print(sys._getframe().f_code.co_name)
        print()

    # with文の始め
    def __enter__(self):
        print(sys._getframe().f_code.co_name)
        print()
        # with ~ as 変数にインスタンスを格納する際に必要
        return self

    # with文の終わり
    def __exit__(self, exc_type, exc_value, traceback):
        print(exc_type, exc_value, traceback)
        print(sys._getframe().f_code.co_name)
        print()

    # ジェネレータ
    def __iter__(self):
        print(sys._getframe().f_code.co_name)
        for i in range(10):
            yield i

    # クラスのインスタンスオブジェクトのメソッド
    # 第1引数に`self`
    def instance_method(self, *params):
        print(self.test)
        print(params)
        print(sys._getframe().f_code.co_name)
        print()

    # クラスメソッド
    @classmethod
    def class_methos(*params):
        print(params)
        print(sys._getframe().f_code.co_name)
        print()

    # スタティックメソッド
    @staticmethod
    def static_method(*params):
        print(params)
        print(sys._getframe().f_code.co_name)
        print()

インスタンス生成で __init__ が呼ばれる

    # コンストラクタ
    def __init__(self):
        self.test = 'hoge'
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
e = Example() # __init__ が出力される

with文で __enter__, __exit__ が呼ばれる

    # with文の始め
    def __enter__(self):
        print(sys._getframe().f_code.co_name)
        print()
        # with ~ as 変数にインスタンスを格納する際に必要
        return self

    # with文の終わり
    def __exit__(self, exc_type, exc_value, traceback):
        print(exc_type, exc_value, traceback)
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
with e as instance: # __enter__ が出力される
    print(instance) # __enter__ で self を return していないと None
# with文が終了すると __exit__ が出力される
  • これは、ファイルを開くときなどの形ですね。
with open('text.txt') as f:
    for line in f:
        pass

for文で __iter__ が呼ばれる

    # ジェネレータ
    def __iter__(self):
        print(sys._getframe().f_code.co_name)
        for i in range(10):
            yield i
  • 実行した場合
print(', '.join([str(i) for i in e]))
print()

# __iter__ が出力され、
# __iter__ からの結果をリスト化し', 'でjoinして 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 を出力
  • 参考:

クラスのインスタンスオブジェクトのメソッド(インスタンス変数へアクセス可)

直接も呼べますが、インスタンス変数へはアクセス出来ません

    # クラスのインスタンスオブジェクトのメソッド
    # 第1引数に`self`
    def instance_method(self, *params):
        print(self.test)
        print(params)
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
e.instance_method(inspect.getouterframes(inspect.currentframe())[0].lineno)

# hoge # __init__ で代入した値を出力
# (74,) # 行番号
# instance_method

クラスメソッド(インスタンス変数へはアクセス出来ない)

インスタンスからも呼べますが、インスタンス変数へはアクセス出来ません

    # クラスメソッド
    @classmethod
    def class_methos(*params):
        print(params)
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
Example.class_methos(inspect.getouterframes(inspect.currentframe())[0].lineno)

# (<class '__main__.Example'>, 78) # 行番号は第2引数に渡されている
# class_methos
  • 参考:

スタティックメソッド(インスタンス変数へはアクセス出来ない)

インスタンスからも呼べますが、インスタンス変数へはアクセス出来ません

    # スタティックメソッド
    @staticmethod
    def static_method(*params):
        print(params)
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
Example.static_method(inspect.getouterframes(inspect.currentframe())[0].lineno)

# (82,) # 行番号
# static_method

スクリプト終了で __del__ が呼ばれる

    # デストラクタ
    def __del__(self):
        print(sys._getframe().f_code.co_name)
        print()
  • 実行した場合
# スクリプト終了直前に __del__ が出力される
  • ガーベージコレクト任せになってしまい任意のタイミングに処理されるかどうかわからないため、__enter__, __exit__によるwith文の方が扱いやすいようです。

メソッド名はどのように確認する?

PHPだと__FUNCTION__で確認できるメソッド名は、Pythonだとsys._getframe().f_code.co_nameで確認できるようです。

  • 参考:

行番号はどのように確認する?

PHPだと__LINE__で確認できる行番号は、Pythonだとinspect.getouterframes(inspect.currentframe())[0].linenoで確認できるようですが、バージョンによって少し異なるようです。今回はPython 3.5.1です。

  • 参考: