馬鹿の一つ覚え: pandas

#### 最初に
```python
import pandas
f = [None] * 8
f[0] = pandas.DataFrame(((0, 1), (2, 3)))
```
ここではデータフレームを複数格納するためのリストfを用意します。

#### テキストファイルを読み込み、DataFrame構造を確認する
```python
f[0] = pandas.read_csv('data_1.tsv', sep = "\t", index_col = 0)
    # 1行目にheader、1列目にindexがあるタブ区切りファイル
print(f[0])    # DataFrame全体を表示
print(type(f[0]))    # pandas.core.frame.DataFrame
print(f[0].columns)    # 1行目に書かれていたheader(列名)を取得
print(f[0].index)    # 1列目に書かれていたindex(行名)を取得
print(f[0].info())    # 各列のデータ型を確認
print(len(f[0]))    # 行数を取得 (headerは除く)
print(len(f[0].columns))    # 列数を取得 (indexは除く)
print(f[0].shape)    # 行数と列数を取得 (headerやindexは除く)
print(f[0].head(4))    # 先頭4行を表示
print(f[0].tail(4))    # 末尾4行を表示
print(type(f[0].values))    # numpy.ndarray
print(f[0].values)    # DataFrameは属性valuesにNumPy配列のobject型として保存されている
f[0] = pandas.read_csv('data_2.tsv', sep = "\t", header = None, index_col = None)
    # headerやindexがないファイル
f[0] = pandas.read_csv('data_3.tsv', sep = "\t", index_col = 0, \
                               header = None, names = ('S1', 'S2', 'S3', 'S4'))
    # headerを付けて読み込む
f[0] = pandas.read_csv('data_4.csv', skiprows = 5, encoding = 'Shift_JIS')
    # 先頭5行を読み飛ばす, Shift_JISのファイル
```
読み込まれるデータは、適当に判断されてint, float, strといった型として格納される。慎重に処理したい場合は dtype = object で読み込み、後で astype('float') などと変換すればよい。

#### サイズを指定し値が初期化されていないデータフレームを作成する
```python
import numpy
f[0] = pandas.DataFrame(numpy.empty((7, 5)))
```
7行5列のデータフレームを作成。

#### 1つのリストからデータフレームを作成する
```python
f[0] = pandas.DataFrame([3, 8, 5, 4])    # 1行4列ではなく、4行1列DataFrame
f[0] = pandas.DataFrame([3, 8, 5, 4], [5, 2, 1, 9])    # 2つめリストは4行のindexに
f[0] = pandas.DataFrame([[3, 8, 5, 4], [5, 2, 1, 9]])    # 2行4列DataFrame
```

#### 新規にDataFrameを作成し、headerとindexを付ける
```python
f[0] = pandas.DataFrame([[0.000, 0.000, 0.000, 0.000,  0.000],
    　                   [1.000, 1.000, 1.000, 1.000,  1.000],
      　                 [1.260, 1.414, 2.000, 4.000,  8.000],
        　               [1.442, 1.732, 3.000, 9.000, 27.000]])
f[0].columns = ['A',  'B',  'C',  'D',  'E']    # headerを付ける、あるいは全体の変更
f[0].index   = ['I1', 'I2', 'I3', 'I4']    # indexをつける、あるいは全体の変更
    #         A      B    C    D     E
    # I1  0.000  0.000  0.0  0.0   0.0
    # I2  1.000  1.000  1.0  1.0   1.0
    # I3  1.260  1.414  2.0  4.0   8.0
    # I4  1.442  1.732  3.0  9.0  27.0
f[0].rename(columns = {'A': 'a', 'B': 'b'}, index = {'I1': 'i1'}, inplace = True)
    # headerおよびindexの一部を変更; 自身を変更する場合は inplace = True
f[0] = d0.rename(columns = {'a': 'aa', 'b': 'bb'}, \
             index = {'i1': 'ii1', 'I4': 'ii4'}, inplace = False)
    # inplace = False の場合は別なDataFrameに
```

#### Excelファイルを読み込みDataFrameにする
```python
book = pandas.ExcelFile('data.xlsx')    # 既存ファイルを読み込む
print(type(book))    # pandas.io.excel._base.ExcelFile
print(type(book.sheet_names))    # シート名のlist
for ws in book.sheet_names:
  f[0] = book.parse(sheet_name = ws, header = None)    # f[0]に上書き
  print(ws, f[0], sep = '\n')    # シート名とDataFrameの内容を表示
```
OpenPyXlをインストールする必要がある (xlrd, xlwt, xlsxwriter等は不要)

#### DataFrameをExcelファイルに書き出す
```python
with pandas.ExcelWriter('data.xlsx') as writer:
  data.to_excel(writer, sheet_name = 'data sheet 1')
```

#### いくつかのオプションを指定をしてExcelファイルを読み込む
```python
foo = excel.parse(excel.sheet_names[0], skiprows = 10, header = 0,  parse_cols = 'C:G, J:K', index_col = 0)
# 最初の10行を読み飛ばし、次の行がheaderで、C列からG列とJ列とK列だけを読み込み、最初のC列をindex
```

#### DataFrameをpickleとして書き出し、読み込む
```python
import pickle
with open('data.pickle', 'wb') as pickle_file:
  pickle.dump(d0, pickle_file)    # pickleファイルとして出力
with open('data.pickle', 'rb') as pickle_file:    # 既存pickleから読み込み
  d1 = pickle.load(pickle_file)
```

#### DataFrameをタブ区切りテキストファイルとして書き出す
```python
f[0].to_csv('data.tsv', sep = '\t', index = True, header = True)    # 新規ファイルに出力
```

#### Excel等で出力されたShift_JISで書かれたカンマ区切りテキストを読み込む
```python
import codecs    # UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte # こんなエラーが出た場合
with codecs.open('data.csv', 'r', 'Shift_JIS', 'ignore') as shift_jis_csv:
  f[0] = pandas.read_csv(shift_jis_csv, skiprows = 6)    # 先頭6行を無視
  print(f[0].iloc[0:8, 0:6])    # 8行6列までを確認
```

#### 列数が一定でないテキストを読み込む
```python
f[0] = pandas.read_csv('data.csv', names = range(8))    # namesでヘッダを付ける
# ParserError: Error tokenizing data. C error: Expected 1 fields in line 7, saw 10 # こんなエラーを回避
```

#### 特定の列に列名でアクセスする
```python
print(f[0]['Col3'])    # Series
print(f[0].Col3)    # Series
print(f[0][['Col5', 'Col3', 'Col1']])
```

#### 特定の連続行にスライスでアクセスする
```python
print(data[0:1])    # 0行目の1行だけのDataFrame
print(data[3:7])    # 3, 4, 5, 6行目のDataFrame
```

#### DataFrameの要素にアクセスする
```python
(n_rows, n_columns) = data.shape	# 4096 (len(data) と同じ値), 4096 (len(data.columns) と同じ値)
print(data.iloc[13])    # 0から数えて13行目の1行全体にアクセス
type(data.iat[128, 256])    # numpy.float64; 0から数えて128行256列の要素にアクセス
print(sum(data.iloc[:, 256]))    # 256列目全体にアクセスし和を計算
import numpy
print(numpy.std(data.iloc[128, :]))    # 128行目全体にアクセスし標準偏差を計算
print(data.iloc[10:20, 40:60])    # 0から数えて、10から19行、40から59列までの部分を表示
print(data.iloc[[10, 20, 30, 40, 50], [150, 160, 170]])    # 指定した行や列のみを表示; headerやindexも表示される
print(data.loc[['foo', 'bar'], data.columns[8]])    # columnsを用いてラベルを取得しlocを利用
print(data.loc[data.index[16:32], 40:50])    # indexを用いてラベルを取得しlocを利用
```
| 要素の指定 | 単数要素(高速) | 複数要素(高負荷) |
|---|---|---|
| ラベルで指定 | at[ , ] | loc[ , ] |
| 番号で指定 | iat[ , ] | iloc[ , ] |

#### 行ラベルや列ラベルから行や列の番号を取得する
```python
print(d.iat[d.index.get_loc('sample_23'), d.columns.get_loc('feature_19')])
```

#### 列を追加および削除する
```python
d0['feature13'] = None    # 列名を付けて1列追加
d0['feature14'] = [i for i in range(5)]    # リストを用意してもう1列追加
d1 =d0.drop(columns = ['feature13', 'feature14'])    # タプルではなくリストで, inplace = True でなければ非破壊
del(d0['feature13'], d0['feature14'])    # 先に追加した2列を削除
```

#### 行を追加および削除する
```python
d0.columns = ['ColumnA',  'ColumnB',  'ColumnC',  'ColumnD',  'ColumnE']
d0.index = ['Index01', 'Index02', 'Index03', 'Index04', 'Index05']
d1.columns = ['ColumnA',  'ColumnB',  'ColumnC',  'ColumnD',  'ColumnE']    # 列名を揃えてからappend()
d0 = d0.append(d1)
d0.drop(index = ['Index01', 'Index03', 'Index04', 0, 2])    # index名で指定; 非破壊なのでd0はの中身は変化なし
d1 = d0.drop(index = ['Index01', 'Index03', 'Index04', 0, 2])    # 5行削除されたデータフレームd1
d0.drop(d0.index[[0, 1, 3]], inplace = True)    # 行番号で削除したい場合はindex[[]]を利用
```

#### 転置する
```python
datat = data.T    # headerとindexは入れ代わる
```

#### pandas.concat()でDataFrameを連結する
```python
print(f[0])
#     y2  y3  y4  y5
# x2   4   6   8  10
# x3   6   9  12  15
# x4   8  12  16  20
# x5  10  15  20  25
print(f[1])
#     y5  y6  y7  y8
# x4  20  24  28  32
# x5  25  30  35  40
# x6  30  36  42  48
# x7  35  42  49  56
print(pandas.concat(f[:2]), axis = 'index')    # headerに基づいて縦方向に連結 (indexの重複あり)
#       y2    y3    y4  y5    y6    y7    y8
# x2   4.0   6.0   8.0  10   NaN   NaN   NaN
# x3   6.0   9.0  12.0  15   NaN   NaN   NaN
# x4   8.0  12.0  16.0  20   NaN   NaN   NaN
# x5  10.0  15.0  20.0  25   NaN   NaN   NaN
# x4   NaN   NaN   NaN  20  24.0  28.0  32.0
# x5   NaN   NaN   NaN  25  30.0  35.0  40.0
# x6   NaN   NaN   NaN  30  36.0  42.0  48.0
# x7   NaN   NaN   NaN  35  42.0  49.0  56.0
print(pandas.concat(f[:2], axis = 'columns'))    # indexに基づいて横方向に連結 (headerの重複あり)
#       y2    y3    y4    y5    y5    y6    y7    y8
# x2   4.0   6.0   8.0  10.0   NaN   NaN   NaN   NaN
# x3   6.0   9.0  12.0  15.0   NaN   NaN   NaN   NaN
# x4   8.0  12.0  16.0  20.0  20.0  24.0  28.0  32.0
# x5  10.0  15.0  20.0  25.0  25.0  30.0  35.0  40.0
# x6   NaN   NaN   NaN   NaN  30.0  36.0  42.0  48.0
# x7   NaN   NaN   NaN   NaN  35.0  42.0  49.0  56.0
data = pandas.concat(f[2:5], keys = ('dataframe2', 'dataframe3', 'dataframe4'), axis = 'index')
data = pandas.concat(f[5:8], keys = ('dataframe5', 'dataframe6', 'dataframe7'), axis = 'columns')

```
keysを用いると、元のデータフレームを区別できます。

#### 数値のみからなるDataFrameをNumPy配列に、またNumPy配列をDataFrameに変換する
```python
data0 = pandas.read_csv('foo.tsv', sep = "\t", index_col = 0)    # headerとindex以外は浮動小数点数
import numpy
data = numpy.array(data0.iloc[0:32, 0:16])    # DataFrameの一部を取り出し、NumPy配列へ
numpy.set_printoptions(linewidth = 160, threshold = numpy.inf)    # 表示幅を160カラムに広げ、省略せずに全て表示
numpy.set_printoptions(precision = 4, suppress = True)    # 小数点以下4桁にまるめ、指数表示せずに
numpy.set_printoptions(formatter = {'float': '{: 0.4f}'.format})    # 小数点以下4桁まで表示
print(type(data))    # numpy.ndarray
print(data.shape)    # (32, 16)
print(data)
data1 = pandas.DataFrame(data)    # pandasのDataFrameに戻す
print(type(data1))    # pandas.core.frame.DataFrame
```

#### print()で表示する際に、折り返されないように、省略されないようにする
```python
pandas.set_option('display.width', 512)    # 表示幅を広くする
pandas.set_option('display.max_columns', 8)    # 8カラムまで表示
pandas.set_option('display.max_rows', 256)    # 265行まで表示
```

#### 重複する行を削除する
```python
nonredundant = data_frame_1.drop_duplicates()    # uniqとは異なり離れている重複行も削除
```

#### 欠損値か否かを判定する
```python
print(f[0].isna())    # 欠損値は True となる
print(f[0].isnull())    # 同上
print(f[0].dropna()    # 欠損値のある行を削除
```
None あるいは numpy.nan などを代入することで欠損値にすることができる。

#### 平均値など列ごとの統計データを取得する
```python
print(d.count())    # Noneなどでない値の数
print(d.mean())    # 各列の平均 (列数に相当する数の平均値が出力される)
print(d.mean(axis = 1)    # 各行の平均 (行数に相当する数の平均値が出力される)
print(d.mean(axis = 'columns')    # 各行の平均 (行数に相当する数の平均値が出力される)
print(d.var())
print(d.std())
print(d.min())
print(d.max())
print(d.describe())    # 各種データを出力
print(d.corr())    # 相関係数
print(d['temperature'].mean())    # 特定の列の平均値
```

#### 散布図行列を描く
```python
pandas.plotting.scatter_matrix(data_frame.iloc[:, 1:5], figsize = (16, 16), c = list(data_frame.iloc[:, 0]), alpha = 1.0)
# 1列から4列目までのデータで散布図, figsizeは単なる図の大きさ, cで0列目のデータで色分けを指定, alphaは透明度で1から0まで小さくすると薄くなる
```

#### 各列の要素に対して関数を適用する
```python
normalized = data_frame.iloc[:, 1:].apply(lambda x: (x - x.mean()) / x.std(), axis = 0)
# 1列目以降の全ての列に対して、その列の平均で引いた後に標準偏差で割る
```

#### Seriesを作り複数のSeriesを列として結合する
```python
s1 = pandas.Series(('foo', 'bar', 'baz'))    # リストをSeriesに
s1 = s1.append(pandas.Series(('qux',)))    # Seriesにして追加
s2 = pandas.Series((1792, 1804, 1806))
s2 = s2.append(pandas.Series((1811,)))
d = pandas.concat((s1, s2), axis = 1)    # 列として結合しデータフレームに
print(d)
#      0     1
# 0  foo  1792
# 1  bar  1804
# 2  baz  1806
# 0  qux  1811
```

#### 列を指定してソートする
```python
d = pandas.DataFrame(numpy.random.randint(20, 90, (8, 5), dtype = int))
d.index = ['r' + str(i) for i in range(8)]
d.columns = ['c' + str(i) for i in range(5)]
d.sort_values(by = 'c3', ascending = True, inplace = True)
print(d)
#     c0  c1  c2  c3  c4
# r2  46  29  62  20  70
# r1  70  65  24  24  38
# r0  33  60  55  42  79
# r5  68  71  73  67  86
# r3  41  68  83  68  49
# r6  37  52  30  73  82
# r4  61  75  36  82  54
# r7  28  55  27  85  61
```

#### 列内の重複数を確認する
```
print(d['Name'].value_counts())    # Name列内の重複
```

#### ウェブサイトの表を取得する
```
tables = pandas.read_html(url)    # URLの文字列を引数で与えるとDataFrameのリストが得られる
```

#### グラフを描く
```python
d.plot()    # DrataFrameあるいはSeriesのメソッドplot()で数値列のみの折れ線グラフ
d.plot.bar()    # 棒グラフ
d.plot.box()    # 箱髭図
d.plot.hist()    # ヒストグラム
d.iloc[:, 3].plot.pie()    # 円グラフ
```

#### 条件を指定して各要素に値を代入する
```python
f[0][((f[0].values % 3 == 1) & (f[0] > 50)) | (~ (f[0] > 10))] = numpy.nan
```
論理積、論理和、否定はそれぞれ &, |, ~ で表現する。
valuesは使わなくても動作する。
上の代入文は、データフレームの全ての値を調べて、3で割った余りが1で、かつ50よりも大きいか、あるいは10以下の要素に対して全て numpy.nan を代入して NaN にする。

#### 全ての要素に対し関数を適用する apply()
```python
def add(x: float, y: float) -> float:
  return x + y    # xに対しyを足してその結果を返す関数
r = pandas.DataFrame(numpy.empty((3, 5), dtype = float),
        index = tuple('ABC'), columns = tuple('abcde')))
print(r)
print(r.loc['B',].apply(add, args = (0.1,)))    # 特定の行に対して
print(r['d'].apply(add, args = (0.2,)))    # 特定の列に対して
print(r.apply(add, args = (0.7,)))    # 全ての要素に対して
print(r)    # r自体はapply()によって変化しない
```

#### シリアライズする
pickleにするメソッドがあり、ファイル拡張子で xz などと指定すると自動的に圧縮、解凍も行われる。
```python
f[0].to_pickle('f0.xz')
f[1] = pandas.read_pickle('f0.xz')
```