データ解析において、最も時間のかかる作業は次の2つです。
- データセットの各列および各行を理解する。
- 欠損値(NaN)や入力ミスなどの不要なデータを取り除くなど、「クリーニング」作業を行う。
この2つの作業には異論がないと考えられます。さらに、これらの作業の厄介な点は、両方の作業を繰り返し行う必要があることです。一連のデータクリーニング作業を一律に手順化することができないところが、特に難しいところではないでしょうか。
ひたすらデータセットの理解とクリーニングの繰り返し
毎月更新されるようなデータでも、ある程度手順を定型化できるにしても、当月のデータや新しく収集したデータをデータセットに追加する際には、繰り返し1と2を行う必要があります。扱うデータが増えれば増えるほど、クリーニングの作業量もそれに比例して増加します。この繰り返し作業の中で、自分がデータ解析を行っているのか、ただデータクリーニングに没頭しているのか、しばしば見失ってしまいます。このメモは備忘録的にまとめたものですが、お役に立てれば幸いです。
当たり前すぎて、メモを取っていなかったりしたのも、気が付く都度追加していきたいと思います。おもむくままにメモをした内容をBlogにまとめました。
データクリーニングで使うPython Coding 一覧
No | やりたいこと | Python Coding | 説明 |
---|---|---|---|
1. | 列番号だけでデータフレームを組みなおす | df=df.iloc[:, [8,11]] |
元のデータフレームの最初の列を0と数えて、8番目と11番目の2列に組み替える |
2. | 3,000行以降のデータを削除する | df=df.iloc[range(3000), :] |
range 関数により、0から3000までの連番を指定しいます。結果、それ以降は削除します。 |
3. | 列名'cols' の各行の文字列に含まれるブランクをとる |
df['cols'] = df['cols'].str.strip() |
全角の空白をとる場合は、df['cols'].str.replace(" ", "") も行う |
4. | 列名'cols' にあるNaN の行を落とす |
df = df[df['cols'].isnull() == False] |
df.dropna() はデータフレームの列のうちどこかの列にNaN があれば行を落としているため、場合によってはすべてがなくなる場合もある。 |
5. | 列名'Date' の日付で降順(新しいのが上に来る)にソートする |
df = df.sort_values(by = 'Date', ascending=False) |
降順(新しいものから古いものへの順序)としたい時、ascending=False とする。後続の処理として重複排除(一番新しいのを残す)をする場合などは、その前処理として降順にしておくとよい |
6. | 列名'EmpID' で最初のを残して他の重複を排除する。 |
df = df.drop_duplicates(['EmpId'], keep='first' ) |
前後にprint('before/after', df.shape) をそれぞれ挟むと何行削除されたかが計算できる。 |
7. | 列名'氏名' に「その他」という名前でないものがあればその行を削除する |
df = df[~df['氏名'].str.contains('その他')] |
‘その他’という言葉を指定してそれが含まれるものを除くため、削除する語を指定する必要がある |
8. | 列名'col1' にNaN がなく、列名'col2' が0でないいずれがであるOR条件でフィルターする |
df = df[(df['col1'].isnull() == False) \| ( df['col2'] != 0)] |
2つの条件を OR をとる場合は、”|” を()で挟むことで条件を指定します |
9. | INT もしくはFloat が取りうる列名'num' に入っているstr等を除外する |
df['num'] = pd.to_numeric(df['num'],errors = 'coerce' ) df = df[df['num'].isnull() == False] |
errors = 'coerce' のオプションでnumeric に変換できないものをNaN とする関数でNaN にしてそれを2つ目の式で削除します。NAN や文字(str )が紛れ込んで、astype(int) で整数に変えられない時には必要です。 |
補足
- データセットの作成の第一歩は、必要とするであろうカラムに絞ることから始まります。その際、列名を指定してデータセットを絞るより、列番号を指定する方が断然、効率的です。また、それぞれの列名の列番号の対応付けを知りたい場合は、以下のようなコードで簡単に分かります。 サンプルコードでは220列ある元のデータセットに対して、列名だけを取り出して、1列 x 220行のデータフレームとして表示します。
1
2
3
4
columns = df.columns
pd.options.display.max_rows = 220
columns = pd.DataFrame(columns)
columns.head(220)
- ブランクを取るのは主にテキストマイニングの際に必要となりますが、中には全角の空白(スペース)で単語を分けている場合がありますので、半角スペースと全角スペースをセットで取り除くことをお勧めします。