cucumber flesh

Rを中心としたデータ分析・統計解析らへんの話題をしていくだけ

ggplot2::geom_sf()での緯度経度のラベルを調整する

要約

  • ggplot2::geom_sf()のデフォルトで表示される緯度経度の軸は桁数が揃っていないことがある
  • scales::degree_format()で桁数の調整が可能
    • 最新のバージョン(1.0.0)では実装されておらず、開発版をインストールして利用する
    • scalesを使わない方法も紹介

はじめに

f:id:u_ribo:20190302222837p:plain

ggplot2で地理空間データを描画すると、両軸のラベルに緯度経度の値が表示されます。下記に示すように、たった2つの関数で地図を表示できて便利なのですが、この図のY軸、緯度の表示が"36.5°N", "36°N"と有効数字の桁数が異なっているのが気になります。 "36.5°N", "36.0°N"のように桁数を合わせたいところです。また、日本語での「北緯」や「東経」、「西経」というラベルも使いたい場面があるでしょう。今回はそのような際に役立つ、geom_sf()の軸ラベルの表示を変更して桁数を揃える方法と表示方法を任意のものに調整する例を紹介します。

続きを読む

ggplot2でのfacetした図のstripを下部に配置する方法と注意点

ggplot2のfacet_wrap()facet_grid()で、ある変数の水準ごとにパネルを分割した描画を行うと、分割の基準となっている変数名がパネルの上部に表示されます。これにはstripという名前がついています。“strip”の辞書を引くと「(金属の)薄板」の意味だそうです(確かに分割した様子は板っぽい…)。

stripの位置は、facet_wrap()facet_grid()それぞれ異なる方法で調整できます。まずはfacet_wrap()の例を示します。次のようにfacet_wrap(strip.position = "bottom")とすることでstripを下部に移動させることができます。できますが…。

library(ggplot2)
library(gghighlight)

p_base <- 
  ggplot(mpg, 
       aes(displ, hwy, color = class)) +
  geom_jitter() +
    gghighlight(use_direct_label = FALSE,
                unhighlighted_colour = "#99999930") +
  hrbrthemes::scale_color_ipsum() +
  guides(color = FALSE)

p_strip_bottom <- 
  p_base +
  facet_wrap(vars(class),
             ncol = dplyr::n_distinct(mpg$class),
             strip.position = "bottom")
p_strip_bottom +
  ggtitle('strip.position = "bottom"だけではx軸の数値ラベルの上部にきてしまう')

f:id:u_ribo:20190224145624p:plain

「違う、そうじゃない」。

stripがx軸のラベルより上にきており、肝心の値がわかりにくくなってしまっています。

そもそもstripの位置は図の上部にあったほうが良いんじゃ…という気がしますが、ここでは諸事情により下部に配置する必要があるとして話を続けます。

次にもう一つのfacetであるfacet_grid()でstrip位置を調整します。こちらは引数switchがラベルの位置指定に使われており、NULL (初期値), “x”, “y”, “both”のいずれかを与えます。デフォルトではstripの配置はパネルの上部・右側ですが、“x”の時は、縦方向にパネルを分割したstripの位置を下部に、“y”が与えられた時には横方向に分割されたstripを左側に配置します。また縦・横両方向にラベルを与える場合でstripを下部・左側にするには“both”を与えます

p_base +
  facet_grid(cols = vars(class), switch = "x") +
  ggtitle('switch = "x"でもx軸の数値ラベルの上部にきてしまう')

f:id:u_ribo:20190224145653p:plain

この問題を解決し、x軸の下にstripを表示するには、下記のようにtheme(strip.placement = "outside")でstripを外側に配置する指定を追加します。これはfacet_wrap()facet_grid()のいずれで作成した場合でも共通です。

p_strip_bottom + 
  # strip.placementは "inside" (初期値) または "outside"が指定可能
  theme(strip.placement = "outside",
        strip.background = element_blank())

f:id:u_ribo:20190224145756p:plain

整理すると、

  • stripの位置を変更するには
    • facet_wrap()ではstrip.position =
    • facet_grid()ではswith =
  • 下部に配置したstripを移動するにはtheme(strip.placement = "outside")

とfacetの処理により異なり、さらにtheme()で調整が必要なのでややこしいです。

参考

atusy.github.io

facetについての全般的な話題はこれを見ておけば大体OKだと思います。逆引き的に参照してもよし。

notchained.hatenablog.com

facet_wrap(strip.position = )facet_grid(swith = )の話題に触れられています。

それでは!

否定条件: stringr 1.4.0で追加された引数negageとpurrrを使った文字列以外のさまざまなデータへの適用例

要約

f:id:u_ribo:20190224083558p:plain

  • 条件に一致しない要素に対してTRUEを返す否定条件をさまざまなデータ型に適用する例を紹介します
    • 文字列データに対しては stringrパッケージ(バージョン1.4.0以降)またはstringiのnegate引数を利用する
    • purrrパッケージのnegate()compose()を用いることで、文字列以外のデータにも適用可能な否定処理を実行できる
  • これらの関数を使った処理では、!演算子を使わずに記述できるためパイプ処理とも親和性が高い

はじめに

ある条件のもとで一致しない、つまり偽 (FALSE)となる要素を 真 (TRUE) とするためにRでは!演算子を使います。

# 1は2と等しくない
1 != 2
## [1] TRUE

また、grep(invert = )のように否定条件の指定が引数で与えられている関数もあります。

# 文字列dを含まない要素の位置を参照する
grep("d", c("abc", "def", "ghi"), invert = TRUE)
## [1] 1 3

この記事ではこのような!演算子を使わない否定条件の処理をさまざまなデータに対して実行する例を紹介します。まずは先日(2月10日)、バージョン1.4.0に更新されたRでの文字列処理の定番であるstringrパッケージによる文字列データへの適用例をみます。続いて、文字列以外の多様なデータ形式に否定処理を行うためにpurrrパッケージの関数を使った処理を示します。

stringr 1.4.0に実装されたnegate引数

stringr 1.4.0では、str_detect()などの主要な関数に引数negateが追加されました。この引数は与えられたパターンに一致しない要素を検出する機能をもちます。引数には論理値を指定し、TRUEを与えた際に否定条件を成立させます。

3つの要素を含んだ文字列ベクトルについて、“a”を含まない“melon”が TRUE となる処理を行います。まずは否定ではない、つまり“a”を含む要素にTRUEを返す処理を確認します。

library(stringr)

fruits <- c("banana", "apple", "melon")

str_detect(fruits, "a")
## [1]  TRUE  TRUE FALSE

目的はこの逆です。引数negateにTRUEを与えて結果をみましょう。

str_detect(fruits, "a", negate = TRUE)
## [1] FALSE FALSE  TRUE

否定条件に一致する要素である“melon”に対してTRUEを得ることができました。

これまでの否定条件は、!演算子を処理の先頭に置くことで指定していました。しかしこの形式だと次のようにパイプ処理と相性がいまいちなようにに思えます。

!str_detect(fruits, "a")
# パイプ処理で否定処理を記述する例
# 1. {}で無名関数化する
fruits %>% {
  !str_detect(., "a")
}
# 2. purr::map_lgl()を使う
fruits %>% 
  purrr::map_lgl(~ !str_detect(.x, "a"))

negate引数が実装されたことで適用する関数の中で処理が完結するようになります。negate引数はもともとstringiパッケージにあったものだそうです(初めて知りました)。stringrパッケージに実装してくれた id:yutannihilation さんに感謝です。

# stringiにもnegate引数は存在する
stringi::stri_detect(fruits, regex = "a", negate = TRUE)

purrrパッケージで否定条件を成立させる

さて、文字列の場合にはstringrでnegate引数を利用すれば良いという話でした。それでは、文字列以外でnegateのような否定の処理を行うにはどうすれば良いでしょうか。シンプルな答えは!演算子を使うことです。ですがここではpurrrパッケージに用意されたnegate()compose()による否定処理を紹介します。

library(purrr, warn.conflicts = FALSE)

まずはnegate()です。この関数は、関数を生成する特殊な関数です。具体的には記述した条件を否定する関数を生成します。引数.pに成立させたくない条件を記述します。例えばNULLでない要素を与えた時にTRUEを返してほしい際は下記のようにします。

fn <- 
  negate(is.null)

fn
## function (x) 
## {
##     !.Primitive("is.null")(x)
## }

!is.null(x)として機能する関数ができました。引数に値を渡して実行結果を確認してみましょう。

# is.null(x) が成立せず FALSE となります
fn(x = NULL)
## [1] FALSE
list(NULL, 1, "a") %>% 
  purrr::map_lgl(~ fn(x = .x))
## [1] FALSE  TRUE  TRUE

便利ですね。では次はnetage()を使って数値が素数でないかを判定したいと思います。primesパッケージに素数かどうかの判定をするis_prime()があるのでそれを用います。

# 1から6までの実数を用意します
nums <- 
  seq(1L, 6L)

primesには素数である場合にTRUEとなる関数が用意されています。その否定処理は次のように!で実現できますがnegate()で同じ処理ができます。

primes::is_prime(nums)
## [1] FALSE  TRUE  TRUE FALSE  TRUE FALSE
nums %>% 
  negate(~ primes::is_prime(.x))()
## [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE

ここで注意するのがnegate(.p = )()のように括弧が後ろに付いている点です。これはパイプ処理をしているせいもありますが、この書き方は見慣れません (パイプ処理を使わない場合は negate(~ primes::is_prime(.x))(nums) とします)。この書き方が嫌な場合は文字列の処理で示したようにmap_lgl()で記述することも可能です。

nums %>% 
  map_lgl(
  negate(~ primes::is_prime(.x)))

map()で各要素に対して処理を適用するようにしているのでやや冗長に感じます。それではpurrrでの否定処理の2番目の例としてcompose()を紹介しましょう。compose()(g•f)(x) = g(f(x))として機能する合成関数を生成する関数です。この合成関数の機能を使うことで、関数を作る必要が生じますが、コードを見通しの良いものにできます。

is_false_prime <- 
  # 作成する関数 (unmatch_str_a()の第一引数に与えた値が str_detect)
  compose(`!`, ~ primes::is_prime(x = .x))
# negate()を使う場合は compose(negate(~ primes::is_prime(.x)))
is_false_prime(nums)
## [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE

論理値ではなく、マッチしなかった値そのものがほしい場合はkeep()で要素を取り出せます。stringrで文字列データを扱う場合はstr_subset()です。

nums %>% 
  keep(negate(~ primes::is_prime(.x)))
## [1] 1 4 6
nums %>% 
  keep(is_false_prime)
fruits %>% 
  str_subset("a", negate = TRUE)
## [1] "melon"

それでは!