如何将具有多行的R数据帧重新格式化为一行

时间:2015-04-28 15:47:12

标签: r reshape

我有以下数据框需要重新格式化为单行,这样我就可以创建一个新数据框,它是许多更简单数据框的集合,新数据框中有一行代表所有数据框一个较简单的原始数据帧的数据。

以下是原始数据框格式的一个简单示例:

> myDf = data.frame(Seconds=seq(0,1,.25), s1=seq(0,8,2), s2=seq(1,9,2))
> 
> myDf
  Seconds s1 s2
1    0.00  0  1
2    0.25  2  3
3    0.50  4  5
4    0.75  6  7
5    1.00  8  9

以下是重新格式化后我想要的样子。每列表示rXsY,其中“rX”表示原始数据帧的行号,“sY”表示原始数据帧的“s1”或“s2”列。新数据框中省略了“Seconds”列,因为其信息隐含在行号中。

> myNewDf
  r1s1 r1s2 r2s1 r2s2 r3s1 r3s2 r4s1 r4s2 r5s1 r5s2
1    0    1    2    3    4    5    6    7    8    9

我怀疑这很简单,可能涉及reshape()melt()和/或cast()的某种组合,但正确的咒语正在逃避我。我可以发布我尝试过的内容,但我认为它会分散注意力可能只是一个简单的问题?如果有人希望我这样做,请在评论中提问。

理想的解决方案还会以某种方式以编程方式根据原始数据框的列名生成新的列名,因为列名并不总是相同。此外,如果不困难,我可以以某种方式同时对相似数据帧的列表执行相同的操作(所有相同的行数,所有相同的列名,但在s1和s2列中具有不同的值)?最终,我需要一个包含来自多个更简单数据帧的数据的单个数据帧,例如......

> myCombinedNewDf # data combined from 4 separate original data frames
  r1s1 r1s2 r2s1 r2s2 r3s1 r3s2 r4s1 r4s2 r5s1 r5s2
1    0    1    2    3    4    5    6    7    8    9
2   10   11   12   13   14   15   16   17   18   19
3   20   21   22   23   24   25   26   27   28   29
4   30   31   32   33   34   35   36   37   38   39

4 个答案:

答案 0 :(得分:3)

使用melt()中的reshape2,你可以这样做:

library(reshape2)

# Melt the data, omitting `Seconds`
df.melted <- melt(myDF[, -1], id.vars = NULL)

# Transpose the values into a single row
myNewDF <- t(df.melted[, 2])

# Assign new variable names
colnames(myNewDF) <- paste0("r", rownames(myDF), df.melted[, 1])

#   r1s1 r2s1 r3s1 r4s1 r5s1 r1s2 r2s2 r3s2 r4s2 r5s2
# 1    0    2    4    6    8    1    3    5    7    9

这会融合数据框,使用第一列(原始数据集中的变量名称)构造新数据集的变量名称,并使用第二列的转置(数据值)作为数据行

如果您想要一种自动化方法来组合数据集,可以更进一步:

# Another data frame
myOtherDF <- data.frame(Seconds = seq(0, 1, 0.25),
                        s1 = seq(1, 9, 2),
                        s2 = seq(0, 8, 2))

# Turn the above steps into a function
colToRow <- function(x) {
    melted <- melt(x[, -1], id.vars = NULL)
    row <- t(melted[, 2])
    colnames(row) <- paste0("r", rownames(x), melted[, 1])
    row
}

# Create a list of the data frames to process
myDFList <- list(myDF, myOtherDF)

# Apply our function to each data frame in the list and append
myNewDF <- data.frame(do.call(rbind, lapply(myDFList, colToRow)))

#   r1s1 r2s1 r3s1 r4s1 r5s1 r1s2 r2s2 r3s2 r4s2 r5s2
# 1    0    2    4    6    8    1    3    5    7    9
# 2    1    3    5    7    9    0    2    4    6    8

答案 1 :(得分:3)

可以使用c(t(therelevantdata))逐行提取相关值。

换句话说:

Values <- c(t(myDf[-1]))

如果此时名称很重要,您可以这样做:

Names <- sprintf("r%ss%s", rep(1:5, each = 2), 1:2)

您可以使用以下命令获取命名向量:

setNames(Values, Names)
# r1s1 r1s2 r2s1 r2s2 r3s1 r3s2 r4s1 r4s2 r5s1 r5s2 
#    0    1    2    3    4    5    6    7    8    9 

或者命名的单行data.frame

setNames(data.frame(t(Values)), Names)
#   r1s1 r1s2 r2s1 r2s2 r3s1 r3s2 r4s1 r4s2 r5s1 r5s2
# 1    0    1    2    3    4    5    6    7    8    9

如果你的list data.frame已经在@cyro111的答案中共享,你可以轻松地执行以下操作:

do.call(rbind, lapply(myDfList, function(x) c(t(x[-1]))))
#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
# [1,]    0    1    2    3    4    5    6    7    8     9
# [2,]   10   11   12   13   14   15   16   17   18    19

使用data.frame转换为as.data.frame,并使用names <-setNames添加名称。

广义为函数:

myFun <- function(indf, asVec = TRUE) {
  values <- c(t(indf[-1]))
  Names <- sprintf("r%ss%s", rep(1:nrow(indf), each = ncol(indf[-1])),
                   1:ncol(indf[-1]))
  out <- setNames(values, Names)
  if (isTRUE(asVec)) out
  else (as.data.frame(as.matrix(t(out))))
}

尝试一下:

myFun(myDf)        # Vector
myFun(myDf, FALSE) # data.frame

list data.frame的{​​{1}}更方便......很多选项: - )

dfList1 <- list(
  data.frame(s = 1:2, a1 = 1:2, a2 = 3:4, a3 = 5:6),
  data.frame(s = 1:2, a1 = 11:12, a2 = 31:32, a3 = 51:52)
)

lapply(dfList1, myFun)
do.call(rbind, lapply(dfList1, myFun))
t(sapply(dfList1, myFun))
as.data.frame(do.call(rbind, lapply(dfList1, myFun)))

答案 2 :(得分:2)

您可以从dcast的devel版本尝试data.table,即v1.9.5,可以使用多个value.var列。创建两列row number('rn'),第二列为分组变量('grp'),并使用dcast。安装详细信息为here

library(data.table)#v1.9.5+
dcast(setDT(myDf[-1])[, c('rn1', 'grp') := list(paste0('r', 1:.N), 1)],
                   grp~rn1, value.var=c('s1', 's2'))
#   grp r1_s1 r2_s1 r3_s1 r4_s1 r5_s1 r1_s2 r2_s2 r3_s2 r4_s2 r5_s2
#1:   1     0     2     4     6     8     1     3     5     7     9

或者我们可以使用reshape

中的base R
 reshape(transform(myDf, rn1=paste0('r', 1:nrow(myDf)), grp=1)[-1], 
         idvar='grp', timevar='rn1', direction='wide')
 #  grp s1.r1 s2.r1 s1.r2 s2.r2 s1.r3 s2.r3 s1.r4 s2.r4 s1.r5 s2.r5
 #1   1     0     1     2     3     4     5     6     7     8     9

更新

如果我们有多个数据框,我们可以将数据集放在一个列表中,然后将lapplydcast一起使用,或者使用rbindlist对列表中的数据集进行rbind,为每个数据集指定一个分组变量数据集,然后在整个数据集上应用dcast

使用@Alex A.的帖子中的'myOtherDF`

 myDFList <- list(myDf, myOtherDF)
 dcast(rbindlist(Map(cbind, myDFList, gr=seq_along(myDFList)))[,-1,
       with=FALSE][, rn1:= paste0('r', 1:.N), by=gr],
          gr~rn1, value.var=c('s1', 's2'))
 #   gr r1_s1 r2_s1 r3_s1 r4_s1 r5_s1 r1_s2 r2_s2 r3_s2 r4_s2 r5_s2
 #1:  1     0     2     4     6     8     1     3     5     7     9
 #2:  2     1     3     5     7     9     0     2     4     6     8

答案 3 :(得分:1)

基础R解决方案

#prepare data
myDf1 = data.frame(Seconds=seq(0,1,.25), s1=seq(0,8,2), s2=seq(1,9,2))
myDf2 = data.frame(Seconds=seq(0,1,.25), s1=seq(10,18,2), s2=seq(11,19,2))

myDfList=list(myDf1,myDf2)

#allocate memory
myCombinedNewDf=data.frame(matrix(NA_integer_,nrow=length(myDfList),ncol=(ncol(myDf1)-1)*nrow(myDf1)))

#reformat
for (idx in 1:length(myDfList))  myCombinedNewDf[idx,]=c(t(myDfList[[idx]][,-1]))

#set colnames
colnames(myCombinedNewDf)=paste0("r",sort(rep.int(1:nrow(myDf1),2)),colnames(myDf1)[-1])

根据请求,处理单独因子列的扩展版本:

#allocate memory
#the first column should ultimately be a factor
#I would use a character column first and later change it to type factor
#note the stringsAsFactors option!
myCombinedNewDf=data.frame(rep(NA_character_,length(myDfList)),
                       matrix(NA_integer_,
                              nrow=length(myDfList),
                              ncol=(ncol(myDf1)-1)*nrow(myDf1)),
                       stringsAsFactors=FALSE)

#reformat
for (idx in 1:length(myDfList))  {
  myCombinedNewDf[idx,-1]=c(t(myDfList[[idx]][,-1]))
  #I have just made up some criterion to get one "yes" and one "no"
  #"yes" if the sum of all values is below 100, "no" otherwise
  myCombinedNewDf[idx,1]=if (sum(myDfList[[idx]][,-1])<100) "yes" else "no"
}

#set colnames
colnames(myCombinedNewDf)=c("flag",
                        paste0("r",
                               sort(rep.int(1:nrow(myDf1),2)),
                               colnames(myDf1)[-1])
                        )
myCombinedNewDf$flag=factor(myCombinedNewDf$flag)
myCombinedNewDf