bashしか知らない俺に速度160倍のcsv処理能力を与えたpandaす

# TL;DR
- もしテーブルデータを扱うなら
- Python 簡単に使いはじめられるよ
- Pandas すごく使いやすいよ
- なおかつbashに比べてめちゃくちゃ速度出るよ

# はじめに
インフラエンジニアだったんですよ、ぼく。
なので、bashで書くshell scriptはそれなりに書いていました。
それなりですけど。

開発を初めて２年目の今、
100万行ほどのデータの整形を求められました。

最初の１年はJavaを使って開発、ほぼIDE使っていて
いわゆるツール的な使い方に慣れていなかったので、
Javaでの処理はイメージがわきませんでした。
（開発環境のセットアップもしてないし）


というところで、簡単に作れるツールといえば、ShellScriptでしょう
と思い手を出しはじめました。



# やりたいこと
行数：100万
列数：400
からなるcsvデータの１カラム「location」を分割して
latitude/longitudeにしたい。
latitude/longitudeは共に数値（少数）で
"(latitude,longitude)" という形で入っている。曲者。

↓これを

|id|location|あと色々４００列くらい
|---|------|---
|1|(35.00,135.00)|
|2|(35.18,135.12)|


↓こうしたい


|id|latitude|longitude|あと色々４００列くらい
|---|------|---|---
|1|35.00|135.00||
|2|35.18|135.12||


# 色々試したこと
## エクセル先生
ロジカルじゃない僕でも直感で操作ができるエクセル先生
SIerをやっているときには散々お世話になりました。
時間はかかるだろうが、コード書かなくても簡単にできるなら
まずは先生の出番じゃないか？と思いました。


### 手順
1. location列の右に列追加
1. location列のみを対象に、データ -> 区切り文字の変更でカンマ区切りにする
1. locationの見出しを変更
2. 保存

### できた？
できませんでした。
エクセルはSJISなんですよ。忘れてました。
csvファイルはutf-8
なので、日本語のカラムが文字化けしちゃうんですね。
（保存時にデータが欠損しました。）

bashでiconv使って文字コード変えてからやろうとも思いましたが、
エラー出ちゃったので細かく見ずにやめました。

## google スプレッドシート
csv大きすぎてインポートできない

## Numbers
そもそも行列の数が足りない

## 最初に作ったscript
こんな感じで作りました。
とりあえずの品なので、細かいところはご愛嬌

```bash
#!/bin/bash

INPUTFILE=$1
OUTFILE=$2

ROWS=$(cat ${INPUTFILE}) # catをheadに変えてデバッグしていた

cnt=0
while read LINE
do
    cnt=`expr ${cnt} + 1`
    # ヘッダ行用の処理
    if [ ${cnt} -eq 1 ]
    then
        fld1=$(echo -n ${LINE} | cut -d, -f2-14)
        fld2="latitude"
        fld3="longitude"
        fld4=$(echo ${LINE} | cut -d, -f16-217) # 218列目になんかゴミデータがあった
        fld5=$(echo ${LINE} | cut -d, -f219-)
        echo ${fld1},${fld2},${fld3},${fld4},${fld5} > ${OUTFILE}
    else
        fld1=$(echo -n ${LINE} | cut -d, -f2-14)
        location=$(echo -n ${LINE} | cut -d, -f15)
        if [ -n "${location}" ]
        then
            location=$(echo -n ${LINE} | cut -d, -f15-16)
            fld2=$(echo -n ${location} | cut -d, -f1| sed -e 's/"(//g')
            fld3=$(echo -n ${location} | cut -d, -f2| sed -e 's/)"//g')
            fld4=$(echo ${LINE} | cut -d, -f17-218)
            fld5=$(echo ${LINE} | cut -d, -f220-)
            echo ${fld1},${fld2},${fld3},${fld4},${fld5} >> ${OUTFILE}
        fi
    fi

done <<_EOD
${ROWS}
_EOD
```

何も考えず、コマンドを多用しているわけですが、
処理時間が一行あたり50msでした。
100万行処理するとおよそ13時間

やりたいことはできるんだけど、、、


# pythonとpandas
## 準備
python のインストールと

```bash
$ pip3 install pandas 
```
これぐらいですね。

## 作ったもの
```bash

import pandas as pd
import time

def getLatitude(st):
    st2=st.replace("(","").replace(")","").split(",")
    return float(st2[0])

def getLongitude(st):
    st2=st.replace("(","").replace(")","").split(",")
    return float(st2[1])

def run(df):
    res = df.assign( # location列からlatitude列を作成する関数実行
        latitude=csv_input.apply(lambda x: getLatitude(x['location']), axis=1)
    ).assign( # location列からlongitude列を作成する関数実行
        longitude=csv_input.apply(lambda x: getLongitude(x['location']), axis=1)
    ).drop( # 列削除
        columns=['location']
    )
    return res

if __name__ == "__main__":
    start = time.time()
    # ファイルをデータフレームとして読み込み
    df = pd.read_csv(filepath_or_buffer="./input.csv", encoding="utf8", sep=",")
    # データ加工
    res = run(df)
    # csvでアウトプット
    res.to_csv("output.csv")

    # かかった時間の計測＆出力
    elapsed_time = time.time() - start
    print ("elapsed_time:{0}".format(elapsed_time) + "[sec]")
```


いけてないだろう箇所はたくさんあると思いますが、
それでもなんと、5分くらいで処理が終わる

当者比160倍

なんて速いんだ


ググったところ
pandasよりnumpyを使った方が高速らしいが
ひとまずこれで、ShellScriptよりも安全な（bashのあのコードだと、カラムの中に区切り文字があると狂ってしまう）処理がかけたし
13時間が5分になったので、満足

# おわりに
これを機にデータ処理関連を学んでいこうと思いました。