2

I have a dataframe that similar to this: (in fact, 16 in a for-loop)

head(data) # A tibble: 1 x 4 AAA AAC AB AC 1 18 25 39 9 2 20 25 30 7 

I want to dynamically change all column names based on the column's original names, something like this (I've tried with str_glue, but I get an error):

### I have a for-loop: (NOT WORKING) (this is a part of the loop) assign(str_glue("df_{str_sub(data[i], 23, - 5)}"), read.delim(data[i], sep = ",", header = T) %>% mutate(ID = Participants, str_glue("New_{str_sub(data[i], 23, - 5)}_AAA") = AAA, str_glue("New_{str_sub(data[i], 23, - 5)}_AAB") = AAC, str_glue("New_{str_sub(data[i], 23, - 5)}_AB") = AB, str_glue("New_{str_sub(data[i], 23, - 5)}_AC") = AC) 

Desired output:

### Note: ### depending on the index-i, ### str_glue("New_{str_sub(data[i], 23, - 5)}_AAA") will get me either 50,100 or 150 ### desired output for i = 1 New_50_AAA New_50_AAC New_50_AB New_50_AC 1 18 25 39 9 2 20 25 30 7 

I'm sure that there's an elegant way to do that. I've seen some related posts, but none seemed to help me. Any ideas? Thanks :)

PS: If there's also a way to dynamically repeat the original's column's name without repeating it with str_, that would be perfect, it would save me 4 lines

EDIT

The whole loop looks like this:

"data" is a list of 16 .txt files, each one is called "xxxxxxxxx_xx_50.txt", "xxxxxxxxx_xx_100.txt" (so on)

for (i in 1:length(data)) { if (grepl("xxxxxxxxx_x1_.txt$", data[i])) { assign(str_glue("df_narr{str_sub(data[i], 23, - 5)}"), read.delim(data[i], sep = ",", header = T) %>% mutate(ID = Participants, str_glue("New_1{str_sub(data[i], 23, - 5)}_AAA") = AAA, str_glue("New_1{str_sub(data[i], 23, - 5)}_AAB") = AAC, str_glue("New_1{str_sub(data[i], 23, - 5)}_AB") = AB, str_glue("New_1{str_sub(data[i], 23, - 5)}_AC") = AC) %>% mutate_if(is.numeric, round, digits = 2)) } else if (grepl("xxxxxxxxx_x2_.txt$", data[i])) { assign(str_glue("df_narr{str_sub(data[i], 23, - 5)}"), read.delim(data[i], sep = ",", header = T) %>% mutate(ID = Participants, str_glue("New_2{str_sub(data[i], 23, - 5)}_AAA") = AAA, str_glue("New_2{str_sub(data[i], 23, - 5)}_AAB") = AAC, str_glue("New_2{str_sub(data[i], 23, - 5)}_AB") = AB, str_glue("New_2{str_sub(data[i], 23, - 5)}_AC") = AC) %>% mutate_if(is.numeric, round, digits = 2)) } } 
6
  • Isn't this just names(data) <- paste0("New_50_", names(data)). Creating multiple objects in the global env is not recommended. You could read all those data into a list and rename if needed Commented Oct 3, 2022 at 17:06
  • so, @akrun, that could be, but I have 16 dataframes, I'm reading them all in a for-loop, some will have 50, some 150, some 100 and some 200, that's why I cannot "set" 50, it will depend on data[i] Commented Oct 3, 2022 at 17:07
  • Are those 50, 150, 200, the number of columns? Commented Oct 3, 2022 at 17:10
  • so, no, in fact, these numbers are in the dataframes' names. For example: each data is called: "xxxxxxxxx_xx_50.txt" or "xxxxxxxxx_xx_100.txt" (so on). But all of them share the very same columns names (AAA,AAC, AC,AB), that's why I want to rename each one with the df's number, that's why I've thought of str_glue to extract only the numbers and glue them to the variable's names Commented Oct 3, 2022 at 17:13
  • @akrun , I'll add the whole loop in order to increase the post Commented Oct 3, 2022 at 17:16

1 Answer 1

2

It is better to keep the datasets in a list and rename them - get the files in the folder with list.files, then extract the digits (\\d+ - one or more digits) before the .txt from the files ('nm1'), loop over the files and the names extracted in Map, read the data and modify the column names by pasteing the 'New_', corresponding digits ('nm') and the original column names

files <- list.files(path = 'path/to/your/folder', pattern = "\\.txt$", full.names = TRUE) nm1 <- sub(".*_(\\d+)\\.txt", "\\1", basename(files)) lst1 <- Map(\(x, nm) { tmp <- read.table(x) num_cols <- sapply(tmp, is.numeric) tmp[num_cols] <- lapply(tmp[num_cols], round, digits = 2) cols_to_rename <- names(tmp) != "Participants" names(tmp)[cols_to_rename] <- paste0("New_", nm, "_", names(tmp)[cols_to_rename]) names(tmp)[!cols_to_rename] <- "ID" tmp }, files, nm1) 

Or using tidyverse

library(dplyr) library(readr) library(purrr) library(stringr) lst2 <- imap(setNames(files, nm1), ~ { nm <- .y read_table(.x) %>% rename_with(~ str_c("New_", nm, "_", .x), -Participants) %>% mutate(across(where(is.numeric), round, digits = 2)) %>% rename(ID = Participants) }) 

or in a for loop

# for storing the output from the `for` loop lst3 <- vector('list', length(files)) # loop over the sequence of files for(i in seq_along(files)) { tmp <- read.table(files[i]) cols_to_rename <- names(tmp) != "Participants" names(tmp)[cols_to_rename] <- paste0("New_", nm1[i], "_", names(tmp)[cols_to_rename]) names(tmp)[!cols_to_rename] <- "ID" num_cols <- sapply(tmp, is.numeric) tmp[num_cols] <- lapply(tmp[num_cols], round, digits = 2) lst3[[i]] <- tmp } 
Sign up to request clarification or add additional context in comments.

8 Comments

this may be a silly question, but since I'm already using a for-loop, is there a way to keep with the index-i ? (I know that is preferable to use map/lapply family to for-loops in R, but I'm still learning them)
@LarissaCury you can use the for loop, but I would advise to keep it in a list and not assign
@LarissaCury Updated with for loop
I still didn't get it all right, but I'm trying to, thank you. I've just updated the post with the rest of the conditinal part inside the for-loop. All 16 txts share those colluns names which I'm trying to rename, but they also share an "ID" name, which is ok to mantain througout all datasets, is there a way to "skip' this one?
@LarissaCury is it Participants the column common in all datasets you want to skip
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.