![A simple silhouette of a kiwi bird. A bright green laser shines from its eye. It is inscribing a bright green dollar symbol with the laser.](/post/2021-07-15-dollar-dollar_files/laser-kiwi-dollar.png)
Kiwi by Georgiana Ionescu. Laser embellishment in honour of Lucy Gray’s flag.
tl;dr
R’s $
data accessor symbol is part of an international ruse. I wrote a function so you can use your local currency symbol instead.
Pull the wool from your eyes
You’re an R user, so you’ll know how to access the contents of a quoted column name from a dataframe with square-bracket notation.
mtcars[["cyl"]]
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
The dollar symbol ($
) does the same thing, of course, in the form dataframe$column
.
mtcars$cyl
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
But, like basically everything in R, it’s just a function.
So you can also use it in the ‘traditional way’ by passing the dataframe and column name to it as arguments inside brackets. You’ll need to use backticks (```) though, because function names can’t start with symbols or numbers.
`$`(mtcars, cyl)
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
Wake up sheeple
But why the dollar symbol? Something something ‘compatability with S’.
Or perhaps a more sinister ploy by R’s original developers, Ihaka and Gentleman?
Like a KIWI CONSPIRACY to raise awareness of the NEW ZEALAND DOLLAR (NZD) and INFLUENCE currency markets? I’m just asking the question.1
So, I’m giving you the FREEDOM to assign the functionality of the dollar symbol to another currency symbol, like, oh, I don’t know, the pound sterling symbol (£
), as a completely random example.
`£` <- `$`
`£`(mtcars, cyl)
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
You can’t use it in the dataframe$column
form, however. That kind of behaviour is reserved for special symbols in R.2
mtcars£cyl
## Error: <text>:1:7: unexpected input
## 1: mtcars£
## ^
Shame. R has a little quirk that will make this work though. Sort of.
You can make a function do this by putting it between percentage symbols (%
). This is called an ‘infix operator’ and you may have seen the {magrittr} pipe (%>%
) as one example.
`%£%` <- `$`
mtcars %£% cyl
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
As a complete coincidence, I live in Great Britain (GB) where we use pound sterling (£
, or ‘GBP’).
R also knows where I live (another conspiracy?).
Sys.getlocale()
## [1] "en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8"
This string is R’s way of keeping track of the location-specific information that influences stuff like the language of error messages.
You can specify different locales for different things. Here’s my locale for ‘monetary’ parameters, for example.
Sys.getlocale(category = "LC_MONETARY")
## [1] "en_GB.UTF-8"
That particular value impacts parameters like the punctuation mark used for decimals and, wouldn’t you know it, the symbol used for currency.
Sys.localeconv()
## decimal_point thousands_sep grouping int_curr_symbol
## "." "" "" "GBP "
## currency_symbol mon_decimal_point mon_thousands_sep mon_grouping
## "£" "." "," "\003\003"
## positive_sign negative_sign int_frac_digits frac_digits
## "" "-" "2" "2"
## p_cs_precedes p_sep_by_space n_cs_precedes n_sep_by_space
## "1" "0" "1" "0"
## p_sign_posn n_sign_posn
## "1" "1"
Mutton dressed as lamb
So, that means I can write you a function that gets the currency symbol for your locale and assigns to it the functionality of the dollar symbol, naturally.
copy_dollar <- function() {
# Get currency symbol for locale
currency <- Sys.localeconv()[["currency_symbol"]]
# Report the locale
if (currency == "$") stop("KIWI CONSPIRATOR!")
locale <- Sys.getlocale(category = "LC_MONETARY")
cat(paste0("Your monetary locale is '", locale, "'\n"))
# Generate and evaluate strings
expr_fn <- paste0("`", currency, "` <<- `$`")
expr_in <- paste0("`%", currency, "%` <<- `$`")
eval(rlang::parse_expr(expr_fn)) # function form
eval(rlang::parse_expr(expr_in)) # infix form
# Report to user
cat(
paste0("Try `", currency, "`(df, col) and df%", currency, "%col\n")
)
}
I used a bit of a trick3 there. You can’t use a string on the left-hand side of the assignment operator, but you can build an R expression as a string and eval()
uate a parsed version of it (with some help from {rlang} in my example).
I also used a special double-headed assignment arrow, <<-
, that makes the objects available in the global environment.4 That means we can use the new functions outside the scope of the copy_dollar()
function.
And now: freeeedooooom.
copy_dollar()
## Your monetary locale is 'en_GB.UTF-8'
## Try `£`(df, col) and df%£%col
We got a couple of messages5 to confirm our location and let us know how we can use the new currency-symbol functions.
And we can see these in the global environment.
ls()
## [1] "%£%" "£" "copy_dollar"
And we can prove that GBP equals NZD, at least as a function for accessing columns of a dataframe.
all(
mtcars%£%cyl == mtcars$cyl,
`£`(mtcars, cyl) == `$`(mtcars, cyl)
)
## [1] TRUE
Okay, works for my location. What about when I’m competing in Japan at the Olympics?6 Well, I can change the monetary locale.
Sys.setlocale("LC_MONETARY", "ja_JP.UTF-8")
## [1] "ja_JP.UTF-8"
Sys.localeconv()
## decimal_point thousands_sep grouping int_curr_symbol
## "." "" "" "JPY "
## currency_symbol mon_decimal_point mon_thousands_sep mon_grouping
## "¥" "." "," "\003\003"
## positive_sign negative_sign int_frac_digits frac_digits
## "" "-" "0" "0"
## p_cs_precedes p_sep_by_space n_cs_precedes n_sep_by_space
## "1" "0" "1" "0"
## p_sign_posn n_sign_posn
## "1" "4"
So now you can see the yen symbol (¥
) as the named currency value for this locale. And you can use the function to activate it for use as a data accessor.
copy_dollar()
## Your monetary locale is 'ja_JP.UTF-8'
## Try `¥`(df, col) and df%¥%col
`¥`(mtcars, cyl)
## [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
Oh, and there’s no point trying to do this if your locale already uses the dollar for currency. I know you want as many dollars as possible, but don’t be greedy.
Sys.setlocale("LC_MONETARY", locale = "en_NZ.UTF-8")
## [1] "en_NZ.UTF-8"
copy_dollar()
## Error in copy_dollar(): KIWI CONSPIRATOR!
I’ll reset my settings to the motherland to prevent any accidental borking.
Sys.setlocale(locale = "en_GB.UTF-8")
## [1] "en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8"
Nothing ever goes wrong in Britain, after all.
Separate your sheep from your goats
While you go and adjust your locale in an act of defiance, be on the lookout for the next New Zealander conspiracy.
I heard that they want to replace the ampersand (&
) symbol in R version 5 with NEWZEAL&
, so stay on your toes.
Hang on…
I think I used sheep-related phrases in all the section titles of this post. And aren’t there like 10 sheep per person in New Zealand?
They’ve got to me already!
Session info
## ─ Session info ───────────────────────────────────────────────────────────────
## setting value
## version R version 4.0.3 (2020-10-10)
## os macOS Mojave 10.14.6
## system x86_64, darwin17.0
## ui X11
## language (EN)
## collate en_GB.UTF-8
## ctype en_GB.UTF-8
## tz Europe/London
## date 2021-07-22
##
## ─ Packages ───────────────────────────────────────────────────────────────────
## package * version date lib source
## blogdown 0.21 2020-10-11 [1] CRAN (R 4.0.2)
## bookdown 0.21 2020-10-13 [1] CRAN (R 4.0.2)
## bslib 0.2.4 2021-01-25 [1] CRAN (R 4.0.2)
## cli 2.5.0 2021-04-26 [1] CRAN (R 4.0.2)
## digest 0.6.27 2020-10-24 [1] CRAN (R 4.0.2)
## evaluate 0.14 2019-05-28 [1] CRAN (R 4.0.1)
## htmltools 0.5.1.1 2021-01-22 [1] CRAN (R 4.0.2)
## jquerylib 0.1.3 2020-12-17 [1] CRAN (R 4.0.2)
## jsonlite 1.7.2 2020-12-09 [1] CRAN (R 4.0.2)
## knitr 1.32 2021-04-14 [1] CRAN (R 4.0.2)
## magrittr 2.0.1 2020-11-17 [1] CRAN (R 4.0.2)
## R6 2.5.0 2020-10-28 [1] CRAN (R 4.0.2)
## rlang 0.4.11 2021-04-30 [1] CRAN (R 4.0.2)
## rmarkdown 2.7 2021-02-19 [1] CRAN (R 4.0.2)
## sass 0.3.1 2021-01-24 [1] CRAN (R 4.0.2)
## sessioninfo 1.1.1 2018-11-05 [1] CRAN (R 4.0.2)
## stringi 1.6.1 2021-05-10 [1] CRAN (R 4.0.2)
## stringr 1.4.0 2019-02-10 [1] CRAN (R 4.0.2)
## withr 2.4.2 2021-04-18 [1] CRAN (R 4.0.2)
## xfun 0.22 2021-03-11 [1] CRAN (R 4.0.2)
## yaml 2.2.1 2020-02-01 [1] CRAN (R 4.0.2)
##
## [1] /Users/matt.dray/Library/R/4.0/library
## [2] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
With apologies to all the lovely people of New Zealand.↩︎
Yep, you could probably change this in the R source code and recompile it, but… effort.↩︎
‘Illusions, Michael.’↩︎
In other words, assign like the single-headed ‘gets’ operator,
<-
, but more so! You may know that I have strong feelings about assigment operators, lol.↩︎Yes, I’m aware I’ve written the messages in English only. I can only break down one international barrier at once, pal.↩︎
I heard that code golf is one of the new sports on display this year?↩︎