Pythonで日付、時刻の比較

Pythonで日付、時刻の比較を行う

急に性能が落ちてきた処理がある。しかし、それがいつからなのか、体感的なもので本当はたいして性能劣化していないのか。
よくわからなかったので、ログファイルを集計して処理時間を計測してみることにした。

簡単に作りたかったのでPythonを利用する。
文字列の日時をdatetimeに変換して、datetimeとdatetimeを引き算し、返ってきたtimedeltaから差を得ればよいようだ。

文字列からdatetimeへの変換

1
2
3
4
import datetime

start_date_time = datetime.datetime.strptime('2018-09-14:14:49:31', '%Y-%m-%d:%H:%M:%S')
end_date_time = datetime.datetime.strptime('2018-09-14:14:51:13', '%Y-%m-%d:%H:%M:%S')

日付のフォーマットを指定すれば、ログファイルの日付フォーマットはいろいろ対応できそうだ。

処理時間を算出する

1
2
3
dist_delta = end_date_time - start_date_time
print(dist_delta.seconds)
print(dist_delta.days)

secondsで差の秒数を取得できる。
何日も差があるようであれば、daysで差の日付を取得できる。

まとめ

数十万行のログファイルから特定のキーで開始時間と終了時間を取得しつつ、処理時間の平均を日ごとに集計したが、1分未満で処理が終わった。Python優秀だ。

同じタグの記事
同じカテゴリの記事

PythonでCSVファイルを読む

PythonでCSVファイルを読む

テキストデータを読むバッチを度々作る。
昔からの習慣でカラムの区切りはタブかバックタブを利用し、プログラムでsplitして読んでいた。特にPythonは簡単に使えるので、.strip()してから.split(‘\t’)という習慣があった。
しかし、CSVファイルを出力するプログラムが、先方の仕様で出力されてしまう場合、それに合わせて読み込む必要がある。
区切り文字だったり、各カラムの囲み文字があったり、自前のプログラム対応するのは面倒だ。そんな時csvモジュールを使う。

CSVファイルを読む

1
2
3
4
5
6
import csv

with open('sample.csv', 'r') as r:
    reader = csv.reader(r, delimiter=',', quotechar='"', skipinitialspace=True)
    for row in reader:
        print(row)

delimiterは区切り文字。デフォルトは’,’。
quotecharはカラムの囲み文字。デフォルトは'”‘。
skipinitialspace=Trueは、区切り文字の前後のスペースを無視してくれる。CSVファイルではあまりないだろうが、下記のようなデータの場合に、都合よく処理してくれる。

1
"ABC", "DEF", 30

skipinitialspace=Trueが指定されないと、2カラム目は’ “DEF”‘になってしまう。
扱うデータによってはスペースにも意味があるかもしれないので、理解して利用する。

まとめ

Python2の頃は、strとunicodeの違いで、日本語が含まれるファイルを読むのに苦労したが、Python3なら何も気にせず読み込むことができる。
これからはcsvモジュールを利用するようにしてみよう。

同じタグの記事
同じカテゴリの記事

OracleのFLASHBACKテーブルをPURGEする

OracleのFLASHBACKテーブルをPURGEする。

Oracle10gからFLASHBACKが有効になっている場合は、単純にDROP TABLEしても元のテーブルがゴミ箱(RECYCLEBIN)に残っている。
しかし、DROP TABLEなんてテスト環境でしか実行しないので、FLASHBACKはあまり必要ない。
なので、通常はゴミ箱を経由しないようにDROP TABLEしている。

FLASHBACKしないDROP TABLE

1
DROP TABLE TABLE_TEST_01 PURGE;

これで、RECYCLEBINに保存せずにDROPできる。

PURGEを忘れてしまった場合

たまにPURGEを付けるのを忘れてしまい、BIN$…なテーブルが残ってしまう。それを消したいとき。

1
PURGE TABLE TABLE_TEST_01;

とすることで削除することができる。
ゴミ箱内のテーブルがすべて不要であれば、ゴミ箱ごとPURGEできる。

1
PURGE RECYCLEBIN;

まとめ

もし、DROP TABLEが本当に誤りだった場合、FLASHBACK TABLE文でテーブルを元に戻せる。

1
FLASHBACK TABLE TABLE_TEST_01 TO BEFORE DROP;

これで助かることがあるかもしれない。やっぱりFLASHBACHは残しておくべきだろうか・・・。

同じタグの記事
同じカテゴリの記事

pythonでデータクラス

pythonでデータクラス

python3.7の新機能を読んでいた。
dataclassアノテーションが使えるようになったようだ。
これで、Kotlinの様に簡単にDtoを定義できるようになる。

dataclassの作り方

1
2
3
4
5
6
7
8
import dataclasses

@dataclasses.dataclass
class Dto37:
    id: int
    name: str

dto = Dto37(1, 'One')

簡単だ。かつては普通のクラスを作って、コンストラクタですべてのプロパティを受け取れるようにしたり、
__str__や__repr__を実装したりしていたのに。

しかし、NamedTupleというのを使えば、python3.6でも似たようなことができるようだ。

typing.NamedTuple

1
2
3
4
5
6
7
from typing import NamedTuple

class Dto36(NamedTuple):
    id: int
    name: str

dto = Dto36(2, 'Two')

なんてことだ。簡単だ。

さらに調べると、collections.namedtupleというのを使えば、python3.1でも似たようなことができていた。

collections.namedtuple

1
2
3
4
5
6
7
from collections import namedtuple

Dto31 = namedtuple('Dto31', ['id', 'name'])
dto = Dto31(3, 'Three')

Dto31_2 = namedtuple('Dto31_2', 'id,name')
dto = Dto31_2(4, 'Four')

これは少し癖がある。データタイプも指定できないし。
でも、パラメータを配列で渡しても良いし、カンマ区切りの文字列や、スペース区切りの文字列で渡しても良い。__repr__も実装されている。結構便利だ。

改めてdataclassアノテーション

今までも、やり方はあったのになぜ今回dataclassを追加したか。
dataclassの説明を読むと、適用されるメソッド(__repr__や__eq__など)のコントロールができたり、公開するフィールドを選んだりすることができるのがdataclassのようだ。読み取り専用のクラスも作成できたり、自由度が高くなっている。
今後、python3.7がメインの環境になった暁には、利用してみよう。

同じタグの記事
同じカテゴリの記事

jqueryのバージョンとprop()

jqueryのバージョンとprop()

古いシステムのメンテナンスを行った。
画面周りだけで対応できそうだったので、javascriptを修正した。

しかし、下記の処理でjavascriptエラーになった。

1
2
3
if ( $('#chkAgree').prop('checked') ) {
    ///
}

jquery.prop()

チェックボックスの状態を取得するのに、prop()で取得しようとしたが、
この部分がエラーになっていた。
jqueryのドキュメントページによると、prop()はjquery1.6から利用できるようだ。
現在利用しているjqueryのバージョンは?

jqueryのバージョン取得方法

1
alert($.fn.jquery);

1.4.3が返ってきた。どおりでエラーになるはずだ。

対処方法

jquery1.6以降ではやってはいけないが、1.4では下記のようにする。

1
2
3
if ( $('#chkAgree').attr('checked') ) {
    ///
}

注意

jquery1.6以降でもattr()は存在するが、htmlに書かれた属性を取得するためのメソッドとして存在する。
jquery1.6以降で画面に表示された情報などを動的に取得する場合はpropメソッドを利用する。

同じタグの記事
同じカテゴリの記事

冬の写真

パソコンの整理をしていたら昨年年末の写真が出てきた。

ロマンスカー

ロマンスカー前から

冬の雲が美しい

これも空が美しい

青いロマンスカー、そして雲

まとめ

最近、カメラ持って散歩に出かけていないな

同じタグの記事
同じカテゴリの記事

sqlite3のデータ型

sqlite3のデータ型

今まで、適当に使っていたsqlite3だが、sqlite3のデータ型は5種類あるようだ。

  • NULL
  • INTEGER
  • REAL
  • TEXT
  • BLOB

数値はintやnumberとして定義してしまっていたが、これは自動でINTEGERと判断されるようだ。
また、charやvarcharと定義した場合は、TEXTとして扱われるようだ。

sqlite3のデータ型の定義は厳密ではなく、可能な限り定義したタイプに変換して登録される。
データ型が異なるからといって、INSERTやUPDATEがエラーになることはない。

INTEGERの項目にはINTEGERとして登録し、
TEXTの項目にはTEXTとして登録される。
INTEGERの項目に’A’を登録しようとするとTEXTの’A’として登録される。
1.5を登録しようとすると、REALの1.5として登録される。
TEXTの項目は、TEXTに変換され登録される。
INTEGERに’1.5 ‘を登録すると、REAL 1.5 として登録され、
TEXTに’1.5 ‘を登録すると、TEXT ‘1.5 ‘として登録される。

かなり上手に型変換してくれるので、余程間違った登録さえしなければ、
数値は数値として、文字は文字として処理できる。

試しに、いろいろな値を入れてみた

データ投入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (
    a TEXT,
    b INTEGER,
    c REAL,
    d BLOB,
    e
);
INSERT INTO t1 VALUES ('A',  'A',  'A',  'A',  'A'  );
INSERT INTO t1 VALUES ('B ', 'B ', 'B ', 'B ', 'B ' );
INSERT INTO t1 VALUES ('1',  '1',  '1',  '1',  '1'  );
INSERT INTO t1 VALUES ('2 ', '2 ', '2 ', '2 ', '2 ' );
INSERT INTO t1 VALUES (' 3', ' 3', ' 3', ' 3', ' 3' );
INSERT INTO t1 VALUES (' 4 ',' 4 ',' 4 ',' 4 ',' 4 ');
INSERT INTO t1 VALUES (5,    5,    5,    5,    5    );
INSERT INTO t1 VALUES (-6,   -6,   -6,   -6,   -6   );
INSERT INTO t1 VALUES (7.1,  7.1,  7.1,  7.1,  7.1  );
INSERT INTO t1 VALUES (-8.2, -8.2, -8.2, -8.2, -8.2 );
INSERT INTO t1 VALUES (0.009,0.009,0.009,0.009,0.009);
INSERT INTO t1 VALUES (NULL, NULL, NULL, NULL, NULL );

検索

1
2
3
4
5
6
7
8
9
10
11
.header ON
.mode COLUMN
SELECT
    '|' || a || '|' AS TEXT,    typeof(a) AS T_TEXT,
    '|' || b || '|' AS INTEGER, typeof(b) AS T_INTEGER,
    '|' || c || '|' AS REAL,    typeof(c) AS T_REAL,
    '|' || d || '|' AS BLOB,    typeof(d) AS T_BLOB,
    '|' || e || '|' AS NONE,    typeof(e) AS T_NONE
FROM
    t1
;

結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TEXT        T_TEXT      INTEGER     T_INTEGER   REAL        T_REAL      BLOB        T_BLOB      NONE        T_NONE
----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------  ----------
|A|         text        |A|         text        |A|         text        |A|         text        |A|         text
|B |        text        |B |        text        |B |        text        |B |        text        |B |        text
|1|         text        |1|         integer     |1.0|       real        |1|         text        |1|         text
|2 |        text        |2|         integer     |2.0|       real        |2 |        text        |2 |        text
| 3|        text        |3|         integer     |3.0|       real        | 3|        text        | 3|        text
| 4 |       text        |4|         integer     |4.0|       real        | 4 |       text        | 4 |       text
|5|         text        |5|         integer     |5.0|       real        |5|         integer     |5|         integer
|-6|        text        |-6|        integer     |-6.0|      real        |-6|        integer     |-6|        integer
|7.1|       text        |7.1|       real        |7.1|       real        |7.1|       real        |7.1|       real
|-8.2|      text        |-8.2|      real        |-8.2|      real        |-8.2|      real        |-8.2|      real
|0.009|     text        |0.009|     real        |0.009|     real        |0.009|     real        |0.009|     real
            null                    null                    null                    null                    null

まとめ

やはり、なかなかの精度で変換してくれている。
これからも使い続けようと思う。

同じタグの記事
同じカテゴリの記事

手軽なのに強力なsqlite3

手軽なのに強力なsqlite3

適当なツールにsqlite3をよく使う。
pythonを使っていると、最初からpython3ライブラリが使えるので、テキストファイルの集計なども :memory: データベースに投入して出力すると、簡単に実装できる。

データベースを:memory:ではなく、ファイルにした場合、データの再利用が可能だ。
そのデータをちょっと見たいとき、わざわざpythonを経由して見るのは面倒なので、sqlite3コマンドでアクセスしている。

SELECTの結果

デフォルトの出力形式は見ずらい。
効率的といえば効率的かもしれないが、すべてのカラムがパイプ区切りでべったりくっついて表示されてしまう。
この設定は.modeで行う。

.mode csv

カンマ区切りで出力される。特定のカラムに絞ったデータをCSVで出力して、次の処理を行うのも良いかもしれない。

.mode tabs

タブ区切りで出力される。データにカンマが含まれることも多いと思うので、こちらで出力しておいた方が、次の利用に問題が出ないかもしれない。

.mode ascii

カラムの区切り文字が0x1F、行の区切り文字が0x1Eで出力される。
データにカンマもタブも含まれている場合は、この形式が良いと思う。

.mode html

html形式で出力される。使えないこともない。

.mode insert TABLENAME

insert文として出力される。これをファイルに出力して、特定のデータのみを移行するというのにも使える。
TABLENAMEはINSERT文のINSERT先のテーブル名として利用される。
指定しないとINSERT先がTABLEになってしまう。

.mode column

コンソール出力で見栄えが良いように出力してくれる。
OracleのSQL*Plusのような感じか。
しかし、.widthと組み合わせて使わないと、カラムのデータが途中で区切れてしまうことがある。要注意。
.widthはcolumnの先頭から文字数をスペース区切りで指定していく。
.width 10 10 10 20 20 20

日本語が登録されたカラムがあると、文字数でカウントされ縦位置がずれてしまうので注意。
数値とコードのみの、トランザクションテーブルなら使いやすい。

.header on

1行目にカラム名が表示されるようになる。
これはよく使う。

.output FILENAME

SELECT結果の出力先をファイルにする。
.modeをasciiやinsert、htmlに設定した場合は、.outputをファイルにしておく。
終わったら、「.output stdout」として、出力先をコンソールに戻しておく。

その他、たまに利用する設定

.timer on

処理時間が表示されるようになる。
SQL*PlusのSET TIMING ONのような感じ。
sqlite3で性能問題に当たるような、大作SQLを作ったことがないので、あまり気にしないが。

.show

変更した設定値の一覧が確認できる。

.table

テーブル一覧の表示。

.read FILENAME

外部ファイルを読み込んで実行する。
.mode insertで出力した結果を取り込むのに使える。

普段、何も考えずによく使う使い方

1
2
3
4
sqlite> .header on
sqlite> .mode column
sqlite> select * from test_table limit 10;
sqlite> ...

まとめ

簡単に使えてとても便利。それにINSERTとSELECTはとても速い。
INSERTは1件コミットにするととても遅いので注意。
SELECTはINDEXを効かせれば、数万レコードのテーブルでも一瞬で返ってくる。
ツールもなかなか使えるし、手放せないデータベースになってきた。

最後に、sqlite3コマンド抜けるのは.quit(または.exit)。

同じタグの記事
同じカテゴリの記事

Kotlin今日の発見 2018年8月3日

Kotlin今日の発見 2018年8月3日

android studioのドキュメントを眺めていた。
Kotlinのことが書かれていた。
また、今更ながら便利な構文を見つけた。

Elvis Operator

代入がnullになってしまうときの処理を書ける。

1
2
3
4
5
6
7
val hashMap = hashMapOf<String, String>(
    "A" to "Alpha",
    "B" to "Bravo",
    "C" to "Charlie")
val nameA = hashMap["A"] ?: "no name."
val nameD = hashMap["D"] ?: "no name."
println("nameA:[$nameA], nameD:[$nameD]")

今まで、if (nameA == null) … と書いてしまっていた。

処理分岐に用いることもできる。

1
2
3
4
fun myFun(hashMap: HashMap<String, String>) {
    val name = hashMap["A"] ?: return
    println("nameA:[$nameA]")
}

これも、ifで判定していた。早く知っていれば…

そもそも、hashMapがnullかもしれない場合

1
2
3
var map: MutableMap<String, String>? = null
val mapSize = map?.size ?: -1
println("mapSize:[$mapSize]")

?.と?:で、nullに対処できる。

複数の値を返却する関数

data classを利用することで、複数の値を返却する関数やメソッドを定義できる。

1
2
3
4
5
6
7
8
9
10
11
data class Result(val code: Int, val msg: String)
fun myFun2(i: Int): Result {
    if (i > 0) {
        return Result(0, "OK")
    }
    return Result(9, "NG")
}
val (code1, msg1) = myFun2(1)
println("code:$code1, msg:$msg1")
val (code2, msg2) = myFun2(-1)
println("code:$code2, msg:$msg2")

戻り値はResultクラスのインスタンスのまま、受け取っても良い気もする。戻り値の使い方次第だけれど。
javaだと、クラスを作るのが面倒なので、複数の値が簡単に返却できればと思うけど、Kotlinなら簡単にdata class定義できるし。

まとめ

やっぱりKotlinは便利。

同じタグの記事
同じカテゴリの記事

pythonからchromeを操作する

pythonからchromeを操作する。

pythonでseleniumライブラリを利用することで、
ChromeやFirefoxを操作できるようだ。

Windows環境で試してみた。

pipでseleniumドライバーをインストール

1
python -m pip install selenium

ChromeのWebDriverをダウンロード

https://sites.google.com/a/chromium.org/chromedriver/downloads

zipファイルがダウンロードされ、解凍すると「chromedriver.exe」というファイルが1つ入っていた。
これを経由してChromeへアクセスするようだ。

pythonから呼び出し

準備(必須ではない)

chromedriver.exeが存在する場所にPATHを通す。
またはPATHが通っているディレクトリにchromedriver.exeを置く。

参考:この方法は必須ではない。
ドライバーのインスタンス生成時に、chromedriver.exeのフルパスを指定できるので、その方法を用いても良い。

pythonからchromeを利用する。

まずは、seleniumのwebdriverをインポート。

1
from selenium import webdriver

webdriverのインスタンスを生成する。

1
2
3
4
5
6
7
<h1>chromedriver.exeにPATHが通っている場合。</h1>
driver = webdriver.Chrome()
<h1>chromedriver.exeにPATHが通っていない場合。</h1>
path_to_chromedriver = 'C:/usr/chromedriver.exe'
driver = webdriver.Chrome(path_to_chromederiver)
<h1>または</h1>
driver = webdriver.Chrome(executable_path = path_to_chromedriver)

ここまで実行するとchromeが立ち上がる。
画面を非表示にしたい場合は、Optionを定義しヘッドレスモードで起動する。

1
2
3
4
5
6
7
8
9
<h1>ヘッドレスオプションの準備</h1>
options = webdriver.ChromeOptions()
options.add_argument('--headless')
<h1>インスタンス生成</h1>
driver = webdriver.Chrome(options = options)
<h1>または</h1>
driver = webdriver.Chrome(
    executable_path = path_to_chromedriver,
    options = options)

URLを開く

1
driver.get('https://www.yahoo.co.jp/')

HTMLを取得する

1
html = driver.page_source

スクリーンショットを保存する。

1
driver.save_screenshot('screenshot.png')

javascriptを実行して、結果を得る。

1
page_height = driver.execure_script('return document.body.scrollHeight')

ウインドウサイズを設定する。

1
driver.set_window_size(1280, 800)

終了処理

1
driver.quit()

これでChromeが閉じる。

まとめ

ひたすらスクリーンショットを取らなくてはならないときなど便利そうだ。

同じタグの記事
同じカテゴリの記事