2012/08/28

文系のための「数の可視化」(4)

数量の可視化に対しては棒グラフ比率の可視化に対しては円グラフを用いた。
では、変化する値の可視化に対しては何を使うべきか?というのが今回の話。
実は、多くの人が意識していないのが、この種のデータの可視化方法。

「とりあえず、棒グラフで良いですかね〜?」

とする人は意外に多いように思える。いや、言ってる人を最近見た。

特別な理由があるわけでもなく、このような選択をしてはいないか?
Excelなど、多くのスプレッドシートソフトは、既定の状態で、棒グラフを表示する。

「いやいや、ちゃんと考えて可視化方法を選びなさい。」

という私のコメントに、

「えっ、グラフの選び方ってあるんですか?聞いたことありません。」

と言われた。ええ、有ります。物事の理屈を考えない人が多い。
重要なことは何を表現したいか、あるいは、どのように表現したいか?

確かに、ある時点における数量を表しているのだから、
棒グラフでも可視化方法として正しいのかもしれない。
しかしながら、ここで改めて考えてみたいことがある。

そもそも、「変化」とは何か?という問題である。

我々が変化する値を観察するときに留意するべきことは、
ある観測した瞬間と、次の瞬間の間にも変化が起きているか、ということである。
実は、折れ線グラフは、そのような連続的な変化を可視化する方法である。

棒グラフの話でも述べたが、基本的に棒グラフに順番は関係無い。
一方、折れ線グラフは、変化の順番が大きな意味が持ち、
その順番は、一般的には時間的な軸に従う順番である。

また、棒グラフ円グラフと同様に、
折れ線グラフを描く際には、色々と注意すべきこともある。
適当に描いて良いという訳ではない。

とりあえず、今回もRで実演しながら折れ線グラフについて考えてみよう。
今回は、法務省出入国管理統計統計表の「国籍別入国外国人(1950−2005)」の一部と、
財務省貿易統計年別輸出入総額のデータを編集し、合わせたもの。

国籍別入国外国人のデータは非常に大きいため、数カ国に絞っている
国の選択は、私の個人的な興味であり、選択された国同士には何の脈絡も無い
なお、年別輸出入総額のデータの単位は「千円」である。

いつものように。

# Windows と Linux の人は次のコマンド
X <- read.table("clipboard", sep=",")

# Mac の人は次のコマンド
X <- read.table(pipe("pbpaste") , sep=",")

データを読み込む。

China,Taiwan,Xianggang,Korea,Philippine,USA,Brazil,Export,Import
1964,562,12357,3844,18865,9105,129908,823,2402348862,2857515493
1965,576,15583,4326,17065,8945,150253,782,3042627204,2940846741
1966,503,17744,4229,25791,10824,173593,1154,3519500700,3428172558
1967,150,20789,4840,33558,12028,187373,1516,3758966022,4198711492
1968,11,25115,5567,39634,14190,199581,2196,4669798348,4675407477
1969,16,27501,6199,44654,13573,255663,2983,5756405162,5408472791
1970,139,46535,12506,71790,20477,315211,5866,6954367159,6797220528
1971,283,38774,8112,77545,12672,271029,3270,8392768263,6909956155
1972,994,47536,12645,85757,10860,281853,4772,8806072248,7228978838
1973,1991,50866,16893,110804,7874,287988,6734,10031426859,10404355041
1974,3161,60876,15463,124263,10411,245826,5765,16207879577,18076381928
1975,4441,77091,19318,129186,12574,237219,6522,16545313718,17170026976
1976,4018,82898,22721,141123,13073,274686,7804,19934618464,19229168610
1977,4039,88813,25620,156605,16138,303755,6153,21648070431,19131779700
1978,5951,99237,23273,184101,20375,275240,7696,20555840563,16727624005
1979,11622,165708,24094,199146,23223,256507,7488,22531538859,24245350997
1980,18336,235549,32239,212973,27902,277980,8381,29382471938,31995325202
1981,17550,305233,46614,250709,37483,310726,9438,33468984502,31464145741
1982,20532,311125,59898,284598,37878,352208,10336,34432500947,32656302574
1983,26606,331634,64727,283971,47887,400984,7992,34909268599,30014784056
1984,51010,351294,52750,292483,49511,437745,9250,40325293701,32321126640
1985,100972,356934,49153,296708,65529,487713,13889,41955659471,31084935207
1986,75275,300272,32271,299602,80508,482670,13434,35289713887,21550717070
1987,73030,360636,30569,360159,85267,479891,12126,33315191383,21736912673
1988,112389,392723,29127,515807,86567,457620,16789,33939183158,24006319859
1989,100144,501907,32007,806065,88296,538117,29241,37822534626,28978572581
1990,117814,610652,38622,978984,108292,564958,67303,41456939674,33855207638
1991,142150,686076,37483,1097601,125329,554147,96337,42359892974,31900153522
1992,187681,745835,39460,1094724,120660,574181,81495,43012281444,29527419360
1993,204302,700294,33391,1069450,109353,549090,70719,40202448725,26826357239
1994,210476,681183,31535,1140372,126739,548265,72236,40497552697,28104327343
1995,229965,614931,20378,1103566,105838,558474,90322,41530895121,31548753881
1996,257393,756785,27761,1224441,106394,606652,94068,44731311206,37993421106
1997,283467,857877,30806,1236597,124856,642933,104323,50937991859,40956182573
1998,299573,874985,53278,960556,129053,688006,77569,50645003938,36653647183
1999,327005,963701,42283,1160034,144305,720142,70794,47547556241,35268008063
2000,385296,944019,49423,1286583,169755,749343,101513,51654197760,40938422968
2001,444441,838001,74704,1342987,186262,715036,81800,48979244311,42415533002
2002,527796,909654,136482,1472096,197136,755196,71763,52108955735,42227505945
2003,537700,816692,163254,1621903,209525,678935,79692,54548350172,44362023352
2004,741659,1117950,226321,1774872,236291,785916,79960,61169979094,49216636346
2005,780924,1315594,250366,2008418,221309,853845,91268,65656544157,56949392181

このデータは、毎年の国籍入国者のデータと毎年の輸出入総額のデータである。
この二種類のデータは、本来は毎日のデータであって、それが年毎に集計されている。

また、年毎に区切っているのは、あくまで、等間隔に観測地点を置くためであって、
毎年のデータの切れ目は実質的に無意味である。したがって、

1月1日〜12月31日を一つの区切りにしようが、
4月1日〜3月31日を区切りにしようが、
9月1日〜8月31日を区切りにしようが、

実質的には変わらないハズ。

重要なことは、実際には常に変化しているが、
観測上の区切りが必要であるというイメージが出来ていること。
折れ線グラフというのは、このイメージを反映した可視化法なのである。


先頭が中国なので、まずは、中国のグラフから書いてみる。
plot(X$China, type="l")

流石に、これは大丈夫であろう。「l」は「line」の頭文字。折れ線グラフを描く。
何が足りていないかも大丈夫であろう。そうそう。タイトルが抜けている。

plot(X$China, type="l")
title("The number of Chinese entrants to Japan")

この状態でもグラフとしては不完全である。
まず、x軸とy軸の軸ラベル、目盛りラベルが不適切である。
# 折れ線グラフを書いてみる
plot(X$China, type="l", xlab="Year", ylab="The number of entry", xaxt="n")

# タイトルを加える
title("The number of Chinese entrants to Japan")

# x軸の目盛りラベルを付ける
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)

ここでは、plot()関数で、xaxt="n"とし、この段階ではx軸を描かず
最後のaxis()関数で、改めて描き直している。

ふむ。これで、一応の体裁は整った。しかし、y軸の目盛りラベルが見難い。
数が非常に大きいので、表現の仕方がコンピュータ上での表現になっている。
とりあえず、見やすいように、千人単位にしてみる。
# 折れ線グラフを描く
plot(X$China, type="l", xlab="Year", ylab="The number of entry", xaxt="n", yaxt="n")

# タイトルを加える
title("The number of Chinese entrants to Japan")

# x軸の目盛りラベルを配置し、
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)

# y軸の目盛りラベルを単位を変えて配置する
axis(side=2, at=seq(0, 8e+05, by=1e+05), labels=seq(0, 8e+02, by=1e+02), las=2)

さて、これで、かなり見やすくなったのが、
y軸の目盛りラベルを修正したことによって、y軸の単位が変更されている。
軸ラベルを「1000人単位」であることが解るように修正しなければならない。
plot(X$China, type="l", xlab="Year", ylab="The number of entry (Thousands)", xaxt="n", yaxt="n")
title("The number of Chinese entrants to Japan")
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=c(2e+05, 4e+05, 6e+05, 8e+05), labels=c(2e+02, 4e+02, 6e+02, 8e+02), las=2)

これで、何のデータであるかが理解できる。

折れ線グラフというのは、このように、変化を表すために利用する。
実は、線で結ばれているのは、ある観測点から次の観測点の間も、
実は、変化し続けている」という状況を表している。

このように、本来は存在するのだけれど、
実際には観測していないので、仮説的に観測点同士をつなげることを、
補間(interpolation)」と呼ぶ。

補間は、折れ線グラフのように1次元のデータだけではなく二次元での補間もある。
二次元での補間を行うことで、数学的に等高線が描けるのであるが、
今回、この話は置いておくことにする。

もちろん、直線では無く、曲線で補間することもあるが、
なぜ、曲線で表すべきであるか、についても考える必要がある。

単に、「格好良い」とか、そういう理由で安易に曲線にするべきではない
今回の例では、数を数えただけの値なので、わざわざ、曲線で表す必要は無いだろう。

さて、話を戻そう。

先程の例では、中国人の入国者数だけであったが、
今度は、他の国の入国者数も同時に表現してみる。

# 前回のプロットを初期化
dev.off()

# For文を使って折れ線グラフを連続して描く

for(i in 1:7){
  par(new=TRUE)
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", yaxt="n", ylim=c(0,2010000))
}

# グラフタイトル、x軸ラベル、y軸ラベルをそれぞれ加える
title("The number of foreigners for entry to Japan",
  xlab="Year", ylab="The number of entry (Thousands)")

# x軸とy軸をそれぞれ描き直す
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=seq(0,2010000,by=500000), labels=seq(0,2010,by=500), las=2)

# 補助線を描き入れる
abline(h=seq(0,2010000,by=500000), lty=2, col="gray")

ここの処理は、円グラフを作成した方法の応用。
For文を使って、複数のグラフを重ね描きしている。

ここでは、新しくseq()関数が登場している。
この関数は、規則的に並んでいるベクトルを作成するための関数で、
from、 to、 by の3つのパラメータによって指定する。

今回は、from=0, to=2010000, by=500000 となっている。
すなわち、0〜2010000 の値を 500000 でベクトルを作成している。

同様に、1000人を単位にしているラベルに関しては、
from=0, to=2010, by=500でベクトルを作成している。

今回の処理でポイントなるのは、
plot()関数 ylim パラメータを明示的に指定している点である。
Rのplot()関数は、データの下限と上限にあわせて、相対的に描画するため、
プロットを重ね描きする際には、全データの上限と下限を確認し、設定する必要がある。

また、plot()関数の他のパラメータは、x軸とy軸のタイトルを空白で指定し、
後のtitle()関数のパラメータを使って描画している。
このように設定しないと、For文の繰り返し回数分だけラベルが重ね描きされる。

さて、今度は、重ね描きをしたことで色々と問題が生じている。
まず、どの線が何を表しているかが不明である。これは技術的な問題
今回は、線種で分けて、凡例を追加することにする。
# 前回のプロットを初期化
dev.off()

# For文を使って折れ線グラフを連続して描く
for(i in 1:7){
  par(new=TRUE)
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", yaxt="n", 
    ylim=c(0,2010000), lty=i)
}
# グラフタイトル、x軸ラベル、y軸ラベルをそれぞれ加える
title("The number of foreigners for entry to Japan",
  xlab="Year", 
  ylab="The number of entry (Thousands)")

# x軸とy軸をそれぞれ描き直す
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=seq(0,2010000,by=500000), labels=seq(0,2010,by=500), las=2)

# 補助線を描き入れる
abline(h=seq(0,2010000,by=500000), lty=2, col="gray")

# 凡例を加える
legend("topleft",legend=colnames(X[,1:7]), lty=c(1:7))

線種は「i」という変数で指定している。
したがって、繰り返し処理が発生する度に1〜7の順で線種が選択される。

これでも良いが、やはり、少々見難い
さらに、見易いようにもうひと工夫してみる。
# 前回のプロットを初期化
dev.off()

# 描画範囲を右方向に少しだけ広げる
for(i in 1:7){
  par(new=TRUE)
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", 
    yaxt="n", ylim=c(0, 2010000), xlim=c(0,nrow(X)+4), lty=i)

  # 行名(国名)を text()関数 を使って描き入れる
  text(x=nrow(X)+2, y=X[nrow(X),i], colnames(X)[i], cex=0.8)
}

# タイトルとx軸、y軸ラベルはそのまま
title("The number of foreigners for entry to Japan",
  xlab="Year", ylab="The number of entry (Thousands)")

# x軸、y軸、補助線、凡例をそれぞれ加える
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=seq(0,2010000,by=500000), labels=seq(0,2010,by=500), las=2)
abline(h=seq(0,2010000,by=500000), lty=2, col="gray")
legend("topleft",legend=colnames(X[,1:7]), lty=c(1:7))

今度は、折れ線の右端に行名を布置してある。
これで、どの線が何を示しているのかが良く解る。

このプロットでは、実際のデータよりも、
4ポイント分(4年分)だけ上限を余分に調整し、
さらに、text()関数を使って、行名(国名)を布置している。

ここで、テキストのx軸の位置は、最終年(2005年)+2年を指定し、
y軸はの位置は、最終年における入国者数を指定している。
この辺りの微調整は直感であり、何回か実験して最終的な位置を決めている
慣れれば、1〜2回程度の微調整で上手く調整できる。

ふむ。これでかなり見やすくなった。

この結果から、米国よりも韓国と台湾からの入国者数が多く、
米国と中国からの入国者数が意外に近いことが解る。

また、中国からの入国者数は、2000年頃から急激に増え始めており、
それ以前は、米国や韓国、台湾からの入国者数と比較してかなり低いことが解る。

さて、このグラフには根本的な問題がある。実は、フィリピン、香港、ブラジルは、
このデータの上位の4カ国と比較して、
全体的に値が小さいので、読み取れる情報限られる

このような場合、何をグラフに記載するべきか、改めて検討するべきである。
述べたいことに対して、情報が不足しているのは大きな問題であるが、
逆に、情報過多となることも避けなければならない。

安易に一つの図に全てを重ね合わせるのではなく、
状況に応じて、複数のグラフに分けることも重要である。

ということで、今度は、フィリピンとブラジルだけを分けることにする。


# 前回のプロットを初期化
dev.off()

# For文で回すのは5番目(フィリピン)と7番目(ブラジル)のデータだけ
for(i in c(5,7)){
  par(new=TRUE)

  # y軸の上限は、フィリピンとブラジルのデータに合わせて再設定
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", 
    yaxt="n", ylim=c(0,300000), xlim=c(0,nrow(X)+4), lty=i)
  text(x=nrow(X)+2, y=X[nrow(X),i], colnames(X)[i], cex=0.8)
}

# グラフタイトルは通常通りに
title("The number of foreigners for entry to Japan",
  xlab="Year", ylab="The number of entry (Thousands)")

# x軸の描画も今までと同じ
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)

# y軸の上限は、フィリピンとブラジルのデータに合わせて再設定
axis(side=2, at=seq(0,300000,by=100000), labels=seq(0,300,by=100), las=2)

# 補助線の位置も軸目盛りに合わせて再設定が必要。
abline(h=seq(0,300000,by=100000), lty=2, col="gray")

# 凡例は、5番目と7番目だけ。
legend("topleft",legend=colnames(X[,c(5,7)]), lty=c(5,7))

先程のゴチャ混ぜグラフよりも、フィリピンとブラジルの状況が見易い。

フィリピンは、1990年〜1997年ごろまで、水平に移行する時期があるものの、
基本的に、入国者数は増加傾向にあることが解る。

一方、ブラジルは、1989年ごろから、急激に入国者数が増加し、
1990年以降は、水平に移行していることが解る。

これらの傾向は、先程のグラフでは、差が見え難く、
判らなかったことである。

折れ線グラフは、他のグラフと比較して、
複数のデータ重ねあわせて描画することが多い。
それゆえに、気を付けないといけないことも多い。

まず、不必要に大量なデータを重ね合わせることを避け、
目的を絞って、必要最低限のものに留める。

特に、値の高低差が激しいデータを扱うときには気を付ける必要がある。
実際には、重要な意味が潜んでいるかもしれないが、
高低差が見難いが故に、重要な情報を見落とす可能性がある。

さてさて、以上が折れ線グラフの基本である。
これらのポイントを抑えておけば、見易く、説得力のあるグラフが作れるだろう。

ところで、今回のデータは、年別輸出入総額のデータも含まれていた。
今度は、外国人の入国者数と、輸出入総額のデータの比較もやってみたい。

まずは、年別の輸出総額のデータから。
少し長いが、良く見れば、理解できるはず。
# 前回のプロットを初期化
dev.off()

png(width=600)
# 第二軸の軸ラベルを描くために余白を調整する
par(oma=c(1,1,1,5))

# 入国者数のグラフを描く
for(i in c(1:4,6)){
  par(new=TRUE)
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", yaxt="n", ylim=c(0,2010000), xlim=c(0,nrow(X)+4), lty=i)
  text(x=nrow(X)+2, y=X[nrow(X),i], colnames(X)[i], cex=0.8)
}

# グラフタイトルとx軸、y軸を加える
title(paste("The number of foreigners for entry to Japan", "and total exports", sep="\n"), 
  xlab="Year", ylab="The number of entry (Thousands)")
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=seq(0,2010000,by=500000), labels=seq(0,2010,by=500), las=2)

# 補助線を加える
abline(h=seq(0,2010000,by=500000), lty=2, col="gray")

# 輸出総額のグラフを追加する
par(new=TRUE)
plot(x=X[,8], type="l", xlab="", ylab="", xaxt="n", yaxt="n", xlim=c(0,nrow(X)+4), col="blue")
  axis(side=4, at=seq(0,max(X[,8]),by=10000000000),   
  labels=formatC(seq(0,max(X[,8]/1000000),by=10000), format="d"), las=2)

# 第二軸の軸ラベルを描画する
mtext("Total exports (Billions)", side=4, outer=TRUE, line=3)

# 凡例を書き入れる
legend("topleft",legend=colnames(X[, c(1:4,6,8)]), lty=c(1:4,6,1), col=rep(c("black","blue"),c(5,1)))

最初のpar()関数では、oma パラメータを指定している。
これは、outer margin のことで、グラフエリアの外側の余白の設定を意味する。
元の状態では、右側の余白が狭すぎて、ラベルや軸メモリを描けないため、
この部分で、微調整を行なっている。値の設定は「勘」。慣れが必要。 

ここで新しく登場した関数は、formatC()関数である。
ここでは「1e+05」のような、コンピュータ独自の表現を
通常の数値に直すために用いている。

さて、今回は、入国者数の単位は「人」で、輸出総額の単位は「千円」である。
したがって、今回のグラフでは「単位」が異なる二つのグラフを描いているので、
左側を第一軸(入国者)、右側を第二軸(輸出総額)とするグラフとなっている。

また、輸出総額の単位は、千円の状態であると、値が大きすぎるので、
第二軸のメモリラベルの単位が「10億円」となるように調整している。

次は、輸入総額との比較。
# 前回のプロットを初期化
dev.off()

# 第二軸の軸ラベルを描くために余白を調整する
par(oma=c(1,1,1,5))

# 入国者数のグラフを描く
for(i in c(1:4,6)){
  par(new=TRUE)
  plot(x=X[,i], type="l", xlab="", ylab="", xaxt="n", yaxt="n", ylim=c(0,2010000), xlim=c(0,nrow(X)+4), lty=i)
  text(x=nrow(X)+2, y=X[nrow(X),i], colnames(X)[i], cex=0.8)
}

# グラフタイトルとx軸、y軸を加える
title(paste("The number of foreigners for entry to Japan", "and total imports", sep="\n"), 
  xlab="Year", ylab="The number of entry (Thousands)")
axis(side=1, at=c(1:nrow(X)), labels=rownames(X), las=2)
axis(side=2, at=seq(0,2010000,by=500000), labels=seq(0,2010,by=500), las=2)

# 補助線を加える
abline(h=seq(0,2010000,by=500000), lty=2, col="gray")

# 輸入総額のグラフを追加する
par(new=TRUE)
plot(x=X[,9], type="l", xlab="", ylab="", xaxt="n", yaxt="n", xlim=c(0,nrow(X)+4), col="red")
  axis(side=4, at=seq(0,max(X[,9]),by=10000000000),   
  labels=formatC(seq(0,max(X[,9]/1000000),by=10000), format="d"), las=2)

# 第二軸の軸ラベルを描画する
mtext("Total imports (Billions)", side=4, outer=TRUE, line=3)

# 凡例を書き入れる
legend("topleft",legend=colnames(X[, c(1:4,6,8)]), lty=c(1:4,6,1), col=rep(c("black","red"),c(5,1)))

さて、折れ線グラフは、このように描かれた。
重要なことは、「変化」を表すために用いるのが折れ線グラフであるということ。

また、折れ線グラフの「線」は、観測点同士を視覚的に結んでいるのでは無く、
「補間」であるという認識も重要。したがって、理屈上は、
観測されていない間も潜在的にはデータは存在している。

折れ線グラフでは、異なる単位のデータを重ねることもできる
その場合には、第一軸と第二軸を分けて、左側に第一軸の軸と軸ラベルを、
右側に第二軸の軸と軸ラベルを用いるのが一般的である。

注意すべき点は、必要以上にグラフを描き入れて、情報過多にならないようにすること。
状況に応じて、複数のグラフに分けることも重要である。

0 件のコメント:

コメントを投稿