🥾

データ分析の前段階の基本作業を「Python実践データ分析100本ノック」で学ぶ

2023/11/20に公開

はじめに

私は業務の一環としてPythonを使ったデータ分析をしているのですが、もっと実力をあげたいと思っています。そこで、書籍「Python実践データ分析100本ノック」に取り組んでみました。その結果、実際に仕事でデータ分析をする際にまず行うべきデータ加工事前分析について、その手順と具体的な方法を学ぶことができました。
本記事では、これから本書に取り組んでみようとしている方に向けて、私が学んだことを共有したいと思います。
なお、本書は基礎編、実践編①②、発展編から構成されていますが、本記事で取り上げているのはこのうち基礎編、実践編①の内容です。実践編②以降で学んだことについては別記事としてまとめたいと思います。

成果物

https://github.com/piz3in/Python100knock

学んだこと

本書には、2ついいところがあると思います。
1つ目は、比較的短時間でできるデータ分析をいくつか行うことで、データ分析手順の全体像を把握できるところです。
2つ目は、他の書籍ではスルーしてしまうような細かい作業も丁寧に取り上げているので、各手順の具体的な方法について詳細なイメージを持てるようになるところです。

ここからは、データ加工と事前分析について、私が学んだ手順と各手順の具体的な方法を記載します。

データ加工

まず、どんなデータがあるかを把握し、データ分析に適した形に加工します。手順は以下の通りです。

  1. 読み込み
  2. 把握
  3. 表記方法の違いの補正
  4. 欠損値の補完
  5. 結合
  6. 必要データの作成
  7. ダンプ

各手順の具体的な内容について、今後使えそうなコードと共に記載します。

1. 読み込み

まず、分析対象となるデータ(csvやExcelなどのファイルデータ)を読み込んで、DataFrameに格納します。

2. 把握

次に、読み込んだデータの大枠とデータの揺れの把握を行います。

2-1. データの大枠の把握

まずは、DataFrameの先頭数行を表示し、どんなデータ列があるかを見ます。また、データ列間の関係を整理します。必要に応じてデータの件数なども確認します。

2-2. データの揺れの把握

次に、データの揺れがあるかを確認します。データの揺れとは「データの入力時のミスや表記方法の違いによりデータの欠損や表記の不整合があること」を言います。データの揺れがある(整合性のない)データで分析を行っても意味のある結果を得ることはできないため、まずは現状を確認し、必要に応じて補正を行うためです。[1]

具体的には、各データの欠損値の状況(有無とその数)および表記方法の違いの有無を確認します。ここでの確認結果は欠損値の補間と表記方法の違いの補正を行ったあとに、確認するのにも用います。

欠損値の状況は以下のようなコードで確認します。
有無の確認:

# 各データ列に欠損値を一つでも含むか判定する
df.isnull().any()

# 特定のデータ列に欠損値を含むか判定する
df["time"].isnull()

欠損値の数の確認:

df.isnull().sum()

表記方法の違い(大文字、小文字や空白の有無など)の有無はpandasのunique関数を用いてユニークな値の一覧を表示するなどして確認します。

# ユニークな要素のndarrayを返す
pd.unique(df["name"])

# ndarrayを昇順にソートする
np.sort(pd.unique(df["name"])) 

# ユニークな要素数を返す
len(pd.unique(df["name"]))

3. 表記方法の違いの補正

3-1. 補正

空白削除や大文字、小文字の変換を行います。

# 空白削除
df["item_name"] = df["item_name"].str.replace(" ","")

# 小文字を大文字に変換
df["item_name"] = df["item_name"].str.upper()
# 大文字を小文字に変換
df["item_name"] = df["item_name"].str.lower()
3-2. 補正の確認

補正後は必ず確認を行います。ここでは、先頭数行を表示したり、ざっとデータをスクロールしたりするなどで、適当に確認を済ましてはいけません。2-2.データ揺れの把握で行ったように、pandasのunique関数を用いて補正した列のユニークな値の一覧を表示するなどしてできるだけ抜け・漏れがないように確認します。

4. 欠損値の補間

欠損値の補間を行います。実際は、分析者だけでは補間作業はできず、現場のスタッフにヒアリングを行ったり、補間作業自体をお願いしたり、他データと照らし合わせたりする必要があります。

4-1. 補間

欠損値に特定の値を代入するなどして補間を行います。

df["date"] = df["date"].fillna(pd.to_datetime("20230101"))
4-2. 補間の確認

補間後も必ず確認を行います。2-2.データ揺れの把握で行ったように、pandasのisnull関数で欠損値がないことを確かめます。

5. 結合

分析対象となるデータは複数のファイルなどに分かれていることが多いです。それらのデータから、分析に必要なデータを抽出し、結合します。

5-1. 結合方法の検討

分析の目的によって結合する際に主とするデータは違います。そのため、結合方法を検討する必要があります。
まずは、データ全体像を把握するために、粒度が一番細かいデータを主として結合します。そこに他のデータから付加したいデータ列が何か、そしてそれらを結合するための共通のキーとなるデータ列は何かを考え、結合します。
次に、主とするデータが複数通り考えられる場合は、まずはデータ数が少ない方を主として、もう一方は一旦無視して分析を行い、その後、もう一方を主として分析を行います。このように、結合方法は楽な方から探索的に検討していくと良いようです。

5-2. 結合

データ同士をpandasのmerge関数を用いて結合します。主とするデータのDataFrameを先に書き(下のコード例のdf1にあたる)、結合方法を指定する引数howにleftを渡します。

# データ同士の結合(横方向の結合)
df = pd.merge(df1, df2[["name", "age"]], how="left", on="key")

ここで、結合方法(how)は下記のように使い分けます。

  • left (right)
    どちらか片方のデータを主としたい(一方のデータを必ず残したい)場合に用いる。
  • inner
    2つのデータで共通のデータのみ抽出したい場合に用いる。(片方のデータにないデータは除外される)
  • outer
    単純にデータを結合したい場合(2つのデータのどちらか片方にのみ存在するデータ列を計上し、もう一方の対象のデータ列はNULLを代入する)に用いる。

また、結合キー(on)は、どちらのデータにも存在するデータ列を指定します(デフォルトはindex)。1つ以上の値を指定することもできます(例: on=["name","age"])。ただし、データ間でカラム名が異なる場合はleft_on, right_onで指定します。

なお、indexが共通のdataframe同士の横結合はconcatでもできます。

df = pd.concat([df1, df2["a"]], axis=1)

また、同じ種類のデータが複数のファイルに分かれている場合に、複数のDataFrameに格納した場合にはpandasのconcat関数を用いて結合します。

# データの結合(縦方向の結合)
df = pd.concat([df1, df2], ignore_index=True)
5-3. 結合の確認

表記方法の違いの補正や欠損値の補間の場合と同様に、データを結合した場合にも、都度その結果を確認します。結合キーが見つからないなど、うまく結合ができなかった時は欠損値が自動で入るため、2-2.データ揺れの確認で行ったのと同様の操作で、欠損値の確認を行います。

6. 必要データの作成

分析に必要なデータ列を追加する必要がある場合は作成し、作成結果の検算をします。

6-1. 作成

まず、必要なデータ列を作成します。具体的には、列同士の四則演算結果や特定の条件に基づいて立てるフラグなどの列を追加します。

6-2. 作成データの検算

データを作成する都度、検算を行います。

7. ダンプ

ここまで加工したデータをファイルにダンプ(=出力)します。以降の分析で、このダンプファイルの読み込みから始めることで、都度ここまでの加工作業を繰り返す必要がなくなります。

補足

本書では、補正、補間、結合など、データ加工をする度に確認を行うことが大事だと繰り返し説いてくれています。

データ加工は、一歩間違えると集計ミスが起き、数字のズレを生みます。間違ったデータを提供することは、会社の経営に大きな影響を及ぼし、(中略)また、個人でみても、データで語るデータサイエンティストが誤ったデータを出すというのは、顧客からの信頼を失います。データを結合したりする度に、件数の確認等を行うこと(中略)。また、なるべくデータの検算ができる列を探し、検算を実行するようにしましょう。[2]

過去、自分が仕事でデータ分析を行った時には正直ここまで都度都度確認をするようにはしていませんでした。可視化の段階になって欠損値の存在に気づき、コードを遡ってバグを探したりしていました。これからは何か加工をしたらすぐに確認するようにしていこう、と思いました。

事前分析

加工ができたら、事前分析を行います。ここでより深くデータを把握することで、どんな分析手法を用いれば良いかの判断材料を得ることができます。

手順は次のようになります。

  1. 全体の数字感の把握
  2. さまざまな切り口での集計
  3. 仮説・疑問の確認

1. 全体の数字感の把握

まず、全体の数字感がどれくらいかを把握します。
具体的には、例えば売上の分析についていえば、全体の売上を把握することや、基本的な統計量をpandasのdescribe関数を用いて算出し、顧客の注文金額の大まかな分布を把握することなどが挙げられます。まず全体の数字感を掴むことで、次にさまざまな切り口(例えば、商品毎や顧客属性毎)で売上を集計するなどして詳細に分析していく際に、各集計結果の全体に対する割合を理解しながら進めることができるためです。
また、対象となるデータが時系列データの場合には、可視化などにより、全期間の増減のトレンドを確認します。さらに、分析の対象期間をどの期間(1ヶ月、1年など)に設定するのが良いかを見るため、どのような期間で変化しているかを確認します。

2. さまざまな切り口での集計

例えば売上の分析では、商品毎や顧客属性毎に集計をし、どの商品の売上が伸びているのかや、全体の売り上げへの貢献が大きい顧客の属性にはどんなものがあるのかなどをみていきます。集計にはpandasのDataFrameのgroupbyメソッドなどを用います。

# 1つの列の値ごとに集計を行う場合
df[["month", "price"]].groupby("month").sum()

# 2つ以上の列の値ごとに集計を行う場合
# (グループラベルをindex(デフォルト)にしたくない場合は引数as_indexをFalseに指定する)
df[["month", "item_name", "price", "quantity"]].groupby(["month", "item_name"],as_index=False).sum()

直感的に見やすい結果を得るためにpivot_table関数を用いた方が良い場合もあります。

pd.pivot_table(
	df,                           # 元データのdataframeを指定
	index="month",                # indexに特定のデータ列を指定
	columns="item_name",           # columunに〃
	values=["price", "quantity"], # 結果を表示するデータ列を指定
	aggfunc="sum"                 # 結果の集計方法を指定(デフォルトはnp.mean())
)

3. 仮説、疑問の確認

集計結果を見て浮かんだ仮説や疑問をさらに集計したり、現場の方にヒアリングして確認します。その際は、必要に応じて可視化を行い、結果を他人に理解しやすいようにします。

さいごに

本書を通じて、データ加工と事前分析の手順と具体的なやり方について、自分の中で整理できたように思います。でも、実際には手順の一部を繰り返し行うことや、各手順の順番を変えた方がいいこともあるかと思います。今後はまずこの整理できた手順をイメージして分析に取り組み、その中で、今は一回ここに戻った方がいいなとか、今回は先にこれをやろうなど、適宜工夫をしながら、さらに自分の中で手順イメージをブラッシュアップしていきたいと思います。

脚注
  1. 売上履歴のデータに注目すると、item_nameやitem_priceに欠損値や表記の整合性がない事に気が付くかと思います。このようにデータ等で顕在する入力ミスや表記方法の違い等が混在し、不整合を起こしている状態を「データの揺れ」と言います。(中略)データの揺れを解消し、整合性を担保する事はデータ分析を行うのに基礎となるべき重要な点です。(中略)データの揺れが残ったまま集計・分析を行っても、全く意味のない結果となってしまいます(p.40-44) ↩︎

  2. p.25 ↩︎

Discussion