read_csv でヘッダあり・なしCSVの読み込み

忙しい方のために
--
```python
# ヘッダありCSV
df = read_csv(filename, header=0)

# ヘッダなしCSV
df = read_csv(filename, header=None, names=['user_id', 'name'])
```

リファレンスを読む
--
pandasでCSV読み込みをするにあたって、ヘッダ行の扱い方の指示はリファレンスを読んでもいまひとつ理解しづらいものがあります。

> #### header : int, list of int, default ‘infer’
> Row number(s) to use as the column names, and the start of the data. Default behavior is to infer the column names: if no names are passed the behavior is identical to header=0 and column names are inferred from the first line of the file, if column names are passed explicitly then the behavior is identical to header=None. Explicitly pass header=0 to be able to replace existing names. The header can be a list of integers that specify row locations for a multi-index on the columns e.g. [0,1,3]. Intervening rows that are not specified will be skipped (e.g. 2 in this example is skipped). Note that this parameter ignores commented lines and empty lines if skip_blank_lines=True, so header=0 denotes the first line of data rather than the first line of the file.
> （機械訳）列名として使用する行番号とデータの開始。デフォルトの動作では、カラム名が推測されます。namesが渡されない場合の動作はheader=0と同じで、カラム名はファイルの最初の行から推測されます。カラム名が明示的に渡される場合の動作はheader=Noneと同じです。既存のnamesを置き換えることができるように、明示的にheader=0を渡します。ヘッダは、例えば[013]のように、列のマルチインデックスのための行位置を指定する整数のリストであることができる。指定されていない途中のローはスキップされます(例:この例では2はスキップされます。)。skip_blank_lines=Trueの場合、このパラメータはコメント行および空行を無視するため、header=0はファイルの最初の行ではなく、データの最初の行を示します。

私はこのリファレンス記載を何度読んでも理解できませんでした。実際の動作と見合わせながら理解できたことをここに記します。

ヘッダ行の扱いは header, names の二つの引数の組み合わせで指示します。
まずheader引数ですが、リファレンスの解説は実際の振る舞いと見合わせながら解釈すると以下のような感じになります。

- ヘッダ行として解釈すべき行を与えます。
- None、int、intリスト、または文字列 `'infer'` を渡せます（リファレンスのデータ型記載、間違っていますね）。
- Noneを渡した場合、ヘッダ行はないという意味になり、最初からデータ行となります。カラム名は無指定なら 0, 1, 2, ... となり、names引数で指定すればその通りになります。
- intを渡した場合、その行がヘッダ行とみなされます。0オリジン。この行の内容がカラム名となります。names引数が指定されていればnamesの指定内容で上書きされます。  
ヘッダ行より上のデータはすべて読み捨てられます。
- intリストを渡した場合、それに含まれるすべての行がヘッダ行とみなされマルチインデックスとなります。
- 'infer' を渡した場合、names引数があればNoneと同様になり、names引数がなければ0と同様になります。この 'infer' がデフォルトです。

最後の 'infer' がデフォルトという仕様のおかげで、header を指定しなくても names を指定するかどうかだけでヘッダ付き・ヘッダなしCSVを読み込み分けることができるということになります。
 
```python
# ヘッダありCSVを読む（一行目をヘッダとし、これをカラム名に採用する）には、header も names も指定しない 
df = read_csv(filename)

# ヘッダなしCSVを読むには、 names だけ指定する  
df = read_csv(filename, names=['user_id', 'name'])
```

header引数が出てこないところがポイントです。

対するnames引数。

> #### names : array-like, optional
> List of column names to use. If file contains no header row, then you should explicitly pass header=None. Duplicates in this list are not allowed.
> （機械訳）使用する列名のリスト。ファイルにヘッダ行が含まれていない場合は、header=Noneを明示的に渡す必要があります。このリストの重複は許可されていません。

まず、header指定を省略するなと言っています。上で書いたnamesだけで制御するやり方は本当は非推奨ということ。なんのためのデフォルト'infer'なのかわからなくなりますが、将来的に引数仕様を整理したいのかもしれません。

そして、動作の詳細。与えた列名の数が実際に存在した列数よりも多すぎたり少なすぎたりした場合に何が起こるかについてリファレンスに記載がありません。実際には次のように動くようです。

- 実際の列数より少なすぎる場合、実際の列の右側から列名が振られます。左側の余った列はすべてインデックスになります。
- 実際の列数より多すぎる場合、右側の余った列名は、オールNaNの列になります。インデックスはRangeIndexになります。
- 実際の列数ぴったりなら、列にはすべて指定通りの列名が振られ、インデックスはRangeIndexになります。

headerを省略せずに書くと、先頭に書いた通り以下のようになります。

```python
# ヘッダありCSV
df = read_csv(filename, header=0)

# ヘッダなしCSV
df = read_csv(filename, header=None, names=['user_id', 'name'])
```