Rで任意の範囲でヒストグラムを作りたい
問題
ggplotではgeom_histgram
を用いるとヒストグラムを作成できる。また、オプションとしてbinwidth
を指定するとビン幅を決めることができる。
library(tidyverse) data <- c( -1, 1, 11, 27, 31) data %>% tibble() %>% ggplot(aes(x = .)) + geom_histogram(binwidth=10, color='white')
上図の各棒は左から 「-5 ~ 5」「5 ~ 10」「10~ 15」,... を区間として値となっている個数を積み上げている。
だが、例えばビン幅10で「-10 ~ 0」「0 ~ 10」「10 ~ 20」...で個数を積み上げたい場合はどうすればいいだろうか?
r-walkalangなどで質問したところ、geom_histgram
では任意の区間で作成することは無理で、どの区間かを表す列をカテゴリカルで作成して棒グラフを作らないといけないようだ。
実装
前述の、「どの区間かを表す列」の作成はcut
系の関数を使うと作成できる。
cut function | R Documentation
また、cut
関数を使いやすくしたcut_interval
, cut_width
, cut_number
も存在する。
それぞれ、どういった指定で区間カテゴリカルデータを作るかで使い分ける
data <- c( -1, 1, 11, 27, 31) # 5個の区間を作成 #最小-1 最大31の32幅を5で割った6.4が全区間の幅となる cut_interval(data, n = 5) # [1] [-1,5.4] [-1,5.4] (5.4,11.8] (24.6,31] (24.6,31] # Levels: [-1,5.4] (5.4,11.8] (11.8,18.2] (18.2,24.6] (24.6,31] # 幅10.0の区間を作る cut_width(data, width = 10.0) # [1] [-5,5] [-5,5] (5,15] (25,35] (25,35] # Levels: [-5,5] (5,15] (15,25] (25,35] # 幅10.0の区間を作る # 幅10.0のうち区間の端の位置を0 + 10n にする # なお、boundryを使わずにcenter = 5.0にすると、中心5.0 + 10n とした区間が作成されるため同じ意味となる。 cut_width(data, width = 10.0, boundary = 0.0) # [1] [-10,0] (0,10] (10,20] (20,30] (30,40] # Levels: [-10,0] (0,10] (10,20] (20,30] (30,40] cut_width(data, width = 10.0, center = 5.0) # [1] [-10,0] (0,10] (10,20] (20,30] (30,40] # Levels: [-10,0] (0,10] (10,20] (20,30] (30,40] # 各区間内に2つ入るようにする # データ数は5なので、2個と3個がそれぞれ入る cut_number(data, n = 2) # [1] [-1,11] [-1,11] [-1,11] (11,31] (11,31] # Levels: [-1,11] (11,31]
これを用いると、『ビン幅10で「-10 ~ 0」「0 ~ 10」「10 ~ 20」...で個数を積み上げ』をおこなえる。
なお、表示される(
は≦
、)
は≧
、[
は<
、]
は>
を指す。
つまり、[-10,0] (0,10]
は -10 < x < 0, 0 ≦ x < 10
となる。
data %>% tibble(x = .) %>% mutate(x = cut_width(x, width = 10.0, boundary = 0.0)) %>% # 0 + 10nを端 ggplot(aes(x = x)) + # ヒストグラムではなく棒グラフとして出力 geom_bar()
また、このときx軸のラベル表示を(0,10]
形式ではなく、擬似的にヒストグラムのようにしたい場合はcut_...
関数のlabels
を使う。
data %>% tibble(x = .) %>% mutate(x = cut_width(x, width = 10.0, boundary = 0.0, labels = c(-10, 0, 10, 20, 30))) %>% # 0 + 10nを端 # label指定 ggplot(aes(x = x)) + # ヒストグラムではなく棒グラフとして出力 geom_bar()
注意点として、geom_histgram
のように、値が存在しない区間は表示をされないため注意。
#data <- c( -1, 1, 11, 20, 31) data2 <- c( -1, 1, 11, 31) data2 %>% tibble(x = .) %>% mutate(x = cut_width(x, width = 10.0, boundary = 0.0, labels = c(-10, 0, 10, 20, 30))) %>% # 0 + 10nを端 # label指定 ggplot(aes(x = x)) + # ヒストグラムではなく棒グラフとして出力 geom_bar()
追記
cut_width
でlabel
をリストで渡すと動的になにかしたいときはlistを作成して渡すことになる。正直面倒なので、cut_width
で生成されたfactor型を文字列変換して起点の値を取得してそれで上書きした方が楽かも。
例えば、
mutate(value_range = cut_width(hoge, width = 0.05, boundary = 0.0), value_range = as.numeric(str_match(as.character(value_range), '([0-9].[0-9]+)')[,1]), # 起点の小数部分のみでfactorからnumericにする value_range = if_else(is.na(value_range), 0, value_range), # 0は正規表現にひっかからないのでreplace
的な。
正規表現部分が、 [0, 0.1)
みたいなときに0は0.0とかじゃないので上手く取れなくてNA
になるので無理やり0にreplaceしてるので、 [
か(
から,
までの部分を取る風にした方がきれいです。ちょっとぱっと書けなかったので手抜きですが。。。