【问题标题】:Text labels with background colour in RR中具有背景颜色的文本标签
【发布时间】:2018-01-04 02:17:27
【问题描述】:

我想知道是否有一种简单的方法可以使用基本图形系统将具有对比背景的文本标签添加到 R 图中。到目前为止,我一直使用rect() 函数以及graphics::strheight()graphics::strwidth() 来分别创建背景框,然后使用text() 将我的文本放在上面:

# Prepare a noisy background:
plot(x = runif(1000), y = runif(1000), type = "p", pch = 16, col = "#40404050")

## Parameters for my text:
myText <- "some Text"
posCoordsVec <- c(0.5, 0.5)
cex <- 2

## Background rectangle: 
textHeight <- graphics::strheight(myText, cex = cex)
textWidth <- graphics::strwidth(myText, cex = cex)
pad <- textHeight*0.3
rect(xleft = posCoordsVec[1] - textWidth/2 - pad, 
        ybottom = posCoordsVec[2] - textHeight/2 - pad, 
        xright = posCoordsVec[1] + textWidth/2 + pad, 
        ytop = posCoordsVec[2] + textHeight/2 + pad,
        col = "lightblue", border = NA)

## Place text:
text(posCoordsVec[1], posCoordsVec[2], myText, cex = cex)

这是结果:

这可以完成这项工作,但它非常乏味,当您开始使用posadjoffset 等来调整文本的位置时会遇到麻烦。我知道TeachingDemos::shadowtext() 可以使文本从背景中脱颖而出,但这会添加轮廓而不是框。

我正在寻找一种使用背景框创建文本的简单方法,例如text(x, y, labels, bg = "grey20")。我不能成为第一个需要这种功能的人,我可能只是错过了一些明显的东西。帮助表示赞赏。谢谢

【问题讨论】:

    标签: r text graphics


    【解决方案1】:

    基础图形

    使用legend

    plot(x = runif(1000), y = runif(1000), type = "p", pch = 16, col = "#40404050")
    legend(0.4, 0.5, "Some text", box.col = "lightblue", bg = "lightblue", adj = 0.2)
    

    输出:

    ggplot2

    geom_label

    library(ggplot2)
    df <- data.frame(x = runif(1000), y = runif(1000))
    ggplot(data = df, aes(x = x , y = y))+ 
      geom_point(alpha = 0.2)+
      geom_label(aes(x = 0.5, y = 0.5, label = "Some text"), 
                 fill = "lightblue", label.size = NA, size = 5)
    

    输出:

    【讨论】:

    • 谢谢,ggplot 似乎确实提供了合适的工具。不过,我正在寻找使用基本图形系统的解决方案。
    • @ikop 我包含了另一个使用来自基本图形系统的图例的解决方案。
    • @mpalanco 有没有办法在底座上缩放盒子?
    • @jay.sf 是的,使用参数 cex,例如:cex = 0.5
    • 您也可以提供box.col = NA来关闭框边框
    【解决方案2】:

    显然,似乎没有一个简单的解决方案。因此,我编写了自己的函数来完成这项工作:

    #' Add text with background box to a plot
    #'
    #' \code{boxtext} places a text given in the vector \code{labels} 
    #' onto a plot in the base graphics system and places a coloured box behind 
    #' it to make it stand out from the background.
    #' 
    #' @param x numeric vector of x-coordinates where the text labels should be 
    #' written. If the length of \code{x} and \code{y} differs, the shorter one 
    #' is recycled.
    #' @param y numeric vector of y-coordinates where the text labels should be 
    #' written. 
    #' @param labels a character vector specifying the text to be written.
    #' @param col.text the colour of the text 
    #' @param col.bg color(s) to fill or shade the rectangle(s) with. The default 
    #' \code{NA} means do not fill, i.e., draw transparent rectangles.
    #' @param border.bg color(s) for rectangle border(s). The default \code{NA}
    #' omits borders. 
    #' @param adj one or two values in [0, 1] which specify the x (and optionally 
    #' y) adjustment of the labels. 
    #' @param pos a position specifier for the text. If specified this overrides 
    #' any adj value given. Values of 1, 2, 3 and 4, respectively indicate 
    #' positions below, to the left of, above and to the right of the specified 
    #' coordinates.
    #' @param offset when \code{pos} is specified, this value gives the offset of 
    #' the label from the specified coordinate in fractions of a character width.
    #' @param padding factor used for the padding of the box around 
    #' the text. Padding is specified in fractions of a character width. If a 
    #' vector of length two is specified then different factors are used for the
    #' padding in x- and y-direction.    
    #' @param cex numeric character expansion factor; multiplied by 
    #' code{par("cex")} yields the final character size. 
    #' @param font the font to be used
    #'
    #' @return Returns the coordinates of the background rectangle(s). If 
    #' multiple labels are placed in a vactor then the coordinates are returned
    #' as a matrix with columns corresponding to xleft, xright, ybottom, ytop. 
    #' If just one label is placed, the coordinates are returned as a vector.
    #' @author Ian Kopacka
    #' @examples
    #' ## Create noisy background
    #' plot(x = runif(1000), y = runif(1000), type = "p", pch = 16, 
    #' col = "#40404060")
    #' boxtext(x = 0.5, y = 0.5, labels = "some Text", col.bg = "#b2f4f480", 
    #'     pos = 4, font = 2, cex = 1.3, padding = 1)
    #' @export
    boxtext <- function(x, y, labels = NA, col.text = NULL, col.bg = NA, 
            border.bg = NA, adj = NULL, pos = NULL, offset = 0.5, 
            padding = c(0.5, 0.5), cex = 1, font = graphics::par('font')){
    
        ## The Character expansion factro to be used:
        theCex <- graphics::par('cex')*cex
    
        ## Is y provided:
        if (missing(y)) y <- x
    
        ## Recycle coords if necessary:    
        if (length(x) != length(y)){
            lx <- length(x)
            ly <- length(y)
            if (lx > ly){
                y <- rep(y, ceiling(lx/ly))[1:lx]           
            } else {
                x <- rep(x, ceiling(ly/lx))[1:ly]
            }       
        }
    
        ## Width and height of text
        textHeight <- graphics::strheight(labels, cex = theCex, font = font)
        textWidth <- graphics::strwidth(labels, cex = theCex, font = font)
    
        ## Width of one character:
        charWidth <- graphics::strwidth("e", cex = theCex, font = font)
    
        ## Is 'adj' of length 1 or 2?
        if (!is.null(adj)){
            if (length(adj == 1)){
                adj <- c(adj[1], 0.5)            
            }        
        } else {
            adj <- c(0.5, 0.5)
        }
    
        ## Is 'pos' specified?
        if (!is.null(pos)){
            if (pos == 1){
                adj <- c(0.5, 1)
                offsetVec <- c(0, -offset*charWidth)
            } else if (pos == 2){
                adj <- c(1, 0.5)
                offsetVec <- c(-offset*charWidth, 0)
            } else if (pos == 3){
                adj <- c(0.5, 0)
                offsetVec <- c(0, offset*charWidth)
            } else if (pos == 4){
                adj <- c(0, 0.5)
                offsetVec <- c(offset*charWidth, 0)
            } else {
                stop('Invalid argument pos')
            }       
        } else {
          offsetVec <- c(0, 0)
        }
    
        ## Padding for boxes:
        if (length(padding) == 1){
            padding <- c(padding[1], padding[1])
        }
    
        ## Midpoints for text:
        xMid <- x + (-adj[1] + 1/2)*textWidth + offsetVec[1]
        yMid <- y + (-adj[2] + 1/2)*textHeight + offsetVec[2]
    
        ## Draw rectangles:
        rectWidth <- textWidth + 2*padding[1]*charWidth
        rectHeight <- textHeight + 2*padding[2]*charWidth    
        graphics::rect(xleft = xMid - rectWidth/2, 
                ybottom = yMid - rectHeight/2, 
                xright = xMid + rectWidth/2, 
                ytop = yMid + rectHeight/2,
                col = col.bg, border = border.bg)
    
        ## Place the text:
        graphics::text(xMid, yMid, labels, col = col.text, cex = theCex, font = font, 
                adj = c(0.5, 0.5))    
    
        ## Return value:
        if (length(xMid) == 1){
            invisible(c(xMid - rectWidth/2, xMid + rectWidth/2, yMid - rectHeight/2,
                            yMid + rectHeight/2))
        } else {
            invisible(cbind(xMid - rectWidth/2, xMid + rectWidth/2, yMid - rectHeight/2,
                            yMid + rectHeight/2))
        }    
    }
    

    此功能允许我在带有背景框的绘图中添加文本,同时保留功能text() 的大部分灵活性。

    示例:

    ## Create noisy background:
    plot(x = runif(1000), y = runif(1000), type = "p", pch = 16, col = "#40404060")
    ## Vector of labels, using argument 'pos' to position right of coordinates:
    boxtext(x = c(0.3, 0.1), y = c(0.6, 0.1), labels = c("some Text", "something else"), 
            col.bg = "#b2f4f4c0", pos = 4, padding = 0.3)
    ## Tweak cex, font and adj:
    boxtext(x = 0.2, y = 0.4, labels = "some big and bold text", 
            col.bg = "#b2f4f4c0", adj = c(0, 0.6), font = 2, cex = 1.8)
    

    【讨论】:

    • plot( c(1,20), c(-0.2,0.2)); boxtext(10, -0.03, "text2", col.bg="cyan", border.bg="red") 上失败; y-box 太高了。
    【解决方案3】:

    使用 altcode 字符快速破解一个框:

    plot(x=runif(1000), y=runif(1000), 
         type="p", pch=16, col="#40404050")
    
    labels <- c("some text", "something else")
    
    boxes <- sapply(nchar(labels), function(n) 
      paste(rep("█", n), collapse=""))
    
    pos <- rbind(c(0.2, .1), c(.5, .5))
    text(pos, labels=boxes, col="#CCCCCC99")
    text(pos, labels=labels)
    

    【讨论】:

    • 不错的 hack,不过最好使用 unicode。试试paste(rep("\U2588", n), collapse="")
    • 仅适用于等宽字体且无换行符
    【解决方案4】:

    祝福你勤劳的心,但plotrixboxed.labels()

    # Prepare a noisy background:
    plot(x = runif(1000), y = runif(1000), type = "p", pch = 16, col = "#40404050")
    
    ## Parameters for my text:
    myText <- "some Text"
    posCoordsVec <- c(0.5, 0.5)
    cex <- 2
    
    ## Background rectangle: 
    textHeight <- graphics::strheight(myText, cex = cex)
    textWidth <- graphics::strwidth(myText, cex = cex)
    pad <- textHeight*0.3
    
    
    ## Place text:
    plotrix::boxed.labels(posCoordsVec[1], posCoordsVec[2], myText, cex = cex, 
          border = NA, bg ="lightblue", xpad = 1.4, ypad = 1.4)
    

    【讨论】:

    • 非常简单!我认为迄今为止最好的解决方案
    猜你喜欢
    • 2018-03-14
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    • 2019-10-14
    • 2016-05-15
    相关资源
    最近更新 更多