Python tips (随時更新)

いずれしらべる

  • タイムスタンプから月だけ取り出す
  • データフレームをスライシングし処理していくループのpythonicな書き方
  • .ixがなくなり.iloc, .locだになるので違いをまとめておく
  • リストに要素を追加
  • 論理ベクトル同士をクロス集計
  • Seriesを結合してデータフレームをつくる
  • _の意味 initや for _
  • pythonicとは: effective python

システム

  • AndroidKindleアプリで.mobiファイルを読む

    • 内部ストレージ\Android\data\com.amazon.kindle\files\にコピーすればOK
  • init"とself

    • init”: クラスのインスタンスを作成したときに初期化処理を行うメソッド
    • self: オブジェクトを参照する
    • str”:
  • アンダースコア(_) の意味: ユーザに見せない処理をする変数を表す。

  • アンダースコア二つで定義される関数は外部の参照を受けないもの。この場合、アンダースコアで囲う。
  • アンダースコア一つで定義される関数は参照はできるが、基本的に外部から参照しない ということを慣習化させたものらしい。
class person():
    def __init__(self, name):
        self.name = name

hunter = person('Elmer Fudd')
print(hunter.name)
import os
os.chdir(path)
  • StringIO

pandas

日付・時刻

  • 処理時間の計測
import time
start = time.time()
# 処理
duration = time.time() - start
print(duration)

  • .dt.days: pandas.timedeltaをintにする
    • Seriesのメソッドなのでリストでは使えない。
import pandas as pd
import datetime as dt

x = pd.to_datetime(pd.Series(['20170701', '20170702']))
y = pd.to_datetime(pd.Series(['20170710', '20170730']))

d = y - x

print(d.dt.days)

merge

# サンプルのデータフレーム生成 (<http://sinhrks.hatenablog.com/entry/2015/01/28/073327> より。感謝) 

import pandas as pd
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                    'C': ['C0', 'C1', 'C2', 'C3'],
                    'D': ['D0', 'D1', 'D2', 'D3']},
                   index=[0, 1, 2, 3])

df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                    'B': ['B4', 'B5', 'B6', 'B7'],
                    'C': ['C4', 'C5', 'C6', 'C7'],
                    'D': ['D4', 'D5', 'D6', 'D7']},
                   index=[0, 1, 2, 5])
  • indexでマージする
    • right_index=True, left_index=True とする
pd.merge(d1, d2, , right_index=True, left_index=True, how='left')

  A_x B_x C_x D_x  A_y  B_y  C_y  D_y
0  A0  B0  C0  D0   A4   B4   C4   D4
1  A1  B1  C1  D1   A5   B5   C5   D5
2  A2  B2  C2  D2   A6   B6   C6   D6
3  A3  B3  C3  D3  NaN  NaN  NaN  NaN

データフレーム

データフレームの複数の列を基準として、外れ値を除外する

import pandas as pd
import numpy as np
from scipy import stats

df = pd.DataFrame(np.random.randn(100, 3))
# 外れ値として100を挿入
df.loc[0, 0] = 100
print(df.head())

# 100以上の値がある業を全て除く
df[(df < 100).all(axis=1)]

# 全体を標準化して3SD以上を除く
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]
  • 各変数ごとに外れ値基準を計算して除外
import pandas as pd

df = pd.DataFrame(np.random.randn(10, 3))
df.head()

def elout(df, fv= 0.05):
    print(df.shape)
    r = pd.DataFrame()
    for c in df:
        v = df[[c]]
        is_in = ((v >= v.quantile(fv)) & (v < v.quantile(1-fv)))
        r = pd.concat([r, is_in], axis=1)
    df2 = df[np.array(r).all(axis=1)]
    print(df2.shape)
    return(df2)

elout(df)


forループでデータフレームを足していく

import pandas as pd
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                    'B': ['B0', 'B1', 'B2', 'B3'],
                    'C': ['C0', 'C1', 'C2', 'C3'],
                    'D': ['D0', 'D1', 'D2', 'D3']},
                   index=[0, 1, 2, 3])

x = pd.DataFrame()
for i in range(4):
    t = df1.loc[i, :]
    x = pd.concat([x, t], axis=1)

print(x)
... 
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3

重複

  • 重複行削除
d = pd.DataFrame({'a': [1, 2, 3, 1],
                  'b': [1, 3, 3, 1]})
d.drop_duplicates()

   a  b
0  1  1
1  2  3
2  3  3
3  1  1
   a  b
0  1  1
1  2  3
2  3  3

  • 一部の列名を変える: .renameでディクショナリを使う。 元データ変更の場合はinplace=True
    • 行名を変えるときはindex={}
d = pd.DataFrame({'a': [1, 2, 3, 1],
                  'b': [1, 3, 3, 1]})
d.rename(columns={'a': 'xx'})

   xx  b
0   1  1
1   2  3
2   3  3
3   1  1

  • 行合計や列合計で割る
import numpy as np
import pandas as pd


dat = pd.DataFrame({'a': np.random.randn(5),
                    'b': np.random.randn(5),
                    'c': np.random.randn(5),
                    })

rsum = dat.sum(axis=1)

>>> dat.div(rsum, axis=0)
          a          b          c
0  1.109889  -1.521357   1.411468
1 -0.221592   0.231812   0.989781
2 -0.045156   0.534391   0.510766
3 -0.127811   0.783268   0.344543
4  4.216794  27.485861 -30.702655

欠損値の扱い

  • .isnan(): 要素ごとにNaN (Not a Number) かどうかを返す。
import numpy as np
v = [1, 2, np.nan]
[np.isnan(x) for x in v]
... 
[False, False, True]
  • Series.isnull(): Series全体で調べる
import numpy as np
import pandas as pd
s = pd.Series([1, 2, np.nan])
s.isnull()
... 
0    False
1    False
2     True
dtype: bool
  • .values.any(): どこかに欠損値があるか
s.isnull().values.any()
...
True
  • 欠損値ならTrue、そうでないならFalseをかえす

スライシング

  • 一部の行や列だけ除く: .drop
d = pd.DataFrame({'a': [1, 2, 3, 1],
                  'b': [1, 3, 3, 1]})

# 行を除く
d.drop(0)

   a  b
1  2  3
2  3  3
3  1  1


# 列を除く
d.drop('a', axis=1)
   b
0  1
1  3
2  3
3  1

ピボットテーブル

  • 縦横で分けて集計
import pandas as pd
import numpy as np
df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 6,
                   'B' : ['A', 'B', 'C'] * 8,
                   'C' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 4,
                   'D' : np.random.randn(24),
                   'E' : np.random.randn(24)})

>>> pd.pivot_table(df, values='D', index=['B'], columns=['A', 'C'], aggfunc=np.sum)
... 
... 
A       one               three                 two          
C       bar       foo       bar       foo       bar       foo
B                                                            
A  1.989904 -0.544853 -1.382726       NaN       NaN  0.929173
B  0.370564 -1.321093       NaN -0.116037  0.125153       NaN
C -0.975050 -3.429380 -2.340092       NaN       NaN -0.735084
  • groupbyで集計したものをpivotで整形する
    • 集計したものはキー変数がインデックスになるが、インデックスを指定してはピボットできない。
    • reset_indexで変数にしてからピボットする。
df = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'],
                   'key2' : ['one', 'two', 'one', 'two', 'one'],
                   'data1' : np.random.randn(5),
                   'data2' : np.random.randn(5)})


gr = df.groupby([df['key1'], df['key2']])
grs = gr.sum()
x = grs.reset_index()
>>> x.pivot(index='key2', columns ='key1')
... 
... 
         data1               data2          
key1         a         b         a         b
key2                                        
one  -2.106043  0.190401  0.232389 -1.706491
two   0.470106  1.871696 -1.518614 -0.059299

統計

散布図と相関係数

  • 散布図
import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(1000)
y = np.random.randn(1000)
fig = plt.figure
ax = fig.add_subplot(1,1,1)

ax.scatter(x,y)

ax.set_title('first scatter plot')
ax.set_xlabel('x')
ax.set_ylabel('y')

fig.show()
  • 散布図と回帰直線
from scipy import stats
import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(1000)
y = np.random.randn(1000)

r = stats.linregress(x, y)
x_line = [x.min(), x.max()]
y_line = [r.slope * i + r.intercept for i in x_line]
plt.scatter(x, y, color='blue', label=x, s=50)
plt.plot(x_line, y_line, color='green', linestyle='-', lw=5)
  • 散布図行列
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

dat = pd.DataFrame({'a': np.random.randn(10), 
                    'b': np.random.randn(10), 
                    'c': np.random.randn(10), 
                    'd': np.random.randn(10), 
                    'e': np.random.randn(10)})

# 単純な相関行列
dat.corr()


# pandas
pd.scatter_matrix(dat)


# seaborn
sns.pairplot(dat)
  • 色を変えて複数の回帰直線を描く
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn
from matplotlib import cm
from scipy import stats

dat = pd.DataFrame({'y': np.random.randn(1000),
                    'x1': np.random.randn(1000),
                    'x2': np.random.randn(1000),
                    'x3': np.random.randn(1000)})

# xの列名
xlabels = ['x1', 'x2', 'x3']
colors = cm.rainbow(np.linspace(0, 1, len(xlabels)))


for x, c in zip(xlabels, colors):
    xv = dat[x]
    slope, intercept, r_value, _, _ = stats.linregress(xv, dat.y)
    x_line = [xv.min(), xv.max()]
    y_line = [slope * i + intercept for i in x_line]
    plt.scatter(xv, dat.y, color=c, label=x, s=50)
    plt.plot(x_line, y_line, color=c, linestyle='-', lw=5)

plt.legend(prop={'size':14})
plt.xlim(-4, 3)
plt.ylim(-4, 3)

重回帰分析

import pandas as pd
from statsmodels.formula.api import ols

x1 = pd.Series([12, 12, 7, 17, 14, 9, 10, 13, 15, 12, 12, 15, 11, 14, 17, 17,
                16, 15, 15, 10, 12, 9, 12, 12, 19, 11, 14, 15, 15, 15, 16, 15,
                12, 10, 11, 12, 15, 13, 15, 12, 12, 12, 13, 17, 13, 11, 14,
                16, 12, 12]) # 母親価値
x2 = pd.Series([2, 2, 2, 3, 2, 2, 3, 3, 3, 1, 3, 3, 2, 2, 4, 2, 4, 3, 4, 2, 2,
                1, 2, 2, 4, 2, 3, 2, 3, 3, 2, 3, 2, 2, 3, 1, 2, 3, 2, 2, 2, 3
                , 3, 3, 2, 3, 2, 4, 2, 2]) # 通園年数
y = pd.Series([6, 11, 11, 13, 13, 10, 10, 15, 11, 11, 16, 14, 10, 13, 12, 15,
               16, 14, 14, 8, 13, 12, 12, 11, 16, 9, 12, 13, 13, 14, 12, 15,
               8, 12, 11, 6, 12, 15, 9, 13, 9, 11, 14, 12, 13, 9, 11, 14, 16,
               8]) # 協調性
dat = pd.concat([x1, x2, y], axis=1)

model = ols("y ~ x1 + x2", data=dat).fit()
print(model.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.314
Model:                            OLS   Adj. R-squared:                  0.284
Method:                 Least Squares   F-statistic:                     10.74
Date:                Thu, 20 Jul 2017   Prob (F-statistic):           0.000144
Time:                        11:36:54   Log-Likelihood:                -106.98
No. Observations:                  50   AIC:                             220.0
Df Residuals:                      47   BIC:                             225.7
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept      5.1716      1.665      3.107      0.003       1.823       8.520
x1             0.3027      0.147      2.064      0.045       0.008       0.598
x2             1.1259      0.471      2.389      0.021       0.178       2.074
==============================================================================
Omnibus:                        0.276   Durbin-Watson:                   2.411
Prob(Omnibus):                  0.871   Jarque-Bera (JB):                0.348
Skew:                          -0.164   Prob(JB):                        0.840
Kurtosis:                       2.756   Cond. No.                         76.2
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
  • scikit-learnを使って
from sklearn import linear_model
clf = linear_model.LinearRegression()
 
# 説明変数に "quality (品質スコア以外すべて)" を利用
X = pd.concat([x1, x2], axis=1).as_matrix()
 
# 目的変数に "quality (品質スコア)" を利用
Y = y.as_matrix()
 
# 予測モデルを作成
clf.fit(X, Y)
 
# 偏回帰係数
print(pd.DataFrame({"Name":['x1', 'x2'],
                    "Coefficients":clf.coef_}).sort_values(by='Coefficients') )
 
# 切片 (誤差)
print(clf.intercept_)

#

## 参考
http://pythondatascience.plavox.info/scikit-learn/%E7%B7%9A%E5%BD%A2%E5%9B%9E%E5%B8%B0
http://pythondatascience.plavox.info/pandas/%E8%A1%8C%E3%83%BB%E5%88%97%E3%82%92%E5%89%8A%E9%99%A4
https://nkmk.github.io/blog/python-pandas-dataframe-rename/
http://nekoyukimmm.hatenablog.com/entry/2015/04/10/094432
http://sinhrks.hatenablog.com/entry/2014/10/13/005327
https://chartio.com/resources/tutorials/how-to-check-if-any-value-is-nan-in-a-pandas-dataframe/