裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

お絵描きだって ggplot より綺麗だよ

2019年12月26日 | ブログラミング

Create supply and demand economics curves with ggplot2

スライドや挿絵などでグラフを描くことがあるが,ggplot2 での指定はかなり煩わしい。

ggplot(mapping = aes(x = x, y = y)) +
  geom_path(data = supply, color = "#0073D9", size = 1) +
  geom_path(data = demand1, color = "#FF4036", size = 1, linetype = "dashed") +
  geom_path(data = demand2, color = "#FF4036", size = 1) +
  geom_segment(data = intersections,
    aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
  geom_segment(data = intersections,
    aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") +
  geom_text(data = plot_labels,
    aes(x = x, y = y, label = label), parse = TRUE) +
  annotate("segment", x = 3.5, xend = 4.5, y = 6, yend = 7,
    arrow = arrow(length = unit(1, "lines")), colour = "grey50") +
  geom_point(data = intersections, size = 3) +
  scale_x_continuous(expand = c(0, 0), breaks = intersections$x,
    labels = expression(Q[1], Q[2])) +
  scale_y_continuous(expand = c(0, 0), breaks = intersections$y,
    labels = expression(P[1], P[2])) +
  labs(x = "Quantity", y = "Price",
    title = "Rightward shift in demand",
    subtitle = "As demand increases, so does price") +
  coord_equal() +
  theme_classic() +
  theme(plot.title = element_text(size = rel(1.3)))

graphics::plot だと以下のように半分くらいの文字数で書ける(描ける)

old = par(mar = c(3, 3, 2.2, 0.2), mgp = c(1.6, 0.6, 0), las = 1, tck = -0.02, bty = "l")
plot(supply, col = "#0073D9", type = "l", xaxt = "n", yaxt = "n",
     xlab = "Quantity", ylab = "Price", asp = 1)

lines(demand1, col = "#FF4036", lty = "dashed")
lines(demand2, col = "#FF4036")
lines(c(0, rep(intersections[1,1], 2)), c(rep(intersections[1,2], 2), 0), lty = "dotted")
lines(c(0, rep(intersections[2,1], 2)), c(rep(intersections[2,2], 2), 0), lty = "dotted")
points(intersections, pch = 19)
text(c(8, 2, 5), 8, expression(S, D[1], D[2]), xpd = TRUE)
arrows(3.5, 6, 4.5, 7, length = 0.05, col = "grey50")
axis(1, at = intersections$x, labels = expression(Q[1], Q[2]))
axis(2, at = intersections$y, labels = expression(P[1], P[2]))
title("Rightward shift in demand", line = 1.2)
mtext ("As demand increases, so does price", line = 0.05)
par(old)



コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 って,ちっとも簡単じゃないよ!(2)

2019年12月25日 | ブログラミング

> 最後にgeom_blankを使い,かつ体裁を最大限整えたグラフを描いてみます。論文にもそのまま使えるほど綺麗なので,参考にしてください。

との触れ込みで,プログラムと図が掲載されていた。

library(dplyr)
library(tidyr)
library(ggplot2)
iris %>%
  gather(feature, value, -Species) %>%
  group_by(Species, feature) %>%
  summarise(mean_value = mean(value))  %>%
  group_by(feature) %>%
  mutate(y_max = max(mean_value)*1.15) %>%
  ggplot(aes(x = Species, y = mean_value, label = mean_value, fill = Species)) +
  geom_bar(stat = "identity", width = 0.5, size = 0.3, color = "grey80")  +
  geom_text(size = 2.4, vjust = -0.5) +
  geom_blank(aes(y = y_max)) +
  facet_wrap(~feature, ncol = 4, scales = "free") +
  scale_y_continuous(expand = c(0, 0)) +
  scale_fill_brewer(palette = "Set1") +
  theme_bw() +
  theme(panel.border = element_blank(),
        axis.line = element_line(color = "grey50", size = 0.2),
        axis.text.x = element_text(size = 8, angle = 45, hjust = 1, vjust = 1),
        axis.text.y = element_text(size = 8),
        axis.title = element_text(size = 8),
        strip.background = element_blank(),
        strip.text = element_text(size = 8, face = "bold"),
        axis.ticks = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none") +
  labs(x = "Species",
      y = "Mean") -> p

なんたる長さ。

この際,平均値を棒グラフで描くことの是非には触れないが,x 軸ラベルも斜めになってジャギー・ジャギー 。

graphics::barplot で書くと

layout(matrix(1:4, 4))
old = par(mar = c(2, 5.5, 2, 1),
    mgp = c(1.8, 0.6, 0), las = 1, bty = "l",
    tck = -0.05, cex.axis = 1.2)
for (i in 1:4) {
    df = iris[, c(i, 5)]
    value = by(df[, 1], df$Species, mean)
    vname = colnames(df)[1]
    pos.bar = barplot(value, bty = "l",
        col = c("firebrick3 ", "dodgerblue3 ", "darkgreen "),
        xlim = c(0, max(value) * 1.1), horiz = TRUE)
    title(main = paste0("mean of ", vname), cex = 0.8)
    text(value, pos.bar, value, pos = 2, col = "white", cex = 1.1)
}
par(old)
layout(1)

ぐらいで(まだまだリファインできるけど),次の図が描けた。

こっちの方がやはり,好きだ。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 って,ちっとも簡単じゃないよ!

2019年12月25日 | ブログラミング

Why I don't use ggplot2

で,

ggplot() + geom_point(data = quakes, aes(x = lat, y = long, colour = stations))

の一行で以下のような図が描けるのはかなり驚くべき事だ。

中略。しかし,ちゃんとした図であるためには,(1) 軸を大きくし,(2) ラベルを大きくし,(3) ラベルをフルネームで,(4) 凡例のタイトルも省略名ではなく詳しく描く必要がある

などと書いている。その他にも,(5) バックグラウンドは白,(6) グリッドラインは描かない,(7) 図の周りの枠は描かない,(8) 軸,ティックマークおよび目盛り文字を灰色でというようなことも必要だろう。タイトル,サブタイトル,キャプションは,このような図を引用する側で付ける(例えば LaTeX の figure 環境では,LaTeX 側で付ける)のでなくてもよいだろう。

つまり,以下のような図である。

あなたは,上の図と比べてどのように評価するだろうか?

上の 1 行だけのプログラムに,どれだけ追加書きすれば良いのだろうか?

キーワードでググりながら,書いてみた。以下のようになった。ggplot2 のヘビーユーザーなら,もっともっと少ない行数で書けるのかもしれないが。できるようだったら教えて欲しい。

library(ggplot2)
data(quakes)
p = ggplot()
p = p +  geom_point(data = quakes, aes(x = lat, y = long, colour = stations)) + # 最初の 1 行プログラム
  labs(
    title = "Locations of Earthquakes off Fiji", # 図のタイトル
    subtitle = "The locations of 1000 seismic events of MB > 4.0", # 図のサブタイトル
    caption = "This is one of the Harvard PRIM-H project data sets.", # 図のキャプション
    x = "Latitude ", # x 軸ラベル
    y = "Longitude", # y 軸ラベル
    colour = "Number of\n stations\n reporting" # 凡例の説明書き
  ) +
  theme(
    plot.title = element_text(size = 16, hjust = 0.5), # タイトルの文字サイズと位置
    plot.subtitle = element_text(size = 14, hjust = 0.5), # サブタイトルの文字サイズと位置
    axis.line = element_line(colour = "gray"), # 軸の色
    axis.ticks = element_line(colour = "gray"), # ティックマークの色
    axis.text = element_text(size = 12), # 軸の目盛り文字の大きさ
    axis.title = element_text(size = 14), # 軸の名前の茂治の大きさ
    panel.border = element_blank(), # 図の周りに枠を描かない
    panel.grid.major = element_blank(), # 主グリッドラインを描かない
    panel.grid.minor = element_blank(), # 副グリッドラインを描かない
    panel.background = element_blank()  # バックグラウンドを白にする
  )

print(p)

 

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(6)

2019年12月24日 | ブログラミング

円グラフでさえ物議を醸すのに,こんなとんでもないグラフが紹介されていた
(紹介するのはしかたないとしても,そもそも,そんなトンデモ機能が実装されていること自体が問題なんだよな)

 

これはしょうもない帯グラフ??

quan <- as.vector(table(iris1$Species))
pos <- cumsum(quan) - quan/2
quantity <- data.frame(Species=c("setosa", "versicolor", "virginica"), quantity=quan, position = pos)                       
                       
pie <- ggplot(iris1, aes(x=factor(1), fill=Species)) + geom_bar(width=1) + geom_text(data=quantity, aes(x=factor(1), y=position, label=quantity) , size=5) + labs(x="", y="")
pie

これが問題のグラフ。一体何なんだ。

pie + coord_polar()

これに比べれば,まだかわいげがある円グラフ。

pie + coord_polar(theta="y")

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(5)

2019年12月24日 | ブログラミング

ggplot2でgeom_lineする時にNAを無視して線をつなぐ

リンクするのは止めておく

時系列データなどで NA があるとき,その前後は線で繋がないというのは graphics::plot でも ggplot でも同じ仕様。

それはそれなりに意味がある。だって,途中がどうなっているかは神のみぞ知ることであるから。勝手に NA の前と後を繋いでしまっては「誤解が生じる可能性があるのだから」。

library("ggplot2")

smp <- data.frame(x = 1:5, y = c(1:3, NA, 5))

ggplot(smp, aes(x = x, y = y, group = 1))

+ geom_line()

+ geom_point()

ggplot(subset(smp, !is.na(y)), aes(x = x, y = y, group = 1))

+geom_line()

+ geom_point()

にするというのだ。

やってはいけない(せめて,破線で繋ぐくらいのことをしてくれ)。

そんなことしてくれなくてもわかるけど,その区間が NA  だということをちゃんと示さないといけないよ。

可視化(見える化)って,そういうもんでしょ?

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(4)

2019年12月24日 | ブログラミング

ジッターは使うな!!!

なぜ,ジッターを使う。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(3)

2019年12月24日 | ブログラミング

この凡例は,何のためにあるのか?(念の為。色は縦軸の大きさの順に割当らているだけです。上の紫から,下の赤まで)

スペースの無駄遣い。
論文の投稿だと図の大きさで料金がかさむこともあるらしい。
無駄な凡例にお金(研究費?)を使う意味がわからない。

 

このグラフも同じ,凡例がなくても何の不都合もない(凡例があって良かった!なんて,誰も思わない)

 

これは,だれも笑うだろう。

 

この例では,x 軸の目盛りに描いてある名前が重なって読めない。
同じことを描いている凡例のスペースを振り分ければ,重なることもないだろうに。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(2)

2019年12月24日 | ブログラミング

発表スライド(?)そのまま挙げるけど,統計学的に「結果として合計をプロットする」ことにどれ殿の意味があるか?群ごとのサンプルサイズが違えば合計をプロットしても意味はない。平均値をプロットするなら意味があるが。そもそも,合計値なら棒グラフでもよいかもしれないが,平均値なら棒グラフで表現するのが適切なのか?

いずれにせよ,大筋が間違えているので,細かなところがああだこうだ議論しても意味をなさない。

 

データの分布を可視化したいなら

plot(len ~ dose, data = ToothGrowth)

で,十分だ。それが「綺麗ではない」というなら,ggplot2 で書いて見ろ。

 

平均値のグラフも描いてあった。

上のグラフに,平均値の位置を描画してやれば,一石二鳥(一石百鳥)だ

繰り返すが,平均値を棒グラフで示すのは統計学的に正しいのか?

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 - ひいきの引き倒し(1)

2019年12月24日 | ブログラミング

ggplot2 の解説記事は,ggplot2 がいかに優れたパッケージかを宣伝するために,「これだけの記述でこんな素敵なグラフが描けますよ!!!」ということを言いたいがために,統計学のセンスがないために変なグラフ描画例を書いて(描いて)しまうこともあるようで。

ggplot(iris) + geom_line(aes(x=Sepal.Length, y=Sepal.Width))

なに?この「折れ線グラフ??」。

「データを可視化する」と言っているけど,データのどのよう特徴を見えるようにしているの?

折れ線グラフは,主に,時系列データの可視化(時間によってある観測値がどのように変化するか)に使われるものではないの?

このグラフで何がわかるの???ひいきの引き倒しとはこのようことであるという典型例。統計学のセンスのかけらもない。バックグラウンドが灰色だとかは言わないでおく。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

どういう目的のページ?

2019年12月16日 | ブログラミング

「平均値の棒グラフ」と題して,以下の図が掲示されています。

library(ggplot2)
ggplot(aes(x=Species, y=Sepal.Length, fill=Species), data=iris) +
  stat_summary(fun.y=mean,geom="bar")

何でも図示すればいいというものではありません。

凡例は何のためにあるの?

平均値を示すのに棒グラフというのは妥当なの?

その他,いいたいことは一杯あるけど,「ggplot の初心者のためには,こんな風なことを挙げておけば十分説得力があるんじゃないの?」臭さがふんぷんです。

ggplot のお勉強の前に,統計学や統計図のお勉強をしておく方がよいでしょうという話。

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

棒グラフをたくさん並べるのは,わかりづらい

2019年12月16日 | ブログラミング

ggplot2tor

gapminder の各大陸,各国,年次別の平均余命のデータ 1704 行,6 列である。

library(tidyverse)
library(gapminder)

gapminder %>%
  mutate(
    year = as.factor(year)
  ) %>%
  ggplot(aes(x = continent, y = lifeExp, fill = year)) +
  stat_summary(fun.y = "mean", geom = "bar",
               alpha = .7, position = position_dodge(0.95)) +
  stat_summary(fun.y = "mean", geom = "point",
               position = position_dodge(0.95),
               size = 1) +
  stat_summary(fun.data = "mean_cl_normal",
               geom = "errorbar",
               position = position_dodge(0.95),
               width = .2)

で,以下のような「綺麗な」図が「簡単に」描ける。

けっしてわかりやすい図とは思えない。

なお,表示されているのは各大陸・年次ごとの数カ国の平均余命の平均値である。

なので,エラーバーを付ける必要性はあまりないかもしれないが,これに関してページの筆者がこんなことを述べている。

the dispersion of life expectancy on the continent of Oceania has increased in recent years. This may be due to mistakes in the data or maybe something has actually changed in life expectancy.

オセアニアの平均余命,近頃伸びが大きいよね。データのミスかもしれないし事実かも知れないが。

いやいや,オセアニアはオーストラリアとニュージーランドの 2 国しかないのであるよ。
図にするのが簡単なあまり,その図を作り上げているデータについて何の注意もしていない。

さて,大規模なデータであるが,必要なのは各大陸別の各国の年次ごとの平均余命である。

素の R で書くと以下のようになる。

df = split(gapminder, gapminder$continent)

で各大陸ごとのデータフレームに分割して,それぞれのデータフレームについて図を重ね描きする。

まずは,土台づくり。

old = par(mgp = c(1.6, 0.6, 0), mar = c(3, 3, 1, 3.5),
    las = 1, tck = -0.01, bty = 'l')
plot(lifeExp ~ year, data = gapminder, type = "n", ylim = c(35, 83),
    ylab = "life expectancy at birth, in years")
col = c("black", "red", "blue", "orange", "brown")

各大陸ごとの処理を for ループで書く。
エラーバーはこの場合は意味がないので,描かない。

for (df2 in df) {
    continent = as.integer(df2$continent[1]) # 大陸は factor なので,対応する整数を取り出す
    mat = matrix(df2$lifeExp, 12) # 国ごとに12年分の平均余命が並んでいるので 12 × 国の数の行列にする
    mean = rowMeans(mat, 2) # 行に対して平均値を取れば 12 年分の平均余命の平均値になる
    lines(seq(1952, 2007, by=5), mean, col=col[continent]) # 折線を描く
    points(seq(1952, 2007, by=5), mean, col=col[continent], pch = 19, cex = 0.5) # マークを付ける
    text(2007, mean[12], df2$continent[1], col=col[continent], xpd=TRUE, pos = 4) # 大陸名を付ける
    title(main = "The Gapminder data on life expectancy, by continent.") # タイトルを付ける
}

以上

やはり,ちゃんと集計結果を見てから図を描かないと,判断を誤るよ。

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

本当にわかりやすい図にするためには,少々の苦労も必要というもの

2019年12月14日 | ブログラミング

ggplot をつかえば簡単に「綺麗な図」が描けますよと...
うそでしょ,必ずしもそうではない。

ggplot2 の公式サイト

install.packages("tidyverse")
install.packages("ggplot2")
devtools::install_github("tidyverse/ggplot2")
library(ggplot2)
ggplot(mpg, aes(displ, hwy, colour = class)) + geom_point()

だけで以下のような「綺麗な図が描けますよ」


このような図を添付した論文を投稿したら,編集者あるいは査読者は「こんな図ではダメだ」というだろう。
そこで,以下のような図を ggplot2 で描くにはどうしたらよい??
どれだけの追加が必要か?教えて!

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ggplot2 を検索するときに,してはいけないこと

2019年12月14日 | ブログラミング

検索時に注意!!!

ggplot2 を検索たとき,.it や .de などのドメインのページはほとんどが危険なページ。クリックしてはいけない。

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

いいたいことの強調!!

2019年12月14日 | ブログラミング

PyTorch 三国志(Ignite・Catalyst・Lightning)

  • 凡例は図中に
  • 強調したいものを強調する!

 

投稿された図

 

手描き風でインパクトをねっらっているのだろうが,それは姑息な手段。

その手法でもよいのだけど,凡例は,図の中に描こう!!

さらに,いいたいこと(黄色の線だろうけど)はもっと太く,アピールできる色(赤とか)で描こう!!

ちょっと,注意することは,データは毎月のものがあるのではないようなので,それを線(曲線)で結んでしまうのがよいことなのかどうなのか,ちょっと疑問。

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

無用な凡例をなぜつける?

2019年12月12日 | ブログラミング

12/6 図を分割する

上の図と,下の図,どこが違う?

凡例がついているかついていないか。

凡例は必要か?

各図の下に Species が描いてある。凡例は不要。図のスペースを無駄に使っているだけ。そのせいで,横幅が制限されて図の下の Species の記述が,重なってしまって不細工極まりない。重ならないように広げればよいじゃないかって?でも,不要なものを描くために図のスペースを広げるのは本末転倒じゃん?というはなし。

上の図と,下の図,プログラムの違いは 1 行のみ。

デフォルトで使ってはいけないという悪い典型例。

library(tidyr)
iris.long <- pivot_longer(data.iris,colnames(data.iris)[1:4],names_to = "Phenotype")

library(ggthemes)
library(ggplot2)
ggplot(iris.long,aes(x=Species,y=value))+
  theme_base()+
  geom_violin(aes(fill=Species))+
  geom_boxplot(width=0.1)+
  facet_grid(. ~ Phenotype)+
  theme(legend.position = 'none') # 凡例描くな!!!!

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村