Python 3におけるpandasとJupyter Notebookを用いたデータ分析と可視化
紹介
Pythonの
pandasパッケージはスプレッドシートの機能を提供しますが、Pythonを使用しているため、従来のグラフィカルスプレッドシートプログラムよりもはるかに速く効率的です。
このチュートリアルでは、大規模なデータセットのセットアップ、pandasのgroupby()およびpivot_table()関数の使用方法、そしてデータの視覚化方法について説明します。
pandasパッケージに慣れるために、私たちのチュートリアル「Python 3におけるpandasパッケージとそのデータ構造の紹介」を読むことができます。
前提条件
このガイドでは、ローカルデスクトップまたはリモートサーバー上でのpandasを使用したデータ処理について説明します。大規模なデータセットを扱うことはメモリ使用量が多くなる可能性があるため、いずれの場合もこのガイドの計算を実行するにはコンピュータに少なくとも2GBのメモリが必要です。
このチュートリアルでは、データを扱うためにJupyter Notebookを使用します。すでにインストールしていない場合は、Python 3のためにJupyter Notebookをインストールして設定する手順に従ってください。
データの設定
このチュートリアルでは、アメリカ合衆国の社会保障データに基づいた赤ちゃんの名前に関するデータを、社会保障のウェブサイトから8MBのzipファイルとして入手し、それを使って作業を進めます。
正しいディレクトリからローカルマシンまたはサーバーでPython 3プログラミング環境を起動しましょう:
- cd environments
- . my_env/bin/activate
さあ、プロジェクトのために新しいディレクトリを作成しましょう。それをnamesと呼び、そのディレクトリに移動しましょう:
- mkdir names
- cd names
このディレクトリ内では、curlコマンドを使用して社会保障局のウェブサイトからzipファイルを取得することができます。
- curl -O https://www.ssa.gov/oact/babynames/names.zip
ファイルがダウンロードされたら、使用するすべてのパッケージがインストールされていることを確認しましょう:
numpyは多次元配列をサポートするために使用されますmatplotlibを使用してデータを可視化するpandasは私たちのデータ分析のためですseabornを使って、私たちの matplotlib の統計グラフをより美しくします。
もしまだそのパッケージがインストールされていなければ、pipを使ってインストールしてください。次のように:
- pip install pandas
- pip install matplotlib
- pip install seaborn
もしまだインストールされていない場合は、numpy パッケージもインストールされます。
さあ、Jupyter Notebookを起動できます。
- jupyter notebook
Jupyter Notebookのウェブインターフェースに入ったら、そこでnames.zipファイルが見えるようになります。
新しいノートブックファイルを作成するには、右上のプルダウンメニューから新規 > Python 3を選択してください。
これによりノートブックが開きます。
使用するパッケージをインポートすることから始めましょう。ノートブックの最上部に、次のように書きます:
import numpy as np
import matplotlib.pyplot as pp
import pandas as pd
import seaborn
このコードを実行し、ALT + ENTERを入力することで新しいコードブロックに移動できます。
Pythonノートブックにグラフをインラインで表示するように指示しましょう:
matplotlib inline
コードを実行して、ALT + ENTERを入力して続けましょう。
ここから、ZIPアーカイブの解凍に進み、CSVデータセットをpandasにロードし、その後pandasのデータフレームを連結します。
ZIPアーカイブを解凍する
現在のディレクトリにzipアーカイブを解凍するために、zipfileモジュールをインポートし、次にファイル名(この場合はnames.zip)を使ってZipFile関数を呼び出します:
import zipfile
zipfile.ZipFile('names.zip').extractall('.')
コードを実行し、ALT + ENTERを入力して続けることができます。
さて、あなたの names ディレクトリを見返すと、CSV形式の名前データの .txt ファイルがあるはずです。これらのファイルは、1881年から2015年までのデータの年に対応しています。これらのファイルは、似たような命名規則に従っています。例えば、2015年のファイルは yob2015.txt と呼ばれ、1927年のファイルは yob1927.txt と呼ばれています。
これらのファイルの形式を見るために、Pythonを使って1つを開き、最初の5行を表示してみましょう:
open('yob2015.txt','r').readlines()[:5]
コードを実行し、ALT + ENTERで続けてください。
['Emma,F,20355\n',
'Olivia,F,19553\n',
'Sophia,F,17327\n',
'Ava,F,16286\n',
'Isabella,F,15504\n']
データのフォーマットは、名前が最初(例:エマやオリビア)、次に性別(女性の名前にはF、男性の名前にはM)、最後にその年にその名前で生まれた赤ちゃんの数です(2015年に生まれたエマという名前の赤ちゃんは20,355人いました)。
この情報を使って、pandasにデータを読み込むことができます。
pandasにCSVデータをロードする
カンマ区切りの値データをpandasに読み込むために、pd.read_csv()関数を使用し、テキストファイルの名前と決定した列名を渡します。これを変数に代入します。この場合、2015年の出生データを使用しているため、names2015とします。
names2015 = pd.read_csv('yob2015.txt', names = ['Name', 'Sex', 'Babies'])
ALT + ENTERを押してコードを実行し、続行します。
これがうまくいったことを確認するために、テーブルの一番上を表示しましょう:
names2015.head()
コードを実行して ALT + ENTER を続けると、次のような出力が表示されます:
私たちのテーブルには、名前、性別、および各名前で生まれた赤ちゃんの数の情報が列ごとに整理されています。
pandasオブジェクトを連結する
pandasオブジェクトを連結することにより、namesディレクトリ内のすべての別々のテキストファイルを操作することができます。
これらを連結するには、まず変数を未 populated リストデータ型に割り当てることによってリストを初期化する必要があります:
all_years = []
それが終わったら、1880年から2015年までのすべてのファイルを年ごとに反復するために for ループを使います。2015年をループに含めるために、2015年の末尾に +1 を追加します。
all_years = []
for year in range(1880, 2015+1):
ループ内で、各テキストファイルの値をリストに追加し、それぞれのファイル名の違いを処理するために文字列フォーマッターを使用します。それらの値をyear変数に渡します。再度、Name、Sex、およびBabiesの列を指定します:
all_years = []
for year in range(1880, 2015+1):
all_years.append(pd.read_csv('yob{}.txt'.format(year),
names = ['Name', 'Sex', 'Babies']))
さらに、各年のための列を作成して、それらを順序付けて保持します。これを、ループが進行するにつれてインデックス-1を使ってそれらを指すことで、各反復の後に行うことができます。
all_years = []
for year in range(1880, 2015+1):
all_years.append(pd.read_csv('yob{}.txt'.format(year),
names = ['Name', 'Sex', 'Babies']))
all_years[-1]['Year'] = year
最後に、pd.concat() 関数を使用して、これを pandas オブジェクトに連結して追加します。この情報を保存するために all_names という変数を使用します。
all_years = []
for year in range(1880, 2015+1):
all_years.append(pd.read_csv('yob{}.txt'.format(year),
names = ['Name', 'Sex', 'Babies']))
all_years[-1]['Year'] = year
all_names = pd.concat(all_years)
今、ALT + ENTERを使ってループを実行し、その後、結果として得られたテーブルの末尾(最も下の行)を表示して出力を確認できます:
all_names.tail()
私たちのデータセットは現在完成しており、pandasで追加の作業を行う準備ができています。
データのグルーピング
pandasを使用すると、.groupby()関数を使って列ごとにデータをグループ化できます。私たちのフルデータセットのためのall_names変数を使って、groupby()を使用してデータを異なるバケットに分割できます。
データセットを性別と年でグループ化しましょう。このように設定できます:
group_name = all_names.groupby(['Sex', 'Year'])
私たちはコードを実行し、ALT + ENTERで続けることができます。
この時点で、group_name 変数を呼び出すと、この出力が得られます:
<pandas.core.groupby.DataFrameGroupBy object at 0x1187b82e8>
これは、DataFrameGroupBy オブジェクトであることを示しています。このオブジェクトにはデータをグループ化する方法に関する指示がありますが、値を表示する方法に関する指示はありません。
値を表示するには、指示を出す必要があります。たとえば、.size()、.mean()、および.sum()を計算することで、テーブルを返すことができます。
.size()を使って始めましょう:
group_name.size()
コードを実行し、ALT + ENTERを続けると、出力はこのようになります。
Sex Year
F 1880 942
1881 938
1882 1028
1883 1054
1884 1172
...
このデータは良さそうですが、もう少し読みやすくすることができます。.unstack 関数を追加することで、より読みやすくすることができます:
group_name.size().unstack()
今、コードを実行し、ALT + ENTERを入力すると、出力は次のようになります:
このデータが示すのは、各年における女性名と男性名の数です。例えば、1889年には1,479の女性名と1,111の男性名が存在しました。2015年には18,993の女性名と13,959の男性名がありました。これは、時が経つにつれて名前の多様性が増していることを示しています。
赤ちゃんの出生総数を求めるには、.sum() 関数を使用できます。以前作成した単一の yob2015.txt ファイルからの names2015 セットという小さなデータセットに適用してみましょう:
names2015.groupby(['Sex']).sum()
コードを実行して続けるために ALT + ENTER を入力しましょう:
これは2015年に生まれた男の赤ちゃんと女の赤ちゃんの総数を示していますが、そのデータセットにはその年に少なくとも5回使用された名前を持つ赤ちゃんのみがカウントされています。
pandas .groupby() 関数は、データを意味のあるグループに分割することを可能にします。
ピボットテーブル
ピボットテーブルはデータを要約するために便利です。これらは、1つのテーブルに保存されたデータを自動的にソート、カウント、合計、または平均することができます。そして、それらの操作の結果を要約されたデータの新しいテーブルに表示することができます。
pandasで、pivot_table()関数はピボットテーブルを作成するために使用されます。
ピボットテーブルを作成するには、まず作業したいDataFrameを呼び出し、次に表示したいデータとそれらのグループ化の方法を指定します。
この例では、all_namesデータを使用し、名前でグループ化された赤ちゃんのデータを1次元に、年を別の次元に表示します:
pd.pivot_table(all_names, 'Babies', 'Name', 'Year')
ALT + ENTERを押してコードを実行し続けると、次の出力が表示されます:
これは多くの空の値を示しているため、一方では行として、他方では列として名前と年を列として保持したいかもしれません。それを達成するために、データを角括弧でグループ化することができます。
pd.pivot_table(all_names, 'Babies', ['Name', 'Year'])
コードを実行して続行するために ALT + ENTER をタイプすると、この表は各名前の記録にある年のデータのみを表示するようになります。
Name Year
Aaban 2007 5.0
2009 6.0
2010 9.0
2011 11.0
2012 11.0
2013 14.0
2014 16.0
2015 15.0
Aabha 2011 7.0
2012 5.0
2014 9.0
2015 7.0
Aabid 2003 5.0
Aabriella 2008 5.0
2014 5.0
2015 5.0
さらに、データをグループ化して、名前と性別を1つの次元として、年を別の次元にすることができます。
pd.pivot_table(all_names, 'Babies', ['Name', 'Sex'], 'Year')
コードを実行し、ALT + ENTERを続けると、次の表が表示されます:
ピボットテーブルを使うと、既存のテーブルから新しいテーブルを作成でき、データのグループ化方法を決定できます。
データを可視化する
pandasをmatplotlibなどの他のパッケージと併用することで、ノートブック内でデータを視覚化することができます。
私たちは、ある名前の人気の推移を視覚化します。そのためには、データを再処理して特定の名前の人気の変化を見ることができるように、インデックスを設定してソートする必要があります。
pandas パッケージを使用すると、階層的または多層インデックスを実行でき、任意の次元数のデータを保存および操作することができます。
私たちはデータを性別、次に名前、次に年でインデックス化する予定です。インデックスをソートすることも必要です。
all_names_index = all_names.set_index(['Sex','Name','Year']).sort_index()
次の行に進むために、ALT + ENTERを入力して実行し、新しいインデックス付きデータフレームをノートブックに表示させます:
all_names_index
コードを実行し、ALT + ENTERで続けると、出力は次のようになります。
次に、名前の人気を時間の経過とともにプロットする関数を書きたいと思います。この関数をname_plotと呼び、sexとnameをパラメータとして渡します。この関数を実行するときに呼び出します。
def name_plot(sex, name):
今回は、作成したテーブルを保持するためにdataという変数を設定します。また、インデックスの値で行を選択するためにpandasのDataFramelocを使用します。私たちのケースでは、locをMultiIndexのフィールドの組み合わせに基づいて、sexとnameのデータの両方を参照するようにしたいと思います。
この構造を私たちの関数に書き入れましょう:
def name_plot(sex, name):
data = all_names_index.loc[sex, name]
最後に、matplotlib.pyplotをppとしてインポートしたので、値をプロットしたいと思います。次に、性別と名前のデータの値をインデックスに対してプロットします。ここでのインデックスは年です。
def name_plot(sex, name):
data = all_names_index.loc[sex, name]
pp.plot(data.index, data.values)
ALT + ENTERを押して実行し、次のセルに移動します。これで、性別と任意の名前を使って関数を呼び出すことができます。例えば、女性の名前を指定するには、Fを使用し、名前をDanicaとします。
name_plot('F', 'Danica')
今、ALT + ENTERを入力すると、以下の出力が得られます:
使用しているシステムによってはフォントの置換に関する警告が表示されることがありますが、データは正しくプロットされますのでご安心ください。
視覚化を見ると、女性の名前ダニカは1990年頃にわずかな人気の上昇があり、2010年の直前にピークに達したことがわかります。
私たちが作成した関数は、複数の名前からデータをプロットするために使用できるため、さまざまな名前にわたる時間の経過に伴う傾向を確認できます。
まず、プロットを少し大きくしましょう:
pp.figure(figsize = (18, 8))
次に、プロットしたいすべての名前のリストを作成しましょう。
pp.figure(figsize = (18, 8))
names = ['Sammy', 'Jesse', 'Drew', 'Jamie']
さて、for ループを使ってリストを繰り返し処理し、各名前のデータをプロットできます。まず、これらの性別中立の名前を女性の名前として試してみましょう:
pp.figure(figsize = (18, 8))
names = ['Sammy', 'Jesse', 'Drew', 'Jamie']
for name in names:
name_plot('F', name)
このデータを理解しやすくするために、凡例を含めましょう:
pp.figure(figsize = (18, 8))
names = ['Sammy', 'Jesse', 'Drew', 'Jamie']
for name in names:
name_plot('F', name)
pp.legend(names)
コードを実行して続けるために ALT + ENTER を入力し、その後、次の出力を受け取ります:
各名前は徐々に女性名として人気を得てきましたが、1980年頃にはジャミーという名前が圧倒的に女性名として人気がありました。
同じ名前をプロットしましょうが、今回は男性の名前として:
pp.figure(figsize = (18, 8))
names = ['Sammy', 'Jesse', 'Drew', 'Jamie']
for name in names:
name_plot('M', name)
pp.legend(names)
もう一度、ALT + ENTERを入力してコードを実行し続けてください。グラフはこのようになります:
このデータは、名前の人気がさらに高まっていることを示しており、ジェシーが一般的に最も人気のある選択肢であり、特に1980年代と1990年代に人気がありました。
ここから、名前データで遊び続けたり、さまざまな名前やその人気に関する視覚化を作成したり、異なるデータを視覚化するための他のスクリプトを作成したりできます。
結論
このチュートリアルでは、データのセットアップから、データを groupby() および pivot_table() でグループ化し、MultiIndex を使ってデータにインデックスを付け、matplotlib パッケージを用いて pandas データを視覚化する方法について紹介しました。
多くの組織や機関は、pandasやデータビジュアライゼーションについて学び続けるために利用できるデータセットを提供しています。たとえば、米国政府はdata.govを通じてデータを提供しています。
matplotlibを使用してデータを視覚化する方法については、「Python 3でmatplotlibを使ってデータをプロットする方法」と「Python 3でmatplotlibを使って単語の頻度をグラフ化する方法」のガイドを参照することで詳しく学ぶことができます。










