Twitter APIで取得したデータをCSV形式で保存するときは、改行コードに気を付けよう

# はじめに
Twitter API で取得したデータをCSVファイルに保存したときに、改行コードが原因で苦労したのでメモ。

# 環境
Ubuntu 18.04.2
Anaconda 3 5.3.1
Python 3.7
Jupyter Notebook

# 問題
[Twitter API で取得できるデータ](http://westplain.sakuraweb.com/translate/twitter/API-Overview.cgi)のうち、`text`・`full_text`・`user->description`といった要素は、ユーザーが自分で文章を入力したデータになるので、改行コードが含まれる場合があります。

改行コードには`\n`・`\r`・`\r\n`の３種類があるのですが、Twitterユーザーが使用している環境によって、その種類が変わってきてしまいます（参考：[改行コード一覧](https://qiita.com/uhooi/items/dc74ff3434aecb17faa2)）。

例えばツイートのtextデータには

```
メッセージ\nメッセージ\nメッセージ
```
```
メッセージ\rメッセージ\rメッセージ
```
```
メッセージ\r\nメッセージ\r\nメッセージ
```

の３種類が含まれるのです。

さて本題。
Twitter APIで取得したデータをpandasのDataFrame型にした後、CSV形式でファイルとして保存したのですが、保存したファイルを読み込むと、データの行数が増えていたり、カラムにズレが生じていて、保存時の形を全く保っていなかったのです。

どういうことかというと、例えば以下のようなコードを実行すると、

```python
# 改行コードが\rのとき
import pandas as pd
from IPython.display import display

df = pd.DataFrame([{ 'created_at': '12:00', 
                     'text': 'メッセージ\rメッセージ\rメッセージ',
                     'tweet_id': 12345,
                     'user_id': 67890,
                    }])  
display(df) # DataFrameを出力

df.to_csv('test.csv', encoding="utf-8") # CSV形式で保存

df = pd.read_csv('test.csv', index_col=0)
display(df) # 読み込んだCSVデータを出力
```

結果は以下のようになります。DataFrame（上）は正常ですが、一度CSV形式に変換すると崩れていますよね（下）。
`\r`を境に行が一段下がり、カラムと値がミスマッチしています（インデックスに値が入ってるしめちゃくちゃ...）。空いているデータはすべて`NaN`になっていますね。
![スクリーンショット 2019-08-09 21.05.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267522/2d8f4707-15ae-6ea9-61a7-8557e8f11579.png)

どうやら、問題児は改行コードの`\r`のみのようです。
`\n`や`\r\n`の場合は、データが崩れることなく保存できます↓

```python
# 改行コードが\nのとき
df = pd.DataFrame([{ 'created_at': '12:00', 
                     'text': 'メッセージ\nメッセージ\nメッセージ',
                     'tweet_id': 12345,
                     'user_id': 67890,
                    }])  
display(df) # DataFrameを出力

df.to_csv('test.csv', encoding="utf-8") # CSV形式で保存

df = pd.read_csv('test.csv', index_col=0)
display(df) # 読み込んだCSVデータを出力
```
![スクリーンショット 2019-08-09 21.10.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267522/283fdc5a-55c8-4603-8ca9-5e4cb7cc47fc.png)

```python
# 改行コードが\r\nのとき
df = pd.DataFrame([{ 'created_at': '12:00', 
                     'text': 'メッセージ\r\nメッセージ\r\nメッセージ',
                     'tweet_id': 12345,
                     'user_id': 67890,
                    }])  
display(df) # DataFrameを出力

df.to_csv('test.csv', encoding="utf-8") # CSV形式で保存

df = pd.read_csv('test.csv', index_col=0)
display(df) # 読み込んだCSVデータを出力
```
![スクリーンショット 2019-08-09 21.10.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267522/ab29d0e2-b17d-0fb3-70b1-d901c5ce94aa.png)

# 解決方法
## その１
`\r`を`\n`に置換する。
`\r\n`に含まれる`\r`は、置換してしまうと改行が１つ増えてしまうので、注意する必要があります。

```python
df = pd.DataFrame([{ 'created_at': '12:00', 
                     'text': 'メッセージ\rメッセージ\rメッセージ',
                     'tweet_id': 12345,
                     'user_id': 67890,
                    }])  
display(df) # DataFrameを出力

# \r\nの場合は除いて、\rを\nに置換する
if ('\r' in df['text'][0]) and not ('\r\n' in df['text'][0]):
    df['text'][0] = df['text'][0].replace('\r', '\n')

df.to_csv('test.csv', encoding="utf-8") # CSV形式で保存

df = pd.read_csv('test.csv', index_col=0)
display(df) # 読み込んだCSVデータを出力
```
![スクリーンショット 2019-08-09 23.05.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267522/a9d13294-50c4-edc9-b0a4-418ee17d53ff.png)

### 追記
改行コードを全部`\n`に統一した方が良さそうですね。以下のコードで`\r\n`と`\r`の両方を置換できます。
**置換する順序を間違えると、`\r\n`が`\n\n`になってしまうので気をつけましょう。**

```python
df['text'][0] = df['text'][0].replace('\r\n', '\n').replace('\r', '\n')

df.to_csv('test.csv', encoding="utf-8") # CSV形式で保存

df = pd.read_csv('test.csv', index_col=0)
display(df) # 読み込んだCSVデータを出力
```

## その2
[pickle](https://note.nkmk.me/python-pandas-to-pickle-read-pickle/)を使用する。

```python
df = pd.DataFrame([{ 'created_at': '12:00', 
                     'text': 'メッセージ\nメッセージ\nメッセージ',
                     'tweet_id': 12345,
                     'user_id': 67890,
                    }])  
display(df) # DataFrameを出力

df.to_pickle('test.pickle') # pickle形式で保存

df = pd.read_pickle('test.pickle')
display(df) # 読み込んだpickleデータを出力
```
![スクリーンショット 2019-08-09 21.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267522/a3e56300-9f3c-20d6-eec6-61cf16ea90c3.png)


pickleファイルはコードで読み込まない限り中身を確認することができない（Excel等で開くことができない）ので、簡単にデータを確認できるようにしたいようなら、`\r`を`\n`置換してCSV形式で保存する方法が良いと思います。

以上