本文へスキップ
からだにいいもの

Rのトピックスを中心に『まだ、まだ、知らない、役に立つ情報?』を発信します。

Rで解析:重なったプロットを整えて見せる「vayr」パッケージの紹介

実験や調査の結果を散布図にすると、同じ座標に多数のデータが重なり、点の密度も群ごとの違いも読み取れなくなることがあります。しかし、重なりを解くために点をずらす場合、ずらす範囲や並べ方を目的に合わせて組み立てるには手間がかかります。

「vayr」パッケージは、ggplot2の位置調整として使える一連の関数を提供します。重なった点を楕円の範囲に散らす調整、点の間隔を均した乱れによる調整、ひまわりの種・円充填・六角格子といった規則的な並びへ置き換える調整が収録されています。

また、ランダム化実験で結果が欠測した場合に、取りうる値の下限と上限で補完し、効果の幅を示すデータを作ることも可能です。本パッケージの利用で、重なったデータを損なわずに示し、結果の読み取りを助けることができるのではないかと考えます。

パッケージバージョンは1.1.0。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。

パッケージのインストール

下記コマンドを実行してください。

# パッケージのインストール
install.packages("vayr")

# パッケージの読み込み
library("vayr")

# コマンド例実行で必要なパッケージを読み込み
# install.packages("ggplot2")
library("ggplot2")
スポンサーリンク

コマンド例

詳細はコメント、パッケージのヘルプを確認してください。

同じ座標に複数のデータが置かれ、個々の点が見分けられなくなった状態を、重複プロット(over-plotted points)と呼びます。5段階や7段階の評価、処置の有無といった、値の種類が少ない変数を散布図にすると起こりやすくなります。

以降のコマンド例では、次の擬似データを共通して使います。

# 乱数の種を固定
set.seed(1129)

# 来店者数を設定
n_kyaku <- 240

# 直売所で行った売り場実験を想定した擬似データを作成
# 品種を3つ用意し、説明POPを出すかどうかを来店者へランダムに割り当てる
naoshi <- data.frame(
  hinshu = rep(c("えびす", "みやこ", "くりゆたか"), each = n_kyaku / 3),
  hyoji  = sample(rep(c("POPなし", "POPあり"), each = n_kyaku / 2)))

# 品種と説明POPの有無から購入意向を作り、7段階の整数に丸める
naoshi$hyouka <- pmin(7, pmax(1, round(
  4 + 0.6 * (naoshi$hyoji == "POPあり") +
    0.5 * (naoshi$hinshu == "えびす") + rnorm(n_kyaku, 0, 1.2))))

# 試食に出したかぼちゃ1個あたりの重さ(kg)を作成
naoshi$omosa <- round(runif(n_kyaku, 1.2, 2.8), 2)

重なった点を楕円の範囲に散らす:position_jitter_ellipseコマンド

重なった点を、指定した楕円の範囲へランダムに散らします。ggplot2のposition_jitterは長方形の範囲に散らすため、四隅まで点が張り出し、群の境目が角張って見えます。楕円にすると、ひとかたまりの散らばりとして見えます。

オプション意味初期値
width重なった点を散らす楕円領域の横幅NULL
height重なった点を散らす楕円領域の高さNULL
seed再現性のための乱数の種NA
# 説明POPの有無ごとに、購入意向の7段階評価を描画
# 楕円の範囲に散らして、同じ評価に集まった点の量を見えるようにする
ggplot(naoshi, aes(x = hyoji, y = hyouka)) +
  geom_point(position = position_jitter_ellipse(width = 0.35, height = 0.35,
                                                seed = 1129),
             alpha = 0.5) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)")

楕円の範囲に散らしつつグループをずらす:position_jitterdodge_ellipseコマンド

楕円の範囲への散らしに、群ごとの横ずらしを加えます。色分けした群が同じ位置に重なったままでは、どの群の点が多いのか判断できません。dodge.widthで群同士を離してから散らします。

オプション意味初期値
jitter.width重なった点を散らす楕円領域の横幅NULL
jitter.height重なった点を散らす楕円領域の高さNULL
dodge.width群をずらす幅1
seed再現性のための乱数の種NA
orientation群を分ける軸。”x” は左右、”y” は上下。ggplot2::position_dodge() の同名引数に対応する“x”
# 説明POPの有無を横軸に取り、品種ごとに色と形を変えて描画
# 品種を左右にずらしてから、それぞれを楕円の範囲に散らす
ggplot(naoshi, aes(x = hyoji, y = hyouka, color = hinshu, shape = hinshu)) +
  geom_point(position = position_jitterdodge_ellipse(jitter.width = 0.2,
                                                     jitter.height = 0.3,
                                                     dodge.width = 0.8,
                                                     seed = 1129)) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)",
       color = "品種", shape = "品種")

間隔を均した乱れで重なりを解く:position_bluenoiseコマンド

楕円の範囲へ点を散らしますが、点同士の間隔ができるだけ均等になるように配置します。単純な乱数では、たまたま点が固まる場所と空く場所ができます。candidatesで指定した数だけ候補を引き、既にある点から最も離れた候補を採用することで、この偏りを抑えます。

オプション意味初期値
width点を散らす楕円領域の横幅NULL
height点を散らす楕円領域の高さNULL
candidates1点あたりに検討する乱数の候補数。大きいほど点の間隔が均等になり、計算に時間がかかる。既定の10で、点が固まる現象はほぼ解消する10
seed再現性のための乱数の種NA
# 候補数を既定の10から20へ増やし、点の間隔をより均等にして描画
ggplot(naoshi, aes(x = hyoji, y = hyouka)) +
  geom_point(position = position_bluenoise(width = 0.35, height = 0.35,
                                           candidates = 20, seed = 1129)) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)")

間隔を均した乱れとグループのずらしを重ねる:position_bluenoisedodgeコマンド

間隔を均した散らしに、群ごとのずらしを加えます。散らす範囲の指定がscatter.width・scatter.heightという名前になり、ずらし幅のdodge.widthと区別されます。

オプション意味初期値
scatter.width点を散らす楕円領域の横幅NULL
scatter.height点を散らす楕円領域の高さNULL
dodge.width群をずらす幅1
candidates1点あたりに検討する乱数の候補数10
seed再現性のための乱数の種NA
orientation群を分ける軸。”x” は左右、”y” は上下。ggplot2::position_dodge() の同名引数に対応する“x”
# 品種ごとに色を変え、群を左右へずらしたうえで間隔を均して散らす
ggplot(naoshi, aes(x = hyoji, y = hyouka, color = hinshu)) +
  geom_point(position = position_bluenoisedodge(scatter.width = 0.2,
                                                scatter.height = 0.3,
                                                dodge.width = 0.8,
                                                candidates = 20,
                                                seed = 1129)) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)", color = "品種")

重なった点をひまわりの種の並びに配置する:position_sunflowerコマンド

ここからは乱数を使わず、規則的な図形へ並べ替える調整です。本コマンドは、同じ座標に重なった点を、ひまわりの種のような渦巻きの並びに置き換えます。乱数を使わないため、同じデータからは常に同じ図が得られます。densityは並びの詰まり具合で、軸の範囲や図の大きさに応じて調整します。

オプション意味初期値
density配置パターンの密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲と図の大きさによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
# 同じ評価に集まった点を、ひまわりの種の並びに置き換えて描画
# 乱数を使わないため、実行のたびに同じ配置になる
ggplot(naoshi, aes(x = hyoji, y = hyouka)) +
  geom_point(position = position_sunflower(density = 8), size = 1.2) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)")

ひまわりの並びとグループのずらしを重ねる:position_sunflowerdodgeコマンド

ひまわりの種の並びに、群ごとのずらしを加えます。群を離してから並べるため、渦巻きが群の数だけ横に並びます。ずらした分だけ1つあたりの幅が狭くなるので、densityは単独で使うときより大きめにします。

オプション意味初期値
width群をずらす幅1
density配置パターンの密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲と図の大きさによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
orientation群を分ける軸。”x” は左右、”y” は上下。ggplot2::position_dodge() の同名引数に対応する“x”
# 品種ごとに渦巻きを分けて、評価の分布を並べて見る
# ずらしで幅が狭くなる分、密度を単独利用より大きくする
ggplot(naoshi, aes(x = hyoji, y = hyouka, color = hinshu)) +
  geom_point(position = position_sunflowerdodge(width = 0.8, density = 14),
             size = 1.2) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)", color = "品種")

大きさの異なる点を円充填で並べる:position_circlepackコマンド

重なった点を、互いに触れ合うように詰めて配置します。点ごとに大きさが違う場合でも重ならないように詰めるため、sizeに重みや量を割り当てた図に向きます。他の調整と違い、点の大きさそのものが配置に影響します。

オプション意味初期値
density円充填の密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲・図の大きさ・サイズスケールによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
# かぼちゃ1個あたりの重さを点の大きさに割り当てて描画
# 大きさの異なる円が重ならないように詰めて配置する
ggplot(naoshi, aes(x = hyoji, y = hyouka, size = omosa)) +
  geom_point(position = position_circlepack(density = 6), alpha = 0.4) +
  scale_size_continuous(range = c(1, 4)) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)", size = "重さ(kg)")

円充填とグループのずらしを重ねる:position_circlepackdodgeコマンド

円充填の配置に、群ごとのずらしを加えます。重みの大きさの違いを保ったまま、群ごとの塊を分けて見比べられます。

オプション意味初期値
width群をずらす幅1
density円充填の密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲・図の大きさ・サイズスケールによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
orientation群を分ける軸。”x” は左右、”y” は上下。ggplot2::position_dodge() の同名引数に対応する“x”
# 品種ごとに塊を分け、重さを大きさに割り当てて詰めて配置
ggplot(naoshi, aes(x = hyoji, y = hyouka, size = omosa, color = hinshu)) +
  geom_point(position = position_circlepackdodge(width = 0.8, density = 12),
             alpha = 0.4) +
  scale_size_continuous(range = c(1, 3)) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)",
       size = "重さ(kg)", color = "品種")

重なった点を六角格子に並べる:position_honeycombコマンド

重なった点を、蜂の巣状の六角格子に並べます。行と列が揃うため、点の数の違いが塊の面積として比べやすくなります。同じ大きさの点を数えて見せたい場合は、円充填よりこちらが向きます。

オプション意味初期値
density配置パターンの密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲と図の大きさによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
# 同じ評価に集まった点を六角格子に並べ、塊の大きさで人数を比べる
ggplot(naoshi, aes(x = hyoji, y = hyouka)) +
  geom_point(position = position_honeycomb(density = 8), size = 1.2) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)")

六角格子への配置とグループのずらしを重ねる:position_honeycombdodgeコマンド

六角格子への配置に、群ごとのずらしを加えます。群ごとの塊が横に並ぶため、同じ評価を選んだ人数の差が見て取れます。

オプション意味初期値
width群をずらす幅1
density配置パターンの密度。既定は 1 だが、多くの場合は調整が必要になる。適切な密度は軸の範囲と図の大きさによって変わる1
aspect_ratiocoord_equal() を使わない場合に生じる、円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要なら既定の 1 でよい。coord_fixed() を使う場合は、その ratio 引数と同じ値にする1
orientation群を分ける軸。”x” は左右、”y” は上下。ggplot2::position_dodge() の同名引数に対応する“x”
# 品種ごとに六角格子の塊を分けて、評価ごとの人数を見比べる
ggplot(naoshi, aes(x = hyoji, y = hyouka, color = hinshu, shape = hinshu)) +
  geom_point(position = position_honeycombdodge(width = 0.8, density = 14),
             size = 1.2) +
  labs(x = "説明POPの掲示", y = "購入意向(7段階)",
       color = "品種", shape = "品種")

ひまわり配置の座標を直接計算する:sunflowerコマンド

position_sunflowerの内部で使われている計算そのものを呼び出します。重なった点の座標を渡すと、ずらした後の座標が返ります。x軸とy軸を一度に扱うのではなく、xを渡せばx座標、yを渡せばy座標だけが返る点に注意してください。両方の座標が必要な場合は、2回に分けて呼び出します。要素が1つだけのベクトルは、重なりがないものとしてそのまま返ります。

オプション意味初期値
x重なった複数の点が共有するx座標のベクトル。長さ1のときは、重なりがないものとしてそのまま返るNULL
y重なった複数の点が共有するy座標のベクトル。長さ1のときは、重なりがないものとしてそのまま返るNULL
density配置パターンの密度なし
aspect_ratio円形配置のゆがみを補正する縦横比。縦に伸びるゆがみには 2 のような大きい値、横に伸びるゆがみには 0.5 のような小さい値を与える。補正が不要な場合は 1 にするなし
# 座標 (2, 5) にちょうど7人分が重なっている状況を想定
# ずらした後のx座標を求める
x_zurashi <- sunflower(x = rep(2, 7), density = 8, aspect_ratio = 1)
# 同じ7点について、ずらした後のy座標を求める
y_zurashi <- sunflower(y = rep(5, 7), density = 8, aspect_ratio = 1)
# 求めた座標を並べて確認
data.frame(x = round(x_zurashi, 3), y = round(y_zurashi, 3))
      x     y
1 1.976 5.022
2 2.005 4.943
3 2.057 5.074
4 1.908 4.984
5 2.079 4.950
6 1.976 5.090
7 1.957 4.917

欠測を極値で補って効果の範囲を求める:impute_extreme_valuesコマンド

ランダム化した実験で回答が得られなかった人がいる場合に、結果が取りうる下限と上限のそれぞれで欠測を埋めたデータを作ります。処置群に最小値・対照群に最大値を入れた場合と、その逆の場合の2通りを縦に積んだデータフレームが返り、行数は元の2倍になります。どちらの向きで埋めたかはscenario列、埋めた行かどうかはimputed列で区別できます。欠測の埋め方によって効果がどこまで動きうるかを示すために使います。

オプション意味初期値
data結果変数と割り当て変数を含むデータフレームなし
outcome結果変数の列名を表す文字列。数値の列であり、その欠測値が補完の対象になるなし
assignmentランダム割り当ての列名を表す文字列。値はちょうど2種類で、欠測があってはならないなし
range結果変数が論理的に取りうる最小値と最大値を並べた長さ2の数値ベクトル。7段階のリッカート尺度であれば c(1, 7) になるなし
treated処置群を表す assignment の値。既定では並べ替え順で2番目の値が使われ、c(“Control”, “Treatment”) なら “Treatment”、c(0, 1) なら 1 になる。並べ替え順で処置群を選べない場合に明示するNULL
# 回答を得られなかった来店者がいる状況を作る
naoshi_kesson <- naoshi
naoshi_kesson$hyouka[c(3, 12, 60, 137)] <- NA

# 評価の下限1と上限7で欠測を埋め、効果の取りうる幅を作る
# 日本語の並べ替え順に頼らないよう、処置群の値を明示する
kekka <- impute_extreme_values(naoshi_kesson, "hyouka", "hyoji",
                               range = c(1, 7), treated = "POPあり")

# 実際に埋められた行だけを取り出して確認
kekka[kekka$imputed == "Outcome imputed", ]
    hinshu   hyoji hyouka omosa    scenario         imputed
3   えびす POPなし      7  1.37 Lower bound Outcome imputed
12  えびす POPなし      7  1.38 Lower bound Outcome imputed
60  えびす POPあり      1  2.23 Lower bound Outcome imputed
137 みやこ POPあり      1  2.30 Lower bound Outcome imputed
243 えびす POPなし      1  1.37 Upper bound Outcome imputed
252 えびす POPなし      1  1.38 Upper bound Outcome imputed
300 えびす POPあり      7  2.23 Upper bound Outcome imputed
377 みやこ POPあり      7  2.30 Upper bound Outcome imputed

2通りの埋め方それぞれで平均を取ると、説明POPの効果がどの範囲に収まるかが分かります。

# 埋め方の向きと説明POPの有無ごとに、購入意向の平均を求める
aggregate(hyouka ~ scenario + hyoji, data = kekka, FUN = mean)
     scenario   hyoji   hyouka
1 Lower bound POPあり 4.508333
2 Upper bound POPあり 4.608333
3 Lower bound POPなし 4.366667
4 Upper bound POPなし 4.266667

下限側では 4.508333 – 4.366667 = 0.141666、上限側では 4.608333 – 4.266667 = 0.341666 となります。欠測をどう埋めるかによって、説明POPの効果はこの幅の中で動くと読み取れます。


この記事が誰かの役に立ちますように。

スポンサーリンク
価格および配送状況は変更される場合があります。購入時は商品ページをご確認ください。
当サイトに表示されている商品情報はAmazonから提供されたものであり、更新または削除される場合があります。
karada-goodはAmazonアソシエイトとして、適格販売により収入を得ています。