Pandasの使い方

Pandasは内部でNumPyを利用しており、二次元配列を「テーブル」として扱えるように機能を追加しています。ここでは、DataFrameの扱い方を中心にPandasの基本的な使い方を確認します。

インストール

pipenv を利用している場合、以下のようにインストールします。

$ pipenv install pandas

プログラムから利用するにはimportが必要です。
慣習的にpandasをimportするときは pd と別名をつけます。

import pandas as pd
import numpy as np

df = pd.DataFrame(
    np.random.randn(6, 4),
    index=list('ABCDEF'),
    columns=['col1', 'col2', 'col3', 'col4']
)

print(df)
       col1      col2      col3      col4
A -1.801003  0.567649 -1.500904 -0.239381
B -0.719186 -0.966656  1.726989  0.906241
C  0.242600  2.890311 -0.321324 -0.457311
D -0.999545  0.845195 -0.830634  0.332034
E  0.430159  1.964199  0.162262  0.596390
F  0.145257 -0.418800  0.653118  1.253135

Series|一次元配列

Seriesは、一次元配列 のデータ構造を管理します。

オブジェクト生成方法

class pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)

以下、生成例です。

>>> pd.Series([54, 21, 94, 74])
0    54
1    21
2    94
3    74
dtype: int64

NumPyの ndarray とは異なり、indexを数値以外にすることもできます。

>>> pd.Series([54, 21, 94, 74], index=['A', 'B', 'C', 'D'])
A    54
B    21
C    94
D    74
dtype: int64

dictionaryを渡す形式でも利用できます。

>>> dict = {'A': 54, 'B': 21, 'C': 94, 'D': 74}
>>> pd.Series(dict)
A    54
B    21
C    94
D    74
dtype: int64

Pandasの date_rangeメソッド を利用すると、連続した日付データを生成できます。
生成した日付データからSeriesオブジェクトを生成することもできます。

>>> date = pd.date_range('20180101', periods=4)
>>> date
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04'], dtype='datetime64[ns]', freq='D')
>>> 
>>> pd.Series(date)
0   2018-01-01
1   2018-01-02
2   2018-01-03
3   2018-01-04
dtype: datetime64[ns]

参考

DataFrame|二次元配列、テーブル

DataFrameは、 二次元配列 のデータ構造を管理します。単純な二次元配列のデータではなく、column(列) index(行) をラベル付けできるテーブル形式のデータ構造になります。

オブジェクト生成方法

class pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)

以下、生成例です。

>>> pd.DataFrame([[180, 68, 1, '19801122'], [164, 54, 2, '19900411']])
     0   1  2         3
0  180  68  1  19801122
1  164  54  2  19900411

DataFrameは、複数のSeriesオブジェクトにラベル付けして、まとめて管理することができるデータ構造と考えることができます。身長、体重といったSeries単位でデータを持っているのであれば、以下のように生成できます。

>>> height = pd.Series([180, 164], index=['yamada', 'suzuki'])
>>> weight = pd.Series([68, 54], index=['yamada', 'suzuki'])
>>> sex = pd.Series([1, 2], index=['yamada', 'suzuki'])
>>> birthday = pd.Series(['19801122', '19900411'], index=['yamada', 'suzuki'])
>>> 
>>> pd.DataFrame({
...     'height': height,
...     'weight': weight,
...     'sex': sex,
...     'birthday': birthday
... })
        height  weight  sex  birthday
yamada     180      68    1  19801122
suzuki     164      54    2  19900411

人物単位でデータを持っているのであれば、以下のように生成できます。

>>> yamada = [180, 68, 1, '19801122']
>>> suzuki = [164, 54, 2, '19900411']
>>> 
>>> pd.DataFrame(
...     [yamada, suzuki],
...     index=['yamada', 'suzuki'],
...     columns=['height', 'weight', 'sex', 'birthday'],
... )
        height  weight  sex  birthday
yamada     180      68    1  19801122
suzuki     164      54    2  19900411

他にも、read_csv read_clipboard pandas.io.sql.read_sqlなどを利用してDataFrameオブジェクトを生成することができます。

  • read_csv
    • csvを読み込みDataFrameオブジェクトを生成
  • read_clipboard
    • クリップボードに保存されている内容を読み込みDataFrameオブジェクトを生成
  • pandas.io.sql.read_sql
    • SELECT文の実行結果からDataFrameオブジェクトを生成

主な属性

ndim(次元数) shape(各次元の配列サイズ) size(要素数) などNumPyの ndarray と同様に利用できます。
Pandasでは加えて、values columns index なども利用できます。

>>> df = pd.DataFrame(
...     np.random.randn(6, 4),
...     index=list('ABCDEF'),
...     columns=['col1', 'col2', 'col3', 'col4']
... )
>>> 
>>> df.ndim
2
>>> 
>>> df.shape
(6, 4)
>>> 
>>> df.size
24
>>> 
>>> df.dtypes
col1    float64
col2    float64
col3    float64
col4    float64
dtype: object
>>> 
>>> df.values
array([[ 0.86228608, -1.52049619, -0.86636716,  0.14673805],
       [ 0.87420066, -0.1262851 , -0.45114235, -0.94013396],
       [ 0.39502002,  0.21415237,  0.02878306,  1.11859551],
       [-0.57662159,  1.16036366,  1.36461432, -0.53426026],
       [-2.1305967 , -0.03367391, -1.88627092,  0.65179348],
       [-0.74469153, -0.72749726,  0.08043063,  1.95564312]])
>>> 
>>> df.columns
Index(['col1', 'col2', 'col3', 'col4'], dtype='object')
>>> 
>>> df.index
Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')

valuesの型はNumPyの ndarray になります。

>>> df.values.__class__.__name__
'ndarray'

転置行列(行と列を入れ替えた行列)も簡単に生成できます。

>>> df
       col1      col2      col3      col4
A  0.862286 -1.520496 -0.866367  0.146738
B  0.874201 -0.126285 -0.451142 -0.940134
C  0.395020  0.214152  0.028783  1.118596
D -0.576622  1.160364  1.364614 -0.534260
E -2.130597 -0.033674 -1.886271  0.651793
F -0.744692 -0.727497  0.080431  1.955643
>>> 
>>> df.T
             A         B         C         D         E         F
col1  0.862286  0.874201  0.395020 -0.576622 -2.130597 -0.744692
col2 -1.520496 -0.126285  0.214152  1.160364 -0.033674 -0.727497
col3 -0.866367 -0.451142  0.028783  1.364614 -1.886271  0.080431
col4  0.146738 -0.940134  1.118596 -0.534260  0.651793  1.955643

loc を利用すると、index、columnの を指定してデータを取得できます。
iloc を利用すると、index、columnの 番号 を指定してデータを取得できます。

>>> df
           height  weight  sex  birthday
yamada        180      68    1  19801122
suzuki        172      54    2  19900411
tanaka        170      66    1  19850904
takahashi     149      40    2  19900222
>>> 
>>> df.loc['yamada']
height           180
weight            68
sex                1
birthday    19801122
Name: yamada, dtype: object
>>> 
>>> df.loc['yamada':'suzuki', 'height':'weight']
        height  weight
yamada     180      68
suzuki     172      54
>>> 
>>> df.loc['yamada':'suzuki', ['height', 'sex']]
        height  sex
yamada     180    1
suzuki     172    2
>>> 
>>> df.iloc[0]
height           180
weight            68
sex                1
birthday    19801122
Name: yamada, dtype: object
>>> 
>>> df.iloc[0:2, 0:2]
        height  weight
yamada     180      68
suzuki     172      54

メソッドで情報確認( info, describe )

infoメソッド

データ構造を確認できます。

>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 6 entries, A to F
Data columns (total 4 columns):
col1    6 non-null float64
col2    6 non-null float64
col3    6 non-null float64
col4    6 non-null float64
dtypes: float64(4)
memory usage: 400.0+ bytes

上記のように、「nullではないデータの数」「変数の型」を確認できます。

describeメソッド

count(総数) mean(平均) std(標準偏差) などの統計量をまとめて確認できます。

>>> df.describe()
           col1      col2      col3      col4
count  6.000000  6.000000  6.000000  6.000000
mean  -0.220067 -0.172239 -0.288325  0.399729
std    1.166167  0.902502  1.084695  1.070771
min   -2.130597 -1.520496 -1.886271 -0.940134
25%   -0.702674 -0.577194 -0.762561 -0.364011
50%   -0.090801 -0.079980 -0.211180  0.399266
75%    0.745470  0.152196  0.067519  1.001895
max    0.874201  1.160364  1.364614  1.955643

参考

DataFrame操作(SQLと比較)

http://pandas.pydata.org/pandas-docs/stable/comparison_with_sql.html でも解説されていますが、DataFrameはテーブル形式のデータ構造なので、SQLのような感覚でデータを扱うことができます。

SELECT

SELECT height, sex
FROM df
LIMIT 3;
>>> df = pd.DataFrame(
...     [[180, 68, 1, '19801122'], [172, 54, 2, '19900411'], [170, 66, 1, '19850904'], [149, 40, 2, '19900222']],
...     index=['yamada', 'suzuki', 'tanaka', 'takahashi'],
...     columns=['height', 'weight', 'sex', 'birthday'],
... )
>>> 
>>> df
           height  weight  sex  birthday
yamada        180      68    1  19801122
suzuki        172      54    2  19900411
tanaka        170      66    1  19850904
takahashi     149      40    2  19900222
>>> 
>>> df[['height', 'sex']].head(3)
        height  sex
yamada     180    1
suzuki     172    2
tanaka     170    1

列の追加

例として「年齢」を追加してみます。

>>> df
           height  weight  sex  birthday
yamada        180      68    1  19801122
suzuki        172      54    2  19900411
tanaka        170      66    1  19850904
takahashi     149      40    2  19900222
>>> 
>>> 
>>> def getAge(birthday):
...     today    = int(pd.to_datetime('today').strftime('%Y%m%d'))
...     return int((today - int(birthday)) / 10000)
... 
>>> 
>>> df['age'] = df['birthday'].apply(lambda date: getAge(date))
>>> 
>>> df
           height  weight  sex  birthday  age
yamada        180      68    1  19801122   38
suzuki        172      54    2  19900411   28
tanaka        170      66    1  19850904   33
takahashi     149      40    2  19900222   28

applyメソッド で日付から年齢を算出する関数を適用しています。

WHERE

条件指定

SELECT *
FROM df
WHERE height > 170 AND sex = 1;
>>> df
           height  weight  sex  birthday
yamada        180      68    1  19801122
suzuki        172      54    2  19900411
tanaka        170      66    1  19850904
takahashi     149      40    2  19900222
>>> 
>>> df[(df['height'] > 170) & (df['sex'] == 1)]
        height  weight  sex  birthday
yamada     180      68    1  19801122

IS NOT NULL の判定

SELECT *
FROM df
WHERE height IS NOT NULL;

notnaメソッド を利用します。

>>> df.loc[df['height'] == 172, 'height'] = np.nan
>>> 
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    2  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
>>> 
>>> df[df['height'].notna()]
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222

ORDER BY

SELECT *
FROM df
ORDER BY height DESC;

並び替えを変更するには、sort_valuesメソッド を利用します。

デフォルトは ascending(昇順) です。降順にするには、ascendingFalse にします。

>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    2  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
>>> 
>>> df.sort_values(by='height', ascending=False)
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
suzuki        NaN      54    2  19900411

GROUP BY

例1

SELECT sex, COUNT(*)
FROM df
GROUP BY sex;
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    2  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
>>>
>>> df.groupby('sex').size()
sex
1    2
2    2
dtype: int64

例2

SELECT sex, AVG(height), AVG(weight)
FROM tips
GROUP BY sex;
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    2  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
>>> 
>>> df.groupby('sex').agg({'height': np.mean, 'weight': np.mean})
     height  weight
sex                
1     175.0      67
2     149.0      47

JOIN

下記データを結合してみます。

>>> df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
>>> df2 = pd.DataFrame({'key': ['B', 'B', 'D', 'E'], 'value': np.random.randn(4)})
>>>
>>> df1
  key     value
0   A -0.533587
1   B -1.784564
2   C  0.712881
3   D -0.665603
>>>
>>> df2
  key     value
0   B -2.200250
1   B -0.380191
2   D -0.086180
3   E  2.167892

INNER JOIN

SELECT *
FROM df1
INNER JOIN df2
ON df1.key = df2.key;
>>> pd.merge(df1, df2, on='key')
  key   value_x   value_y
0   B -2.140503 -1.273683
1   B -2.140503  0.397336
2   D -0.490544 -0.042907

LEFT OUTER JOIN

SELECT *
FROM df1
LEFT JOIN df2
ON df1.key = df2.key;
>>> pd.merge(df1, df2, on='key', how='left')
  key   value_x   value_y
0   A  0.034928       NaN
1   B -2.140503 -1.273683
2   B -2.140503  0.397336
3   C -1.678950       NaN
4   D -0.490544 -0.042907

UPDATE

UPDATE df
SET sex = 0
WHERE sex = 2;
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    2  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    2  19900222
>>> 
>>> df.loc[df['sex'] == 2, 'sex'] = 0
>>> 
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    0  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    0  19900222

DELETE

DELETE FROM df
WHERE sex = 1;
>>> df
           height  weight  sex  birthday
yamada      180.0      68    1  19801122
suzuki        NaN      54    0  19900411
tanaka      170.0      66    1  19850904
takahashi   149.0      40    0  19900222
>>> 
>>> df = df.loc[df['sex'] == 1]
>>> 
>>> df
        height  weight  sex  birthday
yamada   180.0      68    1  19801122
tanaka   170.0      66    1  19850904

参考