R 从角度绘制线段,不管纵横比如何变化

R 从角度绘制线段,不管纵横比如何变化,r,R,我试图在R中绘制一个表,列名称与表成一定角度。我想以与文本相同的角度添加行来分隔这些列名。但是,text()函数中指定的角度似乎与绘图的纵横比无关,而我在segments()函数中使用的角度则取决于绘图的纵横比 以下是我的意思的一个例子: nRows <- 5 nColumns <- 3 theta <- 30 rowLabels <- paste('row', 1:5, sep='') colLabels <- paste('col', 1:3, sep='')

我试图在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)


有没有办法指定一个设置的角度,这样当我调整窗口大小时,图形看起来是正确的?

30弧度的
θ值是一个数据空间角度。它仅适用于在数据空间计算中使用,例如在调用绘制对角线的
segments()

srt
图形参数指定设备空间中的文本旋转,这意味着无论底层打印区域的纵横比如何,文本将按照物理设备上的指定角度呈现

数据和设备空间之间的关系是动态确定的,并受到许多因素的影响:

  • 设备尺寸(GUI窗口客户端区域大小或目标文件大小)
  • 图形多样性(如果使用多图形绘图;请参阅
    mfrow
    mfcol
    图形参数)
  • 任何内页边距和外页边距(大多数绘图都有内页边距,外页边距很少)
  • 任何内部间距(请参见
    xaxs
    yaxs
    图形参数)
  • 绘图范围(
    xlim
    ylim
正确的方法是:(1)动态查询以设备空间距离单位测量的数据空间纵横比,(2)使用它将
theta
从数据空间角度转换为设备空间角度

1:查询纵横比

我们可以通过沿x轴找到1个数据空间单元的设备空间等效物来计算纵横比,对y轴进行同样的计算,然后取比值
y/x
。这些功能就是为此目的而设计的

calcAspectRatio <- function() abs(diff(grconvertY(0:1,'user','device'))/diff(grconvertX(0:1,'user','device')));
简单地说,我发现这是一个非常有趣的数学变换。角度0、90、180和270不受影响,这是有意义的;纵横比的变化不应影响这些角度。垂直延伸将角度拉向y轴,水平延伸将角度拉向x轴。至少我是这么想的


所以,把这些放在一起,我们有下面的解决方案。请注意,为了更加简洁,我重写了您的代码,并做了一些小改动,但大部分都是一样的。显然,最重要的变化是我在
theta
周围添加了对
dataAngleToDevice()
的调用,第二个参数传递
calaspectratio()
。此外,我使用更小(按字体)但更长(按字符串)的列名来更清楚地显示文本的角度,我将文本移到更靠近对角线的位置,从一开始就以弧度存储
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);

nRows可能更容易在网格中实现,如果这是一个选项,我不认为这是可能的(尽管我可能错了),因为
asp
的工作方式。从
plot.window
帮助文件中:“如果asp是一个有限的正值,则设置该窗口,使x方向上的一个数据单元的长度等于asp*y方向上的一个数据单元”现在,如果不指定asp,
y1=nRows+tan(θ*pi/180)
中断,因为弧度取决于通过独立于角度(保持不变)的窗口大小调整来缩放的半径。所以,我没有在BaseR中使用se解决方案,但再一次,我希望被证明不是这样!选择一个
asp
并用
x1=1:nColumns*asp
theta
按长比例缩放,使标题行非常感谢这样一个完整的解决方案!
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()
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);
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)
);