automatic SI prefixes as ticklabels on axis

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

automatic SI prefixes as ticklabels on axis

Jonas Stein
i want to plot values with frequency on a logarithmic x axis.
similar to this example that i found in the web:
http://www.usspeaker.com/jensen%20p15n-graph.gif

I would like to convert long numbers to si prefix notation
like in the example

(200000 to 200k, 35000000 to 3.5 M)

Of course i could create labels by hand, but
i have many files so i need some automatic function.

Has anyone done that in R?

Kind regards and thank you for your help,

--
Jonas Stein <[hidden email]>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
Reply | Threaded
Open this post in threaded view
|

Re: automatic SI prefixes as ticklabels on axis

Jonas Stein
On 2012-01-05, Jonas Stein <[hidden email]> wrote:

> i want to plot values with frequency on a logarithmic x axis.
> similar to this example that i found in the web:
> http://www.usspeaker.com/jensen%20p15n-graph.gif
>
> I would like to convert long numbers to si prefix notation
> like in the example
>
> (200000 to 200k, 35000000 to 3.5 M)
>
> Of course i could create labels by hand, but
> i have many files so i need some automatic function.
>
> Has anyone done that in R?
>
> Kind regards and thank you for your help,

my first try looks like this:
==================================

getSIstring <- function(x){

  sistring <- paste(x);
 
  prefixpairs <- data.frame(c(1e24,1e21,1e18,1e15,1e12,1e9,1e6,1e3,1e0,
                              1e-3,1e-6,1e-9,1e-12,1e-15,1e-18,1e-21,1e-24),
                            c("Y", "Z", "E", "P", "T", "G", "M", "k", " ",
                              "m", "u", "n", "p", "f", "a", "z", "y"))
 
  colnames(prefixpairs) <- c("factor", "prefix")
  i=0
  repeat{i=i+1;
         if (x > prefixpairs$factor[i]) {
           sistring <- paste(x/prefixpairs$factor[i], prefixpairs$prefix[i]); break;}
         if (i >= length(prefixpairs$factor)) break}

   return(sistring)
}

==================================

> getSIstring(2e7)
[1] "20 M"

How can i improve this function?
It would be nice if it could handle lists too like sin() can do
> sin(1:4)
[1]  0.8414710  0.9092974  0.1411200 -0.7568025

kind regards,

--
Jonas Stein <[hidden email]>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
Reply | Threaded
Open this post in threaded view
|

Re: automatic SI prefixes as ticklabels on axis

Rui Barradas
In reply to this post by Jonas Stein
Hello,

You can use your function with 'lapply'


getSIstring(2e7)
getSIstring(2e-8)

lapply(c(2e7, 2e-8, 1234), getSIstring)         # as a list
unlist(lapply(c(2e7, 2e-8, 1234), getSIstring)) # as a character vector

or you can include the 'lapply' in the function body


getSIstring2 <- function(x){
  f <- function(x){
    i=0
    repeat{
        i=i+1
        if (x > prefixpairs$factor[i]) {
            sistring <- paste(x/prefixpairs$factor[i], prefixpairs$prefix[i])
            break
        }
        if (i >= length(prefixpairs$factor)) break
    }
    sistring
  }
  prefixpairs <- data.frame(factor=c(1e24,1e21,1e18,1e15,1e12,1e9,1e6,1e3,1e0,
                              1e-3,1e-6,1e-9,1e-12,1e-15,1e-18,1e-21,1e-24),
                            prefix=c("Y", "Z", "E", "P", "T", "G", "M", "k", " ",
                              "m", "u", "n", "p", "f", "a", "z", "y"))
  x <- sort(x)  # needed?
  unlist(lapply(x, f))
}

getSIstring2(2e7)
getSIstring2(2e-8)
getSIstring2(c(2e7, 2e-8))
getSIstring2(c(2e7, 2e-8, 1234))


I've included the value 1234 because I coudn't understand wether it could be passed to the function.
To return '1 k', use a 'round' inside the paste to round the division value.
See ?round

Rui Barradas
Reply | Threaded
Open this post in threaded view
|

Re: automatic SI prefixes as ticklabels on axis

Rui Barradas
I forgot to say I have commented out your function's first line,

sistring <- paste(x)

It wouldn't cause any problem, it's just not needed.

Rui Barradas
Reply | Threaded
Open this post in threaded view
|

Re: automatic SI prefixes as ticklabels on axis

Ben Tupper-2
In reply to this post by Jonas Stein

On Jan 5, 2012, at 8:02 PM, Jonas Stein wrote:

> On 2012-01-05, Jonas Stein <[hidden email]> wrote:
>> i want to plot values with frequency on a logarithmic x axis.
>> similar to this example that i found in the web:
>> http://www.usspeaker.com/jensen%20p15n-graph.gif
>>
>> I would like to convert long numbers to si prefix notation
>> like in the example
>>
>> (200000 to 200k, 35000000 to 3.5 M)
>>
>> Of course i could create labels by hand, but
>> i have many files so i need some automatic function.
>>
>> Has anyone done that in R?
>>
>> Kind regards and thank you for your help,
>
> my first try looks like this:
> ==================================
>
> getSIstring <- function(x){
>
>  sistring <- paste(x);
>
>  prefixpairs <- data.frame(c(1e24,1e21,1e18,1e15,1e12,1e9,1e6,1e3,1e0,
>                              1e-3,1e-6,1e-9,1e-12,1e-15,1e-18,1e-21,1e-24),
>                            c("Y", "Z", "E", "P", "T", "G", "M", "k", " ",
>                              "m", "u", "n", "p", "f", "a", "z", "y"))
>
>  colnames(prefixpairs) <- c("factor", "prefix")
>  i=0
>  repeat{i=i+1;
>         if (x > prefixpairs$factor[i]) {
>           sistring <- paste(x/prefixpairs$factor[i], prefixpairs$prefix[i]); break;}
>         if (i >= length(prefixpairs$factor)) break}
>
>   return(sistring)
> }
>
> ==================================
>
>> getSIstring(2e7)
> [1] "20 M"
>
> How can i improve this function?
> It would be nice if it could handle lists too like sin() can do
>> sin(1:4)
> [1]  0.8414710  0.9092974  0.1411200 -0.7568025
>

How about using findInterval - that will give you the behavior you describe.  Note I had to reverse the order of your table and I did not make it a data frame.

getSIstring <- function(x){

lut <- rev(c(1e24,1e21,1e18,1e15,1e12,1e9,1e6,1e3,1e0,
                             1e-3,1e-6,1e-9,1e-12,1e-15,1e-18,1e-21,1e-24))
pre <- rev(c("Y", "Z", "E", "P", "T", "G", "M", "k", " ",
                             "m", "u", "n", "p", "f", "a", "z", "y"))

 ix <- findInterval(x, lut)
 
  if (length(ix) > 0 ) {
    sistring <- paste(x/lut[ix], pre[ix])
  } else {
    sistring <- as.character(x)
  }

  return(sistring)
}


> getSIstring(c(4.2e-3,2e7))
[1] "4.2 m" "20 M"


Cheers,
Ben





> kind regards,
>
> --
> Jonas Stein <[hidden email]>
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.

Ben Tupper
Bigelow Laboratory for Ocean Sciences
180 McKown Point Rd. P.O. Box 475
West Boothbay Harbor, Maine   04575-0475
http://www.bigelow.org

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
Reply | Threaded
Open this post in threaded view
|

Re: automatic SI prefixes as ticklabels on axis

Rui Barradas
In reply to this post by Jonas Stein
Ben's solution is much better, I was using only Jonas'  code.

There might be "typo" with large values of 'x',

> getSIstring(1e309)
[1] "Inf Y"

I don't believe this will happen, the range upper limit is much smaller.
Anyway, it can be easily solved.
(Ben's code, slightly changed.)

getSIstring <- function(x){

  lut <- rev(c(1e24,1e21,1e18,1e15,1e12,1e9,1e6,1e3,1e0,
                             1e-3,1e-6,1e-9,1e-12,1e-15,1e-18,1e-21,1e-24))
  pre <- rev(c("Y", "Z", "E", "P", "T", "G", "M", "k", " ",
                             "m", "u", "n", "p", "f", "a", "z", "y"))

  ix <- findInterval(x, lut)

  ifelse(length(ix) > 0, sistring <- paste(x/lut[ix], pre[ix]),
                         sistring <- as.character(x))

  sistring[which(sistring ==  "Inf Y")] <- "Inf"   # make it look better

  return(sistring)
}

getSIstring(c(4.2e-3, 2e7))

x1 <- .Machine$double.xmax
x2 <- x1 + 10^(308 - 16)
getSIstring(c(x1, x2))


Rui Barradas