SlideShare a Scribd company logo
1
PyCon mini Sapporo 2015
山田 聡 @denzowill
2
注意
機械学習とかでてきません
全編泥臭いテキスト処理の話です
だいぶ入門者向けです
3
お前だれよ
山田 聡@denzowill
東京でDBのサポートやってます@株式会社アシスト
(PostgreSQLとかOracleとか)
Python歴1年ちょっと
社内で便利屋的な扱い
最近似顔絵が
似てなくなって来ました。。。
4
だいたいこんな感じで生きてます
5
話すこと
Oracleの稼動統計レポート(Statspack)を
JSONに変換した時の話です
非構造化データを構造化した時の
苦労話です
6
これを
Snapshot Snap Id Snap Time Sessions Curs/Sess
~~~~~~~~ ---------- ------------------ -------- ---------
Begin Snap: 32987 27-5月 -15 09:25:01 477 21.8
End Snap: 32988 27-5月 -15 10:25:01 472 22.7
Elapsed: 60.00 (mins) Av Act Sess: 4.0
DB time: 237.82 (mins) DB CPU: 105.44 (mins)
7
こうしたかった
{
"Elapsed": 60,
"Av_Act_Sess": 4,
"DB_time": 237,
"DB_CPU": 105.44,
"Begin_Snap": {
"Snap_Id": 32987,
"Snap_Time": "27-5月 -15 09:25:01",
"Sessions": 477,
"Curs_Sess": 21.8
},
"End_Snap": {
"Snap_Id": 32988,
"Snap_Time": "27-5月 -15 10:25:01",
"Sessions": 472,
"Curs_Sess": 22.7
}
}
8
構造化された
データは便利
9
構造化すると
加工が楽
プログラマチックに処理できる
いろいろ連携の夢広がりんぐ
10
構造化したい?
11
構造化したい!
12
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
構造化するには
結
果
の
チ
ェ
ッ
ク
13
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
ファイルをしっかりみる
結
果
の
チ
ェ
ッ
ク
14
みるところ
15
単一フォーマット?
→1ファイルに複数のフォーマットがあるか
固定長?特定文字区切り?
→行や列を区切るルールを発見
変なデータがまじることは?
→###等不正なデータの有無
16
Avg %Total
%Tim Total Wait wait Waits Call
Event Waits out Time (s) (ms) /txn Time
---------------------------- ------------ ---- ---------- ------ -------- ------
buffer busy waits 12 0 6 499 12.0 37.6
Disk file operations I/O 77 0 3 39 77.0 18.7
control file sequential read 920 0 3 3 920.0 17.7
rdbms ipc reply 25 0 1 45 25.0 7.1
db file sequential read 51 0 1 15 51.0 4.7
control file parallel write 85 0 0 3 85.0 1.6
固定長データ、列の区切り目を
取得すればよさそう。
データは文字列と数値。
素直なデータ
17
CPU Elapsd Old
Buffer Gets Executions Gets per Exec %Total Time (s) Time (s) Hash Value
--------------- ------------ -------------- ------ -------- --------- ----------
10,074 67 150.4 31.6 0.11 0.08 335360792
select file#, block#, blocks from seg$ where type# = 3 and ts# =
:1
3,808 685 5.6 12.0 0.02 0.01 2482976222
select intcol#,nvl(pos#,0),col#,nvl(spare1,0) from ccol$ where c
on#=:1
1,294 1 1,294.0 4.1 0.09 0.38 2522684317
Module: SQL*Plus
BEGIN statspack.snap; END;
少しクセのあるデータ
基本固定長データなのは同じ
だけど空行区切りの、複数行構成
数字行を開始ともみなせる
でもModule:の行があったりなかったり…
18
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
ある程度規則が見つかったら実装
結
果
の
チ
ェ
ッ
ク
19
大まかにまずは区切る
→セクション単位に分割してから
フォーマット毎にクラスをつくる
→変更多発なので影響を局所化
共通ロジックをベースクラスへ
→子クラス作成中に思ったら親に移す
実装するときに考えていること
20
実際につくったら...
21
セクション1
セクション2
セクション3
区切りa
区切りb
セクション1
セクション2
セクション3
区切り文字で分割
22
セクション1
フォーマットA
セクション2
フォーマットB
セクション3
フォーマットC
セクション名とフォーマットをマッピング
ディクショナリ的な何か
セクション1:フォーマットA
セクション2:フォーマットB
セクション3:フォーマットC
23
セクション1
フォーマットA
セクション2
フォーマットB
セクション3
フォーマットC
フォーマットに対応するパーサを割り当て
フォーマットA用
パース処理
フォーマットC用
パース処理
フォーマットB用
パース処理
どことなくモジュールの
独立性が保たれてる(きがする)
24
パーサ間の関係
フォーマットA用
パース処理
フォーマットC用
パース処理
フォーマットB用
パース処理
ベースパーサ
結構綺麗な
フォーマットの
パーサ
ゆるい継承関係
後が怖くてセクションとパーサがほぼ1:1
25
になりました。
26
パーサどうなった?
27
class ParserBase(object):
def __init__(self, lines=None):
# unicodeで取得したセクションの各行
self.lines = lines
# データの整形
def reformat(self):
# 子クラスでの実装をする
raise Exception("Plz Implement")
# 実際の解析処理
def parse_main(self):
# 子クラスでの実装をする
raise Exception("Plz Implement")
reformat/parse_mainを
継承先で実装していく
基底クラス
28
後は継承して
ひたすらre
29
Avg %Total
%Tim Total Wait wait Waits Call
Event Waits out Time (s) (ms) /txn Time
---------------------------- ------------ ---- ---------- ------ -------- ------
buffer busy waits 12 0 6 499 12.0 37.6
Disk file operations I/O 77 0 3 39 77.0 18.7
control file sequential read 920 0 3 3 920.0 17.7
rdbms ipc reply 25 0 1 45 25.0 7.1
db file sequential read 51 0 1 15 51.0 4.7
control file parallel write 85 0 0 3 85.0 1.6
固定長データ、列の区切り目を
取得すればよさそう。
データは文字列と数値。
素直なデータ
30
def parse_main(self):
:
for line in self.lines:
# ---- --- 的なのが出たら取得開始
if re.search(sep_str, line):
val_flg = True
# ---- --- を[0,4,8..]的に変換
sep_posit_list = self.get_splited_position(line)
continue
# ---- --- 的なのがでるまで無視
if not val_flg:
continue
# データ部分を[0,4,8..]的な位置で分割しながら格納
line_row_list.append(self.split_str_by_posit(line, sep_posit_list))
31
CPU Elapsd Old
Buffer Gets Executions Gets per Exec %Total Time (s) Time (s) Hash Value
--------------- ------------ -------------- ------ -------- --------- ----------
10,074 67 150.4 31.6 0.11 0.08 335360792
select file#, block#, blocks from seg$ where type# = 3 and ts# =
:1
3,808 685 5.6 12.0 0.02 0.01 2482976222
select intcol#,nvl(pos#,0),col#,nvl(spare1,0) from ccol$ where c
on#=:1
1,294 1 1,294.0 4.1 0.09 0.38 2522684317
Module: SQL*Plus
BEGIN statspack.snap; END;
少しクセのあるデータ
基本固定長データなのは同じ
だけど空行区切りの、複数行構成
数字行を開始ともみなせる
でもModule:の行があったりなかったり…
32
def parse_main(self):
:
for line in self.lines:
:
# カンマ、空白、小数点を除いた後、数値だけの行か
# データのブロックの開始判定
if self.is_only_int_line(line):
# SQL文の行ではないのでフラグを初期化
sql_l_flg = False
:
# 文字列バッファの初期化
sql_str = u""
buf_str = u""
# 数値データは、固定長なので通常通り区切って格納
row_list.append(self.split_str_by_posit(line, sep_posit_list))
# モジュール名の行を取得
elif re.search(u"Module:s.+", line):
row_list[-1].append(line.strip()[8:])
# 以降の行はSQL文
sql_l_flg = True
elif sql_l_flg:
sql_str += line.strip()
else:
buf_str += line.strip()
33
大体こんな感じで
微調整しながら
作りました
34
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
実装を終えたらテスト
結
果
の
チ
ェ
ッ
ク
35
Unittest
→JUnitライク、標準モジュール
nose
→もうちょっと高度に
doctest
→Docstringに書いた内容でテストされる
Pythonでのテスト
36
Unittest
→これくらいでちょうどいいかとおもった
nose
→そこまでしなくてもいっか
doctest
→今回は引数とかがでかいのでDocstringには書きづらい
Pythonでのテストを検討した
37
import unittest
import StatspackParser
import sys
# とりあえず対象のレポートを渡したかった
FILE_NAME= sys.argv[1]
class LogicTest(unittest.TestCase):
def setUp(self):
self.file_name = FILE_NAME
def test_parse(self):
sp = StatspackParser(self.file_name)
parsed_data = sp.do_parse()
# テストファイルの該当セクション最後のSQLをチェック
self.assertEqual(parsed_data["SQL_ordered_by_Gets"][-1]["SQL_TEXT"], "select * from emp")
# その他もろもろ
:
if __name__ == '__main__':
# unittest自体の引数ではないので消す
del sys.argv[1]
unittest.main()
38
いろいろみつかった
orz
39
40
→崩れてた
41
Foreground Wait Events DB/Inst: ORCL/ORCL1 Snaps: 32987-32988
-> Only events with Total Wait Time (s) >= .001 are shown
-> ordered by Total Wait Time desc, Waits desc (idle events last)
Avg %Total
%Tim Total Wait wait Waits Call
Event Waits out Time (s) (ms) /txn Time
---------------------------- ------------ ---- ---------- ------ -------- ------
db file sequential read 281,434 0 5,788 21 0.4 37.5
direct path read 14,550 0 1,005 69 0.0 6.5
enq: TX - index contention 168 0 627 3735 0.0 4.1
:
:
Foreground Wait Events DB/Inst: ORCL/ORCL1 Snaps: 32987-32988
-> Only events with Total Wait Time (s) >= .001 are shown
-> ordered by Total Wait Time desc, Waits desc (idle events last)
Avg %Total
%Tim Total Wait wait Waits Call
Event Waits out Time (s) (ms) /txn Time
---------------------------- ------------ ---- ---------- ------ -------- ------
KJC: Wait for msg sends to c 776 0 0 0 0.0 .0
なんかヘッダが何回もでる
42
# 繰り返しのヘッダを取り除く
def remove_duplicate_header_and_info(self):
# ヘッダ行を取得
head = self.guess_header_block()
# 文字列として再整形
total_header_string = u"n".join(head)
# 構成行から一括して削除
# 属性としては1行1要素のリストで持ってたので再度文字列として取得する
line_string = self.get_line_string()
headerless_string = line_string.replace(total_header_string, u"")
# 再度ヘッダを頭にだけ付け直して再設定
self.set_line_string(u"n".join(head) + u"n" + headerless_string)
取り除く前処理追加
43
Begin Snap: 1 20-8月 -15 06:59:18
→割と普通
Begin Snap: 1 08-Aug-15 15:33:11
→英語表記
Begin Snap: 1 20-8譛-15 10:33:18
→なんか化けてる(届いた時点で)
Begin Snap: 1 14-7? -15 23:00:01
→もはや化けてるとかのレベルじゃない
日付ばらばら
44
def parse_date(self, date_string):
# 見つけたフォーマットを全部いれておく
formats = [
u"%d-%m月 %H:%M:%S",
u"%d-%m月 %H:%M",
u"%d-%m月-%y %H:%M", # like 24-12月-14 07:35
:
u"%d-%m? %H:%M", # like 24-12月-14 07:35
u"%d-%m? %H:%M:%S", # like 24-12月-14 07:35
]
# パース出来るまで頑張る
for format_pat in formats:
try:
# unicodeでできないのでstrにする
ret = datetime.datetime.strptime(date_string.encode(self.encode),
format_pat.encode(self.encode))
break
except ValueError:
pass
else:
# 全滅なら投げといて、後でフォーマットを追加する
raise NoMatchDateFormatException(date_string.encode(self.encode))
return ret
手当たりしだい試すことにした
45
Buffer wait Statistics DB/Inst: ORCL/ORCL1 Snaps: 32987-32988
-> ordered by wait time desc, waits desc
Class Waits
------------------------------------------------------------------ -----------
Total Wait Time (s) Avg Time (ms)
------------------- -------------
data block 7,134
75 10
undo header 5
0 0
2nd level bmb 2
0 0
-------------------------------------------------------------
環境によっては折り返されてる
46
折り返しを戻す前処理をいれた
が。
47
崩れ方がまちまちで
統一処理にしづらい
48
処理失敗時パーサを切り替える事にした
retry_flag = True
while retry_flag:
try:
# 解析したディクショナリを取得する
parser_inst.reformat()
result_dict = parser_inst.parse_main()
if result_dict:
if len(result_dict.values()[0]) > 0:
return_dict["PARSED_DATA"][result_dict.keys()[0]] = result_
# 処理は成功しているがデータがとれていない
else:
raise NoValidDataException(parser_inst)
# ここまできたらリトライしない
retry_flag = False
# バージョン差異でパースエラーになる可能性はある
except (IndexError, NoMatchDateFormatException, NoValidDataException):
try:
# 予備のパーサを取得してリトライ
parser_inst = parser_inst.get_sub_parser_inst()
except NoSubParserException as e:
# 予備のパーサが出尽くした
retry_flag = False
49
やってみると
崩れっぷりを事前定義する力技
以外と各セクション3パターン以内
実用レベル範囲内で動いた
50
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
いったりきたりで何とか動いた
結
果
の
チ
ェ
ッ
ク
51
生
フ
ァ
イ
ル
を
観
察
規
則
性
の
発
見
実
装
結
果
の
チ
ェ
ッ
ク
まとめ
52
元データ
社内での利用方法
解析処理 Dict
JSON
csv
PostgreSQLの
JSONBにぶちこむ
顧客資料のベースに
json.dumps
D3.js等で可視化
53
まとめ
解析対象のルールをしっかり判断
データの崩れをどうするか検討
ときには力技での対応
54
ご清聴
ありがとうございました

More Related Content

PDF
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
PDF
PostgreSQL実行計画入門@関西PostgreSQL勉強会
PDF
PostgreSQLとPythonとSQL
PDF
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
PPT
20090107 Postgre Sqlチューニング(Sql編)
PDF
20140531 JPUGしくみ+アプリケーション分科会 勉強会資料
PDF
Pgunconf 20121212-postgeres fdw
PPTX
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
PostgreSQL SQLチューニング入門 実践編(pgcon14j)
PostgreSQL実行計画入門@関西PostgreSQL勉強会
PostgreSQLとPythonとSQL
PostgreSQLの実行計画を読み解こう(OSC2015 Spring/Tokyo)
20090107 Postgre Sqlチューニング(Sql編)
20140531 JPUGしくみ+アプリケーション分科会 勉強会資料
Pgunconf 20121212-postgeres fdw
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~

What's hot (19)

PPTX
SQLチューニング入門 入門編
PDF
Lt ingaoho-jsonb+postgeres fdw
PDF
「plyrパッケージで君も前処理スタ☆」改め「plyrパッケージ徹底入門」
PDF
Rの高速化
PPTX
R -> Python
PDF
[data analytics showcase] B12: サーバー1,000台を監視するということ by 株式会社インサイトテクノロジー 小幡 一郎
PPT
Maatkit で MySQL チューニング
PDF
入門機械学習1,2章
PDF
RailsエンジニアのためのSQLチューニング速習会
PDF
Pg14_sql_standard_function_body
PDF
PostgreSQLの関数属性を知ろう
PDF
Postgre sql9.3 newlockmode_and_etc
PDF
R-hpc-1 TokyoR#11
PDF
Rにおける大規模データ解析(第10回TokyoWebMining)
PDF
統計解析言語Rにおける大規模データ管理のためのboost.interprocessの活用
PDF
Rのデータ構造とメモリ管理
PDF
Hackers Champloo 2016 postgresql-9.6
PDF
PostgreSQL:行数推定を読み解く
PPTX
入門機械学習6章
SQLチューニング入門 入門編
Lt ingaoho-jsonb+postgeres fdw
「plyrパッケージで君も前処理スタ☆」改め「plyrパッケージ徹底入門」
Rの高速化
R -> Python
[data analytics showcase] B12: サーバー1,000台を監視するということ by 株式会社インサイトテクノロジー 小幡 一郎
Maatkit で MySQL チューニング
入門機械学習1,2章
RailsエンジニアのためのSQLチューニング速習会
Pg14_sql_standard_function_body
PostgreSQLの関数属性を知ろう
Postgre sql9.3 newlockmode_and_etc
R-hpc-1 TokyoR#11
Rにおける大規模データ解析(第10回TokyoWebMining)
統計解析言語Rにおける大規模データ管理のためのboost.interprocessの活用
Rのデータ構造とメモリ管理
Hackers Champloo 2016 postgresql-9.6
PostgreSQL:行数推定を読み解く
入門機械学習6章
Ad

Similar to PythonでテキストをJSONにした話(PyCon mini sapporo 2015) (20)

PPTX
そうだ 検証、しよう。
PPTX
Python による 「スクレイピング & 自然言語処理」入門
PDF
chapter6
PDF
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
PDF
S02 t2 my_historyofpythonlearning
KEY
ひのきのぼうだけで全クリ目指す
PDF
DATUM STUDIO PyCon2016 Turorial
PDF
High performance python computing for data science
PDF
C16 45分でわかるPostgreSQLの仕組み by 山田努
PDF
文字列処理
PDF
Python for Data Anaysis第2回勉強会4,5章
PPTX
企業等に蓄積されたデータを分析するための処理機能の提案
PDF
PDF
Introduction to Numpy (and Python) [JPN]
PDF
第1回python勉強会
PDF
研究会20140702:進捗報告
PDF
More modern gpu
PDF
Easy caching and logging package using annotation in Python
PDF
Data processing at spotify using scio
PDF
20171103 pg con-jp-lt-plpgsql
そうだ 検証、しよう。
Python による 「スクレイピング & 自然言語処理」入門
chapter6
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
S02 t2 my_historyofpythonlearning
ひのきのぼうだけで全クリ目指す
DATUM STUDIO PyCon2016 Turorial
High performance python computing for data science
C16 45分でわかるPostgreSQLの仕組み by 山田努
文字列処理
Python for Data Anaysis第2回勉強会4,5章
企業等に蓄積されたデータを分析するための処理機能の提案
Introduction to Numpy (and Python) [JPN]
第1回python勉強会
研究会20140702:進捗報告
More modern gpu
Easy caching and logging package using annotation in Python
Data processing at spotify using scio
20171103 pg con-jp-lt-plpgsql
Ad

More from Satoshi Yamada (11)

PDF
Pythonで業務改善をしたときにあった問題(ライト版)
PDF
pythonでemlファイルを扱う話
PDF
bottle.pyをつかったチャットアプリ作成チュートリアル
PDF
bottleで始めるWEBアプリの最初の一歩
PDF
Requestsで始める5分前帰社
PDF
DBエンジニアに必要だったPythonのスキル
PDF
本気でPythonで宛名書きした話
PDF
10080分でPythonからIP Messeneger
PDF
15分で情シスに怒られる方法
PDF
Djangoで業務改善したい
PDF
201505 PostgreSQLアンカンファレンス(PL/Pythonで作るWEBアプリ)
Pythonで業務改善をしたときにあった問題(ライト版)
pythonでemlファイルを扱う話
bottle.pyをつかったチャットアプリ作成チュートリアル
bottleで始めるWEBアプリの最初の一歩
Requestsで始める5分前帰社
DBエンジニアに必要だったPythonのスキル
本気でPythonで宛名書きした話
10080分でPythonからIP Messeneger
15分で情シスに怒られる方法
Djangoで業務改善したい
201505 PostgreSQLアンカンファレンス(PL/Pythonで作るWEBアプリ)

PythonでテキストをJSONにした話(PyCon mini sapporo 2015)