How to make datas our friends

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

Pythonで文字列を逆順にする

背景

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

やりたいこと

Pythonで文字列"stressed"を逆順で表示する。
(つまり"desserts"になれば成功)

結果

以下の方法で文字列を逆順にできました。

> str = 'stressed'
> str[::-1]
> 'desserts'

解説/考察

2.3.6 Sequence Types -- str, unicode, list, tuple, buffer, xrangeを参考にしました。
sequence type の s は s[i:j:k] を使用して文字列をスライスできるらしい。

s[i] = i'th item of s, origin 0
s[i:j] = slice of s from i to j
s[i:j:k] = slice of s from i to j with step k

日本語に翻訳するとこんな感じでしょうか。
(日本語のリファレンスもどこかにあるのでしょうか?w)

s[i] = 文字列sから0を起点にしてi番目文字を切り出す
s[i:j] = i番目からj番目の文字列を文字列sから切り出す
s[i:j:k] = i番目からj番目の文字列を文字列sからk個おきに切り出す

まず s[i] の動作について一通り試してみました。

> str = 'stressed'
> # 0が起点なので0を指定すると1番目の文字"s"が切り出される
> str[0]
> 's'
> # 同様に0が起点なので1を指定すると2番目の文字"t"が切り出される
> str[1]
> 't'
> # マイナス値を指定すると文字列のけつからi番目の文字を切り出せるっぽい
> str[-1]
> 'd'
> # 文字列の文字数より大きい値は指定できない
> str[9]
> IndexError: string index out of range

続いて s[i:j] も一通りいじってみる。

> # 何も指定しないと全て切り出される
> str[:]
> 'stressed'
> # 0起点に6文字分切り出す
> str[0:6]
> 'stress'
> # 0起点なので2文字目から6文字分切り出す
> str[1:6]
> 'tress'
> # s[i]と違い文字数より大きい値を指定しても怒られない(ただし何も出ない)
> str[10:10]
> ''
> # jを指定しないでiにマイナス値を指定するとけつからi番目までの文字が切り出される
> str[-4:]
> 'ssed'
> # iが文字列の文字数より大きくてもけつで止まる
> str[-9:]
> 'stressed'

最後に s[i:j:k] です。

> # 何も指定しないと全て切り出される
> str[::]
> 'stressed'
> # 1文字づつ切り出す
> str[::1]
> 'stressed'
> # 2文字づつ切り出す
> str[::2]
> 'srse'
> # -1文字づつ切り出す(けつまで繰り返す)
> str[::-1]
> 'desserts'
> # -2文字づつ切り出す(けつまで繰り返す)
> str[::-2]
> 'dset'

以上のことから str[i:j:k] の k に-1を指定すれば文字列を逆順で切り出せる。

CSVファイルをローカルのMySqlDBに取り込む

0. 概要

AppsFlyer(計測ツール)から落としてきたCSVのローデータを、ローカルのMySqlDBに取り込むのに若干苦戦したので、無事成功するまでの過程を後世に残そうと思いメモ。かなり基礎的な技術だと思われるので、対象読者は初心者向けだと思われる。

今回ハマったポイント

CSVの1行目(ヘッダー部分)まで読み込んでしまい型が違うと怒られる。

・ローデータのCSV内にある文字列"null"をNULL値として扱ってもらえず、
 INTEGER型のカラムで型が違うと怒られる。

・LOAD DATA INFILEコマンドを実行しても
 1行目のデータしかテーブルにINSERTされない。

 

はい、見ての通りめちゃ初歩的なミスだと思います(ΦωΦ)

(だって初めてやったんだもん、大目に見て欲しい)

1. 前準備

ローカルDBにCSVを取り込むためにデータベースとテーブルを用意する。
今回はこんな感じで用意しました。

データベースを用意(`・ω・´)ゞ

-- 新しいデータベースを作成する
mysql> CREATE DATABASE localdb_jp;

 テーブルを用意(`・ω・´)ゞ

-- 新しいテーブルを作成する
mysql >
CREATE TABLE tmp_localdb_jp.appsflyer_data (
~~~ 省略 ~~~
);

2. CSVを用意したテーブルに読み込む

早速DLしてきたCSVファイルをMySQL側でロードさせてみる。


MySQLにはLOAD DATA INFILEコマンドという、CSVをさくっと読み込んでくれる超絶便利なコマンドがあるのでそれを使うことにする。
(INSERT文を作って読み込ませるよりめちゃ早い)

参照:MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.6 LOAD DATA INFILE 構文

▼構文はこんな感じ

LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [PARTITION (partition_name,...)]
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]
    [IGNORE number {LINES | ROWS}]
    [(col_name_or_user_var,...)]
    [SET col_name = expr,...]

SET文を使えば色々融通が効きそうな予感、でも今回は使わない。

早速それっぽいものを作成して実行してみる。

▼クエリ

mysql >
LOAD DATA INFILE "/PATH/TO/data.csv"
INTO TABLE tmp_localdb_jp.appsflyer_data
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "\n";

▼実行結果

ERROR 1292 (22007): Incorrect datetime value: 'attributed touch time' for column 'attributed_touch_time' at row 1 

早速怒られる。

そりゃそうだ、そんな簡単に成功しないのは想定内だぜ。

どうやら、1行目もデータとして読み込んでしまっているのが原因で、カラムの型と実際のデータ型が違うよとのこと。

よく見たら構文のところに一行目を飛ばして読み込むっぽいオプションがある。(フィーリングで書き始めないでちゃんと見ましょうねというオチ)

IGNORE number { LINES | ROWS}

早速、それっぽくクエリに足して再度実行してみる。

▼クエリ

mysql >
LOAD DATA INFILE "/PATH/TO/data.csv"
INTO TABLE tmp_localdb_jp.appsflyer_data
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "\n"
IGNORE 1 LINES;

▼実行結果

ERROR 1366 (HY000): Incorrect integer value: 'null' for column 'event_revenue' at row 1

はい、また怒られたー(´^ω^)

今度はnullをNULL値として扱ってもらえずINTEGER型のカラムに文字列が入ってきてるぜ親分!的なエラーです。(その辺柔軟にやってくれよ)

あまり作業量は増やしたくないのですが、渋々CSV内のデータを置換するハメに。(もっといい方法あるなら教えて欲しいんだ)

とりあえず、シェルさんになんとかしてもらう。

# LC_ALL=C sed -i -e 's/null/NULL/g' data.csv

これでひとまずnullがNULLになったのでもう一回トライしてみる。

無事通りました(`・ω・´)ゞキリ

mysql> SELECT COUNT(*) FROM tmp_localdb_jp.appsflyer_data;

+----------+

| COUNT(*) |

+----------+

|     3006 |

+----------+

1 row in set (0.06 sec)

番外編:LOADしてもデータが1行分しか読み込まれない 

 実はここに至るまでに、実行しても1行文しかデータが読み込まれないという状況に悩まされていました。

 

原因は、文字コードの違いで(余計なことして改行コードが変わっていたっぽい)LINES TERMINATED BY "\n"の部分を\nから\rにすることで解決しました。

 

どちらを使えばいいかわからない人はCSVファイルをtxtエディタで一回開いて、コマンドF(検索)で両方検索してみるとどちらの改行コードが使われているかすぐにわかります。