vincentarelbundock / countrycode

R package: Convert country names and country codes. Assigns region descriptors.
https://vincentarelbundock.github.io/countrycode
GNU General Public License v3.0
342 stars 83 forks source link

Convert between currency name and currency codes #293

Closed cjyetman closed 2 years ago

cjyetman commented 2 years ago

A user on StackOverflow recently asked how to convert a currency code to a currency name for nicer looking labels on a plot. In the specific example data they gave, the country name was available, making countrycode(country, "country.name", "currency") an easy solution, however, another commenter still strongly believed that countrycode() should do the currency code and currency name conversion "out-of-the-box".

This is easy enough to do using the custom_dict argument and some modification of countrycode::codelist, and the maintainers here know all to well why currency and iso4217c are "destination-only" codes, but since the data to achieve this is already included in the package, I wonder if there's an appropriate way to do this easily.

The least controversial solution would be to add a custom currency dictionary to our custom dictionaries based on that data we already have. Alternatively, we could consider adding some magic inside countrycode() to switch things up when a destination-only code is used for the origin code and a matching one-to-one destination code is requested.

library(countrycode)

currency_codes <- c("AFN", "EUR", "USD")
currency_codes_n <- c(971, 978, 840)
currencies <- c("Afghani", "Euro", "US Dollar")

custom_dict <- countrycode::codelist
custom_dict <- custom_dict[!duplicated(custom_dict$currency), ]

countrycode(currency_codes, "iso4217c", "iso4217n", custom_dict = custom_dict)
#> [1] 971 978 840
countrycode(currency_codes, "iso4217c", "currency", custom_dict = custom_dict)
#> [1] "Afghani"   "Euro"      "US Dollar"

countrycode(currency_codes_n, "iso4217n", "iso4217c", custom_dict = custom_dict)
#> [1] "AFN" "EUR" "USD"
countrycode(currency_codes_n, "iso4217n", "currency", custom_dict = custom_dict)
#> [1] "Afghani"   "Euro"      "US Dollar"

countrycode(currencies, "currency", "iso4217c", custom_dict = custom_dict)
#> [1] "AFN" "EUR" "USD"
countrycode(currencies, "currency", "iso4217n", custom_dict = custom_dict)
#> [1] 971 978 840
cjyetman commented 2 years ago

possibly related #265

vincentarelbundock commented 2 years ago

Interesting.

One "magic" option would be to include an attribute to the dictionary with a vector of destination-only codes.

attr(codelist, "destination_only")[["currency"]]

> "Euro", .....

I'm not sure what the best data structure would be.

Also not sure if this is too hacky or if a more general/cleaner solution exists.

cjyetman commented 2 years ago

One "magic" option would be to include an attribute to the dictionary with a vector of destination-only codes.

Yeah, I was thinking of something like maintaining a named list matching origin-code -> most-appropriate-dictionary.

pseudo-code...

countrycode <- function(sourcevar, origin, destination) {
  origin_to_dict <- list("country.name" = countrycode::codelist, "iso3c" = countrycode::codelist, "currency" = countrycode::currency_dict)
  dictionary <- origin_to_dict[origin == names(origin_to_dict)]
  ...
}

But then we would have to deal with what are appropriate destination codes for each possible dictionary, and users' expectations about what destination codes they're allowed to use for a given origin code, e.g. what happens if someone wants countrycode(my_values, "currency", "iso3c"). So really we would need to maintain a list of most-appropriate-dictionary for each pair of origin and destination codes, which then makes me worry this is a giant rabbit hole.

vincentarelbundock commented 2 years ago

After reviewing this, my sense is that this is a rehash of the many-to-one mapping issue: https://github.com/vincentarelbundock/countrycode/issues/186

Users can already get a custom dictionary by calling:

codelist[, c("country.name", "iso4217c")]

I think we can close this and revisit in the future if someone decides to invest the considerable efforts that would be needed to implement a truly general many-to-one solution.