追記

  • 2016年の入場者数のデータに更新しました。
  • Bokeh 0.12.6に対応しました。

Pythonでインタラクティブなグラフを描画できるライブラリ「Bokeh」を使って、Wikipediaに掲載されている世界テーマパーク入場者数ランキングを、可視化してみました。

今回は、Bokehのグラフを手軽に描画できるbokeh.charts bkchartsを使いました。

追記(Ver.0.12.6)

Bokeh 0.12.6から、bokeh.chartsは別モジュール(bkcharts)に分離されました。Ver.1.0までは、互換性維持の為に引き続きbokeh.chartsも使用することができますが、importする際に下記の警告が表示されます。

BokehDeprecationWarning:
The bokeh.charts API has moved to a separate 'bkcharts' package.

This compatibility shim will remain until Bokeh 1.0 is released.
After that, if you want to use this API you will have to install
the bkcharts package explicitly.

1.   作成したグラフ

Wikipediaの世界テーマパーク入場者数ランキングは、クリエイティブ・コモンズ 表示-継承ライセンスの下で公開されているので、このグラフのラインセンスも、それを継承してクリエイティブ・コモンズ 表示-継承ライセンスです。

ユニバーサル・スタジオ・ジャパンの入場者数が近年増加していることが分かります。2015年の時点で東京ディズニーシーの入園者数を超えていますが、2016年時点でまだ東京ディズニーランドの入園者数は超えられていません。

2.   前提バージョン

この記事で前提としているのは、下記のバージョンです。

  • python : Ver.3.5.3
  • pandas : Ver.0.20.1
  • bokeh : Ver.0.12.6

3.   必要なモジュールのインポート

1
2
3
4
5
import pandas as pd
# from bokeh.charts import Line  # Ver.0.12.5以前
from bkcharts import Line  # Ver.0.12.6以降
from bokeh.io import show
from bokeh.models import NumeralTickFormatter

4.   WikipediaからDataFrameを抽出・取得

pandasのread_html関数を使います。

1
dflist = pd.read_html('https://en.wikipedia.org/wiki/List_of_amusement_park_rankings', header=0, index_col=0)

2つ目のDataFrameが、世界のテーマパーク入場者数ランキングの表です。

2
3
df = dflist[1]
df.info()
<class 'pandas.core.frame.DataFrame'>
Float64Index: 45 entries, 1.0 to 45.0
Data columns (total 12 columns):
Amusement park    45 non-null object
Location          45 non-null object
2006[1]           35 non-null float64
2008[3]           34 non-null float64
2009[4]           38 non-null float64
2010[5]           40 non-null float64
2011[6]           39 non-null float64
2012[7]           40 non-null float64
2013[8]           40 non-null float64
2014[9]           41 non-null float64
2015[10]          43 non-null float64
2016[11]          45 non-null int64
dtypes: float64(9), int64(1), object(2)
memory usage: 4.6+ KB

5.   データの整形・クリーンナップ

5.1.   カラム名の「[1]」等の注釈を取り除く

「[数字]」(正規表現で指定)を空文字列に置換します。

1
2
df.columns = df.columns.str.replace('\[\d+\]', '')
df.columns
Index(['Amusement park', 'Location', '2006', '2008', '2009', '2010', '2011',
       '2012', '2013', '2014', '2015', '2016'],
      dtype='object')

5.2.   インデックスをテーマパーク名にする

indexを「Amusement park」カラムで置き換えます。

1
df.index
Float64Index([ 1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,  9.0, 10.0, 11.0,
              12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0,  nan, 22.0,
              23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0,
              34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0,
              45.0],
             dtype='float64', name='Rank')
2
3
df.index = df['Amusement park']
df.index
Index(['Magic Kingdom at Walt Disney World Resort',
       'Disneyland Park at Disneyland Resort',
       'Tokyo Disneyland at Tokyo Disney Resort', 'Universal Studios Japan',
       'Tokyo DisneySea at Tokyo Disney Resort',
       'Epcot at Walt Disney World Resort',
       'Disney's Animal Kingdom at Walt Disney World Resort',
       'Disney's Hollywood Studios at Walt Disney World Resort',
       'Universal Studios Florida at Universal Orlando Resort',
       'Universal's Islands of Adventure at Universal Orlando Resort',
       'Disney California Adventure Park at Disneyland Resort',
       'Chimelong Ocean Kingdom', 'Disneyland Park at Disneyland Paris',
       'Lotte World', 'Universal Studios Hollywood',
       'Everland at Everland Resort',
       'Hong Kong Disneyland at Hong Kong Disneyland Resort',
       'Ocean Park Hong Kong', 'Nagashima Spa Land',
       'Europa-Park at Europa-Park Resort',
       'Shanghai Disneyland Park at Shanghai Disney Resort',
       'Walt Disney Studios Park at Disneyland Paris', 'Efteling',
       'Tivoli Gardens', 'SeaWorld Orlando', 'Busch Gardens Tampa Bay',
       'Universal Studios Singapore', 'Knott's Berry Farm', 'OCT East',
       'Window of the World at OCT Resort', 'Happy Valley at OCT Resort',
       'Chimelong Paradise', 'Happy Valley', 'Canada's Wonderland',
       'PortAventura Park at PortAventura World', 'Cedar Point',
       'SeaWorld San Diego', 'Fantawild Adventure',
       'Fantawild Oriental Heritage', 'Kings Island',
       'Six Flags Magic Mountain', 'Hersheypark', 'Six Flags Great Adventure',
       'Liseberg', 'Six Flags Great America'],
      dtype='object', name='Amusement park')

5.3.   テーマパーク名と場所のカラムを削除

drop関数を使って、カラムを削除します。

1
2
df.drop(['Location', 'Amusement park'], axis=1, inplace=True)
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 45 entries, Magic Kingdom at Walt Disney World Resort to Six Flags Great America
Data columns (total 10 columns):
2006    35 non-null float64
2008    34 non-null float64
2009    38 non-null float64
2010    40 non-null float64
2011    39 non-null float64
2012    40 non-null float64
2013    40 non-null float64
2014    41 non-null float64
2015    43 non-null float64
2016    45 non-null int64
dtypes: float64(9), int64(1)
memory usage: 3.9+ KB

5.4.   カラムとインデックスを入れ替える(転置)

1
2
dft = df.T
dft.info()
<class 'pandas.core.frame.DataFrame'>
Index: 10 entries, 2006 to 2016
Data columns (total 45 columns):
Magic Kingdom at Walt Disney World Resort                       10 non-null float64
Disneyland Park at Disneyland Resort                            10 non-null float64
Tokyo Disneyland at Tokyo Disney Resort                         10 non-null float64
Universal Studios Japan                                         10 non-null float64
Tokyo DisneySea at Tokyo Disney Resort                          10 non-null float64
Epcot at Walt Disney World Resort                               10 non-null float64
Disney's Animal Kingdom at Walt Disney World Resort             10 non-null float64
Disney's Hollywood Studios at Walt Disney World Resort          10 non-null float64
Universal Studios Florida at Universal Orlando Resort           10 non-null float64
Universal's Islands of Adventure at Universal Orlando Resort    10 non-null float64
Disney California Adventure Park at Disneyland Resort           10 non-null float64
Chimelong Ocean Kingdom                                         3 non-null float64
Disneyland Park at Disneyland Paris                             10 non-null float64
Lotte World                                                     10 non-null float64
Universal Studios Hollywood                                     10 non-null float64
Everland at Everland Resort                                     10 non-null float64
Hong Kong Disneyland at Hong Kong Disneyland Resort             10 non-null float64
Ocean Park Hong Kong                                            10 non-null float64
Nagashima Spa Land                                              10 non-null float64
Europa-Park at Europa-Park Resort                               10 non-null float64
Shanghai Disneyland Park at Shanghai Disney Resort              1 non-null float64
Walt Disney Studios Park at Disneyland Paris                    10 non-null float64
Efteling                                                        10 non-null float64
Tivoli Gardens                                                  10 non-null float64
SeaWorld Orlando                                                10 non-null float64
Busch Gardens Tampa Bay                                         10 non-null float64
Universal Studios Singapore                                     7 non-null float64
Knott's Berry Farm                                              10 non-null float64
OCT East                                                        7 non-null float64
Window of the World at OCT Resort                               8 non-null float64
Happy Valley at OCT Resort                                      10 non-null float64
Chimelong Paradise                                              8 non-null float64
Happy Valley                                                    8 non-null float64
Canada's Wonderland                                             10 non-null float64
PortAventura Park at PortAventura World                         10 non-null float64
Cedar Point                                                     10 non-null float64
SeaWorld San Diego                                              10 non-null float64
Fantawild Adventure                                             2 non-null float64
Fantawild Oriental Heritage                                     1 non-null float64
Kings Island                                                    10 non-null float64
Six Flags Magic Mountain                                        9 non-null float64
Hersheypark                                                     10 non-null float64
Six Flags Great Adventure                                       9 non-null float64
Liseberg                                                        10 non-null float64
Six Flags Great America                                         2 non-null float64
dtypes: float64(45)
memory usage: 3.9+ KB
3
dft.head()
Amusement park Magic Kingdom at Walt Disney World Resort Disneyland Park at Disneyland Resort Tokyo Disneyland at Tokyo Disney Resort Universal Studios Japan Tokyo DisneySea at Tokyo Disney Resort Epcot at Walt Disney World Resort Disney's Animal Kingdom at Walt Disney World Resort Disney's Hollywood Studios at Walt Disney World Resort Universal Studios Florida at Universal Orlando Resort Universal's Islands of Adventure at Universal Orlando Resort ... Cedar Point SeaWorld San Diego Fantawild Adventure Fantawild Oriental Heritage Kings Island Six Flags Magic Mountain Hersheypark Six Flags Great Adventure Liseberg Six Flags Great America
2006 16640000.0 14730000.0 12900000.0 8500000.0 12100000.0 10460000.0 9100000.0 8910000.0 6000000.0 5300000.0 ... 3070000.0 4260000.0 NaN NaN 3050000.0 2550000.0 2690000.0 2730000.0 2950000.0 NaN
2008 17063000.0 14721000.0 14293000.0 8300000.0 12498000.0 10935000.0 9540000.0 9608000.0 6231000.0 5297000.0 ... 3198000.0 4174000.0 NaN NaN 3126000.0 NaN 2842000.0 2761000.0 3050000.0 NaN
2009 17233000.0 15900000.0 13646000.0 8000000.0 12004000.0 10990000.0 9590000.0 9700000.0 5530000.0 4627000.0 ... 2942000.0 4200000.0 NaN NaN 3000000.0 2500000.0 2807000.0 2634000.0 3150000.0 NaN
2010 16972000.0 15980000.0 14452000.0 8160000.0 12663000.0 10825000.0 9686000.0 9603000.0 5925000.0 5949000.0 ... 3051000.0 3800000.0 NaN NaN 3112000.0 2600000.0 2891000.0 2700000.0 2900000.0 NaN
2011 17142000.0 16140000.0 13996000.0 8500000.0 11930000.0 10825000.0 9783000.0 9699000.0 6044000.0 7674000.0 ... 3143000.0 4294000.0 NaN NaN 3143000.0 2700000.0 2949000.0 NaN 2900000.0 NaN

5 rows × 45 columns

5.5.   DataFrameを、上位6パークのみに絞り込む

今回は(Wikipediaの表がそのようなレイアウトになっているので)、左から6カラム目までが上位6パーク(2016年時点)です。

1
2
dfplot = dft.iloc[:, 0:6]
dfplot
Amusement park Magic Kingdom at Walt Disney World Resort Disneyland Park at Disneyland Resort Tokyo Disneyland at Tokyo Disney Resort Universal Studios Japan Tokyo DisneySea at Tokyo Disney Resort Epcot at Walt Disney World Resort
2006 16640000.0 14730000.0 12900000.0 8500000.0 12100000.0 10460000.0
2008 17063000.0 14721000.0 14293000.0 8300000.0 12498000.0 10935000.0
2009 17233000.0 15900000.0 13646000.0 8000000.0 12004000.0 10990000.0
2010 16972000.0 15980000.0 14452000.0 8160000.0 12663000.0 10825000.0
2011 17142000.0 16140000.0 13996000.0 8500000.0 11930000.0 10825000.0
2012 17536000.0 15963000.0 14847000.0 9700000.0 12656000.0 11063000.0
2013 18588000.0 16202000.0 17214000.0 10100000.0 14084000.0 11229000.0
2014 19332000.0 16769000.0 17300000.0 11800000.0 14100000.0 11454000.0
2015 20492000.0 18278000.0 16600000.0 13900000.0 13600000.0 11798000.0
2016 20395000.0 17943000.0 16540000.0 14500000.0 13460000.0 11712000.0

6.   グラフの描画

6.1.   まずは描画してみる

bokeh.chartsLineを使って、線グラフを描いてみます。

1
2
p = Line(dfplot)
show(p)

6.2.   見映えを変更

オプションを指定して、グラフを見易く調整します。

  • グラフのタイトルを設定
  • X軸、Y軸にラベルを設定
  • Y軸の数字表記を、カンマ付きの読み易い表記に変更
1
2
3
p = Line(dft.iloc[:, 0:6], title='テーマパーク入場者数', xlabel='年', ylabel='入場者数(人)')
p.yaxis.formatter = NumeralTickFormatter(format='0,0')
show(p)

6.3.   X軸の間隔を補正

2007年のデータがないので、X軸の間隔が一定ではなくなってしまっています。 indexをDateTimeIndexに変換して、改善します。

1
dft.index
Index(['2006', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015',
       '2016'],
      dtype='object')
2
3
dft.index = pd.to_datetime(dft.index)
dft.index
DatetimeIndex(['2006-01-01', '2008-01-01', '2009-01-01', '2010-01-01',
               '2011-01-01', '2012-01-01', '2013-01-01', '2014-01-01',
               '2015-01-01', '2016-01-01'],
              dtype='datetime64[ns]', freq=None)

6.4.   再び描画

1
2
3
4
p = Line(dft.iloc[:, 0:6], title='テーマパーク入場者数', xlabel='年', ylabel='入場者数(人)')
# y軸の数字表記変更
p.yaxis.formatter = NumeralTickFormatter(format='0,0')
show(p)

これで完成です。