nebuloso
nebuloso

Reputation: 91

Extract data.frame column names from rows in data.frame

I have a couple of data.frames which have approximately the same structure. For a reproducible example I created two sample dataframes df1 and df2.

df1 <- structure(list(sample = c(2L, 6L), data1 = c(56L, 78L), data2 = c(59L, 
27L), data6 = c(90L, 28L), data1namet = structure(c(1L, 1L), .Label = "Sam1", class = "factor"), 
    data2namab = structure(c(1L, 1L), .Label = "Test2", class = "factor"), 
    dataame = structure(c(1L, 1L), .Label = "Ex3", class = "factor")), .Names = c("sample", 
"data1", "data2", "data3", "data1namet", "data2namab", "dataame"
), class = "data.frame", row.names = c(NA, -2L))

df1
  sample data1 data2 data3 data1namet data2namab dataame
1      2    56    59    90      Sam1     Test2       Ex3
2      6    78    27    28      Sam1     Test2       Ex3

df2 <- structure(list(sample = c(12L, 13L, 17L), data1 = c(56L, 78L, 
3L), data2 = c(59L, 27L, 2L), datest = structure(c(1L, 1L, 
1L), .Label = "Exa9", class = "factor"), dattestr = structure(c(1L, 
1L, 1L), .Label = "cz1", class = "factor")), .Names = c("sample", 
"data1", "data2", "datest", "dattestr"), class = "data.frame", row.names = c(NA, 
-3L))

df2
  sample data1 data2 datest dattestr
1     12   56   59      Exa9       cz1
2     13   78   27      Exa9       cz1
3     17    3    2      Exa9       cz1

The name of the data is saved in the columns after the data columns and I was wondering if there is a way I could restructure the data.frames (about 40 data.frames) that they contain the name of the data in their column name?

df1
  sample Sam1 Test2 Ex3
1      2   56    59  90
2      6   78    27  28

and

df2
  sample Exa9 cz1
1     12   56  59
2     13   78  27
3     17    3   2

EDIT

As I just realised I also have other columns after the data columns so that my input data looks like this

df1 <- structure(list(sample = c(2L, 6L), data1 = c(56L, 78L), data2 = c(59L, 
27L), data3 = c(90L, 28L), data1namet = structure(c(1L, 1L), .Label = "Sam1", class = "factor"), 
    data2namab = structure(c(1L, 1L), .Label = "Test2", class = "factor"), 
    dataame = structure(c(1L, 1L), .Label = "Ex3", class = "factor"), 
    ma = c("Jay", "Jay")), .Names = c("sample", "data1", "data2", 
"data3", "data1namet", "data2namab", "dataame", "ma"), row.names = c(NA, 
-2L), class = "data.frame")

df1
sample data1 data2 data3 data1namet data2namab dataame  ma
1      2    56    59    90       Sam1      Test2     Ex3 Jay
2      6    78    27    28       Sam1      Test2     Ex3 Jay

df2 <- structure(list(sample = c(12L, 13L, 17L), data1 = c(56L, 78L, 
3L), data2 = c(59L, 27L, 2L), datest = structure(c(1L, 1L, 1L
), .Label = "Exa9", class = "factor"), dattestr = structure(c(1L, 
1L, 1L), .Label = "cz1", class = "factor"), add = c(2, 2, 2)), .Names = c("sample", 
"data1", "data2", "datest", "dattestr", "add"), row.names = c(NA, 
-3L), class = "data.frame")

df2
sample data1 data2 datest dattestr add
1     12    56    59   Exa9      cz1   2
2     13    78    27   Exa9      cz1   2
3     17     3     2   Exa9      cz1   2

In this case the ma and add column are not part of the data and should be added at the end like this:

df1
  sample Sam1 Test2 Ex3 ma
1      2   56    59  90 Jay
2      6   78    27  28 Jay

and

df2
  sample Exa9 cz1 add
1     12   56  59 2
2     13   78  27 2
3     17    3   2 2

Upvotes: 0

Views: 1068

Answers (2)

RHertel
RHertel

Reputation: 23788

One could start by identifying which columns should be kept:

keep_col <- which(sapply(df2, is.numeric))

After that, some work is required to extract the new column names and to rename the corresponding columns in the data frame:

names <- df2[1,keep_col[-1] + length(keep_col)-1]
colnames(df2)[keep_col[-1]] <- as.character(unlist(names))

Finally, the dataframe can be reassembled by keeping only the desired columns:

df2 <- df2[,keep_col]
#> df2
#  sample Exa9 cz1
#1     12   56  59
#2     13   78  27
#3     17    3   2

In order to use this transformation for several different dataframes, the code can be wrapped into a function:

summarize_table <- function(x){
 keep_col <- which(sapply(x, is.numeric))
 names <- x[1,keep_col[-1] + length(keep_col)-1] 
 colnames(x)[keep_col[-1]] <- as.character(unlist(names))
 x <- x[,keep_col]
}

If the various dataframes are stored in a list, the function summarize_table() can be used with lapply() to obtain the results for each dataframe:

my_dfs <- list(df1,df2)
out <- lapply(my_dfs,summarize_table)
#> out
#[[1]]
#  sample Sam1 Test2 Ex3
#1      2   56    59  90
#2      6   78    27  28
#
#[[2]]
#  sample Exa9 cz1
#1     12   56  59
#2     13   78  27
#3     17    3   2

EDIT / ADDENDUM

The modified version below should be able to handle also the cases mentioned in the revised post:

summarize_tab2 <- function(x){
 keep_col <- which(sapply(x, is.numeric))
 first_block <- c(keep_col[1],keep_col[which(diff(keep_col)==1)])
 add_col <- FALSE
 if (2 * (length(keep_col) - 1) + 1 < ncol(x)) add_col <- TRUE
 keep_col1 <- keep_col[1:length(first_block)]
 names <- x[1,keep_col1[-1] + length(keep_col1) - 1] 
 colnames(x)[keep_col1[-1]] <- as.character(unlist(names))
 df_t <- x[,keep_col]
 if (add_col) df_t <- cbind(df_t, x[(2 * (ncol(df_t) - 1) + 2):ncol(x)])
 return(df_t)
}
my_dfs <- list(df1, df2, df3, df4)
out <- lapply(my_dfs, summarize_tab2)
#> out
#[[1]]
#  sample Sam1 Test2 Ex3  ma
#1      2   56    59  90 Jay
#2      6   78    27  28 Jay
#
#[[2]]
#  sample Exa9 cz1 add
#1     12   56  59   2
#2     13   78  27   2
#3     17    3   2   2
#
#[[3]]
#  sample Sam1 Test2 Ex3
#1      2   56    59  90
#2      6   78    27  28
#
#[[4]]
#  sample Exa9 cz1
#1     12   56  59
#2     13   78  27
#3     17    3   2

Here the dataframes df3 and df4 are, respectively, the data frames df1and df2 of the original post.

Upvotes: 1

Christoph Sommer
Christoph Sommer

Reputation: 6943

The following should work:

library(plyr)

cols.to.rename <- grep('^data(.)$', colnames(df1))
cols.of.names <- max(cols.to.rename)+seq(1,length(cols.to.rename))
the.names <- lapply(df1[1,cols.of.names], as.character)

df1.mod <- df1
colnames(df1.mod)[cols.to.rename] <- the.names
df1.mod <- df1.mod[-cols.of.names]

It renames all dataX columns to the (first) value in the columns following the last dataX column. It then drops all name columns from the data frame.

Upvotes: 1

Related Questions