pandasのデータフレームでサイズが合ってるのに行列積がとれないときはインデックスとカラムの中身を見よ

#先に結論だけ
Pandasのデータフレームで行列積をとるとき（`A.dot(B)`）は，
`A.columns`と`B.index`の要素を同じくするべし．

```python 
len(A.columns) == len(B.index)
```

が`True`となるだけでなく，

```python 
list(A.columns) == list(B.index)
```
も`True`となることが必要．

```python
A.columns = B.index
```
あるいは

```python
B.index = A.columns
```

とすれば，サイズが合っている限り積がとれる（はず）．

#バージョン
- python: 3.6.4
- pandas: 0.20.3

#そもそも行列の積とは？
- 定義：[行列の積の定義とその理由 | 高校数学の美しい物語](https://mathtrain.jp/matrixproduct)
- 具体的な計算方法：[行列の積の計算方法と例題 - 具体例で学ぶ数学](https://mathwords.net/gyouretsuseki)

数学的には，Aの列の数とBの行の数が等しければABは定義される．
しかし，DataFrameで行列積をとるためにはこれだけではダメだった．

#データフレームの行列積
ひとりひとりの行動をデータとして集めて，行動と属性を対応させたデータと関連させて，その人の光属性値と闇属性値を推定するとする．　　
　　
とりあえずこんな感じのデータがあるとする．

```Python
>>import pandas as pd

>>#ひとりひとりの行動
>>user_action = pd.DataFrame(
    {'user_id' : [1, 2, 3],
     'action_1': [0, 0, 1],
     'action_2': [1, 0, 1], 
     'action_3': [0, 1, 0],
     'action_4': [0, 1, 0],
     'action_5': [1, 0, 1],
     'action_6': [1, 1, 0]},
     columns = ['user_id', 'action_1', 'action_2', 'action_3', 'action_4', 'action_5', 'action_6']
    ).set_index('user_id')

>>#行動と光・闇属性の対応関係
>>action_attribution = pd.DataFrame(
    {'action_id': [1, 2, 3, 4, 5, 6],
     'light': [0, 1, 0, 0, 1, 1],
     'dark': [0, 0, 1, 1, 0, 1]
    }).set_index('action_id')
```
`user_action`（太字のところが`index`, `columns`）

|           |action_1|action_2|action_3|action_4|action_5|action_6|
|----------:|-------:|-------:|-------:|-------:|-------:|-------:|
|**user_id**|        |        |        |        |        |        |
|      **1**|       0|       1|       0|       0|       1|       1|
|      **2**|       0|       0|       1|       1|       0|       1|
|      **3**|       1|       1|       0|       0|       1|       0|

`action_attribution`（太字のところが`index`, `columns`）

|             |dark|light|
|------------:|---:| ---:|
|**action_id**|    |     |
|        **1**|   0|    0|
|        **2**|   0|    1|
|        **3**|   1|    0|
|        **4**|   1|    0|
|        **5**|   0|    1|
|        **6**|   1|    1|

一つ目の`user_action`の列（`columns`）の数と，二つ目の`action_attribution`の行（`index`）の数が同じ．だから，一つ目に二つ目を（右から）掛ければ行列の積をとることができて，

|           |dark|light|
|----------:|---:|----:|
|**user_id**|    |     |
|      **1**|   *|    *|
|      **2**|   *|    *|
|      **3**|   *|    *|

こんな感じに個人ごとの闇属性値と光属性値がわかるはずだ．


##サイズは合ってるのにエラー

```python
>>user_action.dot(action_attribution)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-32-6b8e3241e95e> in <module>()
----> 1 user_action.dot(action_attribution)

~\Anaconda3\lib\site-packages\pandas\core\frame.py in dot(self, other)
    786             if (len(common) > len(self.columns) or
    787                     len(common) > len(other.index)):
--> 788                 raise ValueError('matrices are not aligned')
    789 
    790             left = self.reindex(columns=common, copy=False)

ValueError: matrices are not aligned
```

かける順番が違う？

```python
>>action_attribution.dot(user_action)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-33-65f902a08f21> in <module>()
----> 1 action_attribution.dot(user_action)

~\Anaconda3\lib\site-packages\pandas\core\frame.py in dot(self, other)
    786             if (len(common) > len(self.columns) or
    787                     len(common) > len(other.index)):
--> 788                 raise ValueError('matrices are not aligned')
    789 
    790             left = self.reindex(columns=common, copy=False)

ValueError: matrices are not aligned
```

※順番は最初にやったほうの`user_action.dot(action_attribution)`で合ってる．

サイズは大丈夫だよな…

```python
>>user_action.shape

(3, 6)


>>action_attribution.shape

(6, 2)
```

サイズは大丈夫なのに…

エラーメッセージでググってもよくわからなかった．
そこでエラーメッセージの

```python
~\Anaconda3\lib\site-packages\pandas\core\frame.py in dot(self, other)
    786             if (len(common) > len(self.columns) or
    787                     len(common) > len(other.index)):
--> 788                 raise ValueError('matrices are not aligned')
    789 
    790             left = self.reindex(columns=common, copy=False)
```

に注目してみる．ソースコードを読みにいく．
こんなことが書いてある（抜粋）．

```python:pandas\core\frame.py
    def dot(self, other):
        """
        Matrix multiplication with DataFrame or Series objects

        Parameters
        ----------
        other : DataFrame or Series

        Returns
        -------
        dot_product : DataFrame or Series
        """
        if isinstance(other, (Series, DataFrame)):
            common = self.columns.union(other.index)
            if (len(common) > len(self.columns) or
                    len(common) > len(other.index)):
                raise ValueError('matrices are not aligned')

                #以下略

```

ここの

```python
if (len(common) > len(self.columns) or
                    len(common) > len(other.index)):
```

に引っかかったようだ．`common`って何ぞや？と上のほうに行くと，

```python
common = self.columns.union(other.index)
```

とある．ざっくり説明すると，

```python
A.dot(B)
```
としたとき，`self`は`A`，`other`は`B`にあたると読んでよい．
つまり，`common`というのは`A`の列の名前（の集合）と`B`の行の名前（の集合）をあわせたもの（和集合）にあたる．**ここで大事なのは，`common`はふたつの和集合にあたること．**

```python
if (len(common) > len(self.columns) or
                    len(common) > len(other.index)):
```

に引っかからないようにするには，`len(common)`は`len(self.columns)`および`len(other.index)`と等しくなくてはならない．（`common`のとりかたから，二つより小さくなることはない．`len(self.columns)`と`len(other.index)`は，数学的に積をとれるなら等しい．）

さっきつくったデータフレームの`common`をつくってみる．

```python
>>common = user_action.columns.union(action_attribution.index)
>>common

Index(['action_1', 'action_2', 'action_3', 'action_4', 'action_5', 'action_6',
                1,          2,          3,          4,          5,          6],
      dtype='object')


>>len(common)

12


>>len(user_action.columns)

6


>>len(action_attribution.index)

6
```

となる．ここが原因だったようだ．`user_action.columns`と`action_attribution.index`で違う「名前」をつけていたせいだった．
行名or列名を更新すれば解決するはず．

```python
>>action_attribution.index = user_action.columns
>>action_attribution
```

|             |dark|light|
|------------:|---:| ---:|
|**action_id**|    |     |
| **action_1**|   0|    0|
| **action_2**|   0|    1|
| **action_3**|   1|    0|
| **action_4**|   1|    0|
| **action_5**|   0|    1|
| **action_6**|   1|    1|

```python
>>common = user_action.columns.union(action_attribution.index)
>>common

Index(['action_1', 'action_2', 'action_3', 'action_4', 'action_5', 'action_6'], dtype='object')
```
いけそう！

```python
>>user_action.dot(action_attribution)
```

|           |dark|light|
|----------:|---:|----:|
|**user_id**|    |     |
|      **1**|   1|    3|
|      **2**|   3|    1|
|      **3**|   0|    2|

いけた！

#まとめ

```python
A.dot(B)
```

をとるときは，

```python
A.columns = B.index
```
あるいは

```python
B.index = A.columns
```

とすれば，サイズが合っている限り積がとれる（はず）．