PythonでApacheアクセスログをCSVファイルへ変換する方法

test.logというApacheのアクセスログをCSVファイルに変換する方法です。
ここを参考にしています。
http://www.atmarkit.co.jp/ait/articles/1307/11/news009_3.html

変換後のファイルはtest.csvにします。
test.logの内容

127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326

デフォルトのアクセスログですね。

import re
fr = open('test.log')
fw = open('test.csv', 'w')
line = fr.readline()
while line:
    fline = re.sub(r'^(\S+) (\S+) (\S+) \[([^\]]+)\] "([A-Z]+) ([^ "]+)? HTTP/[0-9.]+" ([0-9]{3}) ([0-9]+|-)', r'\1,\2,\3,\4,\5,\6,\7,\8', line)
    fw.write(fline)
    line = fr.readline()
fr.close()
fw.close()

ファイルのデータを一行ずつ読み込んで正規表現のsub関数を使ってログを
8つのパートに分割しcsv形式で書き込んでいます。

正規表現の部分がなかなか慣れていないので悩むところです。
ログの項目を増やした時のメモ。

■sample accesslog
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
↓↓↓
fline = re.sub(r'^(\S+) (\S+) (\S+) \[([^\]]+)\] "([A-Z]+) ([^ "]+)? HTTP/[0-9.]+" ([0-9]{3}) ([0-9]+|-)', r'\1,\2,\3,\4,\5,\6,\7,\8', line)

■refererとuser agentを追加したとき
222.31.3.3 - frank [10/Oct/2000:13:55:36 -0700] "GET /index.html HTTP/1.0" 200 2326 "http://jbclub.xii.jp/" "Mozilla/5.0 (Linux; U; Android 4.2.2; ja-jp; SO-04E Build/10.3.1.B.0.256) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
↓↓↓
fline = re.sub(r'^(\S+) (\S+) (\S+) \[([^\]]+)\] "([A-Z]+) ([^ "]+)? HTTP/[0-9.]+" ([0-9]{3}) ([0-9]+|-) "([^\"]*)" "([^\"]*)"', r'\1,\2,\3,\4,\5,\6,\7,\8,\9,\10', line)

refererとuser agentを追加したときは”([^\"]*)”を単純に2個追加しました。
これで一旦はできました。

以上

Pythonで回帰分析 (身長と体重の関係、PVとセッションの関係)

Pythonで回帰分析をちょっとやってみましたのでメモです。
↓このまんまです。
http://www.atmarkit.co.jp/ait/articles/1311/05/news003.html

import pandas as pd
import numpy as np
df = pd.read_csv('children_data2005_08_130819.csv', header=-1, skiprows=2, encoding='Shift_JIS')

#データが欠けている行は削除する。
df = df[np.isfinite(df[9])]
df = df[np.isfinite(df[10])]

#身長と体重のデータがある10列目と11列目のみを抽出する
height = df[9]
weight = df[10]

#プロットしてみる
plot(height, weight, 'bo')
Out[8]: []

#単回帰分析を用いてこのモデルの妥当性を検討する。
#pandasにはols関数が用意されていて
#ols関数は
# 目的変数 y
# 説明変数 x
# 直線が原点(0,0)を通るか切片を持つかどうかのパラメータ(intercept)
#を指定すると、最小二乗法(Ordinary Least Squares)を用いてモデルの計算を行う。
model = pd.ols(y=weight, x=height, intercept=True)
plot(model.x['x'], model.y_fitted, 'g-')
Out[17]: []

#modelの中身のポイントを確認してみる
model
Out[20]:
-------------------------Summary of Regression Analysis-------------------------

Formula: Y ~  + 

Number of Observations:         1602
Number of Degrees of Freedom:   2

R-squared:         0.8445
Adj R-squared:     0.8444

Rmse:              2.1957

F-stat (1, 1600):  8691.3653, p-value:     0.0000

Degrees of Freedom: model 1, resid 1600

-----------------------Summary of Estimated Coefficients------------------------
      Variable       Coef    Std Err     t-stat    p-value    CI 2.5%   CI 97.5%
--------------------------------------------------------------------------------
             x     0.0336     0.0004      93.23     0.0000     0.0329     0.0343
     intercept   -17.2343     0.3742     -46.06     0.0000   -17.9677   -16.5009
---------------------------------End of Summary---------------------------------

このモデルの数式

[体重]=[身長]×0.0336-17.2343

このモデルが統計的にどのくらい正しいのか検証する。

□決定係数(R-squared: 0.8445)
説明変数が目的変数のどれくらいを説明できるかを表す値で、0~1の間の値を取り、寄与率と呼ばれることもあります。
今回のモデルでは0.8445となっており、このモデルで84%以上説明できているということができます。

□F検定(F-stat (1, 1600): 8691.3653, p-value: 0.0000)
F検定(F-test)と呼ばれるモデル全体の妥当性を検討する際に使う値です。F値の有意確率(p-value)を判断基準として用います。
今回のモデルの場合は、F値(8691.3653)が十分に大きく、有意確率(0.0000)も十分に小さく0.01以下であるため99%以上の確率で妥当であると言えます。
有意確率の比較は、99%の場合には0.01ですが、95%の確率で検定する場合には、0.05と比較して妥当性を検証します。

□t検定
モデル全体の妥当性をF検定で判断した後は、それぞれのパラメータ(係数aと定数b)の妥当性を検証します。
係数a(x):t-stat=93.23、p-value=0.0000
定数b(intercept):t-stat=-46.06、p-value=0.0000
このモデルの場合、xの有意確率もinterceptの有意確率も0.01よりも十分に小さい値なので、両方の値は妥当であると判断できます。

■PVとセッション数の関係
せっかくなので上のやり方を真似してあるサイトのPVとセッションの関係を
回帰分析してみます。

import pandas as pd
import numpy as np
%pylab
df = pd.read_csv('data.csv', names=['PV','SESSION'], header=-1, encoding='Shift_JIS')
df = df.replace(',','', regex=True)
## 数字に「,」がはいっているので取り除いています。

pv = df['PV'].astype(float)
se = df['SESSION'].astype(float)

plot(pv, se, 'bo')
model = pd.ols(y=se, x=pv, intercept=True)
plot(model.x['x'], model.y_fitted, 'g-')

model
Out[32]: 

-------------------------Summary of Regression Analysis-------------------------

Formula: Y ~  + 

Number of Observations:         61
Number of Degrees of Freedom:   2

R-squared:         0.2639
Adj R-squared:     0.2515

Rmse:          16730.9688

F-stat (1, 59):    21.1571, p-value:     0.0000

Degrees of Freedom: model 1, resid 59

-----------------------Summary of Estimated Coefficients------------------------
      Variable       Coef    Std Err     t-stat    p-value    CI 2.5%   CI 97.5%
--------------------------------------------------------------------------------
             x     0.0319     0.0069       4.60     0.0000     0.0183     0.0455
     intercept 192177.6432 10286.0067      18.68     0.0000 172017.0700 212338.2163
---------------------------------End of Summary---------------------------------

このモデルの数式

[SESSION]=[PV]× 0.0319+192177.6432

このモデルが統計的にどのくらい正しいのか検証する。

□決定係数(R-squared: 0.2639)
今回のモデルでは0.2639となっており、このモデルで26%以上説明できているということでしょうか。

□F検定(F-stat (1, 59): 21.1571, p-value: 0.0000)
今回のモデルは、F値( 21.1571)が小さいですね。
なので、このモデルはだどうではなさそうですね。もっとサンプルがあれば変わってくるのでしょうか。

ということで、あんまりPVとセッション数の関係は妥当性のないモデルのようです。
まあでもいろいろと勉強になりました。

こいつはいろんな場面で使えそうです。
数字のコンマ(,)を取り除く方法

df = df.replace(',','', regex=True)

以上

Pythonでグラフを描く

東京マラソンの記録をPythonを使ってグラフにしてみました。
その内容をメモに残しておきます。

■やりたいこと
東京マラソンの5kmごとのラップタイムをグラフにしたい。

■環境
Windows7にAnacondaをインストールして使っています。

Anaconda:
Python本体と科学技術計算、可視化のライブラリを含んだディストリビューションパケージ
https://store.continuum.io/cshop/anaconda/

■表示する記録
東京マラソンの記録をTime.csvというCSVファイルにします。
こんな感じ。

KM,TIME 
5,0:36:54 
10,1:04:20 
15,1:32:02 
20,1:59:29 
25,2:26:48 
30,2:54:24 
35,3:24:01 
40,3:51:53 
42.195,4:03:56 

これを適当なディレクトリに置きます。
C:\Anaconda\work

■やり方
CSVファイルを取り込みます。

import pandas as pd 
tm = pd.read_csv("C:\Anaconda\work\Time.csv", skiprows=1, names=['km', 'time'], encoding='Shift_JIS') 

In [60]: tm 
Out[60]: 
     km       time 
0   5.000  0:36:54 
1  10.000  1:04:20 
2  15.000  1:32:02 
3  20.000  1:59:29 
4  25.000  2:26:48 
5  30.000  2:54:24 
6  35.000  3:24:01 
7  40.000  3:51:53 
8  42.195  4:03:56 

[9 rows x 2 columns] 

csvで取り込んだだけではtimeは数値ではないので経過時間の形に変換します。

tmd = pd.to_timedelta('0' + tm['time']) 

In [7]: tmd 
Out[7]: 
0   00:36:54 
1   01:04:20 
2   01:32:02 
3   01:59:29 
4   02:26:48 
5   02:54:24 
6   03:24:01 
7   03:51:53 
8   04:03:56 
Name: time, dtype: timedelta64[ns] 

これで経過時間(nanoseconds)になりました。
続いてデータフレームを作ります。

from pandas import DataFrame 

data =  {'KM':tm['km'], 'TIME':tmd } 
frame = DataFrame(data) 

In [12]:frame 
Out[12]: 
       KM     TIME 
0   5.000 00:36:54 
1  10.000 01:04:20 
2  15.000 01:32:02 
3  20.000 01:59:29 
4  25.000 02:26:48 
5  30.000 02:54:24 
6  35.000 03:24:01 
7  40.000 03:51:53 
8  42.195 04:03:56 

[9 rows x 2 columns] 

ここでpandasというライブラリを使っています。
pandasというのはPythonにRのデータフレームのような型を持たせるライブラリで
行列計算ができ、Rでやっていたような集計作業がPythonでも楽にできるようになります。
http://pandas.pydata.org/

つづいてラップタイムを算出します。

list = [frame.TIME[0]] 
n = 0 
while (n < 8): 
  list.append( frame.TIME[n + 1] - frame.TIME[n]) 
  n += 1 

In [16]: list 
Out[16]: 
[numpy.timedelta64(2214000000000,'ns'), 
 numpy.timedelta64(1646000000000,'ns'), 
 numpy.timedelta64(1662000000000,'ns'), 
 numpy.timedelta64(1647000000000,'ns'), 
 numpy.timedelta64(1639000000000,'ns'), 
 numpy.timedelta64(1656000000000,'ns'), 
 numpy.timedelta64(1777000000000,'ns'), 
 numpy.timedelta64(1672000000000,'ns'), 
 numpy.timedelta64(723000000000,'ns')] 

これでラップが計算できたので
frameにデータを挿入します。

 frame['RAP'] = list 

In [18]: frame 
Out[18]: 
       KM     TIME      RAP 
0   5.000 00:36:54 00:36:54 
1  10.000 01:04:20 00:27:26 
2  15.000 01:32:02 00:27:42 
3  20.000 01:59:29 00:27:27 
4  25.000 02:26:48 00:27:19 
5  30.000 02:54:24 00:27:36 
6  35.000 03:24:01 00:29:37 
7  40.000 03:51:53 00:27:52 
8  42.195 04:03:56 00:12:03 

これをグラフにします。

%pylab 
import matplotlib.pyplot as plt 
fig = plt.figure() 
ax = fig.add_subplot(111)
y =  frame.RAP 
x =  frame.KM 
ax.plot(x, y, 'wo') 
plt.xlabel('KM') 
plt.ylabel('TIME') 
plt.grid() 
plt.xlim(xmin = 0) 
plt.xticks(np.arange(0, 45, 5)) 
plt.ylim(ymin = 60 * 10 * 10**9) 
plt.ylim(ymax = 60 * 10 * 4 * 10**9) 
plt.yticks(np.arange(60 * 10 * 10**9, 60 * 10 * 4 * 10**9, 60 * 10 * 10**9 / 4)) 

グラフが一旦できましたが、これだとY軸がnanosecondになっているので
見やすいようにhh:mm:ssで表示するようにします。

from matplotlib.ticker import FuncFormatter as ff 
def m2hm(y, i): 
    h = int(y/1000000000/3600) 
    m = int(y/1000000000/60) 
    s = int(y/1000000000%60) 
    return '%(h)02d:%(m)02d:%(s)02d' % {'h':h,'m':m,'s':s} 
  
ax.yaxis.set_major_formatter(ff(m2hm)) 

できました。

PY_tkmara

もっとうまくできるのかも知れませんが
なんせ素人なのでこんなもんかと。

■その他メモ

線を青色にする。
ax.plot(x, y, color="blue") 

以上

return top