'Annual percentage change dataframe in R
I have a time series dataframe in R, lets call it df, that looks like this:
| Date | City_1 | City_2 | City_3 | ..... | City_n |
|---|---|---|---|---|---|
| 1980-01-01 | 1 | 4 | 6 | ... | 2 |
| 1980-01-02 | 2 | 7 | 6 | ... | 5 |
| 1980-01-03 | 3 | 7 | 1 | ... | 1 |
| 1980-01-04 | 8 | 1 | 8 | ... | 1 |
| 1980-01-05 | 4 | 3 | 5 | ... | 0 |
| ... | ... | ... | ... | ... | 7 |
| 2020-08-20 | 3 | 1 | 8 | ... | 2 |
| 1980-08-21 | 7 | 8 | 3 | ... | 2 |
| 1980-08-22 | 8 | 6 | 5 | ... | 0 |
| 1980-08-23 | 9 | 8 | 2 | ... | 1 |
Here is a reproducible part of my data:
df <- structure(c(17.86, 18.65, 18.42, 19.21, 21.34, 11.35, 10.01,
9.67, 10.12, 10.81, 20.77, 20.99, 20.88, 20.91, 20.5, 13.8, 13.63,
13.6, 13.02, 13.87, 22.07, 22.78, 23.24, 23.33, 24.06), .Dim = c(5L,
5L), class = c("xts", "zoo"), index = structure(c(315532800,
315619200, 315705600, 315792000, 315878400), tzone = "UTC", tclass = "Date"),
.Dimnames = list(NULL, c("Sao Paulo", "Mexico City", "Lima", "Bogota",
"Rio de Janeiro")))
And I want to create a new dataframe that, in each city column, for each date, has the percentage change with respect to the same date the previous year, given by the following formula:
change = [x(t) -x(t-1)]/x(t)
To get a final dataframe, df2 that will look like this:
| Date | City_1 | City_2 | City_3 | ..... | City_n |
|---|---|---|---|---|---|
| 1981-01-01 | a | j | s | ... | b |
| 1981-01-02 | b | k | t | ... | c |
| 1981-01-03 | c | l | u | ... | d |
| 1981-01-04 | d | m | v | ... | t |
| 1981-01-05 | e | n | w | ... | e |
| ... | ... | ... | ... | ... | f |
| 2020-08-20 | f | o | x | ... | h |
| 1980-08-21 | g | p | y | ... | s |
| 1980-08-22 | h | q | z | ... | y |
| 1980-08-23 | i | r | a | ... | u |
Where all the letters are the percentage change of each day, respect last year.
I'm really new at R, but I think that I would need a function. Can you guys help me out?
Solution 1:[1]
--- Update ---
mock data
df <- structure(c(17.86, 18.65, 18.42, 19.21, 21.34, 11.35, 10.01,
9.67, 10.12, 10.81, 20.77, 20.99, 20.88, 20.91, 20.5, 13.8, 13.63,
13.6, 13.02, 13.87, 22.07, 22.78, 23.24, 23.33, 24.06), .Dim = c(5L,
5L), class = c("xts", "zoo"), index = structure(c(315532800,
315619200, 315705600, 315792000, 315878400), tzone = "UTC", tclass = "Date"),
.Dimnames = list(NULL, c("Sao Paulo", "Mexico City", "Lima", "Bogota",
"Rio de Janeiro")))
functions
findyear <- function(year2search,dates){
idx <- which(year2search == dates)
if (length(idx) == 0) idx <- NA
return(idx)
}
change <- function(x1, x2){
return( (x1 - x2)/x1)
}
Using base R
date <- as.Date(anytime::anytime(attributes(df)$index,asUTC = T))
year2search <- date - lubridate::years(1)
idx <- sapply(1:nrow(df), function(x) {findyear(year2search[x],date)})
res <- lapply(colnames(df), function(city) { print(city); df[, (which(colnames(df)==city))] <- change(df[, (which(colnames(df)==city))], df[, (which(colnames(df)==city))][idx])})
df_res <- matrix(unlist(res), ncol=ncol(df), nrow=nrow(df))
colnames(df_res) <- colnames(df)
--Solution for data.frame--
df <- data.frame(city1=c(17.86, 18.65, 18.42, 19.21, 21.34),
city2=c(11.35, 10.01,11.35, 10.01, 10.56),
city3=c(20.77, 20.99, 20.88, 20.91, 20.5),
date = seq(as.Date("1980/01/01"), as.Date("1984/01/01"), by="year"))
First solution using base R
year2search <- df$date - lubridate::years(1)
idx <- sapply(1:nrow(df), function(x) {findyear(year2search[x],df$date)})
res <- lapply(names(df)[1:3], function(city) { print(city); df[[city]] <- change(df[[city]], df[[city]][idx])})
df_res <- matrix(unlist(res), ncol=(ncol(df)-1), nrow=nrow(df))
colnames(df_res) <- names(df)[1:3]
Alternative solution using dplyr
df <- df %>% mutate(year2search = date - lubridate::years(1)) %>%
rowwise %>% mutate(idx = findyear(year2search,df$date))
for (city in names(df)[1:3]){
df[[city]] <- change(df[[city]], df[[city]][df$idx])
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
