Pythonの多次元のリストに関する処理　メモ

# はじめに
この記事ではmacOS Sierra ver.10.12.6上でPython 3.6.1を動かしています。
環境によっては動作が変わる場合もあるかもしれないのでご注意ください。
自分が使うプログラムで使用するために調べた、素晴らしい方々による知恵のまとめやメモのようなものです。
細かな説明とかはできないので、**こう書けばこれができる**くらいの感覚で…

**※コメントにて無駄のない記述方法を頂きましたのでコメント欄も見て頂くことを推奨します**


# csvファイルからデータを読み出す
pandasを使うのが良さそう。
pandasの使い方はこちら→[10分でpandasを学ぶ](https://qiita.com/tkazusa/items/23bc0142bf277d397260) が非常に参考になります。

例えば、`data.csv`というcsvファイルが以下のような構造になってるとします。

| x | y | z |
|:-:|:-:|:-:|
|35|35|0|
|41|49|10|
|35|17|7|
|55|45|13|
|55|20|19|
| …| …| …|

### DataFrameを作成します
```Python:createDataFrame.py
import pandas as pd

df = pd.read_csv("./data.csv")
print(df)
```
###出力
```
>>>python createDataFrame.py
     x   y   z
0   35  35   0
1   41  49  10
2   35  17   7
3   55  45  13
4   55  20  19
5   15  30  26
.    .   .   .
.    .   .   .
```
csvファイルのヘッダとなっていたx、y、zといったラベル毎に見やすく出力されます。
一番左の列には勝手にインデックスも振ってくれてる。

求めたいインデックスの要素だけ取ってくることも可能。

```Python:createDataFrame.py
import pandas as pd

df = pd.read_csv("./data.csv")
print("x列の0行目：{}".format(df.ix[0].x))
print("y列の2行目：{}".format(df.ix[2].y))
print("z列の5行目：{}".format(df.ix[4].z))
```

```
>>>python createDataFrame.py
x列の0行目：35
y列の2行目：17
z列の5行目：19
```

for文で回せば求めたい列だけでリストを作れる。
pandas自体に必要な要素だけを求めたり、ソートをする機能もついてるみたいなので必要であればお調べ下さい。


# 2次元リストの集合演算

やりたいこと

- 無向グラフ$G = (V, E)$を考える
- $V$と$E$はそれぞれノード集合と枝集合とする
- $G$を構成する2つのグラフ$A、B$について、それぞれの枝集合を$E_A、E_B$とする
- $(V, E_A \cup E_B \backslash E_A \cap E_B)$を満たす集合を生成する

以下のように$E_A$と$E_B$の枝情報の2次元リストを用意します。

```
E_A:[[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
E_B:[[0, 2], [0, 3], [0, 4], [0, 7], [0, 8], [0, 9], [1, 9], [1, 10], [2, 6], [3, 4], [5, 6], [5, 8], [7, 10]]

```

ここからどうにか集合演算子等を使って和集合や差集合を求めようとしたんですが、
0〜10の各要素に適用されてしまって、1次元のリストになってしまう問題にぶつかった…
最終的に、見つけたPythonの集合演算に関する記事を参考に以下のように実装しました。

**コメント閲覧推奨**

###集合演算

```Python:set.py
E_A = [[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
E_B = [[0, 2], [0, 3], [0, 4], [0, 7], [0, 8], [0, 9], [1, 9], [1, 10], [2, 6], [3, 4], [5, 6], [5, 8], [7, 10]]

print("枝集合を初期化する")
print("E_A:{}".format(E_A))
print("E_B:{}".format(E_B))

print("集合型(set型)に変換する")
A = set(map(tuple, E_A))
B = set(map(tuple, E_B))

print("A:{}".format(A))
print("B:{}".format(B))
# .union:論理和
# .difference:論理差
# .intersection:論理積
AB = A.union(B).difference(A.intersection(B))
print("AとBの和集合から，AとBの積集合を除く")
print("AB:{}".format(AB))

edgelist = sorted(list(AB))
print("リスト型に変換する")
print("edgelist:{}".format(edgelist))
```

### 出力
```
>>>python set.py
枝集合を初期化する
E_A:[[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
E_B:[[0, 2], [0, 3], [0, 4], [0, 7], [0, 8], [0, 9], [1, 9], [1, 10], [2, 6], [3, 4], [5, 6], [5, 8], [7, 10]]
集合型(set型)に変換する
A:{(5, 6), (0, 7), (0, 6), (2, 4), (3, 9), (0, 10), (1, 9), (0, 4), (1, 10), (0, 3), (7, 8), (0, 2), (5, 8)}
B:{(2, 6), (5, 6), (0, 7), (1, 10), (1, 9), (7, 10), (0, 4), (0, 9), (0, 3), (0, 8), (3, 4), (0, 2), (5, 8)}
AとBの和集合から，AとBの積集合を除く
AB:{(7, 8), (2, 6), (0, 6), (3, 9), (0, 10), (7, 10), (0, 9), (0, 8), (3, 4), (2, 4)}
リスト型に変換する
edgelist:[(0, 6), (0, 8), (0, 9), (0, 10), (2, 4), (2, 6), (3, 4), (3, 9), (7, 8), (7, 10)]

```
なんとか$(V, E_A \cup E_B \backslash E_A \cap E_B)$を満たす集合が作れました。
一旦集合型であったりタブルに変換する冗長にも思える処理があるうえに、最終的に完全にリストに戻せてませんけどね…どなたかリストのまま同じ処理をする方法が分かる方がいたら教えて頂きたいです。

# 2次元リストから一致する要素を排除する
先のような枝集合の2次元リストから、要素が一致するものを排除します。ただし、枝を取り除くので、[0, 2]のような要素を2次元リストから取り除くということです。

以下のように2次元リストと、そこから取り除きたい枝データのリストを用意します。

```
E_A:[[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
edge:[0, 6]
```
情弱な僕は最初 `E_A - edge` なんてしようとしてたんですけど、できるわけないですね、はい。
これもまた先のように集合型に変換して差集合を取ればできそうですが、もっといい方法はないかと思い、結局以下のように実装しました。

**コメント閲覧推奨**

### 2次元リストから指定要素を排除

```Python:list.py
E_A = [[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
edge = [0, 6]

print("E_A:{}".format(E_A))
print("edge:{}".format(edge))

print("一致する要素を見つけて、そのインデックスを指定して取り除く")
for n, i in enumerate(E_A):
    if i == edge:
        del E_A[n]
print("E_A after:{}".format(E_A))
```

### 出力
```
>>>python list.py
E_A:[[0, 2], [0, 3], [0, 4], [0, 6], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
edge:[0, 6]
一致する要素を見つけて、そのインデックスを指定して取り除く
E_A after:[[0, 2], [0, 3], [0, 4], [0, 7], [0, 10], [1, 9], [1, 10], [2, 4], [3, 9], [5, 6], [5, 8], [7, 8]]
```

for文のenumerate関数は、ループ中に使うと今処理している要素のインデックスを第一引数、要素を第二引数で返します。これで一致する要素を探して、見つかったらそのインデックスを指定してリストの要素を排除しています。
見て分かる通り、`edge`リストの要素が一つだから成り立っているだけで、`edge`も2次元のリストだった場合上記の方法ではうまくいかないです。その場合やっぱり集合型で扱うのがいいのかな…
これに関しても上手い方法が分かる方がいたら教えて頂きたいです。


# あとがき
無責任なメモ書きでしたが、上に書いた限定的な状況であればこの方法でとりあえず実装できます。
何か少しでも参考になるところがあれば幸いです。