niszetの日記

10年目エンジニアが最近勉強したことを忘れないようにメモをする

coord_polar()+geom_path()で直線を引くには…

注:そのままでは出来ません…

昨日の記事、 niszet.hatenablog.com

について、kohske‏さんから下記のようなアドバイスをいただきました。

ありがたや…

リンク先の記事、

stackoverflow.com

のコードをコピペして動かしてみてください。記事の中で完結しているのでコピペして動かす方が手っ取り早いのです。

coord_polar()改め、coord_radar()を使用してgeom_line()を使ったときにちゃんと直線になることがわかると思います。

昨日の記事に対してやってみる(ただしこちらはデータ順に線を引きたいのでgeom_pathを使用。詳細は前回の記事にて)と、

library(tidyverse)

d=data.frame(height=c(1,2,2,3,4), weight=c(1,3,4,4,2))

dd <- d %>%  mutate(r=sqrt((height^2.0)+(weight^2.0)),theta=atan(height/weight))

p2 = ggplot() +
    geom_path(data=dd, mapping=aes(x=theta, y=r)) +
    geom_point(data=dd, mapping=aes(x=theta, y=r), size=8, fill="white", shape=21) +
    geom_text(data=dd,mapping=aes(x=theta, y=r, label=seq(1,nrow(dd))))

coord_radar <- function (theta = "x", start = 0, direction = 1) {
    theta <- match.arg(theta, c("x", "y"))
    r <- if (theta == "x") "y" else "x"
    ggproto("CordRadar", CoordPolar, theta = theta, r = r, start = start, 
            direction = sign(direction),
            is_linear = function(coord) TRUE)
}

p2 + coord_radar(theta="x",start = -pi/2, direction = -1) +ylim(0,6)+scale_x_continuous(limits=c(0,2*pi),breaks=seq(0,2*pi,by=pi/2))

で、めでたく

f:id:niszet:20170713230612p:plain

を得ることが出来ました。めでたしめでたし。

Coordの中身をちょっと見てみる。

・・・で、終わってしまうのはなんなので、ちょっとcoordの中身を見ていきます。

ggplot2のオブジェクト、関数の中身を見ていく方法については、先日も紹介したkazutanさんのこちらの資料が良いと思います。

ggplotのオブジェクトから眺めてみる

基本はstr()で見ていき、$で要素にアクセスしていきます。
また、関数は()をつけなければ(そしてRで書かれていれば)中身のソースを読むことが出来るので、それらを駆使してみていきます。場合によって、

hoge <- edit(hoge)

のようにして関数を修正して挙動を調べることもできます。今回はやってませんけど…

やってみよう。

さて、上のcoord_radarとcoord_polarの違いは何かといえば、

is_linear = function(coord) TRUE

の有り無しだけです。実際にcoord_polarの中身を見てみると

coord_polar

# function (theta = "x", start = 0, direction = 1) 
# {
#     theta <- match.arg(theta, c("x", "y"))
#     r <- if (theta == "x") 
#         "y"
#     else "x"
#     ggproto(NULL, CoordPolar, theta = theta, r = r, start = start, 
#         direction = sign(direction))
# }
# <environment: namespace:ggplot2>

同じですね。なお、namespaceはとりあえず今は考えなくて良いです。
このis_linearによって、直線になったり、対象の座標軸にそってうねったりするようです。実際にこれを使っている部分はまだ見れていません。課題。

なお、このis_linearはfunctionとして定義しています。実際にやればわかりますがTRUEを代入ではダメです。使う場所でis_linearを関数として呼び出しているためです。
引数のcoordは抜いても動きましたが、副作用がないかはわかっていません。入れておくべきでしょうね。

さて、ggprotoでCoordPolarを渡しているので、次はこれを見ていきます。
こんな感じ。

str(CoordPolar)

# Classes 'CoordPolar', 'Coord', 'ggproto' <ggproto object: Class CoordPolar, Coord>
#     aspect: function
#     distance: function
#     is_linear: function
#     labels: function
#     range: function
#     render_axis_h: function
#     render_axis_v: function
#     render_bg: function
#     render_fg: function
#     train: function
#     transform: function
#     super:  <ggproto object: Class Coord> 

うーむ、よくわからない。わからないが、それぞれの要素に関数が入っていて、親がCoordなのはわかりますね。
このis_linearにアクセスすると、

CoordPolar$is_linear

# <ggproto method>
#   <Wrapper function>
#     function (...) 
# f(...)
# 
#   <Inner function (f)>
#     function () 
# FALSE

で、FALSEが返ってくることがわかります。 後日ちゃんとソース読んで確認しますが、RStudioを使っているとCoordPolar$ まで入力すると補完候補が出ると思います。このときに一覧に出てこないものはこのオブジェクトでは定義されておらず、super(親)で定義されているものが継承されているのだと思っています。

なお、親の親の親の…と先祖をたどっていって、どこかでTRUEが定義されていて、そのあとFALSEで上書きされていなければ、TRUEを返すはず(たぶん)です。
ちょっとこの辺りもちゃんと調べます。課題。

さて、Coord*** 系の、RStudioで入力補完の候補に出てきたものについて、is_linearがT/Fどちらを返すのかを見てみました。

Coord
# Coord系のすべての元になるオブジェクト。FALSEを返す。これがデフォルトの挙動

CoordCartesian$is_linear 
# 唯一、is_linearがTRUEを返す

CoordFixed 
# superがCoordCartesianなので、is_linearも継承。TRUEを返す

CoordFlip
# Coordをそのまま継承。FALSEを返す

CoordMap
# Coordをそのまま継承。FALSEを返す

CoordPolar
# Coordをそのまま継承。FALSEを返す。前回の記事で使用したのはコレ。

CoordQuickmap
# superがCoordCartesianなので、is_linearも継承。TRUEを返す。CoordMapと名前が似ているが中身は違う点に注意が必要かも?

CoordTrans
# Coordをそのまま継承。FALSEを返す

という結果でした。MapとQuickMapで違う、とか、自分でTRUEを返しているのはCartesianだけで、あとはコレを継承しているからっぽい、とかちょっと面白い。 継承~は前記の通り、僕の認識違いの可能性もあるので、ご注意。

まとめ

is_linearについて簡単に調べるだけでもここまでボリュームが…。
…なので、オレオレgeomを作ってみたい!という方は必要になった部分から少しずつ見ていくのが良いと思いました。出来るだけggplot2と真正面から戦わない方が…
今回、サムネイルはちゃんとなっているはず…u_riboさんにアドバイスいただきました。ありがとうございました。

そして、今回はいつもにもまして勢いで書いております。ここ変だよ!とかご指摘大歓迎です。よろしくお願いします。

使っていると段々とわかってきて、わかってくると面白くなってきますね。

Enjoy!!