目次

ggplot

ggplot関係のメモ

数値の「年」の末尾に「年度」を付ける

グラフの横軸が「年度」だがデータは数値の「年」の場合、「○○年度」と表示したい場合が多い。これまでは、文字列やfactorなどで新しい変数を作成してaes()に与えていたが、やはり数値データは数値データとして使いたい。ようやく理解したのは、データは数値として私、ラベルのフォーマットを変更すればよいということ。

以下のように、scalesパッケージのnumber_formatを使う。この場合、接尾辞として「年度」を付与し、精度は整数まで、桁区切りは無しにする。

scale_x_continuous(
  labels = scales::number_format(suffix = "年度", accuracy = 1, big.mark = ""),
)

scalesパッケージの「*_format」の使い方がいまいちわからなかったが、こういう時に使うということか。

geom_* からデータフレームにアクセス

例えば、2つのgeom_pointで片方はデータを絞り込んでプロットしたいときなど、ggplotに与えられたデータフレームにアクセスしたい場合がある。ggplotに与えるデータフレームをあらかじめ作成してから処理する場合は、元のデータフレームを用いて絞り込み等をかければよいが、パイプでデータ処理をしたうえで、パイプから直接ggplotにデータフレームを与えるとき、しばしばデータ処理を施した段階のデータフレームにアクセスしたいことがある。多くの場合、データフレームの各フィールドに直接アクセスすればよいが、一部のgeom_*のdataに、元のデータフレームに追加でフィルタなどを施したいことがある。そのような場合のトリック。

source_data %>%
filter(年度==2020) %>%
ggplot(aes(x=算数, y=国語)) +
geom_point() +
geom_point(data=~.x %>% filter(理科>=45), color="red", size=5)

ほとんど意味はないし、他の方法もあるが例として。

2020年度に絞り込んで算数と国語の成績で散布図を作成したい。その際、理科の成績が45点以上の時は、さらに赤で大きめの点を重ねたい。その時、2番目のgeom_pointのdataオプションで理科が45点以上に絞ったデータを与えたい。ここで、source_dataからデータを作成しようとすると、ggplotに渡す直前までの処理をもう一度書いて、さらにそこから理科が45点以上というフィルタをかけなければならず、煩雑で変更する際の修正箇所も増える。そこで、dataの値に「~」を付けてformulaとすると、どうも「.x」でggplotに与えられたデータフレームにアクセスできるらしい。それを利用して「~ .x %>% filter(理科>45)」とすると、ggplotに与えられた段階のデータフレームに対してさらに処理を加えることができるようだ。

数値軸の表示範囲

数値軸の表示範囲を決める場合、ズームをするときはcoord_cartesianを利用するが、折れ線グラフの上部にgeom_textなどで値を表示するような場合、最大のデータポイントのテキストが見切れる場合がある。これに対する処理として、geom_blankを上に挿入する方法などがあるが、scale_y_continuousのexpandを利用する方法もある。

... +
scale_y_continuous(expand = c(.1,.1)) +
....

expandはデータの最小、最大に対してどれだけ余白を設けるかを決めているようだ。上記の例では、それぞれ10%の余白を入れる設定になる。これで、最大のデータポイントより上に余白ができるので、ラベルなどがはみ出すのを防げる。

拡張メタファイル EMF形式で保存

WordやPowerPointなどに貼るためにベクタ形式で出力するためには、EMF形式を使う。

library(devEMF)
g <- ggplot(DAT, aes(x=x, y=y)) + geom_point()
emf(file="target.emf", width = 11.4, height = 6.4, family = "Migu 1C")
print(g)
dev.off()

出力先をemf関数で作成してprint関数で出力する。WidthとHeightでキャンバスの解像度を指定できる。解像度を高くすると、文字のフォントが小さくなるため、ggplot側で大きくしてやる必要がある。ベクタ画像なのでキャンバスの解像度と品質はあまり関係ないので、出力する文字のサイズを見ながら解像度を調整すればよい。

ggpairs 散布図行列を描画するパッケージ

複数の変数間の関係を一度に図示するために、散布図行列をggplotで作成するパッケージ。基本的なところは関数にデータフレームを渡せばよいが、少し内容をカスタマイズするためには、各マトリックスを描画するオリジナルなggplot関数を作成するとよい。

library(GGally)
ggpairs(DAT)

基本的には散布図行列を作成する列が入ったデータフレームを渡すだけでよい。

散布図行列の左下、対角線、右上に何を描画するのかは、それぞれlower, diag, upperのパラメータで設定する。それぞれのパラメータはListを取り、リストに連続変数同士(continuos)、連続×カテゴリカル変数同士(combo)、カテゴリカル変数同士(discrete)の場合に、何を描画するのかを指定する。

ggpairs(
   DAT
  ,lower = list(continuous = "point", combo = "box_no_facet")
  ,upper = list(continuous = "cor", discrete = "count")
)

描画できるプロットは、「ggally_xyz」関数の「xyz」に対応している。点の色などを変更したい場合は、関数にパラメータを渡すことができる。その場合は、wrap関数を用いる。利用できるパラメータは、各関数のヘルプを参照。

ggpairs(
   DAT
  ,lower = list(continuous = wrap("points", color="red"), combo = "box_no_facet")
  ,upper = list(continuous = "cor", discrete = "count")
)
# ggally_points関数はgeom_point関数の各種パラメータを取れるので、点の色を赤にする。

基本的なものは用意されているが、細かくカスタマイズしようとすると、パラメータが用意されていない場合がある。例えば、ggally_smoothは、散布図に回帰直線を描画するものであるが、colorオプションでは散布図の点の色は変更できるが、回帰直線の色は指定できない(もしかして方法があるのか?)。また、linetype等も指定しても反映されないようだ。その場合は、ggplotで描画する関数を自作して渡してやればよい。

# まずggplotで目的のプロットを出力する関数を作成
# data, mappingの他に、各関数に与える引数を持ったリストを受け取る引数(ここではpts, smt)を用意しておく
# pts, smtは、do.call関数で各ggplot関数に引き渡す。この場合、必要なパラメータを全て設定しておく必要がある。
# do.call関数は、第一引数の関数に第2引数のリストの中身を展開して関数の引数に与えて実行する。
mygg <- function(data, mapping, pts=list(), smt=list(), ...){
  ggplot(data=data, mapping=mapping, ...) +
    do.call(geom_point, pts) +
    do.call(geom_smooth, smt)
  }

# ggally_関数名の代わりに作成した関数を渡す。
# その際に、各ggplot関数に渡すパラメータを格納したListを同時に渡す
# スタイルが固定なら、あらかじめ自作関数側で指定してもよいだろう
ggpairs(
   title="各回の学習時間同士の関係"
  ,lower = list(continuous=wrap(
                            mygg,
                            pts=list(shape=1, color="red"),
                            smt=list(size=0.5, color="blue", method="lm", se=FALSE)
                            ))
)

ggpairsの他のパラメータはまだ調べていない。とりあえず、描画するプロットをカスタマイズする方法のみメモ。

facetのラベルを折り返す

facetを利用するとき、ラベルが長いとはみ出してしまう。任意の文字数で折り返したい場合、labellerオプションを指定する。

labellerには標準で使えるいくつかの関数が用意されており、label_wrap_genが折り返しに使えそうだが英語を念頭に作られており、文字数を指定しても単語の区切りごとにしか改行されない。日本語の場合、単語の区切りに空白などないのでこの関数では折り返すことができない。単純に文字数で折り返す関数を用意する必要がある。

色々やってみた結果、インラインで簡単な関数を定義するのが手軽だ。

....
.... 
facet_wrap(vars(var_label), ncol=2, labeller = labeller(.default = function(x) str_wrap(x, 10)))

labeller()関数の.defaultオプションに関数を与えてやれば、文字列がその関数で処理されるようになる。そこで、str_wrapで10文字(10バイト?)で改行する関数をインラインで書くと、facetのラベルが10文字ごとに改行される。