0

I have a loop, that creates a tibble at the end of each iteration, tbl. Loop uses different date each time, date.

Assume:

tbl <- tibble(colA=1:5,colB=5:10)  
date <- as.Date("2017-02-28")  

> tbl
# A tibble: 5 x 2
   colA  colB
  <int> <int>
1     1     5
2     2     6
3     3     7
4     4     8
5     5     9

(contents are changing every loop, but tbl, date and all columns (colA, colB) names remain the same)

The output that I want needs to start with output - outputdate1, outputdate2 etc.
With columns inside it as colAdate1, colBdate1, and colAdate2, colBdate2 and so on.

At the moment I am using this piece of code, which works, but is not easy to read:

eval(parse(text = (
  paste0("output", year(date), months(date), " <- tbl %>% rename(colA", year(date), months(date), " = 'colA', colB", year(date), months(date), " = 'colB')")
)))

It produces this code for eval(parse(...) to evaluate:

"output2017February <- tbl %>% rename(colA2017February = 'colA', colB2017February = 'colB')"  

Which gives me the output that I want:

> output2017February
# A tibble: 5 x 2
  colA2017February colB2017February
             <int>            <int>
1                1                5
2                2                6
3                3                7
4                4                8
5                5                9

Is there a better way of doing this? (Preferably with dplyr)
Thanks!

phemark
  • 3
  • 1
  • 5
  • 5
    Repeat with me "`eval(parse())` is evil. A `list` of data.frames is what I should create." – Roland Feb 01 '18 at 12:09
  • I know that eval(parse(..)) is not the best... Hence my question to try and move away from it... :) – phemark Feb 01 '18 at 12:11

1 Answers1

0

This avoids eval and is easier to read:

ym <- "2017February"
assign(paste0("output", ym), setNames(tbl, paste0(names(tbl), ym)))

Partial rename

If you only wanted to replace the names in the character vector old with the corresponding names in the character vector new then use the following:

assign(paste0("output", ym), 
   setNames(tbl, replace(names(tbl), match(old, names(tbl)), new)))

Variation

You might consider putting your data frames in a list instead of having a bunch of loose objects in your workspace:

L <- list()
L[[paste0("output", ym)]] <- setNames(tbl, paste0(names(tbl), ym))

.GlobalEnv could also be used in place of L (omitting the L <- list() line) if you want this style but still to put the objects separately in the global environment.

dplyr

Here it is using dplyr and rlang but it does involve increased complexity:

library(dplyr)
library(rlang)

.GlobalEnv[[paste0("output", ym)]] <- tbl %>% 
                    rename(!!!setNames(names(tbl), paste0(names(tbl), ym)))
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Thanks for the answer. The "assign" part works well. I didnt put it in the question, but is it possible to change only specific columns, and not all of them? (Right now setNames changes all columns). I could rename columns outside the setNames, but maybe it's possible to do that in that one line? – phemark Feb 01 '18 at 15:47
  • Have added partial rename section. – G. Grothendieck Feb 01 '18 at 21:36
  • Great, that works perfectly, thanks! And I will try Variation and dplyr versions too. (What is !!! there?) – phemark Feb 02 '18 at 09:58
  • `rename` and other dplyr functions use non-standard evaluation which require hard-coding of column names. The rlang !! and !!! and related functions are a a way around that. See rlang package vignette and help files. – G. Grothendieck Feb 02 '18 at 12:02