ggplot2轴标签中的SI前缀

17

我经常在GNU R / ggplot中为一些与字节相关的测量绘制图形。内置的坐标轴标签通常是普通数字或科学计数法,例如1兆字节= 1e6。我希望使用SI前缀(千= 1e3,兆= 1e6,吉= 1e9等)替代它们,即坐标应该标记为1.5K、5K、1M、150M、4G等。

我目前使用以下代码:

si_num <- function (x) {

  if (!is.na(x)) {
    if (x > 1e6) { 
      chrs <- strsplit(format(x, scientific=12), split="")[[1]];
      rem <- chrs[seq(1,length(chrs)-6)];
      rem <- append(rem, "M");
    }

    else if (x > 1e3) { 
      chrs <- strsplit(format(x, scientific=12), split="")[[1]];
      rem <- chrs[seq(1,length(chrs)-3)];
      rem <- append(rem, "K");
    }
    else {
      return(x);
    }

    return(paste(rem, sep="", collapse=""));
  }
  else return(NA);
} 

si_vec <- function(x) {
  sapply(x, FUN=si_num);
}

library("ggplot2");

bytes=2^seq(0,20) + rnorm(21, 4, 2);
time=bytes/(1e4 + rnorm(21, 100, 3)) + 8;

my_data = data.frame(time, bytes);

p <- ggplot(data=my_data, aes(x=bytes, y=time)) +
     geom_point() +
     geom_line() +
     scale_x_log10("Message Size [Byte]", labels=si_vec) +
     scale_y_continuous("Round-Trip-Time [us]");
p;

我想知道这个解决方案能否得到改进,因为我的方法在每个图表中需要大量的样板代码。


你可以查看 utils:::print.object_size - James
2个回答

30

我使用 library("sos"); findFn("{SI prefix}") 命令来查找 sitools 包。

构建数据:

bytes <- 2^seq(0,20) + rnorm(21, 4, 2)
time <- bytes/(1e4 + rnorm(21, 100, 3)) + 8
my_data <- data.frame(time, bytes)

加载包:

library("sitools")
library("ggplot2")    

创建图表:

(p <- ggplot(data=my_data, aes(x=bytes, y=time)) +
     geom_point() +
     geom_line() +
     scale_x_log10("Message Size [Byte]", labels=f2si) +
     scale_y_continuous("Round-Trip-Time [us]"))

我不确定这与你的函数相比如何,但至少有人费心编写了它...

我稍微修改了你的代码风格——在行末加上分号虽然无伤大雅,但通常是MATLAB或C程序员的标志...

编辑:我最初定义了一个通用的格式化函数

si_format <- function(...) {
    function(x) f2si(x,...)
}

按照格式(例如)scales::comma_format,但在这种情况下似乎不必要--这只是我不完全理解的更深层次的ggplot2魔法的一部分。

对我来说,OP的代码给出的答案似乎不太正确:最右边的轴刻度是“1000K”,而不是“1M”--可以通过将>1e6测试更改为>=1e6来修复此问题。另一方面,f2si使用小写k--我不知道是否需要K(用toupper()包装结果可以解决这个问题)。

OP的结果(si_vec):

enter image description here

我的结果(f2si):

enter image description here


5

更新:最近版本的scales包包含可打印易读标签的功能。

在这种情况下,可以使用label_bytes

library(ggplot2)
library(scales)

bytes <- 2^seq(0,20) + rnorm(21, 4, 2)

my_data <- data.frame(
    bytes=as.integer(bytes),
    time=bytes / (1e4 + rnorm(21, 100, 3)) + 8
)

ggplot(data=my_data, aes(x=bytes, y=time)) +
    geom_point() +
    geom_line() +
    scale_x_log10("Message Size [Byte]", labels=label_bytes()) +
    scale_y_continuous("Round-Trip-Time [us]")

scales-si-labels

如果你更喜欢使用IEC单位(KiB = 2^10MiB = 2 ^ 20,...),可以指定 labels=label_bytes(units = "auto_binary")。查看下面原始答案中的第二个图表,因为结果非常相似。


原始答案

对于字节,可以使用gdata::humanReadablehumanReadable 支持国际单位制(1000 Byte = 1 KB)以及由IEC定义的二进制前缀(1024 Byte = 1 KiB)。

这个函数 humanReadableLabs 允许自定义参数并处理NA值:

humanReadableLabs <- function(...) {
    function(x) {
        sapply(x, function(val) {
            if (is.na(val)) {
                return("")
            } else {
                return(
                    humanReadable(val, ...)
                )
            }
        })
    }
}

现在更改标签以使用国际单位制前缀和“字节”作为单位很简单:
library(ggplot2)
library(gdata)

bytes <- 2^seq(0,20) + rnorm(21, 4, 2)

my_data <- data.frame(
    bytes=as.integer(bytes),
    time=bytes / (1e4 + rnorm(21, 100, 3)) + 8
)

humanReadableLabs <- function(...) {...}

ggplot(data=my_data, aes(x=bytes, y=time)) +
    geom_point() +
    geom_line() +
    scale_x_log10("Message Size [Byte]", labels=humanReadableLabs(standard="SI")) +
    scale_y_continuous("Round-Trip-Time [us]")

IEC前缀是通过省略standard="SI"来绘制的。请注意,为了使数值易读,必须指定断点。

si-labels

ggplot(data=my_data, aes(x=bytes, y=time)) +
    geom_point() +
    geom_line() +
    scale_x_log10("Message Size [Byte]", labels=humanReadableLabs()) +
    scale_y_continuous("Round-Trip-Time [us]")

iec-labels


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接