微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使 R 中的嵌套 for 循环将输出写入数据帧更有效?

如何解决如何使 R 中的嵌套 for 循环将输出写入数据帧更有效?

我是 R 和 stackoverflow 菜鸟 - 如果问题不合适或结构不合理,请原谅。

我正在尝试编写一些 R 代码,将 nrow x ncol 表/数据帧转换为数据帧,每行包括:RowNumber、列号、列 j 中的值、行 i 原始表/数据框。

我有许多表格/数据框,我想用它们做类似的事情,每个表格/数据框都有不同的行数、列数...

因此,在本例中,我有一个 6 行 x 9 列的数据框,我想将其转换为 54 行的数据框:

#create example data
values <- rnorm(54,mean = 75,sd=3)
table_m <- matrix(values,ncol=9)
table <- as.data.frame(table_m)

我目前的代码如下:

##count rows and columns
nrows <- nrow(table)
ncols <- ncol(table)

#set up empty matrix for output
iterations <- nrows * ncols 
variables <-   3
output <- matrix(ncol=variables,nrow=iterations)

#set up first empty vector
my_vector_1 = c()

#run first nested for loop to create sequence of nrow * copies of column numbers
for (j in 1:ncol(table)) 
  for (i in 1:nrow(table))
  {
    my_vector_1[length(my_vector_1)+1] = colnames(table)[j]
  }

# add to first column of output
output[,1] <- my_vector_1

# set up second empty vector
my_vector_2 = c()

#run second nested for loop to create sequence of ncol * copies of row numbers
for (j in 1:ncol(table)) 
  for (i in 1:nrow(table))
  {
    my_vector_2[length(my_vector_2)+1] = rownames(table)[i]
}

# add to second column of output
output[,2] <- my_vector_2

#create third empty vector
my_vector_3 = c()

#run third nested for loop to pull values from original table/dataframe
for (j in 1:ncol(table)) 
  for (i in 1:nrow(table))
  {
    my_vector_3[length(my_vector_3)+1] = table[i,j]
  }

output[,3] <- my_vector_3

所以,这段代码可以工作,并且可以满足我的需求……但在我的菜鸟状态下,它是通过大量谷歌搜索拼凑而成的,看起来很不雅观。特别是,创建中间向量,然后将它们分配给输出数据帧列似乎有点麻烦 - 但我无法让它工作,试图将值直接放入输出数据帧的列中。

任何关于如何改进代码的想法,都非常欢迎。

非常感谢...

解决方法

这是一种很好的方式,但肯定可以以更短的方式实现。 试试:

table$id <- 1:nrow(table) # Create a row no. column
tidyr::pivot_longer(table,cols = -id)
# A tibble: 54 x 3
      id name  value
   <int> <chr> <dbl>
 1     1 V1     70.3
 2     1 V2     72.8
 3     1 V3     76.1
 4     1 V4     73.1
 5     1 V5     71.9
 6     1 V6     73.8
 7     1 V7     76.4
 8     1 V8     74.1
 9     1 V9     75.5
10     2 V1     73.8
# ... with 44 more rows

我们在这里做什么?

首先,我们将“rownames”作为列添加到数据中(因为出于某种原因,您希望将它们保留在结果数据框中。 然后,我们使用 pivot_longer() 包中的 tidyr 函数。你想对数据做的是重塑。在 R 中有很多可能性,(reshape()reshape2 库,或 pivot_longer() 中的函数 pivot_wider()tidyr

我们希望以“长”形式获得“宽”数据(您可能想看看 this Cheat Sheet,即使函数 gather()spread() 被取代pivot_longer()pivot_wider(),但它们的功能基本相同。

使用函数参数 cols = -id,我们指定除 id 之外的所有变量都应出现在新数据框的值列中。

如果您想得到一个矩阵作为结果,只需在新创建的对象上运行 as.matrix()

,

基础 R 解决方案:

data.frame(c(t(df)))

如果我们想知道这个值在原始data.frame中属于哪个V向量:

data.frame(var = paste0("V",seq_along(df)),val = c(t(df)))

还包括行索引:

transform(data.frame(var = paste0("V",val = c(t(df)),stringsAsFactors = F),idx = ave(var,var,FUN = seq.int))

更强大的解决方案(基于@r2evans 推理):

transform(data.frame(var = names(df),val = do.call("c",df),stringsAsFactors = FALSE,row.names = NULL),FUN = seq.int))

另一个使用 stack() 的更强大的解决方案:

transform(data.frame(stack(df),idx = ave(as.character(ind),ind,FUN = seq.int))

29/12/2020 编辑: 强大的解决方案镜像@Ben's 但在 Base R 中:

transform(data.frame(name = as.character(rep(names(df),nrow(df))),value = c(t(df)),stringsAsFactors = FALSE),id = ave(name,name,FUN = seq.int))

最直接的 Base R 解决方案(反映 Ben 的回答):

# Flatten the data.frame: 
stacked_df <- setNames(within(stack(df),{
  # Coerce index to character type (to enable counting):
  ind <- as.character(ind)
  # Issue a count to each ind element: 
  id <- ave(ind,FUN = seq.int)
  }
  # Rename the data.frame's vector match Ben's accepted solution:
),c("value","name","id"))

# Order the data.frame as in Ben's answer: 
ordered_df <- with(stacked_df,stacked_df[order(id),c("id","value")])

数据:

values <- rnorm(54,mean = 75,sd=3)
table_m <- matrix(values,ncol=9)
df <- as.data.frame(table_m)
,

基于@hello_friend 上面的建议答案,我能够在基础 R 中提出这个解决方案:

##Set up example data
values <- rnorm(54,ncol=9)
df <- as.data.frame(table_m)

##Create intermediate vectors
total_length <- nrow(df)*ncol(df)
statment_count <- rep(seq_along(1:nrow(df)),each =ncol(df),length.out=total_length)
site_count <- rep(seq_along(1:ncol(df)),length.out=total_length)
value = c(t(df))

##join vectors into data frame
output <- data.frame(site = site_count,statement = statment_count,value = value  
                     )

##sort output                    
output <- output[with(output,order(site,-statement)),]

这肯定比我最初使用的一系列 for 循环更简单、更直观。希望这能帮助其他正在为类似问题寻找基本 R 解决方案的人。

此外,为了完整性,为@Ben 和@Ronak Shah 提出的 tidyverse 解决方案添加了“完整”解决方案

##Set up example data
values <- rnorm(54,ncol=9)
table <- as.data.frame(table_m)

output_2 <- table %>% 
            mutate(statement = row_number()) %>%
            pivot_longer(cols = -statement)%>%
            rename(site = name)%>%
            relocate(site) %>%
            mutate(site = as.numeric(gsub("V","",site))) %>%
            arrange(site,desc(statement))  

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。