2016-01-18 6 views
20

Rを使用してテーブルをプロットしようとしています。テキストと同じ角度でこれらの列名を区切る行を追加したいと思います。しかし、text()関数で指定された角度は、プロットの縦横比とは無関係ですが、segments()関数で使用している角度は、プロットの縦横比によって異なります。ここでアスペクト比の変更にもかかわらず、角度からセグメントを描画する

は、私が何を意味するかの例です:

nRows <- 5 
nColumns <- 3 
theta <- 30 

rowLabels <- paste('row', 1:5, sep='') 
colLabels <- paste('col', 1:3, sep='') 

plot.new() 
par(mar=c(1,8,5,1), xpd=NA) 
plot.window(xlim = c(0, nColumns), ylim = c(0, nRows), asp = 1) 
text(labels = rowLabels, x=0, y=seq(from=0.5, to=nRows, by=1), pos=2) 
text(labels = colLabels, x = seq(from = 0.4, to = nColumns, by = 1), y = nRows + 0.1, pos = 4, srt = theta, cex = 1.1) 
segments(x0 = c(0:nColumns), x1 = c(0:nColumns), y0 = 0, y1 = nRows, lwd = 0.5) 
segments(x0 = 0, x1 = nColumns, y0 = 0:nRows, y1 = 0:nRows, lwd = 0.5) 

#column name separators, angle converted to radians 
segments(x0 = 0:(nColumns - 1), x1 = 1:nColumns, y0 = nRows, y1 = nRows + tan(theta * pi/180), lwd = 0.5) 

enter image description here

私はaspを指定せずに自分の好みに合わせて、このプロットウィンドウのサイズを変更できるようにしたい場合は、角度はもはや一致しません:

nRows <- 5 
nColumns <- 3 
theta <- 30 

rowLabels <- paste('row', 1:5, sep='') 
colLabels <- paste('col', 1:3, sep='') 

plot.new() 
par(mar=c(1,8,5,1), xpd=NA) 
plot.window(xlim = c(0, nColumns), ylim = c(0, nRows)) 
text(labels = rowLabels, x=0, y=seq(from=0.5, to=nRows, by=1), pos=2) 
text(labels = colLabels, x = seq(from = 0.4, to = nColumns, by = 1), y = nRows + 0.1, pos = 4, srt = theta, cex = 1.1) 
segments(x0 = c(0:nColumns), x1 = c(0:nColumns), y0 = 0, y1 = nRows, lwd = 0.5) 
segments(x0 = 0, x1 = nColumns, y0 = 0:nRows, y1 = 0:nRows, lwd = 0.5) 

#column name separators, angle converted to radians 
segments(x0 = 0:(nColumns - 1), x1 = 1:nColumns, y0 = nRows, y1 = nRows + tan(theta * pi/180), lwd = 0.5) 

enter image description here

設定した角度を指定する方法はありますか?ウィンドウのサイズを変更すると図が正しく見えるようになりますか?

+5

は、おそらく簡単に、それは私はそれが可能だとは思わないオプション – baptiste

+3

だ場合は(私が間違っている可能性がありますしかし、aspが動作する方法のために。 'プロットから。window_help file: "aspが有限の正の値である場合、ウィンドウは、x方向の1つのデータ単位がy方向のasp * 1データ単位と等しい長さになるように設定されます。"ラジアンは角度に依存せずにウィンドウのサイズ変更によってスケーリングされる半径に依存するため(常に一定)、asp、 'y1 = nRows + tan(theta * pi/180)'を指定します。だから、私は基底Rの解を見ないのですが、もう一度、私はそうでなければ証明されるのが大好きです! – Felix

+0

ヘッダー行を作るとき 'asp'を選び、' x1 = 1:nColumns * asp'で長く 'theta'を指定してください – rawr

答えて

2

thetaの値は30アーク度で、データ空間の角度です。これは、対角線を描画するsegments()への呼び出しのようなデータ空間計算での使用にのみ適しています。

srtグラフィックパラメータは、デバイス空間でのテキストの回転を指定します。つまり、テキストは、基礎となるプロット領域の縦横比にかかわらず、物理デバイス上の指定された角度に従ってレンダリングされます。

データとデバイス空間の間の関係は、動的に決定され、多数の要因によって影響される:

  • デバイス寸法(GUIウインドウのクライアント領域のサイズまたは宛先ファイルサイズ)。
  • 図の多重度(マルチフィギュアプロットを使用する場合はmfrowおよびmfcolのグラフィックパラメータを参照)。
  • 内側と外側の余白(ほとんどのプロットには内側の余白があり、外側はまれです)。
  • 任意の内部スペーシング(およびyaxsグラフィカルパラメータを参照)。
  • プロット範囲(xlimおよびylim)。

あなたが欲しいものを行うための正しい方法は、デバイス空間距離単位で測定された(1)動的データ・スペースのアスペクト比を問い合わせると、(2)データ空間からthetaを変換するためにそれを使用することですデバイス空間の角度に対する角度である。

1:アスペクト比

の検索我々は、x軸に沿った1データ空間部のデバイス空間等価を見つけることによって、アスペクト比を算出し、y軸に対して同じ操作を行うことができ比率はy/xです。この目的のために関数grconvertX() and grconvertY()が作られています。

calcAspectRatio <- function() abs(diff(grconvertY(0:1,'user','device'))/diff(grconvertX(0:1,'user','device'))); 

変換関数は、距離ではなく個々の座標で動作します。しかし、それらはベクトル化されているので、0:1に入力座標系で1単位離れた2つの座標を変換し、diff()を取って出力座標系の等価単位距離を得ることができます。

なぜabs()コールが必要なのか疑問に思うかもしれません。多くのグラフィックスデバイスでは、y軸は上向きではなく下向きに増加するため、データスペースの座標が小さくなるほどデバイススペースの座標が大きくなります。したがって、これらのケースで最初のdiff()コールの結果は否定的です。理論的には、これはx軸では決して起こらないはずですが、ちょうどその場合のabs()コールで商全体をラップすることもできます。

2:そこここに取ることができるいくつかの数学的なアプローチがありますが、私は最も単純な三角関数を取得するための角度のtan()を取ることだと思います

デバイス空間にデータ空間からシータを変換y/xの比率で、アスペクト比を掛けて、atan2()を使用して角度に戻します。

dataAngleToDevice <- function(rad,asp) { 
    rad <- rad%%(pi*2); ## normalize to [0,360) to make following ops easier 
    y <- abs(tan(rad))*ifelse(rad<=pi,1,-1)*asp; ## derive y/x trig ratio with proper sign for y and scale by asp 
    x <- ifelse(rad<=pi/2 | rad>=pi*3/2,1,-1); ## derive x component with proper sign 
    atan2(y,x)%%(pi*2); ## use atan2() to derive result angle in (-180,180], and normalize to [0,360) 
}; ## end dataAngleToDevice() 

簡単に言えば、これは非常に興味深い数学的変換であることがわかります。角度0、90、180、270は影響を受けません。アスペクト比の変更はそれらの角度に影響してはなりません。垂直方向の伸びは、y軸に向かって角度を引っ張り、水平方向の伸びは、x軸に向かって角度を引く。少なくとも私はそれをどのように視覚化しているのですか?


このようにまとめてみると、以下の解決策があります。私はあなたのコードをより簡潔に書き直し、少し変更を加えましたが、ほとんど同じです。明らかに最も重要な変更は、thetaの周りのdataAngleToDevice()への呼び出しを追加し、2番目の引数がcalcAspectRatio()を渡すことです。さらに、テキストの角度をよりはっきりと示すために、小さい(フォント単位で)長い列名を使用し、テキストを対角線に近づけ、最初からthetaをラジアンで格納しました。

nRows <- 5; 
nColumns <- 3; 
theta <- 30*pi/180; 

rowLabels <- paste0('row',1:5); 
colLabels <- do.call(paste,rep(list(paste0('col',1:3)),5L)); 

plot.new(); 
par(mar=c(1,8,5,1),xpd=NA); 
plot.window(xlim=c(0,nColumns),ylim=c(0,nRows)); 
segments(0:nColumns,0,0:nColumns,nRows,lwd=0.5); 
segments(0,0:nRows,nColumns,0:nRows,lwd=0.5); 
text(0,seq(0.5,nRows,1),rowLabels,pos=2); 
## column name separators 
segments(0:(nColumns-1),nRows,1:nColumns,nRows+tan(theta),lwd=0.5); 
text(seq(0.3,nColumns,1),nRows+0.1,colLabels,pos=4,srt=dataAngleToDevice(theta,calcAspectRatio())*180/pi); 

ここでは略正方形のアスペクト比のデモです:

roughly-square

ワイド:

wide

背の高い:

tall


私は、変換のプロットを作っ:グリッド内に達成することが

xlim <- ylim <- c(0,360); 
xticks <- yticks <- seq(0,360,30); 
plot(NA,xlim=xlim,ylim=ylim,xlab='data',ylab='device',axes=F); 
box(); 
axis(1L,xticks); 
axis(2L,yticks); 
abline(v=xticks,col='grey'); 
abline(h=yticks,col='grey'); 
lineParam <- data.frame(asp=c(1/1,1/2,2/1,1/4,4/1),col=c('black','darkred','darkblue','red','blue'),stringsAsFactors=F); 
for (i in seq_len(nrow(lineParam))) { 
    x <- 0:359; 
    y <- dataAngleToDevice(x*pi/180,lineParam$asp[i])*180/pi; 
    lines(x,y,col=lineParam$col[i]); 
}; 
with(lineParam[order(lineParam$asp),], 
    legend(310,70,asp,col,title=expression(bold(aspect)),title.adj=c(NA,0.5),cex=0.8) 
); 

data-angle-to-device

+2

このような完全な解決策をありがとう! – Pascal

関連する問題