简体   繁体   中英

R - Assign class using mutate

I am using the openxlsx package to create excel files. To format a column as US dollars, the examples say to set the class to 'currency':

class(df$Currency) <- 'currency'

However, I would like to apply this to many columns as once and repeat once for currency, once for percentage etc. That is my ultimate goal however I get there - here is what I've tried so far.

First the working example:

df <- data.frame(sales = c(10, 20, 30, 40, 50), returns = c(-5, -10, -20, 0, 0))
class(df$sales) <- 'currency'
class(df$sales)
[1] "currency"

Now using dplyr and mutate Attempt 1:

df %>% 
mutate_all(`class<-`(., 'currency'))
Error: Can't create call to non-callable object

Attempt 2:

df <- df %>% 
`class<-`(., 'currency') 
df
$sales
[1] 10 20 30 40 50
attr(,"class")
[1] "currency"

That gets much much closer to what I wanted but the output is a list and as.data.frame and as.tbl both complain there is no method for class 'currency'.

When I used the class(df$sales) <- 'currency' I was able to just change the class within the existing dataframe.

I have a feeling this is a good chance to learn more about classes (I reviewed the Advanced R section on classes but couldn't make the connection to my problem)

To echo @Frank's comment above:

as.currency <- function(x) {class(x) <- "currency"; x}

iris %>%   mutate_all(funs(as.currency(.))) %>% glimpse
 Observations: 150 Variables: 5 $ Sepal.Length <S3: currency> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.8, 4.8, 4.3, 5.8, 5.7, 5.4, 5.1, 5.7, 5.1, ... $ Sepal.Width <S3: currency> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3.0, 3.0, 4.0, 4.4, 3.9, 3.5, 3.8, 3.8, ... $ Petal.Length <S3: currency> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 1.4, 1.1, 1.2, 1.5, 1.3, 1.4, 1.7, 1.5, ... $ Petal.Width <S3: currency> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, ... $ Species <S3: currency> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1... 

It is possible to use purrr , but the result can only be coerced to a dataframe if each column also inherits from numeric (that is, is both currency and numeric). I don't know if that's good enough for openxlsx .

dfr <- data.frame(x=1:10, y=1:10, z=1:10)
library(purrr)
as.data.frame(map(dfr, `class<-`, c("currency","numeric")))

gives

sapply(x, class)
     x          y          z         
[1,] "currency" "currency" "currency"
[2,] "numeric"  "numeric"  "numeric" 

I am not sure how to do this using dplyr , but here is one way that works.

# list the column names
names <- colnames(df)

# loop through the columns and assign the class 'currency'
for (i in 1:length(names)){

  class(df[, names[i]])  <- 'currency'
}

lapply(df, class)
$sales
[1] "currency"

$returns
[1] "currency"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM