2016-03-30 17 views
13

Đây thực sự là hai câu hỏi trong một (không chắc chắn nếu đi ngược lại các quy tắc SO, nhưng dù sao).ggplot2: geom_text thay đổi kích thước với cốt truyện và văn bản lực/phù hợp trong geom_bar

Câu hỏi đầu tiên là làm thế nào tôi có thể bắt buộc geom_text để vừa với một số geom_bar? (động theo các giá trị được vẽ)

Nhìn xung quanh, các giải pháp tôi tìm thấy đã thay đổi kích thước của nhãn. Điều đó chắc chắn hoạt động, nhưng không phải cho mọi trường hợp. Bạn có thể thay đổi kích thước cho một ô cụ thể để làm cho văn bản vừa với thanh, nhưng khi dữ liệu thay đổi, bạn có thể cần phải thay đổi kích thước của văn bản theo cách thủ công. Vấn đề thực tế của tôi là tôi cần phải tạo ra cốt truyện tương tự để thay đổi dữ liệu liên tục (hàng ngày), vì vậy tôi không thể điều chỉnh kích thước cho từng ô.

Tôi đã thử đặt kích thước của nhãn làm chức năng của dữ liệu. Nó kinda hoạt động, không hoàn hảo, nhưng hoạt động trong nhiều trường hợp.

Nhưng đây là một vấn đề khác, ngay cả khi nhãn phù hợp trong thanh, thay đổi kích thước cốt truyện làm rối tung mọi thứ. Nhìn vào nó, tôi cũng tìm thấy trong số ggplot documentation rằng

nhãn không có chiều cao và chiều rộng, nhưng chúng là đơn vị vật lý, không phải là dữ liệu đơn vị. Lượng không gian họ chiếm trên lô đó không phải là hằng số trong đơn vị dữ liệu: khi bạn thay đổi kích cỡ ô, nhãn vẫn giữ nguyên kích thước, nhưng kích thước của các thay đổi trục .

Điều này đưa tôi đến câu hỏi thứ hai: Có thể thay đổi hành vi mặc định này và để/làm cho nhãn đổi kích thước với ô không?

Và cũng để tôi tinh chỉnh câu hỏi đầu tiên của mình. Có thể ép buộc geom_text để vừa với một số geom_bar, tự động đặt kích thước của văn bản bằng cách sử dụng mối quan hệ thông minh giữa các đơn vị vật lý và đơn vị dữ liệu không?

Vì vậy, để làm theo thực hành tốt, đây là ví dụ tái sản xuất của tôi:

set.seed(1234567) 
data_gd <- data.frame(x = letters[1:5], 
         y = runif(5, 100, 99999)) 

ggplot(data = data_gd, 
     mapping = aes(x = x, y = y, fill = x)) + 
    geom_bar(stat = "identity") + 
    geom_text(mapping = aes(label = y, y = y/2)) 

Mã này tạo ra âm mưu này:

enter image description here

Nếu tôi chỉ đơn giản là thay đổi kích thước cốt truyện, "labels stay the same size, but the size of the axes changes" do đó làm cho các nhãn phù hợp với các thanh (bây giờ có lẽ các nhãn thậm chí còn quá nhỏ).

enter image description here

Vì vậy, đây là câu hỏi thứ hai của tôi. Nó sẽ được tốt đẹp mà các nhãn thay đổi kích cỡ là tốt và giữ khẩu phần khía cạnh liên quan đến các quán bar. Bất kỳ ý tưởng làm thế nào để thực hiện điều này hoặc nếu nó có thể ở tất cả?

Ok, nhưng quay trở lại cách phù hợp với nhãn trong các thanh, giải pháp đơn giản nhất là đặt kích thước của nhãn.

ggplot(data = data_gd, 
     mapping = aes(x = x, y = y, fill = x)) + 
    geom_bar(stat = "identity") + 
    geom_text(mapping = aes(label = y, y = y/2), size = 3) 

Một lần nữa, điều này hoạt động như hình bên dưới, nhưng không thể duy trì/cũng không thay đổi dữ liệu.

enter image description here

Ví dụ, mã rất giống nhau để tạo ra những âm mưu với sản lượng dữ liệu khác nhau kết quả thảm khốc.

data_gd <- data.frame(x = letters[1:30], 
         y = runif(30, 100, 99999)) 
ggplot(data = data_gd, 
     mapping = aes(x = x, y = y, fill = x)) + 
    geom_bar(stat = "identity") + 
    geom_text(mapping = aes(label = y, y = y/2), size = 3) 

enter image description here

Và tôi có thể tiếp tục với các ví dụ, thiết lập kích thước của các nhãn như một chức năng của số chủng loại trên trục x và vân vân. Nhưng bạn nhận được điểm, và có lẽ một trong các bạn ggplot2 chuyên gia có thể cho tôi ý tưởng.

Trả lời

9

một tùy chọn có thể là viết một geom sử dụng textGrob với phương thức drawDetails tùy chỉnh để vừa với không gian được phân bổ, được đặt theo chiều rộng thanh.

library(grid) 
library(ggplot2) 

fitGrob <- function(label, x=0.5, y=0.5, width=1){ 
    grob(x=x, y=y, width=width, label=label, cl = "fit") 
} 
drawDetails.fit <- function(x, recording=FALSE){ 
    tw <- sapply(x$label, function(l) convertWidth(grobWidth(textGrob(l)), "native", valueOnly = TRUE)) 
    cex <- x$width/tw 
    grid.text(x$label, x$x, x$y, gp=gpar(cex=cex), default.units = "native") 
} 


`%||%` <- ggplot2:::`%||%` 

GeomFit <- ggproto("GeomFit", GeomRect, 
        required_aes = c("x", "label"), 

        setup_data = function(data, params) { 
        data$width <- data$width %||% 
         params$width %||% (resolution(data$x, FALSE) * 0.9) 
        transform(data, 
           ymin = pmin(y, 0), ymax = pmax(y, 0), 
           xmin = x - width/2, xmax = x + width/2, width = NULL 
        ) 
        }, 
        draw_panel = function(self, data, panel_scales, coord, width = NULL) { 
        bars <- ggproto_parent(GeomRect, self)$draw_panel(data, panel_scales, coord) 
        coords <- coord$transform(data, panel_scales)  
        width <- abs(coords$xmax - coords$xmin) 
        tg <- fitGrob(label=coords$label, y = coords$y/2, x = coords$x, width = width) 

        grobTree(bars, tg) 
        } 
) 

geom_fit <- function(mapping = NULL, data = NULL, 
        stat = "count", position = "stack", 
        ..., 
        width = NULL, 
        binwidth = NULL, 
        na.rm = FALSE, 
        show.legend = NA, 
        inherit.aes = TRUE) { 

    layer(
    data = data, 
    mapping = mapping, 
    stat = stat, 
    geom = GeomFit, 
    position = position, 
    show.legend = show.legend, 
    inherit.aes = inherit.aes, 
    params = list(
     width = width, 
     na.rm = na.rm, 
     ... 
    ) 
) 
} 


set.seed(1234567) 
data_gd <- data.frame(x = letters[1:5], 
         y = runif(5, 100, 99999)) 

ggplot(data = data_gd, 
     mapping = aes(x = x, y = y, fill = x, label=round(y))) + 
    geom_fit(stat = "identity") + 
    theme() 

enter image description here

+0

awesome !!!, ..., vẫn cố gắng hiểu nó hoạt động như thế nào vì tôi chỉ biết cơ bản về 'ggplot2', nhưng mã hoạt động trơn tru và giải quyết cả hai vấn đề: nó hoàn toàn phù hợp với văn bản trong các thanh và văn bản thay đổi kích thước cùng với cốt truyện. Tôi thực sự không nghĩ rằng điều đó là có thể. Cảm ơn nhiều – elikesprogramming

6

Nếu biểu đồ thanh ngang là OK thì vấn đề không phải là kích thước của nhãn mà là vị trí. Giải pháp của tôi sẽ là

enter image description here

tạo ra bởi mã này:

library(ggplot2) 
data_gd <- data.frame(x = letters[1:26], 
         y = runif(26, 100, 99999)) 
ymid <- mean(range(data_gd$y)) 
ggplot(data = data_gd, 
     mapping = aes(x = x, y = y, fill = x)) + 
    geom_bar(stat = "identity") + 
    geom_text(mapping = aes(label = y, y = y, 
      hjust = ifelse(y < ymid, -0.1, 1.1)), size = 3) + 
    coord_flip() 

Bí quyết được thực hiện trong ba bước:

  1. coord_flip tạo ra một biểu đồ thanh ngang.
  2. Ánh xạ trong geom_text cũng sử dụng tùy theo giá trị của y. Nếu thanh này ngắn hơn một nửa phạm vi của y, thì văn bản được in bên ngoài thanh (phải sang giá trị y). Nếu thanh dài hơn một nửa phạm vi của y thì văn bản sẽ được in bên trong thanh (trái sang giá trị y). Điều này đảm bảo rằng văn bản luôn được in bên trong khu vực ô (nếu không quá dài).
  3. Tôi đã thêm một số khoảng trống bổ sung giữa thanh và văn bản. Nếu bạn muốn văn bản bắt đầu hoặc kết thúc trực tiếp tại giá trị y, bạn có thể sử dụng hjust = ifelse(y < ymid, 0, 1)).
+0

Cảm ơn. Tôi đoán điều này là tốt như nó được, phải không ?, đối với tôi, thanh dọc là phải, nhưng tôi nghĩ rằng nó sẽ được chấp nhận để xoay chỉ các nhãn ('góc = 90') và sử dụng lừa của bạn với' hjust' để đảm bảo văn bản được in bên trong khu vực ô. – elikesprogramming

+0

Vẫn tự hỏi về việc thay đổi kích cỡ ô và nhãn cùng lúc, ..., nhưng tôi gần như chắc chắn là không thể ở tất cả – elikesprogramming

+0

Kích thước văn bản cần được điều chỉnh tùy thuộc vào số lượng thanh, như 'text_size <- if (n_bar <= 20) 4 else 3' hoặc một cái gì đó phức tạp hơn bằng cách sử dụng 'scale_size'. – Uwe

Các vấn đề liên quan