カウントを示す棒グラフとレートを示す折れ線グラフをすべて1つのグラフにプロットする必要があります。両方を別々に行うことができますが、それらを組み合わせると、最初のレイヤー(つまりgeom_bar
)のスケールが2番目のレイヤーと重なります。レイヤー(つまりgeom_line
)。
の軸をgeom_line
右に移動できますか?
ggplot2 2.2.0 以降では、次のような二次軸を追加できます ( ggplot2 2.2.0 の発表から取得):
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
scale_y_continuous(
"mpg (US)",
sec.axis = sec_axis(~ . * 1.20, name = "mpg (UK)")
)
個別の y スケール (互いの変換である y スケールではない) を持つプロットには根本的な欠陥があると考えているため、ggplot2 では不可能です。いくつかの問題:
は可逆ではありません。プロット空間上の点が与えられた場合、それをデータ空間内の点に一意にマップすることはできません。
他のオプションと比較して、正しく読み取るのは比較的困難です。詳細については、Petra Isenberg、Anastasia Bezerianos、Pierre Dragicevic、および Jean-Daniel Fekete によるデュアル スケール データ チャートに関する研究を参照してください。
それらは誤解を招くように簡単に操作されます。軸の相対的なスケールを指定する一意の方法がないため、操作の余地があります。Junkcharts ブログからの 2 つの例: one、two
それらは恣意的です: なぜ 3、4、または 10 ではなく、2 つのスケールしかないのですか?
また、Stephen Few のトピックに関する長い議論を読むこともできます。.
クライアントが 2 つの y スケールを希望する場合があります。彼らに「欠陥のある」スピーチを与えることは、多くの場合無意味です。しかし、私は物事を正しい方法で行うというggplot2の主張が好きです。実際、ggplot は平均的なユーザーに適切な視覚化手法について教育していると確信しています。
ファセットとスケールフリーを使用して、2 つのデータ系列を比較できますか? - たとえば、こちらをご覧ください: https://github.com/hadley/ggplot2/wiki/Align-two-plots-on-a-page
この課題を解決するための技術的バックボーンは、3 年ほど前にKohskeによって提供されました [ KOHSKE ]。そのソリューションに関するトピックと技術は、Stackoverflow [ID: 18989001、29235405、21026598] のいくつかのインスタンスで議論されています。したがって、上記のソリューションを使用して、特定のバリエーションといくつかの説明的なウォークスルーのみを提供します。
グループG1にデータy1があり、グループG2のデータy2が何らかの方法で関連付けられていると仮定します。たとえば、範囲/スケールが変換されたり、ノイズが追加されたりします。したがって、データを 1 つのプロットに一緒にプロットして、左側にy1のスケール、右側にy2のスケールを使用したいとします。
df <- data.frame(item=LETTERS[1:n], y1=c(-0.8684, 4.2242, -0.3181, 0.5797, -0.4875), y2=c(-5.719, 205.184, 4.781, 41.952, 9.911 )) # made up!
> df
item y1 y2
1 A -0.8684 -19.154567
2 B 4.2242 219.092499
3 C -0.3181 18.849686
4 D 0.5797 46.945161
5 E -0.4875 -4.721973
データを次のようなものと一緒にプロットすると
ggplot(data=df, aes(label=item)) +
theme_bw() +
geom_segment(aes(x='G1', xend='G2', y=y1, yend=y2), color='grey')+
geom_text(aes(x='G1', y=y1), color='blue') +
geom_text(aes(x='G2', y=y2), color='red') +
theme(legend.position='none', panel.grid=element_blank())
小さいスケールy1が明らかに大きいスケールy2によって折りたたまれるため、うまく整列しません。
ここでの課題に対応するための秘訣は、最初のスケールy1に対して両方のデータセットを技術的にプロットし、元のスケールy2を示すラベルを使用して 2 番目の軸に対して 2 番目のデータセットをレポートすることです。
そこで、表示する新しい軸の特徴を計算して収集する最初のヘルパー関数CalcFudgeAxisを作成します。この関数は好みに合わせて修正できます (これはy2をy1の範囲にマップするだけです)。
CalcFudgeAxis = function( y1, y2=y1) {
Cast2To1 = function(x) ((ylim1[2]-ylim1[1])/(ylim2[2]-ylim2[1])*x) # x gets mapped to range of ylim2
ylim1 <- c(min(y1),max(y1))
ylim2 <- c(min(y2),max(y2))
yf <- Cast2To1(y2)
labelsyf <- pretty(y2)
return(list(
yf=yf,
labels=labelsyf,
breaks=Cast2To1(labelsyf)
))
}
何をもたらすか:
> FudgeAxis <- CalcFudgeAxis( df$y1, df$y2 )
> FudgeAxis
$yf
[1] -0.4094344 4.6831656 0.4029175 1.0034664 -0.1009335
$labels
[1] -50 0 50 100 150 200 250
$breaks
[1] -1.068764 0.000000 1.068764 2.137529 3.206293 4.275058 5.343822
> cbind(df, FudgeAxis$yf)
item y1 y2 FudgeAxis$yf
1 A -0.8684 -19.154567 -0.4094344
2 B 4.2242 219.092499 4.6831656
3 C -0.3181 18.849686 0.4029175
4 D 0.5797 46.945161 1.0034664
5 E -0.4875 -4.721973 -0.1009335
ここで、 Kohske のソリューションを 2 番目のヘルパー関数PlotWithFudgeAxis (新しい軸の ggplot オブジェクトとヘルパー オブジェクトをスローする関数) にラップしました。
library(gtable)
library(grid)
PlotWithFudgeAxis = function( plot1, FudgeAxis) {
# based on: https://rpubs.com/kohske/dual_axis_in_ggplot2
plot2 <- plot1 + with(FudgeAxis, scale_y_continuous( breaks=breaks, labels=labels))
#extract gtable
g1<-ggplot_gtable(ggplot_build(plot1))
g2<-ggplot_gtable(ggplot_build(plot2))
#overlap the panel of the 2nd plot on that of the 1st plot
pp<-c(subset(g1$layout, name=="panel", se=t:r))
g<-gtable_add_grob(g1, g2$grobs[[which(g2$layout$name=="panel")]], pp$t, pp$l, pp$b,pp$l)
ia <- which(g2$layout$name == "axis-l")
ga <- g2$grobs[[ia]]
ax <- ga$children[[2]]
ax$widths <- rev(ax$widths)
ax$grobs <- rev(ax$grobs)
ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
g <- gtable_add_cols(g, g2$widths[g2$layout[ia, ]$l], length(g$widths) - 1)
g <- gtable_add_grob(g, ax, pp$t, length(g$widths) - 1, pp$b)
grid.draw(g)
}
これですべてをまとめることができます:以下のコードは、提案されたソリューションを日常の環境でどのように使用できるかを示しています。プロット呼び出しは、元のデータy2をプロットしなくなりましたが、クローン バージョンyf (事前に計算されたヘルパー オブジェクトFudgeAxis内に保持されています) をプロットします。これはy1のスケールで実行されます。次に、元の ggplot オブジェクトをKohske のヘルパー関数PlotWithFudgeAxisで操作して、 y2のスケールを保持する 2 番目の軸を追加します。操作されたプロットもプロットします。
FudgeAxis <- CalcFudgeAxis( df$y1, df$y2 )
tmpPlot <- ggplot(data=df, aes(label=item)) +
theme_bw() +
geom_segment(aes(x='G1', xend='G2', y=y1, yend=FudgeAxis$yf), color='grey')+
geom_text(aes(x='G1', y=y1), color='blue') +
geom_text(aes(x='G2', y=FudgeAxis$yf), color='red') +
theme(legend.position='none', panel.grid=element_blank())
PlotWithFudgeAxis(tmpPlot, FudgeAxis)
これで、左側のy1と右側のy2の 2 つの軸で希望どおりにプロットされるようになりました
上記の解決策は、率直に言えば、限られた不安定なハックです。ggplot カーネルで再生すると、事後スケールなどを交換するという警告がスローされます。注意して処理する必要があり、別の設定で望ましくない動作が発生する可能性があります。同様に、必要に応じてレイアウトを取得するためにヘルパー関数をいじる必要があるかもしれません。凡例の配置はそのような問題です (パネルと新しい軸の間に配置されるため、削除しました)。2 軸のスケーリング/位置合わせも少し難しいです。上記のコードは、両方のスケールに「0」が含まれている場合はうまく機能し、そうでない場合は 1 つの軸がシフトされます。明らかに、改善する機会がいくつかあります...
写真を保存したい場合は、呼び出しをデバイスのオープン/クローズにラップする必要があります:
png(...)
PlotWithFudgeAxis(tmpPlot, FudgeAxis)
dev.off()
次の記事は、ggplot2 によって生成された 2 つのプロットを 1 つの行に結合するのに役立ちました。
R のクックブックによる 1 ページ (ggplot2) の複数のグラフ
この場合のコードは次のようになります。
p1 <-
ggplot() + aes(mns)+ geom_histogram(aes(y=..density..), binwidth=0.01, colour="black", fill="white") + geom_vline(aes(xintercept=mean(mns, na.rm=T)), color="red", linetype="dashed", size=1) + geom_density(alpha=.2)
p2 <-
ggplot() + aes(mns)+ geom_histogram( binwidth=0.01, colour="black", fill="white") + geom_vline(aes(xintercept=mean(mns, na.rm=T)), color="red", linetype="dashed", size=1)
multiplot(p1,p2,cols=2)
一見単純な質問のように見えますが、2 つの基本的な質問に悩まされています。A) 比較チャートで表示しながらマルチスカラー データを処理する方法、および次に、B) i) データの融解、ii) ファセット化、iii) 追加などの R プログラミングの経験則を使用せずにこれを実行できるかどうか別のレイヤーを既存のレイヤーに。以下に示すソリューションは、データを再スケーリングせずに処理するため、上記の両方の条件を満たします。次に、前述の手法は使用されません。
この方法について詳しく知りたい方は、以下のリンクをたどってください。 データを再スケーリングせずにバーを並べて 2-y 軸グラフをプロットする方法
変数で使用facet_wrap(~ variable, ncol= )
して、新しい比較を作成できます。同軸じゃないけど似てる。
私は、個別の y スケールが「根本的に欠陥がある」ことをハドリー(および他の人)に認め、同意します。ggplot2
そうは言っても、特にデータがワイドフォーマットであり、データをすばやく視覚化または確認したい場合(つまり、個人的な使用のみ) 、この機能があればいいのにと思うことがよくあります。
tidyverse
ライブラリを使用すると、データを長い形式に変換するのはかなり簡単になりますが (それが機能するように) facet_grid()
、プロセスは以下に示すように簡単ではありません。
library(tidyverse)
df.wide %>%
# Select only the columns you need for the plot.
select(date, column1, column2, column3) %>%
# Create an id column – needed in the `gather()` function.
mutate(id = n()) %>%
# The `gather()` function converts to long-format.
# In which the `type` column will contain three factors (column1, column2, column3),
# and the `value` column will contain the respective values.
# All the while we retain the `id` and `date` columns.
gather(type, value, -id, -date) %>%
# Create the plot according to your specifications
ggplot(aes(x = date, y = value)) +
geom_line() +
# Create a panel for each `type` (ie. column1, column2, column3).
# If the types have different scales, you can use the `scales="free"` option.
facet_grid(type~., scales = "free")
以下は、Dag Hjermannの基本的なデータとプログラミングを組み込み、 user4786271の戦略を改善して、プロットとデータ軸を最適に組み合わせる「変換関数」を作成し、そのような関数は R 内で作成できるという baptist のメモに対応します。 .
#Climatogram for Oslo (1961-1990)
climate <- tibble(
Month = 1:12,
Temp = c(-4,-4,0,5,11,15,16,15,11,6,1,-3),
Precip = c(49,36,47,41,53,65,81,89,90,84,73,55))
#y1 identifies the position, relative to the y1 axis,
#the locations of the minimum and maximum of the y2 graph.
#Usually this will be the min and max of y1.
#y1<-(c(max(climate$Precip), 0))
#y1<-(c(150, 55))
y1<-(c(max(climate$Precip), min(climate$Precip)))
#y2 is the Minimum and maximum of the secondary axis data.
y2<-(c(max(climate$Temp), min(climate$Temp)))
#axis combines y1 and y2 into a dataframe used for regressions.
axis<-cbind(y1,y2)
axis<-data.frame(axis)
#Regression of Temperature to Precipitation:
T2P<-lm(formula = y1 ~ y2, data = axis)
T2P_summary <- summary(lm(formula = y1 ~ y2, data = axis))
T2P_summary
#Identifies the intercept and slope of regressing Temperature to Precipitation:
T2PInt<-T2P_summary$coefficients[1, 1]
T2PSlope<-T2P_summary$coefficients[2, 1]
#Regression of Precipitation to Temperature:
P2T<-lm(formula = y2 ~ y1, data = axis)
P2T_summary <- summary(lm(formula = y2 ~ y1, data = axis))
P2T_summary
#Identifies the intercept and slope of regressing Precipitation to Temperature:
P2TInt<-P2T_summary$coefficients[1, 1]
P2TSlope<-P2T_summary$coefficients[2, 1]
#Create Plot:
ggplot(climate, aes(Month, Precip)) +
geom_col() +
geom_line(aes(y = T2PSlope*Temp + T2PInt), color = "red") +
scale_y_continuous("Precipitation", sec.axis = sec_axis(~.*P2TSlope + P2TInt, name = "Temperature")) +
scale_x_continuous("Month", breaks = 1:12) +
theme(axis.line.y.right = element_line(color = "red"),
axis.ticks.y.right = element_line(color = "red"),
axis.text.y.right = element_text(color = "red"),
axis.title.y.right = element_text(color = "red")) +
ggtitle("Climatogram for Oslo (1961-1990)")
最も注目に値するのは、新しい "変換関数" が、各軸のデータ セットから 2 つのデータ ポイント (通常は各セットの最大値と最小値) だけでうまく機能することです。結果として得られる 2 つの回帰の勾配と切片により、ggplot2 は各軸の最小値と最大値のプロットを正確にペアにすることができます。user4786271が指摘したように、2つの回帰は各データセットを変換し、他のデータセットにプロットします。最初の y 軸のブレーク ポイントを 2 番目の y 軸の値に変換します。2 番目は、2 番目の y 軸のデータを最初の y 軸に従って「正規化」されるように変換します。次の出力は、軸が各データセットの最小値と最大値をどのように揃えているかを示しています。
最大値と最小値を一致させることが最も適切な場合があります。ただし、この方法のもう 1 つの利点は、必要に応じて、主軸データに関連するプログラミング行を変更することにより、第 2 軸に関連付けられたプロットを簡単にシフトできることです。以下の出力は、y1 のプログラミング行の最小降水量入力を「0」に変更するだけで、最小温度レベルを「0」降水量レベルに合わせます。
出典: y1<-(c(max(climate$Precip), min(climate$Precip)))
To: y1<-(c(max(climate$Precip), 0))
結果として得られた新しい回帰と ggplot2 がプロットと軸を自動的に調整して、最小気温を「0」降水レベルの新しい「ベース」に正しく整列させたことに注目してください。同様に、Temperature プロットを簡単に上げて、より明確にすることができます。次のグラフは、上記の行を次のように変更するだけで作成されます。
"y1<-(c(150, 55))"
上記の線は、温度グラフの最大値が「150」の降水レベルと一致し、温度線の最小値が「55」の降水レベルと一致することを示しています。ここでも、ggplot2 とその結果の新しい回帰出力により、グラフが軸との正しい位置合わせを維持できることに注意してください。
上記は望ましい出力ではない可能性があります。ただし、これは、グラフを簡単に操作し、プロットと軸の間に正しい関係を維持する方法の例です。Dag Hjermannのテーマを組み込むことで、プロットに対応する軸の識別が改善されます。