The 12 R functions of Xmas pt6/6

Welcome to the last in this little series of R functions built for fun. If you’ve not been following along from the start feel free to head back to the first post in the series and check that out too. That post explains the thinking behind the series and has an index to the other posts.

Sleigh or ISS

Over the Christmas period, when you look upwards and see a bright object streaking across the sky you might wonder to yourself: “Is that a sleigh, or the International Space Station?”

Today’s first function tries to help us answer that question. Since we can never truly know where Santa’s sleigh might be we must instead figure out where the ISS is, so that we can eliminate that from our question instead.

To do that we’re going to use an API from open-notify.org. This API returns the current latitude and longitude of the ISS above the Earth.

{"timestamp": 1544922185, "iss_position": {"longitude": "-72.5569", "latitude": "-18.5333"}, "message": "success"}

Since the API is completely open, requires no authentication and has no parameters we can access it directly with the jsonlite package.

Remember, access to this API is a privilege, not a right and it would be extremely rude to abuse that privilege. If you do use this API, don’t poll it more than once every 5 seconds and even that is pretty fast and wont introduce a significant change in the results.

But what are we going to do with the latitude and longitude once we have them? If we plot them on a map we’ll be able to determine if that light we saw in the sky was the ISS, which will help us answer our question.

There are lots of great mapping tools in R, but we’re going to fall back on a technique we’ve already used several times during these posts, URL manipulation.

Google maps has a service called “Maps URLs” that we can use without anything more complicated than assembling some text string components into a full URL and then launching that URL.

sleigh_or_iss <- function(){
  returned_data <- jsonlite::fromJSON("http://api.open-notify.org/iss-now.json")
  maps_base_url <- "https://www.google.com/maps/search/?api=1&query="
  lat <- returned_data$iss_position$latitude
  long <- returned_data$iss_position$longitude
  map_url <- paste0(maps_base_url, lat, ",", long)
  browseURL(map_url)
}

When you run the function it will grab the current location of the ISS, use the latitude and longitude to assemble a correctly formatted Google Maps URL and then open that URL in the default browser.

Left: default Google maps zoom level - Right: zoomed out version

The big problem with this is that the result is often so underwhelming. You don’t appear to be able to control the zoom level with the Maps URLs service and so you often end up looking at a screen of blue-sea. Since roughly 70% of the Earth’s surface is covered by water there’s a very high probability that the ISS will be over water at any given moment. Since we often get the picture on the above left when the more zoomed out version above right would be preferable, I don’t consider this function to be a raging success.

Ideas for modifications and improvements:

  • Use one of the fantastic mapping packages available from CRAN
  • Figure out a way to control the zoom level in the URL
  • Find other APIs that return lat/long to locate other things on a map

As I’m not overly happy with this one I’ve included a bonus function at the end of this post.

Twelve days of xmas

Most people have probably heard the “Twelve days of Christmas” song. Here’s a version sung by Burl Ives in case you need a reminder. How many of us remember when those 12 days start, or which day is which though?

This function is here to help. When you run it, it figures out the current date from your computer and then tells you which, if any, of the twelve days it is.

twelve_days <- function(){
  today <- format(Sys.Date(), format = "%b%d")

  days <- list(
    Dec25 = "1st day - A partridge in a pear tree",
    Dec26 = "2nd day - Two turtle doves",
    Dec27 = "3rd day - Three french hens",
    Dec28 = "4th day - Four calling birds",
    Dec29 = "5th day - Five gold rings",
    Dec30 = "6th day - Six geese a-layin",
    Dec31 = "7th day - Seven swans a-swimming",
    Jan01 = "8th day - Eight maids a-milking",
    Jan02 = "9th day - Nine ladies dancing",
    Jan03 = "10th day - Ten lords a-leaping",
    Jan04 = "11th day - Eleven pipers piping",
    Jan05 = "12th day - Twelve drummers drumming"
  )
  if (days[today] %in% days){
    cat("Today is the", days[today][[1]], "\n")
  } else {
    cat("Today's not one of the twelve days, sorry!\n")
  }
}

So if I run it today:

> twelve_days()
Today's not one of the twelve days, sorry!

But if I run it on New Years Day:

> twelve_days()
Today is the 8th day - Eight maids a-milking

Some ideas for modifications and improvements:

  • Are there other useful things you might want to know on a particular date?
  • Find a API that provides the public holidays in your region and re-write the function around those
  • Figure out a way of making the function speak the days

Bonus function: Unwrap package

I said there’d be a bonus function and here it is!

If I could, I’d give each and every person reading this a gift. Sadly the only gift I can give you for Christmas is a package or two. A package from CRAN of course!

Our last function first downloads the PACKAGES file from CRAN if it doesn’t already exist in the current working directory. The PACKAGES file is like an index of all the packages that live on CRAN and it’s used by R when installing packages or checking what packages are available.

After we have the PACKAGES files (the version compressed with gzip), we read it in with read.dcf() since it’s a DCF (Debian Control Field) format file and then use sample() to select a package at random from the file. Once we have that the package name is appended to the official CRAN URL. The resultant URL is then launched in the default browser. This is a fun (but slow!) approach to discovering new packages.

unwrap_package <- function(){
  if (!file.exists("PACKAGES.gz")){
    warning("PACKAGES.gz not found - downloading...")
    download.file("https://cran.rstudio.com/src/contrib/PACKAGES.gz",
                  "PACKAGES.gz")
  }
  con <- gzfile("PACKAGES.gz")
  on.exit(close(con))
  all_pkgs <- read.dcf(con)[, 1]
  cran_url <- " https://CRAN.R-project.org/package="
  pkg <- sample(all_pkgs, 1)
  browseURL(paste0(cran_url, pkg))
}

If you’ve been following along with this series you’ll see some familiar ideas here, just used in a slightly different way.

Run the function to unwrap your package!

Package pages from CRAN

Some ideas for modifications and improvements:

  • If there is a PACKAGES.gz file already downloaded, check the age of the file and replace if it’s over 7 days old.
  • Add a parameter to open a number of CRAN package pages at once, not just one.
  • Figure out how to discover R packages somewhere else, like Github
  • Build your own package discovery API with plumber

We’ve made it to the end of this little series of fun functions for the Christmas period, thanks for following along. I hope you’ve enjoyed the functions and if you do try any of the modifications or improvements let me know. Happy Christmas and happy function writing!