Pandas: データフレームについて--05: データフレームの変容

# データフレームの変容


```python
import pandas as pd
df = pd.DataFrame({
    'a': [1, 2, 3, 4, 5],
    'b': [1.2, 3.4, 5.6, 7.8, 9.0]
})
df
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>1.2</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>3.4</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3</td>
      <td>5.6</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4</td>
      <td>7.8</td>
    </tr>
    <tr>
      <th>4</th>
      <td>5</td>
      <td>9.0</td>
    </tr>
  </tbody>
</table>
</div>



## 1.　データフレームの列，行に関数を適用 `apply`

    apply(func: 'AggFuncType', axis: 'Axis' = 0, raw: 'bool' = False,
          result_type=None, args=(), **kwargs)



```python
import numpy as np
```

### 1.1.　スカラーを対象にする関数の場合

以下の 3 つの例はいずれも同じ結果になる。


```python
np.sqrt(df)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1.000000</td>
      <td>1.095445</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.414214</td>
      <td>1.843909</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1.732051</td>
      <td>2.366432</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2.000000</td>
      <td>2.792848</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2.236068</td>
      <td>3.000000</td>
    </tr>
  </tbody>
</table>
</div>




```python
df.apply(np.sqrt) # axis=0
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1.000000</td>
      <td>1.095445</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.414214</td>
      <td>1.843909</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1.732051</td>
      <td>2.366432</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2.000000</td>
      <td>2.792848</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2.236068</td>
      <td>3.000000</td>
    </tr>
  </tbody>
</table>
</div>




```python
df.apply(np.sqrt, axis=1)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1.000000</td>
      <td>1.095445</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.414214</td>
      <td>1.843909</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1.732051</td>
      <td>2.366432</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2.000000</td>
      <td>2.792848</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2.236068</td>
      <td>3.000000</td>
    </tr>
  </tbody>
</table>
</div>



### 1.2.　1 次元配列を対象とする関数の場合

`sum`, `mean`, `std`, `var`, `median` など 1 次元配列（ベクトル）を対象にする関数は， `axis` の指定により違いがある。

`axis` は 0, 1 で指定できるが，最近のバージョンではそれぞれ，`'index'`, `'columns'` で指定できる。

また，numpy の関数にデータフレームを引数として与え，`axis` を指定しても，同じ結果が得られる。

#### 1.2.1.　列単位に集計


```python
df.apply(sum) # axis=0
```




    a    15.0
    b    27.0
    dtype: float64




```python
df.apply(sum, axis='index')
```




    a    15.0
    b    27.0
    dtype: float64




```python
np.sum(df) # axis=0
```




    a    15.0
    b    27.0
    dtype: float64




```python
np.sum(df, axis='index')
```




    a    15.0
    b    27.0
    dtype: float64



#### 1.2.2.　行単位に集計


```python
df.apply(sum, axis=1)
```




    0     2.2
    1     5.4
    2     8.6
    3    11.8
    4    14.0
    dtype: float64




```python
df.apply(sum, axis='columns')
```




    0     2.2
    1     5.4
    2     8.6
    3    11.8
    4    14.0
    dtype: float64




```python
np.sum(df, axis=1)
```




    0     2.2
    1     5.4
    2     8.6
    3    11.8
    4    14.0
    dtype: float64




```python
np.sum(df, axis='columns')
```




    0     2.2
    1     5.4
    2     8.6
    3    11.8
    4    14.0
    dtype: float64



## 2.　データフレームの各セルに関数を適用 applymap

データフレームの各セル（スカラー）に関数[^1]を適用する。

[^1]: スカラーを引数として，スカラーの結果を返す関数。

`applymap(func: 'PythonFuncType', na_action: 'str | None' = None, **kwargs)`


```python
df.applymap(lambda x: x**2 + 3*x + 5) # 非推奨
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>9</td>
      <td>10.04</td>
    </tr>
    <tr>
      <th>1</th>
      <td>15</td>
      <td>26.76</td>
    </tr>
    <tr>
      <th>2</th>
      <td>23</td>
      <td>53.16</td>
    </tr>
    <tr>
      <th>3</th>
      <td>33</td>
      <td>89.24</td>
    </tr>
    <tr>
      <th>4</th>
      <td>45</td>
      <td>113.00</td>
    </tr>
  </tbody>
</table>
</div>



しかし，ベクトライズ関数がある場合（多くの場合はそうである）には，　`applymap` は避けるべきである。直接データフレームを操作するほうが実行速度が速い。


```python
df**2 + 3*df + 5 # 推奨
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>9</td>
      <td>10.04</td>
    </tr>
    <tr>
      <th>1</th>
      <td>15</td>
      <td>26.76</td>
    </tr>
    <tr>
      <th>2</th>
      <td>23</td>
      <td>53.16</td>
    </tr>
    <tr>
      <th>3</th>
      <td>33</td>
      <td>89.24</td>
    </tr>
    <tr>
      <th>4</th>
      <td>45</td>
      <td>113.00</td>
    </tr>
  </tbody>
</table>
</div>



## 3.　データ変容 transform

    transform(func: 'AggFuncType', axis: 'Axis' = 0, *args, **kwargs)

関数は `lambda` または `'関数名'　` で指定する。

以下のような　，スカラーを引数としてスカラーを返す関数の場合は `axis` に 0 または 1 を指定しても同じ結果になる。

`axis=1` を指定した場合に，列のデータタイプが変わることがある。

### 3.1.　1 つの関数を指定する場合


```python
df.transform(lambda x: x + 1) # axis=0
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2</td>
      <td>2.2</td>
    </tr>
    <tr>
      <th>1</th>
      <td>3</td>
      <td>4.4</td>
    </tr>
    <tr>
      <th>2</th>
      <td>4</td>
      <td>6.6</td>
    </tr>
    <tr>
      <th>3</th>
      <td>5</td>
      <td>8.8</td>
    </tr>
    <tr>
      <th>4</th>
      <td>6</td>
      <td>10.0</td>
    </tr>
  </tbody>
</table>
</div>




```python
df.transform(lambda x: x + 1, axis=1)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>a</th>
      <th>b</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2.0</td>
      <td>2.2</td>
    </tr>
    <tr>
      <th>1</th>
      <td>3.0</td>
      <td>4.4</td>
    </tr>
    <tr>
      <th>2</th>
      <td>4.0</td>
      <td>6.6</td>
    </tr>
    <tr>
      <th>3</th>
      <td>5.0</td>
      <td>8.8</td>
    </tr>
    <tr>
      <th>4</th>
      <td>6.0</td>
      <td>10.0</td>
    </tr>
  </tbody>
</table>
</div>



### 3.2.　複数の関数を指定する場合

複数の関数を指定する場合は，リストで指定する。


```python
df.transform(['sqrt', lambda x: x**2])
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead tr th {
        text-align: left;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr>
      <th></th>
      <th colspan="2" halign="left">a</th>
      <th colspan="2" halign="left">b</th>
    </tr>
    <tr>
      <th></th>
      <th>sqrt</th>
      <th>&lt;lambda&gt;</th>
      <th>sqrt</th>
      <th>&lt;lambda&gt;</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1.000000</td>
      <td>1</td>
      <td>1.095445</td>
      <td>1.44</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.414214</td>
      <td>4</td>
      <td>1.843909</td>
      <td>11.56</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1.732051</td>
      <td>9</td>
      <td>2.366432</td>
      <td>31.36</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2.000000</td>
      <td>16</td>
      <td>2.792848</td>
      <td>60.84</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2.236068</td>
      <td>25</td>
      <td>3.000000</td>
      <td>81.00</td>
    </tr>
  </tbody>
</table>
</div>



### 3.3.　グループ化データフレームを対象とする場合


```python
df2 = pd.DataFrame({
    'gender': ['male', 'male', 'female', 'male', 'female', 'male', 'female'],
    'x': [2, 3, 1, 4, 6, 3, 3]
})
df2
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>gender</th>
      <th>x</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>male</td>
      <td>2</td>
    </tr>
    <tr>
      <th>1</th>
      <td>male</td>
      <td>3</td>
    </tr>
    <tr>
      <th>2</th>
      <td>female</td>
      <td>1</td>
    </tr>
    <tr>
      <th>3</th>
      <td>male</td>
      <td>4</td>
    </tr>
    <tr>
      <th>4</th>
      <td>female</td>
      <td>6</td>
    </tr>
    <tr>
      <th>5</th>
      <td>male</td>
      <td>3</td>
    </tr>
    <tr>
      <th>6</th>
      <td>female</td>
      <td>3</td>
    </tr>
  </tbody>
</table>
</div>




```python
gdf = df2.groupby('gender')
df2['Mean'] = gdf['x'].transform('mean')
df2
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>gender</th>
      <th>x</th>
      <th>Mean</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>male</td>
      <td>2</td>
      <td>3.000000</td>
    </tr>
    <tr>
      <th>1</th>
      <td>male</td>
      <td>3</td>
      <td>3.000000</td>
    </tr>
    <tr>
      <th>2</th>
      <td>female</td>
      <td>1</td>
      <td>3.333333</td>
    </tr>
    <tr>
      <th>3</th>
      <td>male</td>
      <td>4</td>
      <td>3.000000</td>
    </tr>
    <tr>
      <th>4</th>
      <td>female</td>
      <td>6</td>
      <td>3.333333</td>
    </tr>
    <tr>
      <th>5</th>
      <td>male</td>
      <td>3</td>
      <td>3.000000</td>
    </tr>
    <tr>
      <th>6</th>
      <td>female</td>
      <td>3</td>
      <td>3.333333</td>
    </tr>
  </tbody>
</table>
</div>



この結果は，`gender` ごとの `x` の平均値を持つ変数 `Mean` を作ることになる。


```python
df2[df2.gender == 'male']['x'].mean() # male 4 人の平均値
```




    3.0




```python
df2[df2.gender == 'female']['x'].mean() # female 3 人の平均値
```




    3.3333333333333335



## 4.　もっと知りたい人

    help(df.apply)     # Help on method apply in module pandas.core.frame
    help(df.applymap)  # Help on method applymap in module pandas.core.frame
    help(df.transform) # Help on method transform in module pandas.core.frame