How to specify a custom formatter function gt table grand summary? - r

I'm trying to use a custom formatter function to format grand summary rows in gt table.
in the example below I was trying to use seconds_to_period from lubridate but I get
the error "**Error in stop_if_not_gt(data = data) : **
require(tidyverse)
require(lubridate)
sp500 %>%
dplyr::filter(
date >= "2015-01-05" &
date <="2015-01-16"
) %>%
dplyr::arrange(date) %>%
dplyr::mutate(
week = paste0(
"W", strftime(date, format = "%V"))
) %>%
dplyr::select(-adj_close, -volume) %>%
gt(
rowname_col = "date",
groupname_col = "week"
) %>%
grand_summary_rows(
columns = vars(open, high, low, close),
fns = list(
min = ~min(.),
max = ~max(.),
avg = ~mean(.)),
formatter = fmt(fns=seconds_to_period),
use_seps = FALSE
)
I've tried some variations like formatter = fmt(columns=vars(min),fns=seconds_to_period) with no sucess.
Thanks

Since you didn't share the seconds_to_period function, my guess is you are trying a transformation/computation with a format function. As far as I know, that's not possible. formatter arg allows you to apply a format to an already computed summary cell. And the sintaxys to pass the fmt* arguments inside grand_summary_rows is quite different. Instead of passing them inside parenthesis fmt* function, you should pass them as grand_summary_rows arguments:
df |>
gt()|>
grand_summary_rows(
columns = vars(open, high, low, close),
fns = list(
min = ~min(.),
max = ~max(.),
avg = ~mean(.)),
formatter = fmt_number,
decimals = 1,
use_seps = FALSE
)
In any case, in your code you're passing use_steps, which is a fmt_number argument, to a fmt function, which does not admit it. Take a look at fmt docummentation.
Anyway, it's not clear what you a are trying to accomplish. OHLC are prices data. And I guess from your function name (seconds_to_period) that you're trying to give this prices data a time class format. If the case, you should compute/transform the content before trying to format it.

Related

Sorting a column of a table in R

I am using the following code :
Daily_intensity %>%
mutate(weekdays = weekdays(date)) %>%
group_by(weekdays) %>%
summarise(minutes_fairly_very_active = sum(fairlyactiveminutes + veryactiveminutes))
The result is not in the order of the weekdays. What should I add so that I get result in order, from Monday to Sunday?
You could use lubridate instead of base and get an ordered factor of the desired kind without needing to specify the order of the weekdays yourself:
mutate(weekdays = lubridate::wday(date, label = TRUE, week_start = 1))

mutate() command seems not working for objects of different classes

SOLVED
I'm working on replication of the code that is used at the Reproducible Finance with R. A link to the webinar is here: https://www.rstudio.com/resources/webinars/reproducible-finance-with-r/
The data for the exercise was downloaded from Yahoo Finance. The .csv file is here: http://www.reproduciblefinance.com/data/data-download/
Following the instructions provided at the webinar, it happened to me that the code doesn't work as it is in the lesson:
portfolio_returns_tidyquant_rebalanced_monthly %>%
mutate(
dplyr_port_returns = portfolio_returns_dplyr_byhand$returns,
xts_port_returns = coredata(portfolio_returns_xts_rebalanced_monthly)
)%>%
head()
The system doesn't provide any output, nor it shows if there is a mistake in the code.
I then decided to eliminate each new variable I want to create to see if something happens. It turned out that if one variable is not included in the mutate() command the system produces a partial output I need. Below are a code and an output.
portfolio_returns_tidyquant_rebalanced_monthly %>%
mutate(
dplyr_port_returns = portfolio_returns_dplyr_byhand$returns,
# xts_port_returns = coredata(portfolio_returns_xts_rebalanced_monthly)
)%>%
head(2)
Date
returns
dplyr_port_returns
2013-01-31
0.0308487341
0.0308487341
2013-02-28
-0.0008697461
-0.0008697461
In addition, some information about variables:
class(portfolio_returns_xts_rebalanced_monthly)
[1] "xts" "zoo"
class(portfolio_returns_dplyr_byhand)
[1] "tbl_df" "tbl" "data.frame"
The portfolio_returns_xts_rebalanced_monthly was created using the following code:
symbols <- c("SPY", "EFA", "IJS", "EEM", "AGG")
prices <-
getSymbols(
symbols,
src = 'yahoo',
from = "2012-12-31",
to = "2017-12-31",
auto.assign = T,
warnings = F
) %>%
map(~Ad(get(.))) %>%
reduce(merge) %>%
`colnames<-`(symbols)
w <- c(
0.25,
0.25,
0.20,
0.20,
0.10
)
prices_monthly <-
to.monthly(
prices,
indexAt = "lastof",
OHLC = FALSE
)
assets_return_xts <- na.omit(
Return.calculate(
prices_monthly,
method = "log"
)
)
portfolio_returns_xts_rebalanced_monthly <-
Return.portfolio(
assets_return_xts,
weights = w,
rebalance_on = 'months'
) %>%
`colnames<-`("returns")
I'm pretty sure this is somehow connected to a mutate() function and classes of variables, but I couldn't find any information on the matter. Your support is highly appreciated.
UPDATE.
Changing the class of one object from xts to data.frame, and adjusting a code a bit solved the issue.
An updated code:
portfolio_returns_xts_rebalanced_monthly_df <-
data.frame(
date=index(portfolio_returns_xts_rebalanced_monthly),
coredata(portfolio_returns_xts_rebalanced_monthly)
)
portfolio_returns_tidyquant_rebalanced_monthly %>%
mutate(
dplyr_port_returns = portfolio_returns_dplyr_byhand$returns,
xts_port_returns = portfolio_returns_xts_rebalanced_monthly_df$returns
)%>%
head()

How do I extract the key from a key-value pair (character = character) in R?

I have this variable
variable_pairs <- c("var_name1" = "var_id1", "var_name2" = "var_id2")
I need to extract the names, and strip the quotes, so I get something like (var_name1, var_name2)
Is there something to this effect in r? I suspect this is easier done in Python, but I have to do it in R. Also I'm an absolute R beginner.
Many thanks in advance for any help!
EDIT: making my purpose a bit clearer.
I need to send the names in the select = c(year, *indicator_vector_name*)part. I firstly had to do it manually (select = c(year, *total_population, gdp*)) but it's obviously not good. The 'mmr' object then goes to a ggplot function.
indicator_vector <- c('total_population'='SP.POP.TOTL', 'gdp' = 'NY.GDP.MKTP.CD', 'gdp_growth' ='NY.GDP.MKTP.KD.ZG', 'fertility_rate' ='SP.DYN.TFRT.IN', 'pop_growth'='SP.POP.GROW', 'pop_0_14'='SP.POP.0014.TO.ZS', 'pop_65plus'='SP.POP.65UP.TO.ZS', 'trade_ratio' ='NE.TRD.GNFS.ZS')
indicator_vector_name <- noquote(names(indicator_vector))
for (i in indicator_vector){
indicator_data <- WDI(indicator = indicator_vector, country = c(country), start = start, end= end)
}
mmr <- melt(subset(indicator_data, select = c(year, indicator_vector_name)), id.vars = "year")
You don't need to change variable name without quotes. Try this :
indicator_vector_name <- names(indicator_vector)
mmr <- melt(indicator_data[c('year', indicator_vector_name)], id.vars = "year")
We can use pivot_longer
library(dplyr)
library(tidyr)
indicator_data %>%
select(year, all_of(names(indicator_vector)) %>%
pivot_longer(cols = -year)

Format date in Datatable output

library(DT)
seq_dates <- data.frame(dates = as.Date("2017-01-01") + 1:6 * 100)
datatable(seq_dates) %>% formatDate(1, "toDateString")
I get a datatable in viewer pane displaying dates in following format "Mon May 22 2017".
Q - How can I format date column as "MM-YY"
If I do,
dplyr::mutate(seq_dates, dates = format(dates, format = "%b-%Y")) %>%
datatable()
I get the required date format, but in this second case column sorting doesn't work (sorting is done on alphabets rather than dates.)
P.S - I'm implementing this on shiny.
Hi in these cases do I think the best solution is to add a dummy column with the dates in orginal format and have the dates column being sorted according to the values in the DUMMY column. This is in Datatable quite easily done. Example code below.
seq_dates <- data.frame(dates = as.Date("2017-01-01") + 1:6 * 100)
datatable(seq_dates %>% mutate(DUMMY = dates,dates = format(dates, format = "%b-%Y")),
options = list(
columnDefs = list(
list(targets = 1,orderData = 2),
list(targets = 2, visible = FALSE)
)
))
For what it's worth (and using formatDate), the best that I can do is as follows:
datatable(seq_dates) %>%
formatDate(
columns = 1,
method = "toLocaleDateString",
params = list(
'en-US',
list(
year = 'numeric',
month = 'numeric')
)
)
And this yields date values like 4/2017 and 10/2017.
I've tried to find these parameter options (in github and the original datatables documentation) but to no avail. The only example in DT uses the parameters of short, long and numeric.
Converting "%b-%y" "dates" to date format is not an easy thing as I could see...
If you're not too attached to displaying "%b-%y" format, the easy way is to use "%Y-%m" or "%y-%m" format and the filter will work just fine :
library(DT)
seq_dates <- as.data.frame(seq(Sys.Date() - 100, Sys.Date(), by = "m"))
seq_dates <- format(seq_dates, format = "%y-%m")
datatable(seq_dates)
#resulting in
#1 2017-02
#2 2017-03
#3 2017-04
#4 2017-05
#or
#1 17-02
#2 17-03
#3 17-04
#4 17-05
There is a render method that you can use:
datatable( ...
options = list(..
columnDefs = list(..
list(targets = c(1), render = JS(
"function(data, type, row, meta) {",
"return type === 'display' ? new Date(data).toLocaleString() : data;"))))

Which environment should be called when using eval( ) in a function?

I've got a set of functions that I'm trying to work with and I'm struggling to figure out why the assignment isn't working. Here are the functions I'm using:
new_timeline <- function() {
timeline = structure(list(), class="timeline")
timeline$title <- list("text" = list("headline" = NULL, "text" = NULL),
"start_date" = list("year" = NULL, "month" = NULL, "day" = NULL),
"end_date" = list("year" = NULL, "month" = NULL, "day" = NULL))
return(timeline)
}
.add_date <- function(self, date, time_type) {
valid_date <- stringr::str_detect(date, "^[0-9]{4}(-[0-9]{1,2}){0,2}$")
if (!valid_date) {
stringr::str_interp("Your ${time_type} date does not appear to be formatted correctly. It must be of the form 'yyyy-mm-dd'. Only the year is required.") %>% stop()
}
date_elements <- date %>% as.character() %>% stringr::str_split(" ") %>% unlist()
date <- date_elements[1] %>% stringr::str_split("-") %>% unlist()
stringr::str_interp("self$title$${time_type}_date$year <- date[1]") %>% parse(text = .) %>% eval()
if (!is.na(date[2])) stringr::str_interp("self$title$${time_type}_date$month <- date[2]") %>% parse(text = .) %>% eval()
if (!is.na(date[3])) stringr::str_interp("self$title$${time_type}_date$day <- date[3]") %>% parse(text = .) %>% eval()
return(self)
}
edit_title <- function(self, headline = NULL, text = NULL, start_date = NULL, end_date = NULL) {
if (class(self) != "timeline") stop("The object passed must be a timeline object.")
if (is.null(headline) && is.null(self$title$text$headline)) stop("Headline cannot be empty when adding a new title.")
if (!is.null(headline)) self$title$text$headline <- headline
if (!is.null(text)) self$title$text$text <- text
if (!is.null(start_date)) self <- .add_date(self, date = start_date, time_type = "start")
if (!is.null(end_date)) self <- .add_date(self, date = end_date, time_type = "end")
return(self)
}
EDIT: The above code has been severely reduced per a request in the comments. The code is still sufficient to reproduce the error.
I know that's a bit long-winded, so I apologize. The first function establishes a new timeline object. The third function allows us to change the title of the timeline object and the second function is a helper function that handles dates. The code would be used like this:
library(magrittr)
#devtools::install_github("hadley/stringr")
library(stringr)
tl <- new_timeline()
tl <- tl %>% edit_title(headline = "My Timeline", text = "Example", start_date = "2015-10-18")
The code runs with no errors, but when I call tl$title$start_date$year, it comes back as NULL. Using an answer I got in this previous question I asked, I tried to set envir = globalenv() within the eval function. When I do that, the function returns an error saying that object self cannot be found.
So I'm under the impression that self is held in the parent.frame(). So I add both of these to a list: envir = list(globalenv(), parent.frame()). This causes the function to run without error, but there's still no assignment.
Where am I going wrong? Thanks in advance!
As mentioned in the comments, I think you could probably do away with all of the code parsing and just pass variables in [[ for your assignments. Anyway, when you use the pipe operator a bunch of function wrapping happens so determining how many frames to go back is painful. Here are a couple solutions modifying the .add_date function.
You already found one, using <<-, since it searches back through the parent environments until it finds the variable (or doesnt and assigns it in the global).
Another would be just storing the function environment() and passing that to eval.
A third would be counting how many frames deep you go, and using sys.frame to tell eval which environment to look in.
.add_date <- function(self, date, time_type) {
valid_date <- stringr::str_detect(date, "^[0-9]{4}(-[0-9]{1,2}){0,2}$")
if (!valid_date) {
stringr::str_interp("Your ${time_type} date does not appear to be formatted correctly. It must be of the form 'yyyy-mm-dd'. Only the year is required.") %>% stop()
}
## Examining environemnts
e <- environment() # current env
efirst <- sys.nframe() # frame number
print(paste("Currently in frame", efirst))
envs <- stringr::str_interp("${date}") %>% parse(text=.) %>% {.; sys.frames()} # list of frames
elast <- stringr::str_interp("${date}") %>% parse(text=.) %>% {.; sys.nframe()} # number of last
print(paste("Went", elast, "frames deep."))
## Go back this many frames in eval
goback <- efirst-elast
date_elements <- date %>% as.character() %>% stringr::str_split(" ") %>% unlist()
date <- date_elements[1] %>% stringr::str_split("-") %>% unlist()
## Solution 1: use sys.frame
stringr::str_interp("self$title$${time_type}_date$year <- date[1]") %>%
parse(text = .) %>% eval(envir=sys.frame(goback))
## Solution 2: use environment defined in function
if (!is.na(date[2])) stringr::str_interp("self$title$${time_type}_date$month <- date[2]") %>%
parse(text = .) %>% eval(envir=e)
return(self)
}

Resources