How to make a loop to insert every column in a data.frame in another by every two columns of the another data.frame?

Viewed 40
dfData <- data.frame(DistA = c(10, 8, 15, 22, 15), 
                     DistB = c(15, 35, 40, 33, 20),
                     DistC = c(20,40,50,45,30),
                     DistD = c(60,55,55,48,50))

>dfData
DistA DistB DistC DistD
1    10    15    20    60
2     8    35    40    55
3    15    40    50    55
4    22    33    45    48
5    15    20    30    50

## CREATE THE COLUMNS TO INSERT IN THE dfData
cols <- ceiling(seq_along(dfData[])/2)
new_cols <- tapply(names(dfData[]), cols, function(x) 
  sprintf('diff_%s', paste0(x, collapse = '')))
new_columns<- sapply(split.default(dfData[], cols), function(x) 
 paste(  round((x[[2]] / x[[1]] -1)*100,2)   ,"%",sep=""))
new_columns=as.data.frame(new_columns)


Expected output:

    DistA DistB DiffB-A  DistC DistD  Diff D-C
 1    10    15   50%    20    60    200%
 2     8    35  337.5%  40    55    37.5%
 3    15    40 166.67%  50    55     10%
 4    22    33   50%    45    48    6.67%
 5    15    20  33.33%  30    50   66.67%

Attempt


sapply(seq(3,length(dfData),by=3),function(x)
    dfData[,x]<-new_columns)

I'm trying to make a loop to insert the new_column's column within DfData by every two columns of dfData

2 Answers

The function select from dplyr offers a solution:

library(dplyr)

dfData <- data.frame(DistA = c(10, 8, 15, 22, 15), 
                     DistB = c(15, 35, 40, 33, 20),
                     DistC = c(20,40,50,45,30),
                     DistD = c(60,55,55,48,50))

dfData <- dfData %>%
  mutate(`DistB-A` = paste(round(100*(DistB - DistA)/DistA, 2), "%"),
         `DistD-C` = paste(round(100*(DistD - DistC)/DistC, 2), "%")) %>%
  select(DistA, DistB, `DistB-A`, DistC, DistD, `DistD-C`)

> dfData
  DistA DistB  DistB-A DistC DistD DistD-C
1    10    15     50 %    20    60   200 %
2     8    35  337.5 %    40    55  37.5 %
3    15    40 166.67 %    50    55    10 %
4    22    33     50 %    45    48  6.67 %
5    15    20  33.33 %    30    50 66.67 %

Using select helper functions, see here, you could make the case generic.

I found the solution was just to find a way to create the sequence of numbers to reorder the columns based on the number of columns:

dfData <- data.frame(DistA = c(10, 8, 15, 22, 15), 
                     DistB = c(15, 35, 40, 33, 20),
                     DistC = c(20,40,50,45,30),
                     DistD = c(60,55,55,48,50))


## CREATE THE COLUMNS TO INSERT IN THE dfData
cols <- ceiling(seq_along(dfData[])/2)
new_cols <- tapply(names(dfData[]), cols, function(x) 
  sprintf('diff_%s', paste0(x, collapse = '')))
new_columns<- sapply(split.default(dfData[], cols), function(x) 
  paste(  round((x[[2]] / x[[1]] -1)*100,2)   ,"%",sep=""))
new_columns=as.data.frame(new_columns)

 
 # create the sequence of numbers based on the number of columns 1,2,5,3,4,6                    
order=unlist(Map(c, split(1:length(dfData), as.integer(gl(length(dfData), 2, 
                                      length(dfData)))), length(dfData)+seq(1:length(new_columns))), use.names = FALSE)

# join the two dataframes
dfData2=cbind(dfData,new_columns)

#reorder the columns
dfData2=dfData2[,order]
dfData2
Related